diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index dae36adc..c5d8751b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,7 +10,8 @@ The format of this change log follows the advice given at [Keep a CHANGELOG](htt ## [Unreleased] ### Added -- Added support for the `--tags` and `--name` options into the `behat` command. +- Added support for the `--tags` and `--name` options to the `behat` command. +- Added support for the `--testsuite` and `--filter` options to the `phpunit` command. ### Changed - ACTION SUGGESTED: If you are using GitHub Actions, it's recomended to use `!cancelled()` instead of `always()` for moodle-plugin-ci tests. Adding a final step that always returns failure when the workflow is cancelled will ensure that cancelled workflows are not marked as successful. For a working example, please reference the updated `gha.dist.yml` file. diff --git a/docs/CLI.md b/docs/CLI.md index 1507eab6..f857f0bf 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -1975,7 +1975,7 @@ Run PHPUnit on a plugin ### Usage -* `phpunit [-m|--moodle MOODLE] [--coverage-text] [--coverage-clover] [--coverage-pcov] [--coverage-xdebug] [--coverage-phpdbg] [--fail-on-incomplete] [--fail-on-risky] [--fail-on-skipped] [--fail-on-warning] [--testdox] [--] ` +* `phpunit [-m|--moodle MOODLE] [--testsuite TESTSUITE] [--filter FILTER] [--testdox] [--coverage-text] [--coverage-clover] [--coverage-pcov] [--coverage-xdebug] [--coverage-phpdbg] [--fail-on-incomplete] [--fail-on-risky] [--fail-on-skipped] [--fail-on-warning] [--] ` Run PHPUnit on a plugin @@ -2001,6 +2001,36 @@ Path to Moodle * Is negatable: no * Default: `'.'` +#### `--testsuite` + +PHPUnit testsuite option to use + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `''` + +#### `--filter` + +PHPUnit filter option to use + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `''` + +#### `--testdox` + +Enable testdox formatter + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--coverage-text` Generate and print code coverage report in text format @@ -2091,16 +2121,6 @@ Treat tests with warnings as failures * Is negatable: no * Default: `false` -#### `--testdox` - -Enable testdox formatter - -* Accept value: no -* Is value required: no -* Is multiple: no -* Is negatable: no -* Default: `false` - #### `--help|-h` Display help for the given command. When no command is given display help for the list command diff --git a/src/Command/PHPUnitCommand.php b/src/Command/PHPUnitCommand.php index 602bb91b..bcecf1f8 100644 --- a/src/Command/PHPUnitCommand.php +++ b/src/Command/PHPUnitCommand.php @@ -29,6 +29,9 @@ protected function configure(): void $this->setName('phpunit') ->setDescription('Run PHPUnit on a plugin') + ->addOption('testsuite', null, InputOption::VALUE_REQUIRED, 'PHPUnit testsuite option to use', '') + ->addOption('filter', null, InputOption::VALUE_REQUIRED, 'PHPUnit filter option to use', '') + ->addOption('testdox', null, InputOption::VALUE_NONE, 'Enable testdox formatter') ->addOption('coverage-text', null, InputOption::VALUE_NONE, 'Generate and print code coverage report in text format') ->addOption('coverage-clover', null, InputOption::VALUE_NONE, 'Generate code coverage report in Clover XML format') ->addOption('coverage-pcov', null, InputOption::VALUE_NONE, 'Use the pcov extension to calculate code coverage') @@ -37,8 +40,7 @@ protected function configure(): void ->addOption('fail-on-incomplete', null, InputOption::VALUE_NONE, 'Treat incomplete tests as failures') ->addOption('fail-on-risky', null, InputOption::VALUE_NONE, 'Treat risky tests as failures') ->addOption('fail-on-skipped', null, InputOption::VALUE_NONE, 'Treat skipped tests as failures') - ->addOption('fail-on-warning', null, InputOption::VALUE_NONE, 'Treat tests with warnings as failures') - ->addOption('testdox', null, InputOption::VALUE_NONE, 'Enable testdox formatter'); + ->addOption('fail-on-warning', null, InputOption::VALUE_NONE, 'Treat tests with warnings as failures'); } protected function initialize(InputInterface $input, OutputInterface $output): void @@ -80,6 +82,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function resolveOptions(InputInterface $input): array { $options = []; + + if ($input->getOption('testsuite')) { + $options[] = [ + '--testsuite', + $input->getOption('testsuite'), + ]; + } + + if ($input->getOption('filter')) { + $options[] = [ + '--filter', + $input->getOption('filter'), + ]; + } + if ($this->supportsCoverage() && $input->getOption('coverage-text')) { $options[] = [ '--coverage-text', @@ -109,10 +126,15 @@ private function resolveOptions(InputInterface $input): array $this->plugin->directory, ]; } else { - $options[] = [ - '--testsuite', - $this->plugin->getComponent(), - ]; + // Only can set testsuite if it has not been passed via command line option. + if (!$input->getOption('testsuite')) { + // TODO: Check if this is correct, not sure how the component name can work here. + // TODO: If anything, it should be the component + "_testsuite" or something like that. + $options[] = [ + '--testsuite', + $this->plugin->getComponent(), + ]; + } } return array_merge(...$options); // Merge all options into a single array. diff --git a/tests/Command/PHPUnitCommandTest.php b/tests/Command/PHPUnitCommandTest.php index 9e550e22..dff1cb87 100644 --- a/tests/Command/PHPUnitCommandTest.php +++ b/tests/Command/PHPUnitCommandTest.php @@ -21,7 +21,7 @@ class PHPUnitCommandTest extends MoodleTestCase { - protected function executeCommand($pluginDir = null, $moodleDir = null): CommandTester + protected function executeCommand($pluginDir = null, $moodleDir = null, array $cmdOptions = []): CommandTester { if ($pluginDir === null) { $pluginDir = $this->pluginDir; @@ -37,10 +37,15 @@ protected function executeCommand($pluginDir = null, $moodleDir = null): Command $application->add($command); $commandTester = new CommandTester($application->find('phpunit')); - $commandTester->execute([ - 'plugin' => $pluginDir, - '--moodle' => $moodleDir, - ]); + $cmdOptions = array_merge( + [ + 'plugin' => $pluginDir, + '--moodle' => $moodleDir, + ], + $cmdOptions + ); + $commandTester->execute($cmdOptions); + $this->lastCmd = $command->execute->lastCmd; // We need this for assertions against the command run. return $commandTester; } @@ -49,6 +54,40 @@ public function testExecute() { $commandTester = $this->executeCommand(); $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/vendor.bin.phpunit/', $this->lastCmd); + $this->assertMatchesRegularExpression('/--testsuite.*local_ci/', $this->lastCmd); + $this->assertDoesNotMatchRegularExpression('/--configuration.*local\/ci/', $this->lastCmd); + } + + public function testExecuteWithPHPUnitXMLFile() + { + $fs = new Filesystem(); + $fs->touch($this->pluginDir . '/phpunit.xml'); + $commandTester = $this->executeCommand(); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/vendor.bin.phpunit/', $this->lastCmd); + $this->assertMatchesRegularExpression('/--configuration.*local\/ci/', $this->lastCmd); + $this->assertDoesNotMatchRegularExpression('/--testsuite.*local_ci/', $this->lastCmd); + } + + public function testExecureWithTestSuite() + { + $commandTester = $this->executeCommand(null, null, ['--testsuite' => 'some_testsuite']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/vendor.bin.phpunit/', $this->lastCmd); + $this->assertMatchesRegularExpression('/--testsuite.*some_testsuite/', $this->lastCmd); + $this->assertDoesNotMatchRegularExpression('/--configuration.*local\/ci/', $this->lastCmd); + $this->assertDoesNotMatchRegularExpression('/--testsuite.*local_ci/', $this->lastCmd); + } + + public function testExecuteWithFilter() + { + $commandTester = $this->executeCommand(null, null, ['--filter' => 'some_filter']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/vendor.bin.phpunit/', $this->lastCmd); + $this->assertMatchesRegularExpression('/--filter.*some_filter/', $this->lastCmd); + $this->assertMatchesRegularExpression('/--testsuite.*local_ci/', $this->lastCmd); + $this->assertDoesNotMatchRegularExpression('/--configuration.*local\/ci/', $this->lastCmd); } public function testExecuteNoTests() @@ -70,6 +109,7 @@ public function testExecuteNoPlugin() public function testExecuteNoMoodle() { $this->expectException(\InvalidArgumentException::class); + // TODO: Check what's happening here. moodleDir should be the 2nd parameter, but then the test fails. $this->executeCommand($this->moodleDir . '/no/moodle'); } }