From ddd7ee67628c0110fab131b3e7f57dc9d15b908d Mon Sep 17 00:00:00 2001 From: Michel Hunziker Date: Mon, 30 Dec 2024 20:01:32 +0100 Subject: [PATCH] Use content instead of line number for the fingerprint --- composer.json | 2 +- phpcs.xml.dist | 1 + phpstan.neon.dist | 2 + phpunit.xml.dist | 6 +++ src/Report/Gitlab.php | 38 ++++++++++++++++- tests/Fixtures/MixedViolations.php | 52 ----------------------- tests/Fixtures/ReportFixture.php | 20 --------- tests/Fixtures/SingleViolation.php | 41 ------------------- tests/Report/GitlabTest.php | 66 ++++++++++++++++++++++++------ tests/_files/Mixed.json | 1 + tests/_files/Mixed.php | 10 +++++ tests/_files/Multiple.json | 1 + tests/_files/Multiple.php | 16 ++++++++ tests/_files/Single.json | 1 + tests/_files/Single.php | 10 +++++ 15 files changed, 139 insertions(+), 128 deletions(-) delete mode 100644 tests/Fixtures/MixedViolations.php delete mode 100644 tests/Fixtures/ReportFixture.php delete mode 100644 tests/Fixtures/SingleViolation.php create mode 100644 tests/_files/Mixed.json create mode 100644 tests/_files/Mixed.php create mode 100644 tests/_files/Multiple.json create mode 100644 tests/_files/Multiple.php create mode 100644 tests/_files/Single.json create mode 100644 tests/_files/Single.php diff --git a/composer.json b/composer.json index ca9f405..afaa48a 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require-dev": { "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^8.5.14 || ^9.0", - "squizlabs/php_codesniffer": "^3.3.1" + "squizlabs/php_codesniffer": "^3.5.0" }, "autoload": { "psr-4": { diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 0cf9d38..4d9ed81 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -15,4 +15,5 @@ src tests + tests/_files/* diff --git a/phpstan.neon.dist b/phpstan.neon.dist index bb86c9e..7ff1558 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,5 +4,7 @@ parameters: paths: - src - tests + excludePaths: + - tests/_files/ scanDirectories: - vendor/squizlabs/php_codesniffer diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6503d20..6c7186c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,6 +7,12 @@ colors="true" forceCoversAnnotation="true" > + + + + + + tests diff --git a/src/Report/Gitlab.php b/src/Report/Gitlab.php index 156ed8f..3a511fc 100644 --- a/src/Report/Gitlab.php +++ b/src/Report/Gitlab.php @@ -11,8 +11,11 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Reports\Report; +use SplFileObject; +use function is_string; use function md5; +use function preg_replace; use function rtrim; use function str_replace; @@ -26,15 +29,25 @@ class Gitlab implements Report public function generateFileReport($report, File $phpcsFile, $showSources = false, $width = 80) { $hasOutput = false; + $fingerprints = []; foreach ($report['messages'] as $line => $lineErrors) { - foreach ($lineErrors as $column => $colErrors) { + $lineContent = $this->getContentOfLine($phpcsFile->getFilename(), $line); + + foreach ($lineErrors as $colErrors) { foreach ($colErrors as $error) { + $fingerprint = md5($report['filename'] . $lineContent . $error['source']); + if (isset($fingerprints[$fingerprint])) { + ++$fingerprints[$fingerprint]; + } else { + $fingerprints[$fingerprint] = 1; + } + $issue = [ 'type' => 'issue', 'categories' => ['Style'], 'check_name' => $error['source'], - 'fingerprint' => md5($report['filename'] . $error['message'] . $line . $column), + 'fingerprint' => $fingerprint . '-' . $fingerprints[$fingerprint], 'severity' => $error['type'] === 'ERROR' ? 'major' : 'minor', 'description' => str_replace(["\n", "\r", "\t"], ['\n', '\r', '\t'], $error['message']), 'location' => [ @@ -68,4 +81,25 @@ public function generate( ) { echo '[' . rtrim($cachedData, ',') . ']' . PHP_EOL; } + + /** + * @param string $filename + * @param int $line + * @return string + */ + private function getContentOfLine($filename, $line) + { + $file = new SplFileObject($filename); + + if (!$file->eof()) { + $file->seek($line - 1); + $contents = $file->current(); + + if (is_string($contents)) { + return (string) preg_replace('/\s+/', '', $contents); + } + } + + return ''; + } } diff --git a/tests/Fixtures/MixedViolations.php b/tests/Fixtures/MixedViolations.php deleted file mode 100644 index ec307b0..0000000 --- a/tests/Fixtures/MixedViolations.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD-3-Clause License - */ - -declare(strict_types=1); - -namespace MichehTest\PhpCodeSniffer\Fixtures; - -class MixedViolations implements ReportFixture -{ - public function getReportData(): array - { - return [ - 'filename' => 'tests/Report/GitlabTest.php', - 'errors' => 1, - 'warnings' => 1, - 'fixable' => 1, - 'messages' => [ - 23 => [ - 32 => [ - 0 => [ - 'message' => 'There must be a single space between the colon and type in a return type declaration', - 'source' => 'PSR12.Functions.ReturnTypeDeclaration.SpaceBeforeReturnType', - 'severity' => 5, - 'fixable' => true, - 'type' => 'ERROR', - ], - ], - ], - 34 => [ - 9 => [ - 0 => [ - 'message' => 'Line exceeds 120 characters; contains 124 characters', - 'source' => 'Generic.Files.LineLength.TooLong', - 'severity' => 5, - 'fixable' => false, - 'type' => 'WARNING', - ], - ], - ], - ], - ]; - } - - public function getExpectedOutput(): string - { - return '{"type":"issue","categories":["Style"],"check_name":"PSR12.Functions.ReturnTypeDeclaration.SpaceBeforeReturnType","fingerprint":"766bf1f7eff186fd667447bfb0291078","severity":"major","description":"There must be a single space between the colon and type in a return type declaration","location":{"path":"tests/Report/GitlabTest.php","lines":{"begin":23,"end":23}}},{"type":"issue","categories":["Style"],"check_name":"Generic.Files.LineLength.TooLong","fingerprint":"517eda4b85a33cd09fc933259f873191","severity":"minor","description":"Line exceeds 120 characters; contains 124 characters","location":{"path":"tests/Report/GitlabTest.php","lines":{"begin":34,"end":34}}},'; - } -} diff --git a/tests/Fixtures/ReportFixture.php b/tests/Fixtures/ReportFixture.php deleted file mode 100644 index eba6ae6..0000000 --- a/tests/Fixtures/ReportFixture.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD-3-Clause License - */ - -declare(strict_types=1); - -namespace MichehTest\PhpCodeSniffer\Fixtures; - -interface ReportFixture -{ - /** - * @return array{filename: string, errors: int, warnings: int, fixable: int, messages: array} - */ - public function getReportData(): array; - - public function getExpectedOutput(): string; -} diff --git a/tests/Fixtures/SingleViolation.php b/tests/Fixtures/SingleViolation.php deleted file mode 100644 index 7768434..0000000 --- a/tests/Fixtures/SingleViolation.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD-3-Clause License - */ - -declare(strict_types=1); - -namespace MichehTest\PhpCodeSniffer\Fixtures; - -class SingleViolation implements ReportFixture -{ - public function getReportData(): array - { - return [ - 'filename' => 'src/Report/Gitlab.php', - 'errors' => 1, - 'warnings' => 0, - 'fixable' => 1, - 'messages' => [ - 18 => [ - 25 => [ - 0 => [ - 'message' => 'Header blocks must not contain blank lines', - 'source' => 'PSR12.Files.FileHeader.SpacingInsideBlock', - 'severity' => 5, - 'fixable' => true, - 'type' => 'ERROR', - ], - ], - ], - ], - ]; - } - - public function getExpectedOutput(): string - { - return '{"type":"issue","categories":["Style"],"check_name":"PSR12.Files.FileHeader.SpacingInsideBlock","fingerprint":"21f4cd5b7d88d9eb67402be5d56caa8b","severity":"major","description":"Header blocks must not contain blank lines","location":{"path":"src/Report/Gitlab.php","lines":{"begin":18,"end":18}}},'; - } -} diff --git a/tests/Report/GitlabTest.php b/tests/Report/GitlabTest.php index a6e4663..ebe2e26 100644 --- a/tests/Report/GitlabTest.php +++ b/tests/Report/GitlabTest.php @@ -10,12 +10,16 @@ namespace MichehTest\PhpCodeSniffer\Report; use Micheh\PhpCodeSniffer\Report\Gitlab; -use MichehTest\PhpCodeSniffer\Fixtures\MixedViolations; -use MichehTest\PhpCodeSniffer\Fixtures\ReportFixture; -use MichehTest\PhpCodeSniffer\Fixtures\SingleViolation; +use PHP_CodeSniffer\Config; use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Files\LocalFile; +use PHP_CodeSniffer\Reporter; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Runner; use PHPUnit\Framework\TestCase; +use function file_get_contents; + class GitlabTest extends TestCase { /** @@ -23,6 +27,22 @@ class GitlabTest extends TestCase */ private $report; + /** + * @var Config + */ + private static $config; + + + public static function setUpBeforeClass(): void + { + self::$config = new Config(); + self::$config->basepath = __DIR__ . '/../'; + self::$config->standards = ['PSR12']; + + $runner = new Runner(); + $runner->config = self::$config; + $runner->init(); + } protected function setUp(): void { @@ -48,27 +68,49 @@ public function testGenerateWithEmpty(): void } /** - * @return array>> + * @return array> */ public function violations(): array { return [ - 'single' => [SingleViolation::class], - 'mixed' => [MixedViolations::class], + 'single' => ['Single'], + 'mixed' => ['Mixed'], + 'multiple' => ['Multiple'], ]; } /** - * @param class-string $class * @covers \Micheh\PhpCodeSniffer\Report\Gitlab::generateFileReport * @dataProvider violations */ - public function testGenerateFileReport(string $class): void + public function testGenerateFileReport(string $fileName): void { - $fixture = new $class(); - self::assertInstanceOf(ReportFixture::class, $fixture); + $phpPath = __DIR__ . '/../_files/' . $fileName . '.php'; + self::assertFileExists($phpPath); + + $outputPath = __DIR__ . '/../_files/' . $fileName . '.json'; + self::assertFileExists($outputPath); + + $file = $this->createFile($phpPath); - $this->expectOutputString($fixture->getExpectedOutput()); - $this->report->generateFileReport($fixture->getReportData(), $this->createMock(File::class)); + $this->expectOutputString((string) file_get_contents($outputPath)); + $this->report->generateFileReport($this->getReportData($file), $file); + } + + private function createFile(string $path): File + { + $file = new LocalFile($path, new Ruleset(self::$config), self::$config); + $file->process(); + + return $file; + } + + /** + * @return array{filename: string, errors: int, warnings: int, fixable: int, messages: array} + */ + private function getReportData(File $file): array + { + $reporter = new Reporter(self::$config); + return $reporter->prepareFileReport($file); // @phpstan-ignore return.type } } diff --git a/tests/_files/Mixed.json b/tests/_files/Mixed.json new file mode 100644 index 0000000..4c17192 --- /dev/null +++ b/tests/_files/Mixed.json @@ -0,0 +1 @@ +{"type":"issue","categories":["Style"],"check_name":"Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine","fingerprint":"0da49d5a2f6b30f151e4abef2fccd4e2-1","severity":"major","description":"Opening brace should be on a new line","location":{"path":"_files/Mixed.php","lines":{"begin":7,"end":7}}},{"type":"issue","categories":["Style"],"check_name":"Generic.Files.LineLength.TooLong","fingerprint":"0be4b9620fcab36dcef7ba50bd3ac097-1","severity":"minor","description":"Line exceeds 120 characters; contains 143 characters","location":{"path":"_files/Mixed.php","lines":{"begin":8,"end":8}}}, \ No newline at end of file diff --git a/tests/_files/Mixed.php b/tests/_files/Mixed.php new file mode 100644 index 0000000..08338e4 --- /dev/null +++ b/tests/_files/Mixed.php @@ -0,0 +1,10 @@ +