Skip to content

Commit fa184aa

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 fa184aa

File tree

1 file changed

+54
-15
lines changed

1 file changed

+54
-15
lines changed

src/Framework/Constraint/String/StringMatchesFormatDescription.php

+54-15
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,65 @@ protected function failureDescription(mixed $other): string
6060
return 'string matches format description';
6161
}
6262

63+
/**
64+
* Returns a useful diff with the 'actual' differences
65+
*
66+
* The expected string can contain placeholders like %s and %d.
67+
* By using 'diff' such placeholders compared to the real output are
68+
* always objectively different, although we don't want to show them as different.
69+
*
70+
* This method removes the objective differences by figuring out if an objective
71+
* difference is allowed by a placeholder.
72+
*
73+
* The final result should only contain the differences that caused the failing test.
74+
*/
6375
protected function additionalFailureDescription(mixed $other): string
6476
{
65-
$from = explode("\n", $this->formatDescription);
66-
$to = explode("\n", $this->convertNewlines($other));
67-
68-
foreach ($from as $index => $line) {
69-
if (isset($to[$index]) && $line !== $to[$index]) {
70-
$line = $this->regularExpressionForFormatDescription($line);
77+
$expected = explode("\n", $this->formatDescription);
78+
$output = explode("\n", $this->convertNewlines($other));
79+
80+
for ($oIndex = 0, $eIndex = 0, $length = count($output); $oIndex < $length; $oIndex++) {
81+
$multiLineMatch = false;
82+
if (isset($expected[$eIndex]) && $expected[$eIndex] !== $output[$oIndex]) {
83+
$regEx = $this->regularExpressionForFormatDescription($expected[$eIndex]);
84+
$compareTo = $output[$oIndex];
85+
$matches = [];
86+
87+
// if we do a multiline match we have to consider all following lines as well
88+
if ($this->isMultilineMatch($expected[$eIndex])) {
89+
$multiLineMatch = true;
90+
$compareTo = implode("\n", array_slice($output, $oIndex));
91+
}
7192

72-
if (preg_match($line, $to[$index]) > 0) {
73-
$from[$index] = $to[$index];
93+
if (preg_match($regEx, $compareTo, $matches) > 0) {
94+
$lines = 1;
95+
// if we matched multiple lines we have to sync $expected and $output
96+
if ($multiLineMatch) {
97+
$lines = count(explode("\n", $matches[0]));
98+
}
99+
// we sync at least one line
100+
$expected[$eIndex] = $output[$oIndex];
101+
// for multiline matches we sync the matched lines to $expected
102+
for ($i = 1; $i < $lines; $i++) {
103+
++$eIndex;
104+
++$oIndex;
105+
array_splice($expected, $eIndex, 0, [$output[$oIndex]]);
106+
}
74107
}
75108
}
109+
$eIndex++;
76110
}
111+
$expectedString = implode("\n", $expected);
112+
$outputString = implode("\n", $output);
77113

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

84117
private function regularExpressionForFormatDescription(string $string): string
85118
{
86-
$string = strtr(
119+
// only add the end of string check ($) for single line comparisons
120+
$endOfLine = $this->isMultilineMatch($string) ? '' : '$';
121+
$string = strtr(
87122
preg_quote($string, '/'),
88123
[
89124
'%%' => '%',
@@ -101,8 +136,7 @@ private function regularExpressionForFormatDescription(string $string): string
101136
'%0' => '\x00',
102137
],
103138
);
104-
105-
return '/^' . $string . '$/s';
139+
return '/^' . $string . $endOfLine . '/s';
106140
}
107141

108142
private function convertNewlines(string $text): string
@@ -114,4 +148,9 @@ private function differ(): Differ
114148
{
115149
return new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n"));
116150
}
151+
152+
private function isMultilineMatch(string $line): bool
153+
{
154+
return preg_match('#%a#i', $line) > 0;
155+
}
117156
}

0 commit comments

Comments
 (0)