diff --git a/Makefile b/Makefile
index e4b674e..5f51c2e 100644
--- a/Makefile
+++ b/Makefile
@@ -3,10 +3,20 @@ phpcheck:
phpcheck-coverage-html:
@phpcheck --coverage-html build/coverage
+ @xdg-open build/coverage/index.html
-phpcheck-coverage-text:
+phpcheck-coverage-console:
@phpcheck --coverage-text
+phpcheck-coverage-text:
+ @phpcheck --coverage-text build/coverage.txt
+
+phpcheck-log-junit:
+ @phpcheck --log-junit build/phpcheck.xml
+
+phpcheck-log-text:
+ @phpcheck --log-text build/phpcheck.txt
+
phpcheck-no-defects:
@phpcheck -d
diff --git a/README.md b/README.md
index 491e344..2fd6643 100644
--- a/README.md
+++ b/README.md
@@ -109,19 +109,22 @@ The `phpcheck` program accept a number of arguments and options:
path File or folder with checks [default: "checks"]
Options:
- --bootstrap[=BOOTSTRAP] A PHP script that is included before the checks run
- -f, --filter[=FILTER] Filter the checks that will be run
- -i, --iterations=ITERATIONS How many times each check will be run [default: 100]
- -j, --log-junit[=LOG-JUNIT] Log check execution in JUnit XML format to file
- -d, --no-defects[=NO-DEFECTS] Ignore previous defects [default: false]
- -h, --help Display this help message
- -q, --quiet Do not output any message
- -s, --seed[=SEED] Seed the random number generator to get repeatable runs
- -V, --version Display this application version
- --ansi Force ANSI output
- --no-ansi Disable ANSI output
- -n, --no-interaction Do not ask any interactive question
- -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+ --bootstrap[=BOOTSTRAP] A PHP script that is included before the checks run
+ --coverage-html[=COVERAGE-HTML] Generate HTML code coverage report [default: false]
+ --coverage-text[=COVERAGE-TEXT] Generate text code coverage report [default: false]
+ -f, --filter[=FILTER] Filter the checks that will be run
+ -i, --iterations=ITERATIONS How many times each check will be run [default: 100]
+ -j, --log-junit[=LOG-JUNIT] Log check execution to JUnit XML file [default: false]
+ -t, --log-text[=LOG-TEXT] Log check execution to text file [default: false]
+ -d, --no-defects[=NO-DEFECTS] Ignore previous defects [default: false]
+ -h, --help Display this help message
+ -q, --quiet Do not output any message
+ -s, --seed[=SEED] Seed the random number generator to get repeatable runs
+ -V, --version Display this application version
+ --ansi Force ANSI output
+ --no-ansi Disable ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
The `--bootstrap` parameter can be included in a _phpcheck.xml_ or _phpcheck.xml.dist_ file. See [ours](phpcheck.xml.dist) for an example.
diff --git a/bin/phpcheck b/bin/phpcheck
index b757cf0..d28fd0f 100755
--- a/bin/phpcheck
+++ b/bin/phpcheck
@@ -22,8 +22,13 @@ foreach ($autoloaders as $file) {
unset($file);
use Datashaman\PHPCheck\CheckCommand;
+use SebastianBergmann\CodeCoverage\CodeCoverage;
use Symfony\Component\Console\Application;
+$coverage = new CodeCoverage();
+$coverage->filter()->addDirectoryToWhitelist('./src');
+$coverage->start('phpcheck');
+
$application = new Application('phpcheck', CheckCommand::VERSION);
$command = new CheckCommand();
$application->add($command);
diff --git a/phpcheck.xml.dist b/phpcheck.xml.dist
index d88d91d..0a4033e 100644
--- a/phpcheck.xml.dist
+++ b/phpcheck.xml.dist
@@ -1,11 +1,15 @@
+
+
diff --git a/src/CheckCommand.php b/src/CheckCommand.php
index df42f5b..f711478 100644
--- a/src/CheckCommand.php
+++ b/src/CheckCommand.php
@@ -28,11 +28,12 @@ protected function configure(): void
$this
->setDescription('Run checks.')
->addOption('bootstrap', null, InputOption::VALUE_OPTIONAL, 'A PHP script that is included before the checks run')
- ->addOption('coverage-html', null, InputOption::VALUE_OPTIONAL, 'Generate code coverage report in HTML', false)
- ->addOption('coverage-text', null, InputOption::VALUE_OPTIONAL, 'Generate code coverage report in text', false)
+ ->addOption('coverage-html', null, InputOption::VALUE_OPTIONAL, 'Generate HTML code coverage report', false)
+ ->addOption('coverage-text', null, InputOption::VALUE_OPTIONAL, 'Generate text code coverage report', false)
->addOption('filter', 'f', InputOption::VALUE_OPTIONAL, 'Filter the checks that will be run')
->addOption('iterations', 'i', InputOption::VALUE_REQUIRED, 'How many times each check will be run', Runner::MAX_ITERATIONS)
- ->addOption('log-junit', 'j', InputOption::VALUE_OPTIONAL, 'Log check execution in JUnit XML format to file')
+ ->addOption('log-junit', 'j', InputOption::VALUE_OPTIONAL, 'Log check execution to JUnit XML file', false)
+ ->addOption('log-text', 't', InputOption::VALUE_OPTIONAL, 'Log check execution to text file', false)
->addOption('no-defects', 'd', InputOption::VALUE_OPTIONAL, 'Ignore previous defects', false)
->addOption('seed', 's', InputOption::VALUE_OPTIONAL, 'Seed the random number generator to get repeatable runs')
->addArgument('path', InputArgument::OPTIONAL, 'File or folder with checks', 'checks');
diff --git a/src/Coverage/Coverage.php b/src/Coverage/Coverage.php
new file mode 100644
index 0000000..c7add8e
--- /dev/null
+++ b/src/Coverage/Coverage.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace Datashaman\PHPCheck\Coverage;
+
+use Datashaman\PHPCheck\Runner;
+
+abstract class Coverage
+{
+ protected $input;
+
+ public function __construct(Runner $runner)
+ {
+ $this->input = $runner->getInput();
+ }
+
+ public function __destruct()
+ {
+ global $coverage;
+
+ $coverage->stop();
+ }
+}
diff --git a/src/Coverage/HtmlCoverage.php b/src/Coverage/HtmlCoverage.php
new file mode 100644
index 0000000..370e03a
--- /dev/null
+++ b/src/Coverage/HtmlCoverage.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace Datashaman\PHPCheck\Coverage;
+
+use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlFacade;
+
+class HtmlCoverage extends Coverage
+{
+ public function __destruct()
+ {
+ global $coverage;
+
+ parent::__destruct();
+
+ $writer = new HtmlFacade();
+ $writer->process($coverage, $this->input->getOption('coverage-html'));
+ }
+}
diff --git a/src/Coverage/TextCoverage.php b/src/Coverage/TextCoverage.php
new file mode 100644
index 0000000..dc9d5b6
--- /dev/null
+++ b/src/Coverage/TextCoverage.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace Datashaman\PHPCheck\Coverage;
+
+use SebastianBergmann\CodeCoverage\Report\Text;
+
+class TextCoverage extends Coverage
+{
+ public function __destruct()
+ {
+ global $coverage;
+
+ parent::__destruct();
+
+ $writer = new Text();
+
+ if ($this->input->getOption('coverage-text')) {
+ $output = $writer->process($coverage, false);
+ \file_put_contents($this->input->getOption('coverage-text'), $output);
+
+ return;
+ }
+
+ $color = true;
+
+ if ($this->input->getOption('no-ansi') !== false) {
+ $color = false;
+ }
+
+ print $writer->process($coverage, $color);
+ }
+}
diff --git a/src/Reporters/HtmlCoverageReporter.php b/src/Reporters/HtmlCoverageReporter.php
deleted file mode 100644
index 60e42a8..0000000
--- a/src/Reporters/HtmlCoverageReporter.php
+++ /dev/null
@@ -1,57 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace Datashaman\PHPCheck\Reporters;
-
-use Datashaman\PHPCheck\CheckEvents;
-use Datashaman\PHPCheck\Events;
-use SebastianBergmann\CodeCoverage\CodeCoverage;
-use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlFacade;
-
-class HtmlCoverageReporter extends Reporter
-{
- protected $coverage;
-
- public static function getSubscribedEvents(): array
- {
- return [
- CheckEvents::END => 'onEnd',
- CheckEvents::END_ALL => 'onEndAll',
- CheckEvents::START => 'onStart',
- CheckEvents::START_ALL => 'onStartAll',
- ];
- }
-
- public function onEnd(Events\EndEvent $event): void
- {
- $this->coverage->stop();
- }
-
- public function onEndAll(Events\EndAllEvent $event): void
- {
- $writer = new HtmlFacade();
- $writer->process($this->coverage, $this->input->getOption('coverage-html'));
- }
-
- public function onStart(Events\StartEvent $event): void
- {
- $this->coverage->start($event->method->getName());
- }
-
- public function onStartAll(Events\StartAllEvent $event): void
- {
- if (!$this->input->getOption('coverage-html')) {
- $this->output->writeln('You must specify a folder with --coverage-html');
- exit(1);
- }
-
- $this->coverage = new CodeCoverage();
- $this->coverage->filter()->addDirectoryToWhitelist('./src');
- }
-}
diff --git a/src/Reporters/TextCoverageReporter.php b/src/Reporters/TextCoverageReporter.php
deleted file mode 100644
index d2ed882..0000000
--- a/src/Reporters/TextCoverageReporter.php
+++ /dev/null
@@ -1,61 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace Datashaman\PHPCheck\Reporters;
-
-use Datashaman\PHPCheck\CheckEvents;
-use Datashaman\PHPCheck\Events;
-use SebastianBergmann\CodeCoverage\CodeCoverage;
-use SebastianBergmann\CodeCoverage\Report\Text;
-
-class TextCoverageReporter extends Reporter
-{
- protected $coverage;
-
- public static function getSubscribedEvents(): array
- {
- return [
- CheckEvents::END => 'onEnd',
- CheckEvents::END_ALL => 'onEndAll',
- CheckEvents::START => 'onStart',
- CheckEvents::START_ALL => 'onStartAll',
- ];
- }
-
- public function onEnd(Events\EndEvent $event): void
- {
- $this->coverage->stop();
- }
-
- public function onEndAll(Events\EndAllEvent $event): void
- {
- $writer = new Text();
-
-
- if ($this->input->getOption('coverage-text')) {
- $output = $writer->process($this->coverage, false);
- file_put_contents($this->input->getOption('coverage-text'), $output);
-
- return;
- }
-
- print $writer->process($this->coverage, true);
- }
-
- public function onStart(Events\StartEvent $event): void
- {
- $this->coverage->start($event->method->getName());
- }
-
- public function onStartAll(Events\StartAllEvent $event): void
- {
- $this->coverage = new CodeCoverage();
- $this->coverage->filter()->addDirectoryToWhitelist('./src');
- }
-}
diff --git a/src/Runner.php b/src/Runner.php
index c996e4a..6bd6b1e 100644
--- a/src/Runner.php
+++ b/src/Runner.php
@@ -141,22 +141,39 @@ public function execute(InputInterface $input, OutputInterface $output): void
$this->input = $input;
$this->output = $output;
+ if ($input->getOption('coverage-html') !== false) {
+ if (is_null($input->getOption('coverage-html'))) {
+ $output->writeln('You must specify a directory for coverage-html');
+ exit(1);
+ }
+ $coverage = new Coverage\HtmlCoverage($this);
+ }
+
+ if ($input->getOption('coverage-text') !== false) {
+ $coverage = new Coverage\TextCoverage($this);
+ }
+
$config = $this->getConfig();
$this->maxIterations = (int) $input->getOption('iterations');
- $this->dispatcher->addSubscriber(new Reporters\ConsoleReporter($this));
+ $this->dispatcher->addSubscriber(new Subscribers\ConsoleReporter($this));
- if ($input->getOption('coverage-html') !== false) {
- $this->dispatcher->addSubscriber(new Reporters\HtmlCoverageReporter($this));
- }
-
- if ($input->getOption('coverage-text') !== false) {
- $this->dispatcher->addSubscriber(new Reporters\TextCoverageReporter($this));
+ if ($input->getOption('log-junit') !== false) {
+ if (is_null($input->getOption('log-junit'))) {
+ $output->writeln('You must specify a filename for log-junit');
+ exit(1);
+ }
+ $reporter = new Subscribers\JUnitReporter($this);
+ $this->dispatcher->addSubscriber($reporter);
}
- if ($input->getOption('log-junit')) {
- $reporter = new Reporters\JUnitReporter($this);
+ if ($input->getOption('log-text') !== false) {
+ if (is_null($input->getOption('log-text'))) {
+ $output->writeln('You must specify a filename for log-text');
+ exit(1);
+ }
+ $reporter = new Subscribers\TextReporter($this);
$this->dispatcher->addSubscriber($reporter);
}
@@ -168,6 +185,13 @@ public function execute(InputInterface $input, OutputInterface $output): void
include_once $bootstrap;
}
+ if (isset($config->subscribers)) {
+ foreach ($config->subscribers->subscriber as $subscriber) {
+ $class = (string) $subscriber['class'];
+ $this->dispatcher->addSubscriber(new $class($this));
+ }
+ }
+
$event = new Events\StartAllEvent();
$this->dispatcher->dispatch(CheckEvents::START_ALL, $event);
@@ -249,16 +273,16 @@ function ($class) {
} catch (ExecutionFailure $failure) {
$event = new Events\FailureEvent(
$method,
- $failure->args,
- $failure->cause
+ $failure->getArgs(),
+ $failure->getCause()
);
$this->dispatcher->dispatch(CheckEvents::FAILURE, $event);
$status = 'FAILURE';
} catch (ExecutionError $error) {
$event = new Events\ErrorEvent(
$method,
- $error->args,
- $error->cause
+ $error->getArgs(),
+ $error->getCause()
);
$this->dispatcher->dispatch(CheckEvents::ERROR, $event);
$status = 'ERROR';
diff --git a/src/Reporters/ConsoleReporter.php b/src/Subscribers/ConsoleReporter.php
similarity index 99%
rename from src/Reporters/ConsoleReporter.php
rename to src/Subscribers/ConsoleReporter.php
index d3df629..c6b14be 100644
--- a/src/Reporters/ConsoleReporter.php
+++ b/src/Subscribers/ConsoleReporter.php
@@ -9,7 +9,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck\Reporters;
+namespace Datashaman\PHPCheck\Subscribers;
use Datashaman\PHPCheck\CheckCommand;
use Datashaman\PHPCheck\CheckEvents;
diff --git a/src/Reporters/JUnitReporter.php b/src/Subscribers/JUnitReporter.php
similarity index 95%
rename from src/Reporters/JUnitReporter.php
rename to src/Subscribers/JUnitReporter.php
index d4f6535..6cbb522 100644
--- a/src/Reporters/JUnitReporter.php
+++ b/src/Subscribers/JUnitReporter.php
@@ -9,7 +9,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-namespace Datashaman\PHPCheck\Reporters;
+namespace Datashaman\PHPCheck\Subscribers;
use Datashaman\PHPCheck\CheckEvents;
use Datashaman\PHPCheck\Events;
@@ -67,7 +67,7 @@ public function onFailure(Events\FailureEvent $event): void
);
}
- public function onEndAll(Events\EndAllEvent $event): void
+ public function onEndAll(): void
{
$this->testsuite->asXML($this->input->getOption('log-junit'));
}
diff --git a/src/Subscribers/Reporter.php b/src/Subscribers/Reporter.php
new file mode 100644
index 0000000..7c39673
--- /dev/null
+++ b/src/Subscribers/Reporter.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace Datashaman\PHPCheck\Subscribers;
+
+abstract class Reporter extends Subscriber
+{
+}
diff --git a/src/Reporters/Reporter.php b/src/Subscribers/Subscriber.php
similarity index 89%
rename from src/Reporters/Reporter.php
rename to src/Subscribers/Subscriber.php
index c7c28b1..ae4db60 100644
--- a/src/Reporters/Reporter.php
+++ b/src/Subscribers/Subscriber.php
@@ -1,6 +1,4 @@
-
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace Datashaman\PHPCheck\Subscribers;
+
+use Datashaman\PHPCheck\CheckEvents;
+use Datashaman\PHPCheck\Events;
+use ReflectionClass;
+
+class TextReporter extends Reporter
+{
+ protected $file;
+
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ CheckEvents::START_ALL => 'onStartAll',
+ CheckEvents::START => 'onStart',
+ CheckEvents::SUCCESS => 'onSuccess',
+ CheckEvents::ERROR => 'onError',
+ CheckEvents::FAILURE => 'onFailure',
+ CheckEvents::END_ALL => 'onEndAll',
+ ];
+ }
+
+ public function onStartAll(Events\StartAllEvent $event): void
+ {
+ $this->file = fopen($this->input->getOption('log-text'), 'a');
+ $this->report('onStartAll', $event);
+ }
+
+ public function onEndAll(Events\EndAllEvent $event): void
+ {
+ $this->report('onEndAll', $event);
+
+ if (is_resource($this->file)) {
+ fclose($this->file);
+ }
+ }
+
+ public function __call(string $name, array $args)
+ {
+ $this->report($name, ...$args);
+ }
+
+ protected function report(string $name, Events\Event $event)
+ {
+ $shortName = preg_replace(
+ '/Event$/',
+ '',
+ (new ReflectionClass($event))->getShortName()
+ );
+ $message = sprintf(
+ '%s [%-10s]',
+ strftime('%F %T', (int) $event->time),
+ strtoupper($shortName)
+ );
+
+ if (
+ in_array(
+ $name,
+ [
+ 'onError',
+ 'onFailure',
+ 'onStart',
+ 'onSuccess',
+ ]
+ )
+ ) {
+ $message .= ' ' . $this->getMethodSignature($event->method);
+ }
+
+ if (
+ $event instanceof Events\ResultEvent
+ ) {
+ if (!is_null($event->args)) {
+ $args = preg_replace(
+ [
+ '/^\[/',
+ '/\]$/',
+ ],
+ '',
+ json_encode($event->args)
+ );
+
+ $message .= '(' . $args . ')';
+ }
+
+ if ($event->cause) {
+ $message .= ' caused ' . get_class($event->cause) . '("' . $event->cause->getMessage() . '")';
+ }
+ }
+
+ $message .= "\n";
+
+ fwrite($this->file, $message);
+ }
+}