From 322993a0b057457ab363929c3ca37bce6eb4affb Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 9 Oct 2023 14:15:58 +0200 Subject: [PATCH] Output analysed files count & elapsed time (#32) --- README.md | 12 +++++++- bin/detect-collisions | 12 ++++++-- src/CollisionDetector.php | 15 ++++++++-- src/DetectionResult.php | 51 +++++++++++++++++++++++++++++++++ tests/CollisionDetectorTest.php | 43 +++++++++++++++++++++------ 5 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 src/DetectionResult.php diff --git a/README.md b/README.md index 3c095e8..422a93e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Check duplicate types: vendor/bin/detect-collisions dir1 dir2 dir3 # relative to cwd ``` -Example output: +Example error output: ``` Foo\NamespacedClass2 is defined 2 times: > /tests/sample-collisions/file2.php @@ -27,6 +27,16 @@ GlobalInterface1 is defined 2 times: > /tests/sample-collisions/file2.php ``` +Example success output: +``` +OK (no name collision found) + * analysed files: 9867 + * excluded files: 0 + * elapsed time: 1.057 s +``` + +- Note the performance: **10 000 files takes few seconds**! + ## Configuration: If file named `collision-detector.json` is present within current working directory, its contents are taken as configuration options. Possible config options: ```json5 diff --git a/bin/detect-collisions b/bin/detect-collisions index 784457c..2c45de2 100755 --- a/bin/detect-collisions +++ b/bin/detect-collisions @@ -67,7 +67,12 @@ try { } $detector = new CollisionDetector($config); - $collisions = $detector->getCollidingTypes(); + + $startTime = microtime(true); + $result = $detector->getCollidingTypes(); + $elapsedTime = microtime(true) - $startTime; + + $collisions = $result->getCollisions(); } catch (FileParsingException | InvalidConfigException $e) { $exit($e->getMessage()); } @@ -82,7 +87,10 @@ foreach ($collisions as $name => $fileLines) { } if ($collisions === []) { - echo "OK: no name collision found". PHP_EOL; + echo "OK (no name collision found)". PHP_EOL; + echo " * analysed files: " . $result->getAnalysedFilesCount() . PHP_EOL; + echo " * excluded files: " . $result->getExcludedFilesCount() . PHP_EOL; + echo " * elapsed time: " . round($elapsedTime, 3) . ' s' . PHP_EOL; } echo PHP_EOL; diff --git a/src/CollisionDetector.php b/src/CollisionDetector.php index 3abaedd..3cff3dd 100644 --- a/src/CollisionDetector.php +++ b/src/CollisionDetector.php @@ -60,10 +60,9 @@ public function __construct(DetectionConfig $config) } /** - * @return array> * @throws FileParsingException */ - public function getCollidingTypes(): array + public function getCollidingTypes(): DetectionResult { $groups = [ self::TYPE_GROUP_CLASS, @@ -71,10 +70,13 @@ public function getCollidingTypes(): array self::TYPE_GROUP_CONSTANT, ]; $types = []; + $filesAnalysed = 0; + $filesExcluded = 0; foreach ($this->config->getScanPaths() as $scanPath) { foreach ($this->listPhpFilesIn($scanPath) as $filePath) { if ($this->isExcluded($filePath)) { + $filesExcluded++; continue; } @@ -86,11 +88,14 @@ public function getCollidingTypes(): array } } catch (FileParsingException $e) { if ($this->config->shouldIgnoreParseFailures()) { + $filesExcluded++; continue; } throw $e; } + + $filesAnalysed++; } } @@ -117,7 +122,11 @@ public function getCollidingTypes(): array } } - return $collidingTypes; + return new DetectionResult( + $filesAnalysed, + $filesExcluded, + $collidingTypes + ); } private function stripCwdFromPath(string $path): string diff --git a/src/DetectionResult.php b/src/DetectionResult.php new file mode 100644 index 0000000..2661a39 --- /dev/null +++ b/src/DetectionResult.php @@ -0,0 +1,51 @@ +> + */ + private $collisions; + + /** + * @param array> $collisions + */ + public function __construct(int $analysedFilesCount, int $excludedFilesCount, array $collisions) + { + $this->analysedFilesCount = $analysedFilesCount; + $this->excludedFilesCount = $excludedFilesCount; + $this->collisions = $collisions; + } + + public function getAnalysedFilesCount(): int + { + return $this->analysedFilesCount; + } + + public function getExcludedFilesCount(): int + { + return $this->excludedFilesCount; + } + + /** + * @return array> + */ + public function getCollisions(): array + { + return $this->collisions; + } + +} diff --git a/tests/CollisionDetectorTest.php b/tests/CollisionDetectorTest.php index 1ae5f42..fc91723 100644 --- a/tests/CollisionDetectorTest.php +++ b/tests/CollisionDetectorTest.php @@ -19,8 +19,8 @@ public function testBinScript(): void { $expectedNoDirectoryRegex = '~^ERROR: No directories provided, use e.g. `detect-collisions src tests` or setup scanPaths in~'; $expectedInvalidDirectoryRegex = '~^ERROR: Provided directory to scan ".*?nonsense" is not directory nor a file~'; - $expectedSuccessWithConfigRegex = '~^Using config .*?' . PHP_EOL . PHP_EOL . 'OK: no name collision found~'; - $expectedSuccessRegex = '~^OK: no name collision found~'; + $expectedSuccessWithConfigRegex = '~^Using config .*?' . PHP_EOL . PHP_EOL . 'OK \(no name collision found\)~'; + $expectedSuccessRegex = '~^OK \(no name collision found\)~'; $space = ' '; // bypass editorconfig checker $expectedClasses = <<> $expectedResults * @dataProvider provideCases */ - public function testCollisionDetection(array $paths, array $excludedPaths, array $expectedResults): void + public function testCollisionDetection( + array $paths, + array $excludedPaths, + int $expectedAnalysedFiles, + int $expectedExcludedFiles, + array $expectedResults + ): void { $detector = new CollisionDetector( new DetectionConfig( @@ -95,12 +101,11 @@ public function testCollisionDetection(array $paths, array $excludedPaths, array __DIR__ ) ); - $collidingClasses = $detector->getCollidingTypes(); + $result = $detector->getCollidingTypes(); - self::assertEquals( - $expectedResults, - $collidingClasses - ); + self::assertSame($expectedAnalysedFiles, $result->getAnalysedFilesCount()); + self::assertSame($expectedExcludedFiles, $result->getExcludedFilesCount()); + self::assertEquals($expectedResults, $result->getCollisions()); } private function runCommand(string $command, int $expectedExitCode): string @@ -143,18 +148,24 @@ public function provideCases(): iterable yield 'allowed duplicates' => [ 'paths' => [__DIR__ . '/data/allowed-duplicates'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [], ]; yield 'use statements' => [ 'paths' => [__DIR__ . '/data/use-statement'], // basically tests that isWithinUseStatement is working properly 'excludedPaths' => [], + 'expectedAnalysedFiles' => 3, + 'expectedExcludedFiles' => 0, 'expectedResults' => [], ]; yield 'simple cases' => [ 'paths' => [__DIR__ . '/data/basic-cases/simple.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'DuplicateClass' => [ new FileLine('/data/basic-cases/simple.php', 3), @@ -174,6 +185,8 @@ public function provideCases(): iterable yield 'html case' => [ 'paths' => [__DIR__ . '/data/basic-cases/html.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Bar' => [ new FileLine('/data/basic-cases/html.php', 3), @@ -185,6 +198,8 @@ public function provideCases(): iterable yield 'fatal error' => [ 'paths' => [__DIR__ . '/data/fatal-error/code.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Exists' => [ new FileLine('/data/fatal-error/code.php', 6), @@ -196,6 +211,8 @@ public function provideCases(): iterable yield 'groups' => [ 'paths' => [__DIR__ . '/data/basic-cases/groups.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Go' => [ new FileLine('/data/basic-cases/groups.php', 3), @@ -209,6 +226,8 @@ public function provideCases(): iterable yield 'groups with enum' => [ 'paths' => [__DIR__ . '/data/basic-cases/groups-with-enum.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Go' => [ new FileLine('/data/basic-cases/groups-with-enum.php', 3), @@ -223,6 +242,8 @@ public function provideCases(): iterable yield 'multi namespace' => [ 'paths' => [__DIR__ . '/data/basic-cases/multiple-namespaces.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Foo\X' => [ new FileLine('/data/basic-cases/multiple-namespaces.php', 5), @@ -234,6 +255,8 @@ public function provideCases(): iterable yield 'multi namespace braced' => [ 'paths' => [__DIR__ . '/data/basic-cases/multiple-namespaces-braced.php'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 1, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Foo\X' => [ new FileLine('/data/basic-cases/multiple-namespaces-braced.php', 4), @@ -245,6 +268,8 @@ public function provideCases(): iterable yield 'more files' => [ 'paths' => [__DIR__ . '/data/multiple-files'], 'excludedPaths' => [], + 'expectedAnalysedFiles' => 5, + 'expectedExcludedFiles' => 0, 'expectedResults' => [ 'Foo\NamespacedClass' => [ new FileLine('/data/multiple-files/colliding1.php', 11), @@ -276,6 +301,8 @@ public function provideCases(): iterable yield 'more files with exclude' => [ 'paths' => [__DIR__ . '/data/multiple-files'], 'excludedPaths' => [__DIR__ . '/data/multiple-files/colliding3.php'], + 'expectedAnalysedFiles' => 4, + 'expectedExcludedFiles' => 1, 'expectedResults' => [ 'GlobalClass' => [ new FileLine('/data/multiple-files/colliding1.php', 4),