From 784dff992ad1574bc3a60e49c556cd02c2e07c5d Mon Sep 17 00:00:00 2001 From: Sandro Gehri Date: Thu, 5 Sep 2024 22:34:28 +0200 Subject: [PATCH] Accept traits in the class filter --- .../Configuration/AbstractConfiguration.php | 3 -- src/Support/MutationGenerator.php | 4 +- tests/Fixtures/Classes/SizeHelper.php | 4 ++ tests/Fixtures/Traits/SizeHelperTrait.php | 13 ++++++ tests/Unit/MutationGeneratorTest.php | 20 +++++++++ tests/Unit/Support/FileFinderTest.php | 44 +++++++++---------- 6 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 tests/Fixtures/Traits/SizeHelperTrait.php diff --git a/src/Support/Configuration/AbstractConfiguration.php b/src/Support/Configuration/AbstractConfiguration.php index cb7a213..81f8de9 100644 --- a/src/Support/Configuration/AbstractConfiguration.php +++ b/src/Support/Configuration/AbstractConfiguration.php @@ -16,9 +16,6 @@ abstract class AbstractConfiguration implements ConfigurationContract */ private ?array $paths = null; - /** - * @var bool - */ private ?bool $coveredOnly = null; /** diff --git a/src/Support/MutationGenerator.php b/src/Support/MutationGenerator.php index 8a6f205..4943c33 100644 --- a/src/Support/MutationGenerator.php +++ b/src/Support/MutationGenerator.php @@ -154,11 +154,11 @@ private function doesNotContainClassToMutate(string $contents, array $classesToM $namespace = preg_quote(implode('\\', $parts)); $classOrNamespace = preg_quote($classOrNamespace); - if (preg_match("/namespace\\s+$namespace/", $contents) === 1 && preg_match("/class\\s+$class.*/", $contents) === 1) { + if (preg_match("/namespace\\s+$namespace/", $contents) === 1 && preg_match("/(?:class|trait)\\s+$class.*/", $contents) === 1) { return false; } - if (preg_match("/class\\s+$class\[{\\s*\]/", $contents) === 1) { + if (preg_match("/(?:class|trait)\\s+$class\[{\\s*\]/", $contents) === 1) { return false; } diff --git a/tests/Fixtures/Classes/SizeHelper.php b/tests/Fixtures/Classes/SizeHelper.php index 69da8d1..7dff9e0 100644 --- a/tests/Fixtures/Classes/SizeHelper.php +++ b/tests/Fixtures/Classes/SizeHelper.php @@ -4,8 +4,12 @@ namespace Tests\Fixtures\Classes; +use Tests\Fixtures\Traits\SizeHelperTrait; + class SizeHelper { + use SizeHelperTrait; + public static function isBig(int $size): bool { return $size >= 100; diff --git a/tests/Fixtures/Traits/SizeHelperTrait.php b/tests/Fixtures/Traits/SizeHelperTrait.php new file mode 100644 index 0000000..e688778 --- /dev/null +++ b/tests/Fixtures/Traits/SizeHelperTrait.php @@ -0,0 +1,13 @@ +generator = new MutationGenerator; @@ -102,6 +103,25 @@ classesToMutate: $classes, [[SizeHelper::class, AgeHelper::class], 2], ]); +it('generates mutations for the given file if it contains the given trait', function (array $classes, int $expectedCount): void { + $mutations = $this->generator->generate( + file: new SplFileInfo(dirname(__DIR__).'/Fixtures/Traits/SizeHelperTrait.php', '', ''), + mutators: [SmallerToSmallerOrEqual::class], + classesToMutate: $classes, + ); + + expect($mutations) + ->toBeArray() + ->toHaveCount($expectedCount); +})->with([ + [[SizeHelperTrait::class], 1], + [[SizeHelper::class], 0], + [[SizeHelperTrait::class, SizeHelper::class], 1], + [['SizeHelperTrait'], 1], + [['Tests\\Fixtures\\Traits\\SizeHelperTrai'], 1], + [['Invalid\\Namespace\\SizeHelperTrait'], 0], +]); + it('ignores lines with the ignore annotation', function (): void { $mutations = ($this->generate)(<<<'PHP' toHaveCount(2) - ->getIterator()->current()->getRealPath()->toEndWith('AgeHelper.php'); + ->toHaveCount(3) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); expect(FileFinder::files([getcwd().'/tests/Fixtures'], [])) - ->toHaveCount(2) - ->getIterator()->current()->getRealPath()->toEndWith('AgeHelper.php'); + ->toHaveCount(3) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); }); it('finds files by path', function (): void { @@ -22,71 +22,71 @@ it('excludes a file by full path', function (): void { expect(FileFinder::files(['tests/Fixtures'], ['tests/Fixtures/Classes/AgeHelper.php'])) - ->toHaveCount(1) - ->getIterator()->current()->getRealPath()->toEndWith('SizeHelper.php'); + ->toHaveCount(2) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); }); it('excludes a file by relative path', function (): void { expect(FileFinder::files(['tests/Fixtures'], ['Classes/AgeHelper.php'])) - ->toHaveCount(1) - ->getIterator()->current()->getRealPath()->toEndWith('SizeHelper.php'); + ->toHaveCount(2) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); }); it('excludes a file by pattern', function (): void { expect(FileFinder::files(['tests/Fixtures'], ['Classes/Age*.php'])) - ->toHaveCount(1) - ->getIterator()->current()->getRealPath()->toEndWith('SizeHelper.php'); + ->toHaveCount(2) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); }); it('excludes a file by pattern ending with an asterisk', function (): void { expect(FileFinder::files(['tests/Fixtures'], ['Classes/Age*'])) - ->toHaveCount(1) - ->getIterator()->current()->getRealPath()->toEndWith('SizeHelper.php'); + ->toHaveCount(2) + ->getIterator()->current()->getRealPath()->toEndWith('SizeHelperTrait.php'); }); it('does not exclude files with an incomplete path', function (): void { expect(FileFinder::files(['tests/Fixtures'], ['Classes/Age'])) - ->toHaveCount(2); + ->toHaveCount(3); }); it('excludes a directory by full path', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['tests/Fixtures/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['tests/Fixtures/Classes', 'tests/Fixtures/Traits'])) ->toBeEmpty(); }); it('excludes a directory by relative path', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['Classes', 'Traits'])) ->toBeEmpty(); - expect(FileFinder::files(['tests/Fixtures'], ['Classes/'])) + expect(FileFinder::files(['tests/Fixtures'], ['Classes/', 'Traits/'])) ->toBeEmpty(); }); it('excludes a directory by pattern', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['tests/*/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['tests/*/Classes', 'tests/*/Traits'])) ->toBeEmpty(); }); it('excludes a directory by pattern with multiple wildcards', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['*/*/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['*/*/Classes', '*/*/Traits'])) ->toBeEmpty(); }); it('excludes a directory by pattern with double asterisk wildcard', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['**/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['**/Classes', '**/Traits'])) ->toBeEmpty(); }); it('excludes by absolute path', function (): void { - expect(FileFinder::files(['tests/Fixtures'], ['/tests/Fixtures/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], ['/tests/Fixtures/Classes', '/tests/Fixtures/Traits'])) ->toBeEmpty(); - expect(FileFinder::files(['tests/Fixtures'], [getcwd().'/tests/Fixtures/Classes'])) + expect(FileFinder::files(['tests/Fixtures'], [getcwd().'/tests/Fixtures/Classes', getcwd().'/tests/Fixtures/Traits'])) ->toBeEmpty(); expect(FileFinder::files(['tests/Fixtures'], ['/tests/Fixtures/Invalid'])) ->not->toBeEmpty(); expect(FileFinder::files(['tests/Fixtures'], ['/tests/Fixtures/Classes/AgeHelper.php'])) - ->toHaveCount(1); + ->toHaveCount(2); });