From bd98a078a936d15b3ac6f5b34236145eddaa1ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 28 Nov 2016 15:06:09 +0100 Subject: [PATCH] Add path wildcard feature to Enumerator (#300) Add wildcard path enumeration and test with vfs --- composer.json | 3 +- composer.lock | 153 +++++++++++++++++- src/Instrument/FileSystem/Enumerator.php | 46 ++++-- .../Instrument/FileSystem/EnumeratorTest.php | 120 ++++++++++++++ 4 files changed, 306 insertions(+), 16 deletions(-) create mode 100644 tests/Go/Instrument/FileSystem/EnumeratorTest.php diff --git a/composer.json b/composer.json index 87f99ffa..58258947 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ }, "require-dev": { - "symfony/console": "~2.1|~3.0" + "symfony/console": "~2.1|~3.0", + "adlawson/vfs": "^0.12" }, "suggest": { diff --git a/composer.lock b/composer.lock index ea9209ab..318b6bfe 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e3d1de8b52b65893bfeb07065c834602", - "content-hash": "4d8be20f4ee1e27498811ed2ad1fa5b8", + "hash": "8c78ac407bda1d77d9c8e7a7e3babcf3", + "content-hash": "3a2a6809d1e0c19e600feaf151ceb89d", "packages": [ { "name": "andrewsville/php-token-reflection", @@ -233,6 +233,155 @@ } ], "packages-dev": [ + { + "name": "adlawson/vfs", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/adlawson/php-vfs.git", + "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adlawson/php-vfs/zipball/e955034419d6a8f92c9a8ea2e626eeed96b41095", + "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "psr/log": "^1.0" + }, + "require-dev": { + "adlawson/timezone": "^1.0", + "fabpot/php-cs-fixer": "^1.9", + "mockery/mockery": "^0.9", + "phpunit/phpunit": "^4.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Vfs\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrew Lawson", + "homepage": "http://adlawson.com" + } + ], + "description": "Virtual file system", + "homepage": "https://github.com/adlawson/php-vfs", + "keywords": [ + "dir", + "directory", + "file", + "fs", + "read", + "stream", + "system", + "virtual", + "wrapper", + "write" + ], + "time": "2016-02-20 12:46:01" + }, + { + "name": "mikey179/vfsStream", + "version": "v1.6.4", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "0247f57b2245e8ad2e689d7cee754b45fbabd592" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/0247f57b2245e8ad2e689d7cee754b45fbabd592", + "reference": "0247f57b2245e8ad2e689d7cee754b45fbabd592", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2016-07-18 14:02:57" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10 12:19:37" + }, { "name": "symfony/console", "version": "v3.0.4", diff --git a/src/Instrument/FileSystem/Enumerator.php b/src/Instrument/FileSystem/Enumerator.php index 85effdaf..62510d27 100644 --- a/src/Instrument/FileSystem/Enumerator.php +++ b/src/Instrument/FileSystem/Enumerator.php @@ -15,6 +15,7 @@ */ class Enumerator { + /** * Path to the root directory, where enumeration should start * @@ -40,14 +41,14 @@ class Enumerator * Initializes an enumerator * * @param string $rootDirectory Path to the root directory - * @param array $includePaths List of additional include paths - * @param array $excludePaths List of additional exclude paths + * @param array $includePaths List of additional include paths + * @param array $excludePaths List of additional exclude paths */ public function __construct($rootDirectory, array $includePaths = [], array $excludePaths = []) { $this->rootDirectory = $rootDirectory; - $this->includePaths = $includePaths; - $this->excludePaths = $excludePaths; + $this->includePaths = $includePaths; + $this->excludePaths = $excludePaths; } /** @@ -78,24 +79,26 @@ public function enumerate() public function getFilter() { $rootDirectory = $this->rootDirectory; - $includePaths = $this->includePaths; - $excludePaths = $this->excludePaths; + $includePaths = $this->includePaths; + $excludePaths = $this->excludePaths; + + return function (\SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) { - return function(\SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) { if ($file->getExtension() !== 'php') { return false; }; - $realPath = $file->getRealPath(); + $fullPath = $this->getFileFullPath($file); + $dir = dirname($fullPath); // Do not touch files that not under rootDirectory - if (strpos($realPath, $rootDirectory) !== 0) { + if (strpos($fullPath, $rootDirectory) !== 0) { return false; } if (!empty($includePaths)) { $found = false; - foreach ($includePaths as $includePath) { - if (strpos($realPath, $includePath) === 0) { + foreach ($includePaths as $includePattern) { + if (fnmatch($includePattern, $dir)) { $found = true; break; } @@ -105,8 +108,8 @@ public function getFilter() } } - foreach ($excludePaths as $excludePath) { - if (strpos($realPath, $excludePath) === 0) { + foreach ($excludePaths as $excludePattern) { + if (fnmatch($excludePattern, $dir)) { return false; } } @@ -114,4 +117,21 @@ public function getFilter() return true; }; } + + /** + * Return the real path of the given file + * + * This is used for testing purpose with virtual file system. + * In a vfs the 'realPath' methode will always return false. + * So we have a chance to mock this single function to return different path. + * + * @param \SplFileInfo $file + * + * @return string + */ + protected function getFileFullPath(\SplFileInfo $file) + { + return $file->getRealPath(); + } + } diff --git a/tests/Go/Instrument/FileSystem/EnumeratorTest.php b/tests/Go/Instrument/FileSystem/EnumeratorTest.php new file mode 100644 index 00000000..108243a3 --- /dev/null +++ b/tests/Go/Instrument/FileSystem/EnumeratorTest.php @@ -0,0 +1,120 @@ +mount(); + + $testPaths = [ + '/base/sub/test', + '/base/sub/sub/test' + ]; + + // Setup some files we test against + foreach ($testPaths as $path) { + mkdir('vfs://' . $path, 0777, true); + touch('vfs://' . $path . DIRECTORY_SEPARATOR . 'TestClass.php'); + } + } + + public static function tearDownAfterClass() + { + static::$fileSystem->unmount(); + } + + /** + * @return array + */ + public function pathsProvider() + { + return [ + [ + // No include or exclude, every folder should be there + ['vfs://base/sub/test', 'vfs://base/sub/sub/test'], + [], + [] + ], + [ + // Exclude double sub folder + ['vfs://base/sub/test'], + [], + ['vfs://base/sub/sub/test'] + ], + [ + // Exclude all, expected shout be empty + [], + [], + ['vfs://base/sub/test', 'vfs://base/sub/sub/test'] + ], + [ + // Exclude all sub using wildcard + [], + [], + ['vfs://base/*/test'] + ], + [ + // Includepath using wildcard should not break + ['vfs://base/sub/test', 'vfs://base/sub/sub/test'], + ['vfs://base/*'], + [] + ] + ]; + } + + /** + * Test wildcard path matching for Enumerator. + * + * @dataProvider pathsProvider + * + * @param array $expectedPaths + * @param array $includePaths + * @param array $excludePaths + */ + public function testExclude($expectedPaths, $includePaths, $excludePaths) + { + $testPaths = []; + + /** @var Enumerator $mock */ + $mock = $this->getMockBuilder(Enumerator::class) + ->setConstructorArgs(['vfs://base', $includePaths, $excludePaths]) + ->setMethods(['getFileFullPath']) + ->getMock(); + + // Mock getFileRealPath method to provide a pathname + // VFS does not support getRealPath() + $mock->expects($this->any()) + ->method('getFileFullPath') + ->will($this->returnCallback(function (\SplFileInfo $file) { + return $file->getPathname(); + })); + + $iterator = $mock->enumerate(); + + foreach ($iterator as $file) { + $testPaths[] = $file->getPath(); + } + + sort($testPaths); + sort($expectedPaths); + + $this->assertEquals($expectedPaths, $testPaths); + } +}