hasParameterOption('--parallel')) { return true; } return $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; } /** * Sets a global value that can be accessed by the parent process and all workers. */ public static function setGlobal(string $key, string|int|bool|Stringable $value): void { $data = ['value' => $value instanceof Stringable ? $value->__toString() : $value]; $_ENV[self::GLOBAL_PREFIX.$key] = json_encode($data, JSON_THROW_ON_ERROR); } /** * Returns the given global value if one has been set. */ public static function getGlobal(string $key): string|int|bool|null { $placesToCheck = [$_SERVER, $_ENV]; foreach ($placesToCheck as $location) { if (array_key_exists(self::GLOBAL_PREFIX.$key, $location)) { // @phpstan-ignore-next-line return json_decode((string) $location[self::GLOBAL_PREFIX.$key], true, 512, JSON_THROW_ON_ERROR)['value'] ?? null; } } return null; } /** * {@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 { $handlers = array_filter( array_map(fn (string $handler): object|string => Container::getInstance()->get($handler), self::HANDLERS), fn (object|string $handler): bool => $handler instanceof HandlesArguments, ); $filteredArguments = array_reduce( $handlers, fn (array $arguments, HandlesArguments $handler): array => $handler->handleArguments($arguments), $arguments ); $exitCode = $this->paratestCommand()->run(new ArgvInput($filteredArguments), new CleanConsoleOutput()); return CallsAddsOutput::execute($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 (string $handler): object|string => Container::getInstance()->get($handler), self::HANDLERS), fn (object|string $handler): bool => $handler instanceof HandlersWorkerArguments, ); return array_reduce( $handlers, fn (array $arguments, HandlersWorkerArguments $handler): array => $handler->handleWorkerArguments($arguments), $arguments ); } /** * 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 { $arguments = new ArgvInput(); foreach (self::UNSUPPORTED_ARGUMENTS 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); } }