diff --git a/src/Plugins/Shard.php b/src/Plugins/Shard.php index f48260bb5..80e6740d2 100644 --- a/src/Plugins/Shard.php +++ b/src/Plugins/Shard.php @@ -85,7 +85,7 @@ private function allTests(array $arguments): array { $output = (new Process([ 'php', - ...$this->removeParallelArguments($arguments), + ...$this->removeParallelizationArguments($arguments), '--list-tests', ]))->mustRun()->getOutput(); @@ -94,6 +94,18 @@ private function allTests(array $arguments): array return array_values(array_unique($matches[1])); } + /** + * Removes both parallel and processes arguments from the arguments array. + * This is useful when running commands that don't support parallel execution. + * + * @param array $arguments + * @return array + */ + private function removeParallelizationArguments(array $arguments): array + { + return $this->removeProcessesArguments($this->removeParallelArguments($arguments)); + } + /** * @param array $arguments * @return array @@ -103,6 +115,34 @@ private function removeParallelArguments(array $arguments): array return array_filter($arguments, fn (string $argument): bool => ! in_array($argument, ['--parallel', '-p'], strict: true)); } + /** + * @param array $arguments + * @return array + */ + private function removeProcessesArguments(array $arguments): array + { + return array_values(array_filter($arguments, function (string $argument) { + if (str_starts_with($argument, '--processes') && str_contains($argument, '=')) { + return false; + } + + static $skipNext = false; + if ($skipNext) { + $skipNext = false; + + return false; + } + + if ($argument === '--processes') { + $skipNext = true; + + return false; + } + + return true; + })); + } + /** * Builds the filter argument for the given tests to run. */ diff --git a/tests/Unit/Plugins/Shard.php b/tests/Unit/Plugins/Shard.php new file mode 100644 index 000000000..911658f34 --- /dev/null +++ b/tests/Unit/Plugins/Shard.php @@ -0,0 +1,76 @@ +getMethod('removeParallelizationArguments'); + $method->setAccessible(true); + + $arguments = [ + 'php', + 'bin/pest', + '--processes=12', + '--parallel', + '--shard=1/4', + '--verbose', + ]; + + $result = $method->invoke($shard, $arguments); + + expect($result)->toBe(['php', 'bin/pest', '--shard=1/4', '--verbose']) + ->and($result)->not->toContain('--processes=12') + ->and($result)->not->toContain('--parallel'); +}); + +it('removes space-separated processes arguments', function () { + $output = new BufferedOutput; + $shard = new Shard($output); + + $reflection = new ReflectionClass($shard); + $method = $reflection->getMethod('removeParallelizationArguments'); + $method->setAccessible(true); + + $arguments = [ + 'php', + 'bin/pest', + '--processes', + '8', + '--parallel', + '--other-flag', + ]; + + $result = $method->invoke($shard, $arguments); + + expect($result)->toBe(['php', 'bin/pest', '--other-flag']) + ->and($result)->not->toContain('--processes') + ->and($result)->not->toContain('8') + ->and($result)->not->toContain('--parallel'); +}); + +it('preserves non-parallel arguments when filtering', function () { + $output = new BufferedOutput; + $shard = new Shard($output); + + $reflection = new ReflectionClass($shard); + $method = $reflection->getMethod('removeParallelizationArguments'); + $method->setAccessible(true); + + $arguments = [ + 'php', + 'bin/pest', + '--filter', + 'UserTest', + '--verbose', + '--stop-on-failure', + ]; + + $result = $method->invoke($shard, $arguments); + + expect($result)->toBe($arguments); // Should be unchanged +}); diff --git a/tests/Visual/Shard.php b/tests/Visual/Shard.php new file mode 100644 index 000000000..6d01266d3 --- /dev/null +++ b/tests/Visual/Shard.php @@ -0,0 +1,48 @@ + 'DefaultPrinter', + 'COLLISION_IGNORE_DURATION' => 'true', + ]); + + $process->run(); + + expect($process->getExitCode())->toBe(0) + ->and($process->getOutput()) + ->not->toContain('Unknown option "--processes"') + ->toContain('Tests:') + ->toContain('Shard:'); +})->skipOnWindows(); + +test('shard removes processes from list-tests subprocess call', function () { + // This test verifies that --processes doesn't get passed to the --list-tests call + $process = new Process([ + 'php', + 'bin/pest', + '--processes=2', + '--shard=1/1', + '--parallel', + 'tests/Fixtures/ExampleTest.php', + ], dirname(__DIR__, 2), [ + 'COLLISION_PRINTER' => 'DefaultPrinter', + 'COLLISION_IGNORE_DURATION' => 'true', + ]); + + $process->run(); + + expect($process->getExitCode())->toBe(0) + ->and($process->getOutput()) + ->not->toContain('Unknown option "--processes"') + ->not->toContain('The "--processes" option requires a value') + ->toContain('Shard:'); // Just check that shard output exists, don't be too specific +})->skipOnWindows();