diff --git a/composer.json b/composer.json index b18f188..41db2e8 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "require": { "php": "^8.3", "composer-plugin-api": "^2.0", + "brianium/paratest": "^7.17", "composer/composer": "^2.9", "dg/bypass-finals": "^1.9", "ergebnis/composer-normalize": "^2.50", diff --git a/src/Command/TestsCommand.php b/src/Command/TestsCommand.php index fc1706c..36e5711 100644 --- a/src/Command/TestsCommand.php +++ b/src/Command/TestsCommand.php @@ -103,6 +103,12 @@ protected function configure(): void mode: InputOption::VALUE_OPTIONAL, description: 'Filter which tests to run based on a pattern.', ) + ->addOption( + name: 'parallel', + shortcut: 'p', + mode: InputOption::VALUE_OPTIONAL, + description: 'Run tests in parallel using ParaTest. Optional number of workers.', + ) ->addOption( name: 'min-coverage', mode: InputOption::VALUE_REQUIRED, @@ -123,8 +129,6 @@ protected function configure(): void */ protected function execute(InputInterface $input, OutputInterface $output): int { - $output->writeln('Running PHPUnit tests...'); - try { $minimumCoverage = $this->resolveMinimumCoverage($input); } catch (InvalidArgumentException $invalidArgumentException) { @@ -134,13 +138,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $arguments = [ - $this->getAbsolutePath('vendor/bin/phpunit'), '--configuration=' . parent::getConfigFile(self::CONFIG), '--bootstrap=' . $this->resolvePath($input, 'bootstrap'), - '--display-deprecations', - '--display-phpunit-deprecations', - '--display-incomplete', - '--display-skipped', ]; if (! $input->getOption('no-cache')) { @@ -149,11 +148,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int $coverageReportPath = $this->configureCoverageArguments($input, $arguments, null !== $minimumCoverage); - if ($input->getOption('filter')) { + if (null !== $input->getOption('filter')) { $arguments[] = '--filter=' . $input->getOption('filter'); } - $command = new Process([...$arguments, $input->getArgument('path')]); + $parallel = null !== $input->getOption('parallel'); + + $command = $this->getAbsolutePath(\sprintf('vendor/bin/%s', $parallel ? 'paratest' : 'phpunit')); + + if (! $parallel) { + $arguments[] = '--display-deprecations'; + $arguments[] = '--display-phpunit-deprecations'; + $arguments[] = '--display-incomplete'; + $arguments[] = '--display-skipped'; + } else { + $arguments[] = '--processes=' . ($input->getOption('parallel') ?: 'auto'); + } + + $output->writeln('Running tests using ' . ($parallel ? 'ParaTest' : 'PHPUnit') . '...'); + + $command = new Process([$command, ...$arguments, $input->getArgument('path')]); $result = parent::runProcess($command, $output); diff --git a/tests/GitAttributes/CandidateProviderTest.php b/tests/GitAttributes/CandidateProviderTest.php index 84f840a..ec63f61 100644 --- a/tests/GitAttributes/CandidateProviderTest.php +++ b/tests/GitAttributes/CandidateProviderTest.php @@ -26,7 +26,7 @@ #[CoversClass(CandidateProvider::class)] final class CandidateProviderTest extends TestCase { - private readonly CandidateProvider $provider; + private CandidateProvider $provider; /** * @return void diff --git a/tests/GitAttributes/ExistenceCheckerTest.php b/tests/GitAttributes/ExistenceCheckerTest.php index f07bb68..eeb1621 100644 --- a/tests/GitAttributes/ExistenceCheckerTest.php +++ b/tests/GitAttributes/ExistenceCheckerTest.php @@ -34,9 +34,9 @@ final class ExistenceCheckerTest extends TestCase /** * @property ObjectProphecy $filesystem */ - private readonly ObjectProphecy $filesystem; + private ObjectProphecy $filesystem; - private readonly ExistenceChecker $checker; + private ExistenceChecker $checker; /** * @return void diff --git a/tests/GitIgnore/ReaderTest.php b/tests/GitIgnore/ReaderTest.php index f61d669..8ddd42a 100644 --- a/tests/GitIgnore/ReaderTest.php +++ b/tests/GitIgnore/ReaderTest.php @@ -33,7 +33,7 @@ #[UsesClass(GitIgnore::class)] final class ReaderTest extends TestCase { - private readonly Reader $reader; + private Reader $reader; /** * @return void