Skip to content

Commit 8243458

Browse files
Improve failed phpt test diff
In case of multiline placeholder %a or %A we try to match multiple lines at the same time. The tricky part is to keep the expected output and the real output in sync since they can be different in size and amount of lines. Co-authored-by: Volker Dusch <github@wallbash.com> Co-authored-by: Sebastian Feldmann <sf@sebastian-feldmann.info>
1 parent dea8cff commit 8243458

File tree

1 file changed

+59
-13
lines changed

1 file changed

+59
-13
lines changed

src/Framework/Constraint/String/StringMatchesFormatDescription.php

+59-13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
use const DIRECTORY_SEPARATOR;
1313
use const PHP_EOL;
14+
use function array_slice;
15+
use function array_splice;
16+
use function count;
1417
use function explode;
1518
use function implode;
1619
use function preg_match;
@@ -60,30 +63,68 @@ protected function failureDescription(mixed $other): string
6063
return 'string matches format description';
6164
}
6265

66+
/**
67+
* Returns a useful diff with the 'actual' differences.
68+
*
69+
* The expected string can contain placeholders like %s and %d.
70+
* By using 'diff' such placeholders compared to the real output are
71+
* always objectively different, although we don't want to show them as different.
72+
*
73+
* This method removes the objective differences by figuring out if an objective
74+
* difference is allowed by a placeholder.
75+
*
76+
* The final result should only contain the differences that caused the failing test.
77+
*/
6378
protected function additionalFailureDescription(mixed $other): string
6479
{
65-
$from = explode("\n", $this->formatDescription);
66-
$to = explode("\n", $this->convertNewlines($other));
80+
$expected = explode("\n", $this->formatDescription);
81+
$output = explode("\n", $this->convertNewlines($other));
82+
83+
for ($oIndex = 0, $eIndex = 0, $length = count($output); $oIndex < $length; $oIndex++) {
84+
$multiLineMatch = false;
6785

68-
foreach ($from as $index => $line) {
69-
if (isset($to[$index]) && $line !== $to[$index]) {
70-
$line = $this->regularExpressionForFormatDescription($line);
86+
if (isset($expected[$eIndex]) && $expected[$eIndex] !== $output[$oIndex]) {
87+
$regEx = $this->regularExpressionForFormatDescription($expected[$eIndex]);
88+
$compareTo = $output[$oIndex];
89+
$matches = [];
90+
91+
// if we do a multiline match we have to consider all following lines as well
92+
if ($this->isMultilineMatch($expected[$eIndex])) {
93+
$multiLineMatch = true;
94+
$compareTo = implode("\n", array_slice($output, $oIndex));
95+
}
7196

72-
if (preg_match($line, $to[$index]) > 0) {
73-
$from[$index] = $to[$index];
97+
if (preg_match($regEx, $compareTo, $matches) > 0) {
98+
$lines = 1;
99+
100+
// if we matched multiple lines we have to sync $expected and $output
101+
if ($multiLineMatch) {
102+
$lines = count(explode("\n", $matches[0]));
103+
}
104+
// we sync at least one line
105+
$expected[$eIndex] = $output[$oIndex];
106+
107+
// for multiline matches we sync the matched lines to $expected
108+
for ($i = 1; $i < $lines; $i++) {
109+
$eIndex++;
110+
$oIndex++;
111+
array_splice($expected, $eIndex, 0, [$output[$oIndex]]);
112+
}
74113
}
75114
}
115+
$eIndex++;
76116
}
117+
$expectedString = implode("\n", $expected);
118+
$outputString = implode("\n", $output);
77119

78-
$from = implode("\n", $from);
79-
$to = implode("\n", $to);
80-
81-
return $this->differ()->diff($from, $to);
120+
return $this->differ()->diff($expectedString, $outputString);
82121
}
83122

84123
private function regularExpressionForFormatDescription(string $string): string
85124
{
86-
$string = strtr(
125+
// only add the end of string check ($) for single line comparisons
126+
$endOfLine = $this->isMultilineMatch($string) ? '' : '$';
127+
$string = strtr(
87128
preg_quote($string, '/'),
88129
[
89130
'%%' => '%',
@@ -102,7 +143,7 @@ private function regularExpressionForFormatDescription(string $string): string
102143
],
103144
);
104145

105-
return '/^' . $string . '$/s';
146+
return '/^' . $string . $endOfLine . '/s';
106147
}
107148

108149
private function convertNewlines(string $text): string
@@ -114,4 +155,9 @@ private function differ(): Differ
114155
{
115156
return new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n"));
116157
}
158+
159+
private function isMultilineMatch(string $line): bool
160+
{
161+
return preg_match('#%a#i', $line) > 0;
162+
}
117163
}

0 commit comments

Comments
 (0)