diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 2bc06fdae..34483a165 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -116,6 +116,8 @@ final class RunCommand extends BaseCommand private const OPTION_USE_NOOP_MUTATORS = 'noop'; + private const OPTION_EXECUTE_ONLY_COVERING_TEST_CASES = 'only-covering-test-cases'; + /** @var string */ private const OPTION_MIN_MSI = 'min-msi'; @@ -257,6 +259,12 @@ protected function configure(): void InputOption::VALUE_NONE, 'Use noop mutators that do not change AST. For debugging purposes.', ) + ->addOption( + self::OPTION_EXECUTE_ONLY_COVERING_TEST_CASES, + null, + InputOption::VALUE_NONE, + 'Execute only those test cases that cover mutated line, not the whole file with covering test cases. Can dramatically speed up Mutation Testing for slow test suites. For PHPUnit / Pest it uses `--filter` option', + ) ->addOption( self::OPTION_MIN_MSI, null, @@ -444,7 +452,8 @@ private function createContainer(IO $io, LoggerInterface $logger): Container $gitDiffFilter, $gitDiffBase, (bool) $input->getOption(self::OPTION_LOGGER_GITHUB), - (bool) $input->getOption(self::OPTION_USE_NOOP_MUTATORS) + (bool) $input->getOption(self::OPTION_USE_NOOP_MUTATORS), + (bool) $input->getOption(self::OPTION_EXECUTE_ONLY_COVERING_TEST_CASES) ); } diff --git a/src/Configuration/Configuration.php b/src/Configuration/Configuration.php index fd6649bf0..87df313a3 100644 --- a/src/Configuration/Configuration.php +++ b/src/Configuration/Configuration.php @@ -87,6 +87,7 @@ class Configuration private bool $dryRun; /** @var array> */ private array $ignoreSourceCodeMutatorsMap; + private bool $executeOnlyCoveringTestCases; /** * @param string[] $sourceDirectories @@ -123,7 +124,8 @@ public function __construct( int $msiPrecision, int $threadCount, bool $dryRun, - array $ignoreSourceCodeMutatorsMap + array $ignoreSourceCodeMutatorsMap, + bool $executeOnlyCoveringTestCases ) { Assert::nullOrGreaterThanEq($timeout, 0); Assert::allString($sourceDirectories); @@ -161,6 +163,7 @@ public function __construct( $this->threadCount = $threadCount; $this->dryRun = $dryRun; $this->ignoreSourceCodeMutatorsMap = $ignoreSourceCodeMutatorsMap; + $this->executeOnlyCoveringTestCases = $executeOnlyCoveringTestCases; } public function getProcessTimeout(): float @@ -317,4 +320,9 @@ public function getIgnoreSourceCodeMutatorsMap(): array { return $this->ignoreSourceCodeMutatorsMap; } + + public function getExecuteOnlyCoveringTestCases(): bool + { + return $this->executeOnlyCoveringTestCases; + } } diff --git a/src/Configuration/ConfigurationFactory.php b/src/Configuration/ConfigurationFactory.php index 601bdd42e..07907742f 100644 --- a/src/Configuration/ConfigurationFactory.php +++ b/src/Configuration/ConfigurationFactory.php @@ -118,7 +118,8 @@ public function create( ?string $gitDiffFilter, ?string $gitDiffBase, bool $useGitHubLogger, - bool $useNoopMutators + bool $useNoopMutators, + bool $executeOnlyCoveringTestCases ): Configuration { $configDir = dirname($schema->getFile()); @@ -170,7 +171,8 @@ public function create( $msiPrecision, $threadCount, $dryRun, - $ignoreSourceCodeMutatorsMap + $ignoreSourceCodeMutatorsMap, + $executeOnlyCoveringTestCases ); } diff --git a/src/Container.php b/src/Container.php index 44c5e7820..00f6ee536 100644 --- a/src/Container.php +++ b/src/Container.php @@ -166,6 +166,7 @@ final class Container public const DEFAULT_GIT_DIFF_BASE = null; public const DEFAULT_USE_GITHUB_LOGGER = false; public const DEFAULT_USE_NOOP_MUTATORS = false; + public const DEFAULT_EXECUTE_ONLY_COVERING_TEST_CASES = false; public const DEFAULT_NO_PROGRESS = false; public const DEFAULT_FORCE_PROGRESS = false; public const DEFAULT_EXISTING_COVERAGE_PATH = null; @@ -684,7 +685,8 @@ public static function create(): self self::DEFAULT_GIT_DIFF_FILTER, self::DEFAULT_GIT_DIFF_BASE, self::DEFAULT_USE_GITHUB_LOGGER, - self::DEFAULT_USE_NOOP_MUTATORS + self::DEFAULT_USE_NOOP_MUTATORS, + self::DEFAULT_EXECUTE_ONLY_COVERING_TEST_CASES ); } @@ -715,7 +717,8 @@ public function withValues( ?string $gitDiffFilter, ?string $gitDiffBase, bool $useGitHubLogger, - bool $useNoopMutators + bool $useNoopMutators, + bool $executeOnlyCoveringTestCases ): self { $clone = clone $this; @@ -790,7 +793,8 @@ static function (self $container) use ( $gitDiffFilter, $gitDiffBase, $useGitHubLogger, - $useNoopMutators + $useNoopMutators, + $executeOnlyCoveringTestCases ): Configuration { return $container->getConfigurationFactory()->create( $container->getSchemaConfiguration(), @@ -815,7 +819,8 @@ static function (self $container) use ( $gitDiffFilter, $gitDiffBase, $useGitHubLogger, - $useNoopMutators + $useNoopMutators, + $executeOnlyCoveringTestCases ); } ); diff --git a/src/TestFramework/AbstractTestFrameworkAdapter.php b/src/TestFramework/AbstractTestFrameworkAdapter.php index 9f8fa0336..dc9e19284 100644 --- a/src/TestFramework/AbstractTestFrameworkAdapter.php +++ b/src/TestFramework/AbstractTestFrameworkAdapter.php @@ -91,7 +91,10 @@ public function getInitialTestRunCommandLine( array $phpExtraArgs, bool $skipCoverage ): array { - return $this->getCommandLine($this->buildInitialConfigFile(), $extraOptions, $phpExtraArgs); + return $this->getCommandLine( + $phpExtraArgs, + $this->argumentsAndOptionsBuilder->buildForInitialTestsRun($this->buildInitialConfigFile(), $extraOptions) + ); } /** @@ -109,14 +112,17 @@ public function getMutantCommandLine( string $extraOptions ): array { return $this->getCommandLine( - $this->buildMutationConfigFile( - $tests, - $mutantFilePath, - $mutationHash, - $mutationOriginalFilePath - ), - $extraOptions, - [] + [], + $this->argumentsAndOptionsBuilder->buildForMutant( + $this->buildMutationConfigFile( + $tests, + $mutantFilePath, + $mutationHash, + $mutationOriginalFilePath + ), + $extraOptions, + $tests + ) ); } @@ -154,20 +160,18 @@ protected function buildMutationConfigFile( /** * @param string[] $phpExtraArgs + * @param string[] $testFrameworkArgs * * @return string[] */ private function getCommandLine( - string $configPath, - string $extraOptions, - array $phpExtraArgs + array $phpExtraArgs, + array $testFrameworkArgs ): array { - $frameworkArgs = $this->argumentsAndOptionsBuilder->build($configPath, $extraOptions); - return $this->commandLineBuilder->build( $this->testFrameworkExecutable, $phpExtraArgs, - $frameworkArgs + $testFrameworkArgs ); } diff --git a/src/TestFramework/CommandLineArgumentsAndOptionsBuilder.php b/src/TestFramework/CommandLineArgumentsAndOptionsBuilder.php index d18ad2c93..817c5b40f 100644 --- a/src/TestFramework/CommandLineArgumentsAndOptionsBuilder.php +++ b/src/TestFramework/CommandLineArgumentsAndOptionsBuilder.php @@ -35,6 +35,8 @@ namespace Infection\TestFramework; +use Infection\AbstractTestFramework\Coverage\TestLocation; + /** * @internal */ @@ -43,5 +45,12 @@ interface CommandLineArgumentsAndOptionsBuilder /** * @return string[] */ - public function build(string $configPath, string $extraOptions): array; + public function buildForInitialTestsRun(string $configPath, string $extraOptions): array; + + /** + * @param TestLocation[] $tests + * + * @return string[] + */ + public function buildForMutant(string $configPath, string $extraOptions, array $tests): array; } diff --git a/src/TestFramework/Factory.php b/src/TestFramework/Factory.php index 68f5fc7ed..13536f572 100644 --- a/src/TestFramework/Factory.php +++ b/src/TestFramework/Factory.php @@ -102,7 +102,8 @@ public function create(string $adapterName, bool $skipCoverage): TestFrameworkAd $this->jUnitFilePath, $this->projectDir, $this->infectionConfig->getSourceDirectories(), - $skipCoverage + $skipCoverage, + $this->infectionConfig->getExecuteOnlyCoveringTestCases() ); } @@ -120,7 +121,8 @@ public function create(string $adapterName, bool $skipCoverage): TestFrameworkAd $this->jUnitFilePath, $this->projectDir, $this->infectionConfig->getSourceDirectories(), - $skipCoverage + $skipCoverage, + $this->infectionConfig->getExecuteOnlyCoveringTestCases() ); } diff --git a/src/TestFramework/PhpUnit/Adapter/PestAdapterFactory.php b/src/TestFramework/PhpUnit/Adapter/PestAdapterFactory.php index 591b5d6f9..57250d62b 100644 --- a/src/TestFramework/PhpUnit/Adapter/PestAdapterFactory.php +++ b/src/TestFramework/PhpUnit/Adapter/PestAdapterFactory.php @@ -64,7 +64,8 @@ public static function create( string $jUnitFilePath, string $projectDir, array $sourceDirectories, - bool $skipCoverage + bool $skipCoverage, + bool $executeOnlyCoveringTestCases = false ): TestFrameworkAdapter { Assert::string($testFrameworkConfigDir, 'Config dir is not allowed to be `null` for the Pest adapter'); @@ -97,7 +98,7 @@ public static function create( $projectDir, new JUnitTestCaseSorter() ), - new ArgumentsAndOptionsBuilder(), + new ArgumentsAndOptionsBuilder($executeOnlyCoveringTestCases), new VersionParser(), new CommandLineBuilder() ); diff --git a/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapterFactory.php b/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapterFactory.php index cbc52b6c1..cfb13dccf 100644 --- a/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapterFactory.php +++ b/src/TestFramework/PhpUnit/Adapter/PhpUnitAdapterFactory.php @@ -67,7 +67,8 @@ public static function create( string $jUnitFilePath, string $projectDir, array $sourceDirectories, - bool $skipCoverage + bool $skipCoverage, + bool $executeOnlyCoveringTestCases = false ): TestFrameworkAdapter { Assert::string($testFrameworkConfigDir, 'Config dir is not allowed to be `null` for the Pest adapter'); @@ -100,7 +101,7 @@ public static function create( $projectDir, new JUnitTestCaseSorter() ), - new ArgumentsAndOptionsBuilder(), + new ArgumentsAndOptionsBuilder($executeOnlyCoveringTestCases), new VersionParser(), new CommandLineBuilder() ); diff --git a/src/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilder.php b/src/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilder.php index c95c734aa..cfadbb9cc 100644 --- a/src/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilder.php +++ b/src/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilder.php @@ -35,18 +35,30 @@ namespace Infection\TestFramework\PhpUnit\CommandLine; +use function array_key_exists; use function array_map; use function array_merge; +use function count; use function explode; +use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\TestFramework\CommandLineArgumentsAndOptionsBuilder; use function ltrim; +use function preg_quote; +use function rtrim; /** * @internal */ final class ArgumentsAndOptionsBuilder implements CommandLineArgumentsAndOptionsBuilder { - public function build(string $configPath, string $extraOptions): array + private bool $executeOnlyCoveringTestCases; + + public function __construct(bool $executeOnlyCoveringTestCases) + { + $this->executeOnlyCoveringTestCases = $executeOnlyCoveringTestCases; + } + + public function buildForInitialTestsRun(string $configPath, string $extraOptions): array { $options = [ '--configuration', @@ -64,4 +76,36 @@ public function build(string $configPath, string $extraOptions): array return $options; } + + /** + * @param TestLocation[] $tests + */ + public function buildForMutant(string $configPath, string $extraOptions, array $tests): array + { + $options = $this->buildForInitialTestsRun($configPath, $extraOptions); + + if ($this->executeOnlyCoveringTestCases && count($tests) > 0) { + $filterString = '/'; + $usedTestCases = []; + + foreach ($tests as $testLocation) { + $testCaseString = $testLocation->getMethod(); + + if (array_key_exists($testCaseString, $usedTestCases)) { + continue; + } + + $usedTestCases[$testCaseString] = true; + + $filterString .= preg_quote($testCaseString, '/') . '|'; + } + + $filterString = rtrim($filterString, '|') . '/'; + + $options[] = '--filter'; + $options[] = $filterString; + } + + return $options; + } } diff --git a/tests/e2e/PestTestFramework/tests/ForPestWithDataProviderTest.php b/tests/e2e/PestTestFramework/tests/ForPestWithDataProviderTest.php index 4dfd85156..123e187ad 100644 --- a/tests/e2e/PestTestFramework/tests/ForPestWithDataProviderTest.php +++ b/tests/e2e/PestTestFramework/tests/ForPestWithDataProviderTest.php @@ -10,7 +10,7 @@ [2.0, 4.0, 0.5] ]); -test('tests division with shared dataset', function (float $a, float $b, float $expectedResult) { +test('tests division with shared dataset, with special | char', function (float $a, float $b, float $expectedResult) { $sourceClass = new ForPestWithDataProvider(); expect($sourceClass->div($a, $b))->toBe($expectedResult); diff --git a/tests/phpunit/Configuration/ConfigurationAssertions.php b/tests/phpunit/Configuration/ConfigurationAssertions.php index dd0e482bd..5aa9edd46 100644 --- a/tests/phpunit/Configuration/ConfigurationAssertions.php +++ b/tests/phpunit/Configuration/ConfigurationAssertions.php @@ -83,7 +83,8 @@ private function assertConfigurationStateIs( int $expectedMsiPrecision, int $expectedThreadCount, bool $expectedDryRyn, - array $expectedIgnoreSourceCodeMutatorsMap + array $expectedIgnoreSourceCodeMutatorsMap, + bool $expectedExecuteOnlyCoveringTestCases ): void { $this->assertSame($expectedTimeout, $configuration->getProcessTimeout()); $this->assertSame($expectedSourceDirectories, $configuration->getSourceDirectories()); @@ -132,6 +133,7 @@ private function assertConfigurationStateIs( $this->assertSame($expectedThreadCount, $configuration->getThreadCount()); $this->assertSame($expectedDryRyn, $configuration->isDryRun()); $this->assertSame($expectedIgnoreSourceCodeMutatorsMap, $configuration->getIgnoreSourceCodeMutatorsMap()); + $this->assertSame($expectedExecuteOnlyCoveringTestCases, $configuration->getExecuteOnlyCoveringTestCases()); } /** diff --git a/tests/phpunit/Configuration/ConfigurationFactoryTest.php b/tests/phpunit/Configuration/ConfigurationFactoryTest.php index 4b27ea5d2..f746aed8d 100644 --- a/tests/phpunit/Configuration/ConfigurationFactoryTest.php +++ b/tests/phpunit/Configuration/ConfigurationFactoryTest.php @@ -138,7 +138,8 @@ public function test_it_can_create_a_configuration( ?float $expectedMinMsi, bool $expectedShowMutations, ?float $expectedMinCoveredMsi, - array $expectedIgnoreSourceCodeMutatorsMap + array $expectedIgnoreSourceCodeMutatorsMap, + bool $inputExecuteOnlyCoveringTestCases ): void { $config = $this ->createConfigurationFactory($ciDetected) @@ -165,7 +166,8 @@ public function test_it_can_create_a_configuration( $inputGitDiffFilter, $inputGitDiffBase, $inputUseGitHubAnnotationsLogger, - $inputUseNoopMutators + $inputUseNoopMutators, + $inputExecuteOnlyCoveringTestCases ) ; @@ -198,7 +200,8 @@ public function test_it_can_create_a_configuration( $inputMsiPrecision, $inputThreadsCount, $inputDryRun, - $expectedIgnoreSourceCodeMutatorsMap + $expectedIgnoreSourceCodeMutatorsMap, + $inputExecuteOnlyCoveringTestCases ); } @@ -272,6 +275,7 @@ public function valueProvider(): iterable false, null, [], + true, ]; yield 'null timeout' => self::createValueForTimeout( @@ -717,6 +721,7 @@ public function valueProvider(): iterable false, null, [], + false, ]; yield 'complete' => [ @@ -813,6 +818,7 @@ public function valueProvider(): iterable true, 81.5, [], + false, ]; } @@ -885,6 +891,7 @@ private static function createValueForTimeout( false, null, [], + false, ]; } @@ -957,6 +964,7 @@ private static function createValueForTmpDir( false, null, [], + false, ]; } @@ -1030,6 +1038,7 @@ private static function createValueForCoveragePath( false, null, [], + false, ]; } @@ -1102,6 +1111,7 @@ private static function createValueForPhpUnitConfigDir( false, null, [], + false, ]; } @@ -1175,6 +1185,7 @@ private static function createValueForNoProgress( false, null, [], + false, ]; } @@ -1248,6 +1259,7 @@ private static function createValueForIgnoreMsiWithNoMutations( false, null, [], + false, ]; } @@ -1321,6 +1333,7 @@ private static function createValueForMinMsi( false, null, [], + false, ]; } @@ -1394,6 +1407,7 @@ private static function createValueForMinCoveredMsi( false, $expectedMinCoveredMsi, [], + false, ]; } @@ -1468,6 +1482,7 @@ private static function createValueForTestFramework( false, null, [], + false, ]; } @@ -1541,6 +1556,7 @@ private static function createValueForInitialTestsPhpOptions( false, null, [], + false, ]; } @@ -1615,6 +1631,7 @@ private static function createValueForTestFrameworkExtraOptions( false, null, [], + false, ]; } @@ -1688,6 +1705,7 @@ private static function createValueForTestFrameworkKey( false, null, [], + false, ]; } @@ -1765,6 +1783,7 @@ private static function createValueForMutators( false, null, [], + false, ]; } @@ -1843,6 +1862,7 @@ private static function createValueForIgnoreSourceCodeByRegex( false, null, $expectedIgnoreSourceCodeMutatorsMap, + false, ]; } diff --git a/tests/phpunit/Configuration/ConfigurationTest.php b/tests/phpunit/Configuration/ConfigurationTest.php index 93746ea10..3686b8514 100644 --- a/tests/phpunit/Configuration/ConfigurationTest.php +++ b/tests/phpunit/Configuration/ConfigurationTest.php @@ -87,7 +87,8 @@ public function test_it_can_be_instantiated( int $msiPrecision, int $threadsCount, bool $dryRun, - array $ignoreSourceCodeMutatorsMap + array $ignoreSourceCodeMutatorsMap, + bool $executeOnlyCoveringTestCases ): void { $config = new Configuration( $timeout, @@ -117,7 +118,8 @@ public function test_it_can_be_instantiated( $msiPrecision, $threadsCount, $dryRun, - $ignoreSourceCodeMutatorsMap + $ignoreSourceCodeMutatorsMap, + $executeOnlyCoveringTestCases ); $this->assertConfigurationStateIs( @@ -149,7 +151,8 @@ public function test_it_can_be_instantiated( $msiPrecision, $threadsCount, $dryRun, - $ignoreSourceCodeMutatorsMap + $ignoreSourceCodeMutatorsMap, + $executeOnlyCoveringTestCases ); } @@ -184,6 +187,7 @@ public function valueProvider(): iterable 0, false, [], + false, ]; yield 'nominal' => [ @@ -230,6 +234,7 @@ public function valueProvider(): iterable [ 'For_' => ['.*someMethod.*'], ], + true, ]; } } diff --git a/tests/phpunit/ContainerTest.php b/tests/phpunit/ContainerTest.php index a46f66965..7503c08d1 100644 --- a/tests/phpunit/ContainerTest.php +++ b/tests/phpunit/ContainerTest.php @@ -95,7 +95,8 @@ public function test_it_can_build_lazy_source_file_data_factory_that_fails_on_us Container::DEFAULT_GIT_DIFF_FILTER, Container::DEFAULT_GIT_DIFF_BASE, Container::DEFAULT_USE_GITHUB_LOGGER, - Container::DEFAULT_USE_NOOP_MUTATORS + Container::DEFAULT_USE_NOOP_MUTATORS, + Container::DEFAULT_EXECUTE_ONLY_COVERING_TEST_CASES ); $traces = $newContainer->getUnionTraceProvider()->provideTraces(); @@ -144,7 +145,8 @@ public function test_it_provides_a_friendly_error_when_attempting_to_configure_i Container::DEFAULT_GIT_DIFF_FILTER, Container::DEFAULT_GIT_DIFF_BASE, Container::DEFAULT_USE_GITHUB_LOGGER, - Container::DEFAULT_USE_NOOP_MUTATORS + Container::DEFAULT_USE_NOOP_MUTATORS, + Container::DEFAULT_EXECUTE_ONLY_COVERING_TEST_CASES, ); } } diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php index b46876f9a..bc5c85802 100644 --- a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php +++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php @@ -129,7 +129,7 @@ public function test_it_provides_initial_test_run_command_line_when_no_coverage_ { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default') ; @@ -163,7 +163,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') ->willReturn([ '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', @@ -215,7 +215,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') ->willReturn([ '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php index 212fdc7ba..6410d8b5a 100644 --- a/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php +++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php @@ -129,7 +129,7 @@ public function test_it_provides_initial_test_run_command_line_when_no_coverage_ { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default') ; @@ -163,7 +163,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') ->willReturn([ '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', @@ -215,7 +215,7 @@ public function test_it_provides_initial_test_run_command_line_when_coverage_rep { $this->cliArgumentsBuilder ->expects($this->once()) - ->method('build') + ->method('buildForInitialTestsRun') ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') ->willReturn([ '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', diff --git a/tests/phpunit/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilderTest.php b/tests/phpunit/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilderTest.php index 45055b14d..5a271b666 100644 --- a/tests/phpunit/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilderTest.php +++ b/tests/phpunit/TestFramework/PhpUnit/CommandLine/ArgumentsAndOptionsBuilderTest.php @@ -35,6 +35,9 @@ namespace Infection\Tests\TestFramework\PhpUnit\CommandLine; +use function array_map; +use Generator; +use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\TestFramework\PhpUnit\CommandLine\ArgumentsAndOptionsBuilder; use PHPUnit\Framework\TestCase; @@ -47,7 +50,7 @@ final class ArgumentsAndOptionsBuilderTest extends TestCase protected function setUp(): void { - $this->builder = new ArgumentsAndOptionsBuilder(); + $this->builder = new ArgumentsAndOptionsBuilder(false); } public function test_it_can_build_the_command_without_extra_options(): void @@ -59,7 +62,7 @@ public function test_it_can_build_the_command_without_extra_options(): void '--configuration', $configPath, ], - $this->builder->build($configPath, '') + $this->builder->buildForInitialTestsRun($configPath, '') ); } @@ -74,7 +77,7 @@ public function test_it_can_build_the_command_with_extra_options(): void '--verbose', '--debug', ], - $this->builder->build($configPath, '--verbose --debug') + $this->builder->buildForInitialTestsRun($configPath, '--verbose --debug') ); } @@ -88,7 +91,85 @@ public function test_it_can_build_the_command_with_extra_options_that_contains_s $configPath, '--path=/a path/with spaces', ], - $this->builder->build($configPath, '--path=/a path/with spaces') + $this->builder->buildForInitialTestsRun($configPath, '--path=/a path/with spaces') ); } + + /** + * @dataProvider provideTestCases + */ + public function test_it_can_build_the_command_with_filter_option_for_covering_tests_for_mutant(bool $executeOnlyCoveringTestCases, array $testCases, ?string $expectedFilterOptionValue = null): void + { + $configPath = '/the config/path'; + + $builder = new ArgumentsAndOptionsBuilder($executeOnlyCoveringTestCases); + + $expectedArgumentsAndOptions = [ + '--configuration', + $configPath, + '--path=/a path/with spaces', + ]; + + if ($executeOnlyCoveringTestCases) { + $expectedArgumentsAndOptions[] = '--filter'; + $expectedArgumentsAndOptions[] = $expectedFilterOptionValue; + } + + $this->assertSame( + $expectedArgumentsAndOptions, + $builder->buildForMutant( + $configPath, + '--path=/a path/with spaces', + array_map( + static fn (string $testCase): TestLocation => TestLocation::forTestMethod($testCase), + $testCases + ) + ) + ); + } + + public function provideTestCases(): Generator + { + yield '--only-covering-test-cases is disabled' => [ + false, + [ + 'App\Test::test_case1', + ], + ]; + + yield '1 test case' => [ + true, + [ + 'App\Test::test_case1', + ], + '/App\\\\Test\:\:test_case1/', + ]; + + yield '2 test cases' => [ + true, + [ + 'App\Test::test_case1', + 'App\Test::test_case2', + ], + '/App\\\\Test\:\:test_case1|App\\\\Test\:\:test_case2/', + ]; + + yield '2 simple test cases, 1 with data set and special character >' => [ + true, + [ + 'App\Test::test_case1 with data set "With special character >"', + 'App\Test::test_case2', + ], + '/App\\\\Test\:\:test_case1 with data set "With special character \\>"|App\\\\Test\:\:test_case2/', + ]; + + yield '2 simple test cases, 1 with data set and special character @' => [ + true, + [ + 'App\Test::test_case1 with data set "With special character @"', + 'App\Test::test_case2', + ], + '/App\\\\Test\:\:test_case1 with data set "With special character @"|App\\\\Test\:\:test_case2/', + ]; + } }