hasParameterOption('--parallel') || $argv->hasParameterOption('-p'); } /** * If this code is running in a worker process rather than the main process. */ public static function isWorker(): bool { $argvValue = Arr::get($_SERVER, 'PARATEST'); assert(is_string($argvValue) || is_int($argvValue) || is_null($argvValue)); return ((int) $argvValue) === 1; } /** * {@inheritdoc} */ public function handleArguments(array $arguments): array { if ($this->hasArgumentsThatWouldBeFasterWithoutParallel()) { return $this->runTestSuiteInSeries($arguments); } if (self::isEnabled()) { exit($this->runTestSuiteInParallel($arguments)); } if (self::isWorker()) { return $this->runWorkerHandlers($arguments); } return $arguments; } /** * Runs the test suite in parallel. This method will exit the process upon completion. * * @param array $arguments */ private function runTestSuiteInParallel(array $arguments): int { if (! class_exists(ParaTestCommand::class)) { $this->askUserToInstallParatest(); return Command::FAILURE; } $handlers = array_filter( array_map(fn ($handler): object|string => Container::getInstance()->get($handler), self::HANDLERS), fn ($handler): bool => $handler instanceof HandlesArguments, ); $filteredArguments = array_reduce( $handlers, fn ($arguments, HandlesArguments $handler): array => $handler->handleArguments($arguments), $arguments ); $exitCode = $this->paratestCommand()->run(new ArgvInput($filteredArguments), new CleanConsoleOutput()); return (new CallsAddsOutput())($exitCode); } /** * Runs any handlers that have been registered to handle worker arguments, and returns the modified arguments. * * @param array $arguments * @return array */ private function runWorkerHandlers(array $arguments): array { $handlers = array_filter( array_map(fn ($handler): object|string => Container::getInstance()->get($handler), self::HANDLERS), fn ($handler): bool => $handler instanceof HandlersWorkerArguments, ); return array_reduce( $handlers, fn ($arguments, HandlersWorkerArguments $handler): array => $handler->handleWorkerArguments($arguments), $arguments ); } /** * Outputs a message to the user asking them to install ParaTest as a dev dependency. */ private function askUserToInstallParatest(): void { /** @var OutputInterface $output */ $output = Container::getInstance()->get(OutputInterface::class); $output->writeln([ 'Pest Parallel requires ParaTest to run.', 'Please run composer require --dev brianium/paratest.', ]); } /** * Builds an instance of the Paratest command. */ private function paratestCommand(): Application { /** @var non-empty-string $rootPath */ $rootPath = TestSuite::getInstance()->rootPath; $command = ParaTestCommand::applicationFactory($rootPath); $command->setAutoExit(false); $command->setName('Pest'); $command->setVersion(version()); return $command; } /** * Whether the command line arguments contain any arguments that are * not supported or are suboptimal when running in parallel. */ private function hasArgumentsThatWouldBeFasterWithoutParallel(): bool { $unsupportedArguments = ['--todo', '--retry']; $arguments = new ArgvInput(); foreach ($unsupportedArguments as $unsupportedArgument) { if ($arguments->hasParameterOption($unsupportedArgument)) { return true; } } return false; } /** * Removes any parallel arguments. * * @param array $arguments * @return array */ private function runTestSuiteInSeries(array $arguments): array { $arguments = $this->popArgument('--parallel', $arguments); return $this->popArgument('-p', $arguments); } }