From 5a444e6f30231e33b81e41880d5378df13e9a239 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 3 Jun 2024 11:39:44 +0200 Subject: [PATCH 1/5] Highlight in reverse inline differences --- .../OutputFormatterStyleConfigurator.php | 6 +- src/Differ/DiffColorizer.php | 54 +++++++-- tests/phpunit/Differ/DiffColorizerTest.php | 113 ++++++++++++++---- 3 files changed, 136 insertions(+), 37 deletions(-) diff --git a/src/Console/OutputFormatterStyleConfigurator.php b/src/Console/OutputFormatterStyleConfigurator.php index 0a23644d0a..dcbe9d6040 100644 --- a/src/Console/OutputFormatterStyleConfigurator.php +++ b/src/Console/OutputFormatterStyleConfigurator.php @@ -71,8 +71,10 @@ private static function configureMutantStyle(OutputFormatterInterface $formatter private static function configureDiffStyle(OutputFormatterInterface $formatter): void { - $formatter->setStyle('diff-add', new OutputFormatterStyle('green')); - $formatter->setStyle('diff-del', new OutputFormatterStyle('red')); + $formatter->setStyle('diff-add', new OutputFormatterStyle('green', null, ['bold'])); + $formatter->setStyle('diff-add-inline', new OutputFormatterStyle( 'green', null, ['bold', 'reverse'])); + $formatter->setStyle('diff-del', new OutputFormatterStyle('red', null, ['bold'])); + $formatter->setStyle('diff-del-inline', new OutputFormatterStyle( 'red', null, ['bold', 'reverse'])); } private static function configureMutationScoreStyle(OutputFormatterInterface $formatter): void diff --git a/src/Differ/DiffColorizer.php b/src/Differ/DiffColorizer.php index b317b28b0c..00a044e04f 100644 --- a/src/Differ/DiffColorizer.php +++ b/src/Differ/DiffColorizer.php @@ -35,7 +35,7 @@ namespace Infection\Differ; -use function array_map; +use Webmozart\Assert\Assert; use function explode; use function implode; use function sprintf; @@ -49,21 +49,49 @@ class DiffColorizer { public function colorize(string $diff): string { - $lines = array_map( - static function (string $line): string { - if (str_starts_with($line, '-')) { - return sprintf('%s', $line); - } + $lines = explode("\n", $diff); + foreach ($lines as $index => $line) { + if (! str_starts_with($line, '+')) { + continue; + } - if (str_starts_with($line, '+')) { - return sprintf('%s', $line); - } + $prevIndex = $index - 1; + $prevLine = $lines[$prevIndex]; - return $line; - }, - explode("\n", $diff), - ); + Assert::same($prevLine[0], '-'); + + $lines[$prevIndex] = sprintf('-%s', + $this->inlineDiff(substr($prevLine, 1), substr($line, 1), '', '') + ); + $lines[$index] = sprintf('+%s', + $this->inlineDiff(substr($line, 1), substr($prevLine, 1), '', '') + ); + } return sprintf('%s%s', "\n", implode("\n", $lines)); } + + private function inlineDiff(string $previousLine, string $nextLine, string $leftAddition, string $rightAddition): string + { + $previousLineLength = mb_strlen($previousLine); + $nextLineLength = mb_strlen($nextLine); + + $start = $previousLineLength; + while ($start && 0 !== mb_strpos($nextLine, mb_substr($previousLine, 0, $start))) { + --$start; + } + + $end = $start; + while ($end < $previousLineLength && mb_strrpos($nextLine, ($t = mb_substr($previousLine, $end)), $start) !== ($nextLineLength - mb_strlen($t))) { + ++$end; + } + + $return = $previousLine; + if ($start < $end) { + $return = substr_replace($return, $rightAddition, $end, 0); + $return = substr_replace($return, $leftAddition, $start, 0); + } + + return $return; + } } diff --git a/tests/phpunit/Differ/DiffColorizerTest.php b/tests/phpunit/Differ/DiffColorizerTest.php index 5ea26e7f4a..8772a0274f 100644 --- a/tests/phpunit/Differ/DiffColorizerTest.php +++ b/tests/phpunit/Differ/DiffColorizerTest.php @@ -37,36 +37,105 @@ use Infection\Differ\DiffColorizer; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; #[CoversClass(DiffColorizer::class)] final class DiffColorizerTest extends TestCase { - public function test_id_adds_colours_to_a_given_diff(): void + /** + * @param non-empty-string $originalDiff + * @param non-empty-string $expected + */ + #[DataProvider('provideDiffs')] + public function test_id_adds_colours_to_a_given_diff(string $originalDiff, string $expected): void { - $originalDiff = <<<'CODE' - --- Original - +++ New - @@ @@ - function ($a) { - - return $a < 0; - + return $a <= 0; - } - CODE; - - $expected = <<<'CODE' - - --- Original - +++ New - @@ @@ - function ($a) { - - return $a < 0; - + return $a <= 0; - } - CODE; - $actual = (new DiffColorizer())->colorize($originalDiff); $this->assertSame($expected, $actual); } + + /** + * @return list> + */ + public static function provideDiffs(): array + { + return [ + 'full-deletion' => [ + <<<'CODE' + function ($a) { + - exit(); + + + } + CODE, + <<<'CODE' + + function ($a) { + - exit(); + + + } + CODE, + ], + 'full-addition' => [ + <<<'CODE' + function ($a) { + - + + exit(); + } + CODE, + <<<'CODE' + + function ($a) { + - + + exit(); + } + CODE, + ], + 'partial-deletion' => [ + <<<'CODE' + function ($a) { + - return 'foo' . 'bar'; + + return 'foo'; + } + CODE, + <<<'CODE' + + function ($a) { + - return 'foo' . 'bar'; + + return 'foo'; + } + CODE, + ], + 'partial-addition' => [ + <<<'CODE' + function ($a) { + - return 'foo'; + + return 'foo' . 'bar'; + } + CODE, + <<<'CODE' + + function ($a) { + - return 'foo'; + + return 'foo' . 'bar'; + } + CODE, + ], + 'deletion-and-addition' => [ + <<<'CODE' + function ($a, $b) { + - return $a && $b; + + return $a || $b; + } + CODE, + <<<'CODE' + + function ($a, $b) { + - return $a && $b; + + return $a || $b; + } + CODE, + ], + ]; + } } From 083f61379d283ecac7a4a5ecea32a5b39377df13 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 3 Jun 2024 11:40:13 +0200 Subject: [PATCH 2/5] Get rid of useless diff header --- src/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Container.php b/src/Container.php index 9ed41fc19a..606860dfff 100644 --- a/src/Container.php +++ b/src/Container.php @@ -285,7 +285,7 @@ public static function create(): self $container->getPrinter(), $container->getMutantCodeFactory(), ), - Differ::class => static fn (): Differ => new Differ(new BaseDiffer(new UnifiedDiffOutputBuilder())), + Differ::class => static fn (): Differ => new Differ(new BaseDiffer(new UnifiedDiffOutputBuilder(''))), SyncEventDispatcher::class => static fn (): SyncEventDispatcher => new SyncEventDispatcher(), ParallelProcessRunner::class => static fn (self $container): ParallelProcessRunner => new ParallelProcessRunner($container->getConfiguration()->getThreadCount()), DryProcessRunner::class => static fn (): DryProcessRunner => new DryProcessRunner(), From 97bbd2fcc93b5bea6856d576374e19d0c15b00a9 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 3 Jun 2024 11:50:59 +0200 Subject: [PATCH 3/5] CS Fix --- psalm-baseline.xml | 5 + .../OutputFormatterStyleConfigurator.php | 4 +- src/Differ/DiffColorizer.php | 24 +++- tests/phpunit/Differ/DiffColorizerTest.php | 112 +++++++++--------- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index a59fc0899e..194258e7ef 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,10 @@ + + + $lines[$prevIndex] + + converters[$name->toLowerString()]($node)]]> diff --git a/src/Console/OutputFormatterStyleConfigurator.php b/src/Console/OutputFormatterStyleConfigurator.php index dcbe9d6040..349c3f41e6 100644 --- a/src/Console/OutputFormatterStyleConfigurator.php +++ b/src/Console/OutputFormatterStyleConfigurator.php @@ -72,9 +72,9 @@ private static function configureMutantStyle(OutputFormatterInterface $formatter private static function configureDiffStyle(OutputFormatterInterface $formatter): void { $formatter->setStyle('diff-add', new OutputFormatterStyle('green', null, ['bold'])); - $formatter->setStyle('diff-add-inline', new OutputFormatterStyle( 'green', null, ['bold', 'reverse'])); + $formatter->setStyle('diff-add-inline', new OutputFormatterStyle('green', null, ['bold', 'reverse'])); $formatter->setStyle('diff-del', new OutputFormatterStyle('red', null, ['bold'])); - $formatter->setStyle('diff-del-inline', new OutputFormatterStyle( 'red', null, ['bold', 'reverse'])); + $formatter->setStyle('diff-del-inline', new OutputFormatterStyle('red', null, ['bold', 'reverse'])); } private static function configureMutationScoreStyle(OutputFormatterInterface $formatter): void diff --git a/src/Differ/DiffColorizer.php b/src/Differ/DiffColorizer.php index 00a044e04f..a13f35ec64 100644 --- a/src/Differ/DiffColorizer.php +++ b/src/Differ/DiffColorizer.php @@ -35,11 +35,17 @@ namespace Infection\Differ; -use Webmozart\Assert\Assert; use function explode; use function implode; +use function mb_strlen; +use function mb_strpos; +use function mb_strrpos; +use function mb_substr; use function sprintf; use function str_starts_with; +use function substr; +use function substr_replace; +use Webmozart\Assert\Assert; /** * @internal @@ -50,21 +56,24 @@ class DiffColorizer public function colorize(string $diff): string { $lines = explode("\n", $diff); + foreach ($lines as $index => $line) { - if (! str_starts_with($line, '+')) { + if (!str_starts_with($line, '+')) { continue; } + Assert::greaterThan($index, 0); + $prevIndex = $index - 1; $prevLine = $lines[$prevIndex]; Assert::same($prevLine[0], '-'); $lines[$prevIndex] = sprintf('-%s', - $this->inlineDiff(substr($prevLine, 1), substr($line, 1), '', '') + $this->inlineDiff(substr($prevLine, 1), substr($line, 1), '', ''), ); $lines[$index] = sprintf('+%s', - $this->inlineDiff(substr($line, 1), substr($prevLine, 1), '', '') + $this->inlineDiff(substr($line, 1), substr($prevLine, 1), '', ''), ); } @@ -77,16 +86,19 @@ private function inlineDiff(string $previousLine, string $nextLine, string $left $nextLineLength = mb_strlen($nextLine); $start = $previousLineLength; - while ($start && 0 !== mb_strpos($nextLine, mb_substr($previousLine, 0, $start))) { + + while ($start !== 0 && mb_strpos($nextLine, mb_substr($previousLine, 0, $start)) !== 0) { --$start; } $end = $start; - while ($end < $previousLineLength && mb_strrpos($nextLine, ($t = mb_substr($previousLine, $end)), $start) !== ($nextLineLength - mb_strlen($t))) { + + while ($end < $previousLineLength && mb_strrpos($nextLine, $t = mb_substr($previousLine, $end), $start) !== ($nextLineLength - mb_strlen($t))) { ++$end; } $return = $previousLine; + if ($start < $end) { $return = substr_replace($return, $rightAddition, $end, 0); $return = substr_replace($return, $leftAddition, $start, 0); diff --git a/tests/phpunit/Differ/DiffColorizerTest.php b/tests/phpunit/Differ/DiffColorizerTest.php index 8772a0274f..7eff31df28 100644 --- a/tests/phpunit/Differ/DiffColorizerTest.php +++ b/tests/phpunit/Differ/DiffColorizerTest.php @@ -56,85 +56,85 @@ public function test_id_adds_colours_to_a_given_diff(string $originalDiff, strin } /** - * @return list> + * @return array> */ public static function provideDiffs(): array { return [ 'full-deletion' => [ <<<'CODE' - function ($a) { - - exit(); - + - } - CODE, + function ($a) { + - exit(); + + + } + CODE, <<<'CODE' - - function ($a) { - - exit(); - + - } - CODE, + + function ($a) { + - exit(); + + + } + CODE, ], 'full-addition' => [ <<<'CODE' - function ($a) { - - - + exit(); - } - CODE, + function ($a) { + - + + exit(); + } + CODE, <<<'CODE' - - function ($a) { - - - + exit(); - } - CODE, + + function ($a) { + - + + exit(); + } + CODE, ], 'partial-deletion' => [ <<<'CODE' - function ($a) { - - return 'foo' . 'bar'; - + return 'foo'; - } - CODE, + function ($a) { + - return 'foo' . 'bar'; + + return 'foo'; + } + CODE, <<<'CODE' - - function ($a) { - - return 'foo' . 'bar'; - + return 'foo'; - } - CODE, + + function ($a) { + - return 'foo' . 'bar'; + + return 'foo'; + } + CODE, ], 'partial-addition' => [ <<<'CODE' - function ($a) { - - return 'foo'; - + return 'foo' . 'bar'; - } - CODE, + function ($a) { + - return 'foo'; + + return 'foo' . 'bar'; + } + CODE, <<<'CODE' - - function ($a) { - - return 'foo'; - + return 'foo' . 'bar'; - } - CODE, + + function ($a) { + - return 'foo'; + + return 'foo' . 'bar'; + } + CODE, ], 'deletion-and-addition' => [ <<<'CODE' - function ($a, $b) { - - return $a && $b; - + return $a || $b; - } - CODE, + function ($a, $b) { + - return $a && $b; + + return $a || $b; + } + CODE, <<<'CODE' - - function ($a, $b) { - - return $a && $b; - + return $a || $b; - } - CODE, + + function ($a, $b) { + - return $a && $b; + + return $a || $b; + } + CODE, ], ]; } From a203cbe04fea7a69f76e8ab5e6fedce76ce234aa Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 3 Jun 2024 12:01:22 +0200 Subject: [PATCH 4/5] Adapt unit tests on style --- tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php b/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php index 0a57178cef..0841d5f5d9 100644 --- a/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php +++ b/tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php @@ -48,7 +48,7 @@ public function test_it_adds_styles(): void { $formatter = $this->createMock(OutputFormatterInterface::class); $formatter - ->expects($this->exactly(14)) + ->expects($this->exactly(16)) ->method('setStyle') ; From 7ba20b6b798066c4829e3d69593abfce05f0017a Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 3 Jun 2024 14:14:59 +0200 Subject: [PATCH 5/5] Fix header slice offset for Stryker --- src/Logger/Html/StrykerHtmlReportBuilder.php | 4 +--- .../Logger/Html/StrykerHtmlReportBuilderTest.php | 12 ------------ 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Logger/Html/StrykerHtmlReportBuilder.php b/src/Logger/Html/StrykerHtmlReportBuilder.php index 9d49139cc6..d134470715 100644 --- a/src/Logger/Html/StrykerHtmlReportBuilder.php +++ b/src/Logger/Html/StrykerHtmlReportBuilder.php @@ -91,7 +91,7 @@ final class StrykerHtmlReportBuilder ]; private const PLUS_LENGTH = 1; - private const DIFF_HEADERS_LINES_COUNT = 3; + private const DIFF_HEADERS_LINES_COUNT = 1; public function __construct(private readonly MetricsCalculator $metricsCalculator, private readonly ResultsCollector $resultsCollector) { @@ -276,8 +276,6 @@ private function retrieveReplacementFromDiff(string $diff): string static fn (string $line): string => isset($line[0]) ? substr($line, self::PLUS_LENGTH) : $line, array_filter( /* - --- Original - +++ New @@ @@ */ array_slice($lines, self::DIFF_HEADERS_LINES_COUNT), diff --git a/tests/phpunit/Logger/Html/StrykerHtmlReportBuilderTest.php b/tests/phpunit/Logger/Html/StrykerHtmlReportBuilderTest.php index bb41572d19..58e703dc85 100644 --- a/tests/phpunit/Logger/Html/StrykerHtmlReportBuilderTest.php +++ b/tests/phpunit/Logger/Html/StrykerHtmlReportBuilderTest.php @@ -289,8 +289,6 @@ private static function initHtmlReportCollector(Collector $collector): void self::createMutantExecutionResult( DetectionStatus::KILLED, <<<'DIFF' - --- Original - +++ New @@ @@ use function array_fill_keys; final class ForHtmlReport @@ -320,8 +318,6 @@ final class ForHtmlReport self::createMutantExecutionResult( DetectionStatus::ESCAPED, <<<'DIFF' - --- Original - +++ New @@ @@ { public function add(int $a, int $b) : int @@ -348,8 +344,6 @@ public function add(int $a, int $b) : int self::createMutantExecutionResult( DetectionStatus::ESCAPED, <<<'DIFF' - --- Original - +++ New @@ @@ public function add(int $a, int $b) : int { @@ -376,8 +370,6 @@ public function add(int $a, int $b) : int self::createMutantExecutionResult( DetectionStatus::ESCAPED, <<<'DIFF' - --- Original - +++ New @@ @@ default: break; @@ -406,8 +398,6 @@ public function add(int $a, int $b) : int self::createMutantExecutionResult( DetectionStatus::KILLED, <<<'DIFF' - --- Original - +++ New @@ @@ use function array_fill_keys; final class ForHtmlReport2 @@ -434,8 +424,6 @@ final class ForHtmlReport2 self::createMutantExecutionResult( DetectionStatus::KILLED, <<<'DIFF' - --- Original - +++ New @@ @@ use function array_fill_keys; final class ForHtmlReport2