diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 5ec9103d1..dc3311e43 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -57,9 +57,12 @@ use Infection\Logger\GitHub\NoFilesInDiffToMutate; use Infection\Metrics\MinMsiCheckFailed; use Infection\Process\Runner\InitialTestsFailed; +use Infection\Resource\Processor\CpuCoresCountProvider; use Infection\TestFramework\Coverage\XmlReport\NoLineExecutedInDiffLinesMode; use Infection\TestFramework\TestFrameworkTypes; use InvalidArgumentException; +use function is_numeric; +use function max; use const PHP_SAPI; use Psr\Log\LoggerInterface; use function sprintf; @@ -67,6 +70,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use function trim; +use Webmozart\Assert\Assert; /** * @internal @@ -177,7 +181,7 @@ protected function configure(): void self::OPTION_THREADS, 'j', InputOption::VALUE_REQUIRED, - 'Number of threads to use by the runner when executing the mutations', + 'Number of threads to use by the runner when executing the mutations. Use "max" to auto calculate it.', Container::DEFAULT_THREAD_COUNT ) ->addOption( @@ -478,8 +482,7 @@ private function createContainer(IO $io, LoggerInterface $logger): Container ? Container::DEFAULT_TEST_FRAMEWORK_EXTRA_OPTIONS : $testFrameworkExtraOptions, $filter, - // TODO: more validation here? - (int) $input->getOption(self::OPTION_THREADS), + $this->getThreadCount($input), // To keep in sync with Container::DEFAULT_DRY_RUN (bool) $input->getOption(self::OPTION_DRY_RUN), $gitDiffFilter, @@ -643,4 +646,18 @@ private function getUseGitHubLogger(InputInterface $input): ?bool self::OPTION_LOGGER_GITHUB )); } + + private function getThreadCount(InputInterface $input): int + { + $threads = $input->getOption(self::OPTION_THREADS); + + if (is_numeric($threads)) { + return (int) $threads; + } + + Assert::same($threads, 'max', sprintf('The value of option `--threads` must be of type integer or string "max". String "%s" provided.', $threads)); + + // we subtract 1 here to not use all the available cores by Infection + return max(1, CpuCoresCountProvider::provide() - 1); + } } diff --git a/src/Resource/Processor/CpuCoresCountProvider.php b/src/Resource/Processor/CpuCoresCountProvider.php new file mode 100644 index 000000000..9d8599c53 --- /dev/null +++ b/src/Resource/Processor/CpuCoresCountProvider.php @@ -0,0 +1,99 @@ + 0) { + return $cpuCount; + } + } + + return 1; + } +} diff --git a/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php b/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php index 62b91da45..3f0fc006e 100644 --- a/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php +++ b/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php @@ -41,7 +41,6 @@ use function in_array; use Infection\CannotBeInstantiated; use Infection\Command\ConfigureCommand; -use Infection\Command\RunCommand; use Infection\Config\ConsoleHelper; use Infection\Config\Guesser\SourceDirGuesser; use Infection\Configuration\Schema\SchemaConfigurationFactory; @@ -66,6 +65,7 @@ use Infection\Mutator\NodeMutationGenerator; use Infection\Process\Runner\IndexedProcessBearer; use Infection\Process\ShellCommandLineExecutor; +use Infection\Resource\Processor\CpuCoresCountProvider; use Infection\TestFramework\AdapterInstaller; use Infection\TestFramework\Coverage\JUnit\TestFileTimeData; use Infection\TestFramework\Coverage\NodeLineRangeData; @@ -96,7 +96,6 @@ final class ProjectCodeProvider */ public const NON_TESTED_CONCRETE_CLASSES = [ ConfigureCommand::class, - RunCommand::class, Application::class, ProgressFormatter::class, ComposerExecutableFinder::class, @@ -113,6 +112,7 @@ final class ProjectCodeProvider NullSubscriber::class, FormatterName::class, ShellCommandLineExecutor::class, + CpuCoresCountProvider::class, ]; /** diff --git a/tests/phpunit/Command/RunCommandTest.php b/tests/phpunit/Command/RunCommandTest.php new file mode 100644 index 000000000..c05cbc2e9 --- /dev/null +++ b/tests/phpunit/Command/RunCommandTest.php @@ -0,0 +1,61 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value of option `--threads` must be of type integer or string "max". String "abc" provided.'); + + $app = new Application(SingletonContainer::getContainer()); + + $tester = new CommandTester($app->find('run')); + + $result = $tester->execute(['--threads' => 'abc']); + $this->assertSame(1, $result); + } +}