From b543b541e48bc45cdb2ae30365e1f787f76a2302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Kr=C3=A4mer?= <4996022+floriankraemer@users.noreply.github.com> Date: Tue, 1 Oct 2024 21:33:41 +0200 Subject: [PATCH] Fixes and config refactoring (#11) * Fixing the interface declaration * Working on the docker setup * Fixing phpstan issues * Refactoring the config --- Makefile | 4 +- composer.json | 1 - config.yml | 19 +++-- docker-compose.yml | 1 + docker/php/Dockerfile | 6 ++ docs/Configuration.md | 10 +++ src/Application.php | 3 + .../Cognitive/CognitiveMetricsCollection.php | 20 +++-- .../Cognitive/CognitiveMetricsCollector.php | 41 +++++------ .../Exporter/DataExporterInterface.php | 4 +- src/Business/Cognitive/ScoreCalculator.php | 20 ++--- src/Business/MetricsFacade.php | 10 ++- src/Command/CognitiveMetricsCommand.php | 40 +++++++--- .../CognitiveMetricTextRenderer.php | 46 +++++++++--- src/Config/CognitiveConfig.php | 27 +++++++ src/Config/ConfigFactory.php | 35 +++++++++ src/Config/ConfigLoader.php | 24 +++--- src/Config/ConfigService.php | 7 +- src/Config/MetricsConfig.php | 18 +++++ .../Cognitive/BaselineServiceTest.php | 73 +++++++++++++++++++ .../CognitiveMetricsCollectorTest.php | 18 +++-- tests/Unit/Business/Cognitive/DeltaTest.php | 27 +++++++ .../Cognitive/ScoreCalculatorTest.php | 2 +- tests/Unit/Config/ConfigLoaderTest.php | 20 +---- 24 files changed, 360 insertions(+), 116 deletions(-) create mode 100644 src/Config/CognitiveConfig.php create mode 100644 src/Config/ConfigFactory.php create mode 100644 src/Config/MetricsConfig.php create mode 100644 tests/Unit/Business/Cognitive/BaselineServiceTest.php diff --git a/Makefile b/Makefile index 6c4ce19..64907eb 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,8 @@ coverage-html: docker compose run php composer test-coverage-html all: - @echo "Running CS-Fixer, CS-Checker, Static Analyser and Tests" - docker compose run php composer all + @echo "Running CS-Checker, Static Analyser and Tests" + docker compose run -T php composer all shell: @echo "Running shell" diff --git a/composer.json b/composer.json index c4d17bb..4f2524b 100644 --- a/composer.json +++ b/composer.json @@ -75,7 +75,6 @@ "bin/phpbench run tests/Benchmark/ --report=aggregate" ], "all": [ - "@csfix", "@cscheck", "@analyze", "@test" diff --git a/config.yml b/config.yml index 7ef89c2..3e0731b 100644 --- a/config.yml +++ b/config.yml @@ -1,35 +1,38 @@ cognitive: + excludeFilePatterns: excludePatterns: + scoreThreshold: 0.5 + showOnlyMethodsExceedingThreshold: false metrics: lineCount: threshold: 60 scale: 25.0 + enabled: true argCount: threshold: 4 scale: 1.0 + enabled: true returnCount: threshold: 2 scale: 5.0 + enabled: true variableCount: threshold: 2 scale: 5.0 + enabled: true propertyCallCount: threshold: 2 scale: 15.0 + enabled: true ifCount: threshold: 3 scale: 1.0 + enabled: true ifNestingLevel: threshold: 1 scale: 1.0 + enabled: true elseCount: threshold: 1 scale: 1.0 - -halstead: - threshold: - difficulty: 0.0 - effort: 0.0 - time: 0.0 - bugs: 0.0 - volume: 0.0 + enabled: true diff --git a/docker-compose.yml b/docker-compose.yml index a8afd5a..f057de7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,3 +9,4 @@ services: command: "tail -f /dev/null" ports: - 9003:9003 + user: php diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 972bd55..cfe1efb 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -16,3 +16,9 @@ RUN echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN git config --global --add safe.directory /app + +# Create a new user +RUN useradd -m php + +# Set the new user as the default user for the container +USER php diff --git a/docs/Configuration.md b/docs/Configuration.md index 5bfef72..f1fc5b5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -57,3 +57,13 @@ cognitive: ``` It is recommended to play with the values until you get weights that you are comfortable with. The default values are a good starting point. + +## Excluding Files + +Regular expression patterns can be defined to exclude files from the analysis. + +```yaml +cognitive: + excludeFilePatterns: + - '(.*)Test.php' +``` diff --git a/src/Application.php b/src/Application.php index 6e02f4d..1dc8f9f 100644 --- a/src/Application.php +++ b/src/Application.php @@ -51,6 +51,9 @@ private function registerServices(): void ->setPublic(true); $this->containerBuilder->register(CognitiveMetricTextRenderer::class, CognitiveMetricTextRenderer::class) + ->setArguments([ + new Reference(OutputInterface::class) + ]) ->setPublic(true); $this->containerBuilder->register(BaselineService::class, BaselineService::class) diff --git a/src/Business/Cognitive/CognitiveMetricsCollection.php b/src/Business/Cognitive/CognitiveMetricsCollection.php index 84c4556..87a9b83 100644 --- a/src/Business/Cognitive/CognitiveMetricsCollection.php +++ b/src/Business/Cognitive/CognitiveMetricsCollection.php @@ -80,8 +80,9 @@ public function contains(CognitiveMetrics $otherMetric): bool public function getClassWithMethod(string $class, string $method): ?CognitiveMetrics { - if (isset($this->metrics[$class . '::' . $method])) { - return $this->metrics[$class . '::' . $method]; + $fullMethod = $class . '::' . $method; + if (isset($this->metrics[$fullMethod])) { + return $this->metrics[$fullMethod]; } return null; @@ -94,6 +95,16 @@ public function filterByClassName(string $className): CognitiveMetricsCollection }); } + private function assertMetricHasGetter( + CognitiveMetrics $metric, + string $getter, + string $property + ): void { + if (!method_exists($metric, $getter)) { + throw new InvalidArgumentException("Property '$property' does not exist in CognitiveMetrics class"); + } + } + /** * Group the collection by a property of the CognitiveMetrics object * @@ -107,12 +118,9 @@ public function groupBy(string $property): array foreach ($this->metrics as $metric) { $getter = 'get' . ucfirst($property); - if (!method_exists($metric, $getter)) { - throw new InvalidArgumentException("Property '$property' does not exist in CognitiveMetrics class"); - } + $this->assertMetricHasGetter($metric, $getter, $property); $key = $metric->$getter(); - if (!isset($grouped[$key])) { $grouped[$key] = new self(); } diff --git a/src/Business/Cognitive/CognitiveMetricsCollector.php b/src/Business/Cognitive/CognitiveMetricsCollector.php index 10acf19..f974839 100644 --- a/src/Business/Cognitive/CognitiveMetricsCollector.php +++ b/src/Business/Cognitive/CognitiveMetricsCollector.php @@ -6,6 +6,7 @@ use Phauthentic\CognitiveCodeAnalysis\Business\DirectoryScanner; use Phauthentic\CognitiveCodeAnalysis\CognitiveAnalysisException; +use Phauthentic\CognitiveCodeAnalysis\Config\CognitiveConfig; use Phauthentic\CognitiveCodeAnalysis\Config\ConfigService; use Phauthentic\CognitiveCodeAnalysis\PhpParser\CognitiveMetricsVisitor; use PhpParser\Error; @@ -34,38 +35,38 @@ public function __construct( $this->parser = $parserFactory->createForHostVersion(); } - /** - * @param array $config - * @return array - */ - private function getExcludePatternsFromConfig(array $config): array - { - if (isset($config['excludePatterns'])) { - return $config['excludePatterns']; - } - - return []; - } - /** * Collect cognitive metrics from the given path * * @param string $path - * @param array $config + * @param CognitiveConfig $config * @return CognitiveMetricsCollection + * @throws CognitiveAnalysisException */ - public function collect(string $path, array $config = []): CognitiveMetricsCollection + public function collect(string $path, CognitiveConfig $config): CognitiveMetricsCollection { - $files = $this->findSourceFiles($path, $this->getExcludePatternsFromConfig($config)); + $files = $this->findSourceFiles($path, $config->excludeFilePatterns); return $this->findMetrics($files); } + private function getCodeFromFile(SplFileInfo $file): string + { + $code = file_get_contents($file->getRealPath()); + + if ($code === false) { + throw new CognitiveAnalysisException("Could not read file: {$file->getRealPath()}"); + } + + return $code; + } + /** * Collect metrics from the found source files * * @param iterable $files * @return CognitiveMetricsCollection + * @throws CognitiveAnalysisException */ private function findMetrics(iterable $files): CognitiveMetricsCollection { @@ -81,11 +82,7 @@ private function findMetrics(iterable $files): CognitiveMetricsCollection $plugin->beforeFindMetrics($file); } - $code = file_get_contents($file->getRealPath()); - - if ($code === false) { - throw new CognitiveAnalysisException("Could not read file: {$file->getRealPath()}"); - } + $code = $this->getCodeFromFile($file); $this->traverser->addVisitor($visitor); $this->traverseAbstractSyntaxTree($code); @@ -139,7 +136,7 @@ private function processMethodMetrics( private function isExcluded(string $classAndMethod): bool { - $regexes = $this->configService->getConfig()['cognitive']['excludePatterns']; + $regexes = $this->configService->getConfig()->excludePatterns; foreach ($regexes as $regex) { if (preg_match('/' . $regex . '/', $classAndMethod, $matches)) { diff --git a/src/Business/Cognitive/Exporter/DataExporterInterface.php b/src/Business/Cognitive/Exporter/DataExporterInterface.php index b8820f7..727f000 100644 --- a/src/Business/Cognitive/Exporter/DataExporterInterface.php +++ b/src/Business/Cognitive/Exporter/DataExporterInterface.php @@ -4,10 +4,12 @@ namespace Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Exporter; +use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollection; + /** * */ interface DataExporterInterface { - //public function export(array $metrics, string $filename): void; + public function export(CognitiveMetricsCollection $metrics, string $filename): void; } diff --git a/src/Business/Cognitive/ScoreCalculator.php b/src/Business/Cognitive/ScoreCalculator.php index 9fa5f54..f4634ee 100644 --- a/src/Business/Cognitive/ScoreCalculator.php +++ b/src/Business/Cognitive/ScoreCalculator.php @@ -4,6 +4,8 @@ namespace Phauthentic\CognitiveCodeAnalysis\Business\Cognitive; +use Phauthentic\CognitiveCodeAnalysis\Config\CognitiveConfig; + /** * */ @@ -25,13 +27,11 @@ class ScoreCalculator /** * @param CognitiveMetrics $metrics - * @param array $metricConfiguration + * @param CognitiveConfig $config * @return void */ - public function calculate(CognitiveMetrics $metrics, array $metricConfiguration = []): void + public function calculate(CognitiveMetrics $metrics, CognitiveConfig $config): void { - $metricConfiguration = $metricConfiguration['metrics']; - // List of metric types to process $metricTypes = [ 'LineCount' => 'lineCount', @@ -45,7 +45,7 @@ public function calculate(CognitiveMetrics $metrics, array $metricConfiguration ]; // Calculate and set weights for each metric type - $this->calculateMetricWeights($metricTypes, $metrics, $metricConfiguration); + $this->calculateMetricWeights($metricTypes, $metrics, $config); // Calculate the overall score $this->calculateScore($metrics); @@ -65,11 +65,13 @@ private function calculateScore(CognitiveMetrics $metrics): void /** * @param array $metricTypes * @param CognitiveMetrics $metrics - * @param array $config + * @param CognitiveConfig $config * @return void */ - private function calculateMetricWeights(array $metricTypes, CognitiveMetrics $metrics, array $config): void + private function calculateMetricWeights(array $metricTypes, CognitiveMetrics $metrics, CognitiveConfig $config): void { + $metricConfigs = $config->metrics; + foreach ($metricTypes as $methodSuffix => $configKey) { $getMethod = 'get' . $methodSuffix; $setMethod = 'set' . $methodSuffix . 'Weight'; @@ -77,8 +79,8 @@ private function calculateMetricWeights(array $metricTypes, CognitiveMetrics $me $metrics->{$setMethod}( $this->calculateLogWeight( $metrics->{$getMethod}(), - $config[$configKey]['threshold'], - $config[$configKey]['scale'] + $metricConfigs[$configKey]->threshold, + $metricConfigs[$configKey]->scale, ) ); } diff --git a/src/Business/MetricsFacade.php b/src/Business/MetricsFacade.php index 1b89547..9dcd57a 100644 --- a/src/Business/MetricsFacade.php +++ b/src/Business/MetricsFacade.php @@ -10,6 +10,7 @@ use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Exporter\HtmlExporter; use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Exporter\JsonExporter; use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\ScoreCalculator; +use Phauthentic\CognitiveCodeAnalysis\Config\CognitiveConfig; use Phauthentic\CognitiveCodeAnalysis\Config\ConfigService; /** @@ -36,10 +37,10 @@ public function __construct( */ public function getCognitiveMetrics(string $path): CognitiveMetricsCollection { - $metricsCollection = $this->cognitiveMetricsCollector->collect($path, $this->configService->getConfig()['cognitive']); + $metricsCollection = $this->cognitiveMetricsCollector->collect($path, $this->configService->getConfig()); foreach ($metricsCollection as $metric) { - $this->scoreCalculator->calculate($metric, $this->configService->getConfig()['cognitive']); + $this->scoreCalculator->calculate($metric, $this->configService->getConfig()); } return $metricsCollection; @@ -56,6 +57,11 @@ public function loadConfig(string $configFilePath): void $this->configService->loadConfig($configFilePath); } + public function getConfig(): CognitiveConfig + { + return $this->configService->getConfig(); + } + public function metricsCollectionToCsv(CognitiveMetricsCollection $metricsCollection, string $filename): void { (new CsvExporter())->export($metricsCollection, $filename); diff --git a/src/Command/CognitiveMetricsCommand.php b/src/Command/CognitiveMetricsCommand.php index 0271fd0..77a0518 100644 --- a/src/Command/CognitiveMetricsCommand.php +++ b/src/Command/CognitiveMetricsCommand.php @@ -90,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Render the metrics to the console. - $this->metricTextRenderer->render($metricsCollection, [], $output); + $this->metricTextRenderer->render($metricsCollection, $this->metricsFacade->getConfig()); return Command::SUCCESS; } @@ -142,32 +142,54 @@ private function handleExportOptions(InputInterface $input, OutputInterface $out $reportType = $input->getOption(self::OPTION_REPORT_TYPE); $reportFile = $input->getOption(self::OPTION_REPORT_FILE); - // If both options are missing, return success - if ($reportType === null && $reportFile === null) { + if ($this->areBothReportOptionsMissing($reportType, $reportFile)) { return true; } - // If one of the options is provided but the other is missing, return false + if ($this->isOneReportOptionMissing($reportType, $reportFile, $output)) { + return false; + } + + if (!$this->isValidReportType($reportType, $output)) { + return false; + } + + $this->exportMetrics($reportType, $metricsCollection, $reportFile); + + return true; + } + + private function areBothReportOptionsMissing(?string $reportType, ?string $reportFile): bool + { + return $reportType === null && $reportFile === null; + } + + private function isOneReportOptionMissing(?string $reportType, ?string $reportFile, OutputInterface $output): bool + { if (($reportType !== null && $reportFile === null) || ($reportType === null && $reportFile !== null)) { $output->writeln('Both report type and file must be provided.'); - return false; + return true; } + return false; + } - // Validate the report type option + private function isValidReportType(?string $reportType, OutputInterface $output): bool + { if (!in_array($reportType, ['json', 'csv', 'html'])) { $message = sprintf('Invalid report type `%s` provided. Only `json`, `csv`, and `html` are accepted.', $reportType); $output->writeln('' . $message . ''); return false; } + return true; + } - // Proceed with export based on the report type + private function exportMetrics(string $reportType, CognitiveMetricsCollection $metricsCollection, string $reportFile): void + { match ($reportType) { 'json' => $this->metricsFacade->metricsCollectionToJson($metricsCollection, $reportFile), 'csv' => $this->metricsFacade->metricsCollectionToCsv($metricsCollection, $reportFile), 'html' => $this->metricsFacade->metricsCollectionToHtml($metricsCollection, $reportFile), default => null, }; - - return true; } } diff --git a/src/Command/Presentation/CognitiveMetricTextRenderer.php b/src/Command/Presentation/CognitiveMetricTextRenderer.php index 98aa364..ca169f5 100644 --- a/src/Command/Presentation/CognitiveMetricTextRenderer.php +++ b/src/Command/Presentation/CognitiveMetricTextRenderer.php @@ -6,6 +6,7 @@ use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetrics; use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollection; +use Phauthentic\CognitiveCodeAnalysis\Config\CognitiveConfig; use RuntimeException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Output\OutputInterface; @@ -15,34 +16,55 @@ */ class CognitiveMetricTextRenderer { + public function __construct( + private readonly OutputInterface $output + ) { + } + /** * @param CognitiveMetricsCollection $metricsCollection - * @param array> $baseline - * @param OutputInterface $output */ - public function render(CognitiveMetricsCollection $metricsCollection, array $baseline, OutputInterface $output): void + public function render(CognitiveMetricsCollection $metricsCollection, CognitiveConfig $config): void { $groupedByClass = $metricsCollection->groupBy('class'); foreach ($groupedByClass as $className => $metrics) { - $output->writeln("Class: $className"); - - $table = new Table($output); - $table->setStyle('box'); - $table->setHeaders($this->getTableHeaders()); - $rows = []; foreach ($metrics as $metric) { + if ( + $config->showOnlyMethodsExceedingThreshold && + $metric->getScore() <= $config->scoreThreshold + ) { + continue; + } + $row = $this->prepareTableRow($metric); $rows[] = $row; } - $table->setRows($rows); - $table->render(); - $output->writeln(""); + if (empty($rows)) { + continue; + } + + $this->renderTable((string)$className, $rows); } } + /** + * @param string $className + * @param array $rows + */ + private function renderTable(string $className, array $rows): void + { + $table = new Table($this->output); + $table->setStyle('box'); + $table->setHeaders($this->getTableHeaders()); + $this->output->writeln("Class: $className"); + $table->setRows($rows); + $table->render(); + $this->output->writeln(""); + } + /** * @return string[] */ diff --git a/src/Config/CognitiveConfig.php b/src/Config/CognitiveConfig.php new file mode 100644 index 0000000..a559fbd --- /dev/null +++ b/src/Config/CognitiveConfig.php @@ -0,0 +1,27 @@ + $excludeFilePatterns + * @param array $excludePatterns + * @param array $metrics + */ + public function __construct( + public readonly array $excludeFilePatterns, + public readonly array $excludePatterns, + public readonly array $metrics, + public readonly bool $showOnlyMethodsExceedingThreshold, + public readonly float $scoreThreshold + ) { + } +} diff --git a/src/Config/ConfigFactory.php b/src/Config/ConfigFactory.php new file mode 100644 index 0000000..575bf79 --- /dev/null +++ b/src/Config/ConfigFactory.php @@ -0,0 +1,35 @@ + $config + * @return CognitiveConfig + */ + public function fromArray(array $config): CognitiveConfig + { + $metrics = []; + foreach ($config['cognitive']['metrics'] as $name => $metric) { + $metrics[$name] = new MetricsConfig( + $metric['threshold'], + $metric['scale'], + $metric['enabled'] + ); + } + + return new CognitiveConfig( + $config['cognitive']['excludeFilePatterns'], + $config['cognitive']['excludePatterns'], + $metrics, + $config['cognitive']['showOnlyMethodsExceedingThreshold'], + $config['cognitive']['scoreThreshold'] + ); + } +} diff --git a/src/Config/ConfigLoader.php b/src/Config/ConfigLoader.php index c0d3a88..357a469 100644 --- a/src/Config/ConfigLoader.php +++ b/src/Config/ConfigLoader.php @@ -85,6 +85,17 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultValue([]) ->end() ->end() + ->arrayNode('excludeFilePatterns') + ->scalarPrototype() + ->defaultValue([]) + ->end() + ->end() + ->floatNode('scoreThreshold') + ->defaultValue(0.5) + ->end() + ->booleanNode('showOnlyMethodsExceedingThreshold') + ->defaultValue(false) + ->end() ->arrayNode('metrics') ->useAttributeAsKey('metric') ->arrayPrototype() @@ -108,19 +119,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->end() - ->arrayNode('halstead') - ->children() - ->arrayNode('threshold') - ->children() - ->floatNode('difficulty')->end() - ->floatNode('effort')->end() - ->floatNode('time')->end() - ->floatNode('bugs')->end() - ->floatNode('volume')->end() - ->end() - ->end() - ->end() - ->end() ->end(); return $treeBuilder; diff --git a/src/Config/ConfigService.php b/src/Config/ConfigService.php index f67b31b..c9f409d 100644 --- a/src/Config/ConfigService.php +++ b/src/Config/ConfigService.php @@ -34,11 +34,8 @@ public function loadConfig(string $configFilePath): void ]); } - /** - * @return array - */ - public function getConfig(): array + public function getConfig(): CognitiveConfig { - return $this->config; + return (new ConfigFactory())->fromArray($this->config); } } diff --git a/src/Config/MetricsConfig.php b/src/Config/MetricsConfig.php new file mode 100644 index 0000000..92621d6 --- /dev/null +++ b/src/Config/MetricsConfig.php @@ -0,0 +1,18 @@ +baselineService = new BaselineService(); + } + + public function testLoadBaselineThrowsExceptionIfFileDoesNotExist(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Baseline file does not exist.'); + + $this->baselineService->loadBaseline('non_existent_file.json'); + } + + public function testLoadBaselineThrowsExceptionIfInvalidJson(): void + { + $filePath = tempnam(sys_get_temp_dir(), 'baseline'); + file_put_contents($filePath, 'invalid json'); + + $this->expectException(JsonException::class); + + try { + $this->baselineService->loadBaseline($filePath); + } finally { + unlink($filePath); + } + } + + public function testLoadBaselineSuccess(): void + { + $filePath = tempnam(sys_get_temp_dir(), 'baseline'); + $baselineData = [ + 'TestClass' => [ + 'methods' => [ + 'testMethod' => [ + 'complexity' => 8, + 'size' => 18 + ] + ] + ] + ]; + + file_put_contents($filePath, json_encode($baselineData)); + + $result = $this->baselineService->loadBaseline($filePath); + + $this->assertIsArray($result); + $this->assertArrayHasKey('TestClass', $result); + $this->assertArrayHasKey('methods', $result['TestClass']); + $this->assertArrayHasKey('testMethod', $result['TestClass']['methods']); + + unlink($filePath); // Clean up + } +} diff --git a/tests/Unit/Business/Cognitive/CognitiveMetricsCollectorTest.php b/tests/Unit/Business/Cognitive/CognitiveMetricsCollectorTest.php index 0fd910e..dff86bd 100644 --- a/tests/Unit/Business/Cognitive/CognitiveMetricsCollectorTest.php +++ b/tests/Unit/Business/Cognitive/CognitiveMetricsCollectorTest.php @@ -22,6 +22,7 @@ class CognitiveMetricsCollectorTest extends TestCase { private CognitiveMetricsCollector $metricsCollector; + private ConfigService $configService; protected function setUp(): void { @@ -35,13 +36,18 @@ protected function setUp(): void new ConfigLoader(), ) ); + + $this->configService = new ConfigService( + new Processor(), + new ConfigLoader(), + ); } public function testCollectWithValidDirectoryPath(): void { $path = './tests/TestCode'; - $metricsCollection = $this->metricsCollector->collect($path); + $metricsCollection = $this->metricsCollector->collect($path, $this->configService->getConfig()); $this->assertInstanceOf(CognitiveMetricsCollection::class, $metricsCollection); $this->assertCount(23, $metricsCollection); @@ -66,7 +72,7 @@ public function testCollectWithExcludedClasses(): void $path = './tests/TestCode'; - $metricsCollection = $metricsCollector->collect($path); + $metricsCollection = $metricsCollector->collect($path, $this->configService->getConfig()); $this->assertInstanceOf(CognitiveMetricsCollection::class, $metricsCollection); $this->assertCount(22, $metricsCollection); @@ -76,7 +82,7 @@ public function testCollectWithValidFilePath(): void { $path = './tests/TestCode/Paginator.php'; - $metricsCollection = $this->metricsCollector->collect($path); + $metricsCollection = $this->metricsCollector->collect($path, $this->configService->getConfig()); $this->assertInstanceOf(CognitiveMetricsCollection::class, $metricsCollection); $this->assertGreaterThan(0, $metricsCollection->count(), 'CognitiveMetrics collection should not be empty'); @@ -85,7 +91,7 @@ public function testCollectWithValidFilePath(): void public function testCollectWithInvalidPath(): void { $this->expectException(RuntimeException::class); - $this->metricsCollector->collect('/invalid/path'); + $this->metricsCollector->collect('/invalid/path', $this->configService->getConfig()); } public function testCollectWithUnreadableFile(): void @@ -93,7 +99,7 @@ public function testCollectWithUnreadableFile(): void $path = './tests/TestCode/UnreadableFile.php'; $this->expectException(RuntimeException::class); - $this->metricsCollector->collect($path); + $this->metricsCollector->collect($path, $this->configService->getConfig()); } /** @@ -101,7 +107,7 @@ public function testCollectWithUnreadableFile(): void */ public function testCollectedMetrics(): void { - $metricsCollection = $this->metricsCollector->collect('./tests/TestCode2'); + $metricsCollection = $this->metricsCollector->collect('./tests/TestCode2', $this->configService->getConfig()); $metrics = $metricsCollection->getClassWithMethod('\TestClassForCounts', 'test'); $this->assertNotNull($metrics); diff --git a/tests/Unit/Business/Cognitive/DeltaTest.php b/tests/Unit/Business/Cognitive/DeltaTest.php index 138472d..661caa8 100644 --- a/tests/Unit/Business/Cognitive/DeltaTest.php +++ b/tests/Unit/Business/Cognitive/DeltaTest.php @@ -44,4 +44,31 @@ public function testDeltaWhenEqual(): void $this->assertFalse($delta->hasIncreased()); $this->assertSame(0.0, $delta->getValue()); } + + public function testDeltaHasChanged(): void + { + $before = 10.0; + $after = 2.0; + + $delta = new Delta($before, $after); + $this->assertFalse($delta->hasNotChanged()); + } + + public function testDeltaHasNotChanged(): void + { + $before = 10.0; + $after = 10.0; + + $delta = new Delta($before, $after); + $this->assertTrue($delta->hasNotChanged()); + } + + public function testDeltaToString(): void + { + $before = 10.0; + $after = 5.0; + + $delta = new Delta($before, $after); + $this->assertSame('-5', (string)$delta); + } } diff --git a/tests/Unit/Business/Cognitive/ScoreCalculatorTest.php b/tests/Unit/Business/Cognitive/ScoreCalculatorTest.php index f96824b..bfad217 100644 --- a/tests/Unit/Business/Cognitive/ScoreCalculatorTest.php +++ b/tests/Unit/Business/Cognitive/ScoreCalculatorTest.php @@ -43,7 +43,7 @@ public function testCalculate(): void new ConfigLoader() ))->getConfig(); - $this->scoreCalculator->calculate($this->metrics, $config['cognitive']); + $this->scoreCalculator->calculate($this->metrics, $config); // Assert the final score $this->assertGreaterThan(0, $this->metrics->getScore()); diff --git a/tests/Unit/Config/ConfigLoaderTest.php b/tests/Unit/Config/ConfigLoaderTest.php index 43bdab5..87d9561 100644 --- a/tests/Unit/Config/ConfigLoaderTest.php +++ b/tests/Unit/Config/ConfigLoaderTest.php @@ -35,32 +35,15 @@ public function testConfigTreeBuilder(): void 'scale' => 1.0, ], ], - ], - 'halstead' => [ - 'threshold' => [ - 'difficulty' => 5.0, - 'effort' => 3.0, - 'time' => 2.0, - 'bugs' => 1.0, - 'volume' => 4.0, - ], - ], + ] ]; $processedConfig = $processor->process($configTree, [$config]); $this->assertArrayHasKey('cognitive', $processedConfig); - $this->assertArrayHasKey('halstead', $processedConfig); - - // Assertions for 'cognitive' metrics $this->assertArrayHasKey('lineCount', $processedConfig['cognitive']['metrics']); $this->assertEquals(60.0, $processedConfig['cognitive']['metrics']['lineCount']['threshold']); $this->assertEquals(25.0, $processedConfig['cognitive']['metrics']['lineCount']['scale']); - - // Assertions for 'halstead' thresholds - $this->assertArrayHasKey('threshold', $processedConfig['halstead']); - $this->assertEquals(5.0, $processedConfig['halstead']['threshold']['difficulty']); - $this->assertEquals(3.0, $processedConfig['halstead']['threshold']['effort']); } public function testEmptyConfig(): void @@ -73,7 +56,6 @@ public function testEmptyConfig(): void $processedConfig = $processor->process($configTree, []); $this->assertEmpty($processedConfig['cognitive']['metrics']); - $this->assertEmpty($processedConfig['halstead']['threshold']); } public function testInvalidConfig(): void