diff --git a/composer.json b/composer.json index b0ac92d3cc..376873cae1 100644 --- a/composer.json +++ b/composer.json @@ -73,6 +73,7 @@ "files": [ "tests/_files/deprecation-trigger/trigger_deprecation.php", "tests/unit/Event/AbstractEventTestCase.php", + "tests/unit/TextUI/AbstractSouceFilterTestCase.php", "tests/unit/Framework/MockObject/TestDoubleTestCase.php", "tests/unit/Metadata/Parser/AttributeParserTestCase.php", "tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php", diff --git a/tests/unit/TextUI/AbstractSouceFilterTestCase.php b/tests/unit/TextUI/AbstractSouceFilterTestCase.php new file mode 100644 index 0000000000..accc20b1a6 --- /dev/null +++ b/tests/unit/TextUI/AbstractSouceFilterTestCase.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use const DIRECTORY_SEPARATOR; +use function ltrim; +use function realpath; +use function str_replace; +use PHPUnit\Framework\TestCase; + +abstract class AbstractSouceFilterTestCase extends TestCase +{ + protected static function createSource( + ?FilterDirectoryCollection $includeDirectories = null, + ?FilterDirectoryCollection $excludeDirectories = null, + ?FileCollection $includeFiles = null, + ?FileCollection $excludeFiles = null, + ): Source { + return new Source( + null, + false, + $includeDirectories ?? FilterDirectoryCollection::fromArray([]), + $includeFiles ?? FileCollection::fromArray([]), + $excludeDirectories ?? FilterDirectoryCollection::fromArray([]), + $excludeFiles ?? FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + } + + protected static function fixturePath(?string $subPath = null): string + { + $path = realpath(__DIR__ . '/../..') . '/_files/source-filter'; + + if ($subPath !== null) { + $path = $path . '/' . ltrim($subPath, '/'); + } + + return str_replace('/', DIRECTORY_SEPARATOR, $path); + } +} diff --git a/tests/unit/TextUI/SourceFilterTest.php b/tests/unit/TextUI/SourceFilterTest.php index cffa955dfe..fe41843333 100644 --- a/tests/unit/TextUI/SourceFilterTest.php +++ b/tests/unit/TextUI/SourceFilterTest.php @@ -9,256 +9,422 @@ */ namespace PHPUnit\TextUI\Configuration; -use const DIRECTORY_SEPARATOR; -use function realpath; +use function json_encode; +use function sprintf; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; #[CoversClass(SourceFilter::class)] #[Small] -final class SourceFilterTest extends TestCase +final class SourceFilterTest extends AbstractSouceFilterTestCase { public static function provider(): array { - $fixtureDirectory = realpath(__DIR__ . '/../../_files/source-filter'); - return [ 'file included using file' => [ - true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( - [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), - ], - ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource(includeFiles: FileCollection::fromArray( [ - 'functions' => [], - 'methods' => [], + new File(self::fixturePath('/a/PrefixSuffix.php')), ], - false, - false, - false, - ), + )), ], 'file included using file, but excluded using directory' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( - [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), - ], - ), - FilterDirectoryCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeFiles: FileCollection::fromArray([ + new File(self::fixturePath('/a/PrefixSuffix.php')), + ]), + excludeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + self::fixturePath('/a'), '', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, ), ], 'file included using file, but excluded using file' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + excludeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, ), ], 'file included using directory' => [ - true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + ), + ], + 'file included using directory, but excluded using file' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeFiles: FileCollection::fromArray( + [ + new File(self::fixturePath('/a/PrefixSuffix.php')), + ], + ), + ), + ], + 'file included using directory, but excluded using directory' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('/a'), '', '.php'), + ], + ), + ), + ], + 'file included using directory, but wrong suffix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', 'Foobar.php'), + ], + ), + ), + ], + 'file included using directory, but wrong prefix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'WrongPrefix', '.php'), + ], + ), + ), + ], + 'file included using directory, but not excluded by suffix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', 'WrongSuffix.php'), + ], + ), + ), + ], + 'file included using directory, but not excluded by prefix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'BadPrefix', '.php'), + ], + ), + ), + ], + 'directory wildcard does not include files at same level' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'a/*', '.php'), + ], + ), + ), + ], + 'directory wildcard with suffix does not match files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/Pre*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard with suffix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard with prefix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*a'), '', '.php'), + ], + ), + ), + ], + 'directory wildcards with prefix and suffix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*a/c*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard includes files' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard includes files in sub-directories' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/*'), '', '.php'), + ], + ), + ), + ], + 'wildcard includes all files' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*'), '', '.php'), + ], + ), + ), + ], + 'globstar includes files at globstar\'s level' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/**'), '', '.php'), + ], + ), + ), + ], + 'globstar includes files at globstar\'s level (2)' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/c/**'), '', '.php'), + ], + ), + ), + ], + 'globstar includes all files' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('**'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, ), ], - 'file included using directory, but excluded using file' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + 'globstar with any single char prefix includes sibling files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('a/c/Z**'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + ), + ], + 'globstar with any more than a single char prefix does not include sibling files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/c/d/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new FilterDirectory( + self::fixturePath('a/c/ZZ**'), + '', + '.php', + ), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, ), ], - 'file included using directory, but excluded using directory' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + 'globstar includes zero directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('**/a'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray( + ), + ], + 'globstar with trailing directory' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + self::fixturePath('/a/**/d'), '', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, + ), + ], + 'question mark' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/?/d'), '', '.php'), + ], + ), + ), + ], + 'multiple question marks' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('?/?/d'), '', '.php'), + ], + ), ), ], ]; } #[DataProvider('provider')] - public function testDeterminesWhetherFileIsIncluded(bool $expected, string $file, Source $source): void + public function testDeterminesWhetherFileIsIncluded(array $expectations, Source $source): void { - $this->assertSame($expected, (new SourceFilter((new SourceMapper)->map($source)))->includes($file)); + foreach ($expectations as $file => $shouldInclude) { + $this->assertFileExists($file); + $this->assertSame( + $shouldInclude, + (new SourceFilter((new SourceMapper)->map($source)))->includes($file), + sprintf('expected match to return %s for: %s', json_encode($shouldInclude), $file), + ); + } } } diff --git a/tests/unit/TextUI/SourceMapperTest.php b/tests/unit/TextUI/SourceMapperTest.php index 13ef787fa4..c4cb7387f9 100644 --- a/tests/unit/TextUI/SourceMapperTest.php +++ b/tests/unit/TextUI/SourceMapperTest.php @@ -9,19 +9,15 @@ */ namespace PHPUnit\TextUI\Configuration; -use const DIRECTORY_SEPARATOR; use const PHP_OS_FAMILY; -use function realpath; -use function str_replace; use Generator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; #[CoversClass(SourceMapper::class)] #[Small] -final class SourceMapperTest extends TestCase +final class SourceMapperTest extends AbstractSouceFilterTestCase { public static function provider(): Generator { @@ -348,52 +344,9 @@ public static function provider(): Generator ]; } - public static function fixturePath(?string $subPath = null): string - { - $path = realpath(__DIR__ . '/../../_files/source-filter'); - - if ($subPath !== null) { - $path = $path . '/' . $subPath; - } - - return str_replace('/', DIRECTORY_SEPARATOR, $path); - } - #[DataProvider('provider')] public function testDeterminesWhetherFileIsIncluded(array $expected, Source $source): void { $this->assertEquals($expected, (new SourceMapper)->map($source)); } - - private static function createSource( - ?FilterDirectoryCollection $includeDirectories = null, - ?FilterDirectoryCollection $excludeDirectories = null, - ?FileCollection $includeFiles = null, - ?FileCollection $excludeFiles = null, - ): Source { - return new Source( - null, - false, - $includeDirectories ?? FilterDirectoryCollection::fromArray([]), - $includeFiles ?? FileCollection::fromArray([]), - $excludeDirectories ?? FilterDirectoryCollection::fromArray([]), - $excludeFiles ?? FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - [ - 'functions' => [], - 'methods' => [], - ], - false, - false, - false, - ); - } }