Skip to content

Commit 39ce5df

Browse files
Merge branch '12.1'
2 parents dea8cff + 5162331 commit 39ce5df

File tree

1 file changed

+62
-12
lines changed

1 file changed

+62
-12
lines changed

src/Framework/Constraint/String/StringMatchesFormatDescription.php

+62-12
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,72 @@ 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));
6782

68-
foreach ($from as $index => $line) {
69-
if (isset($to[$index]) && $line !== $to[$index]) {
70-
$line = $this->regularExpressionForFormatDescription($line);
83+
for ($oIndex = 0, $eIndex = 0, $length = count($output); $oIndex < $length; $oIndex++) {
84+
$multiLineMatch = false;
7185

72-
if (preg_match($line, $to[$index]) > 0) {
73-
$from[$index] = $to[$index];
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+
}
96+
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+
105+
// we sync at least one line
106+
$expected[$eIndex] = $output[$oIndex];
107+
108+
// for multiline matches we sync the matched lines to $expected
109+
for ($i = 1; $i < $lines; $i++) {
110+
$eIndex++;
111+
$oIndex++;
112+
113+
array_splice($expected, $eIndex, 0, [$output[$oIndex]]);
114+
}
74115
}
75116
}
117+
118+
$eIndex++;
76119
}
77120

78-
$from = implode("\n", $from);
79-
$to = implode("\n", $to);
121+
$expectedString = implode("\n", $expected);
122+
$outputString = implode("\n", $output);
80123

81-
return $this->differ()->diff($from, $to);
124+
return $this->differ()->diff($expectedString, $outputString);
82125
}
83126

84127
private function regularExpressionForFormatDescription(string $string): string
85128
{
86-
$string = strtr(
129+
// only add the end of string check ($) for single line comparisons
130+
$endOfLine = $this->isMultilineMatch($string) ? '' : '$';
131+
$string = strtr(
87132
preg_quote($string, '/'),
88133
[
89134
'%%' => '%',
@@ -102,7 +147,7 @@ private function regularExpressionForFormatDescription(string $string): string
102147
],
103148
);
104149

105-
return '/^' . $string . '$/s';
150+
return '/^' . $string . $endOfLine . '/s';
106151
}
107152

108153
private function convertNewlines(string $text): string
@@ -114,4 +159,9 @@ private function differ(): Differ
114159
{
115160
return new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n"));
116161
}
162+
163+
private function isMultilineMatch(string $line): bool
164+
{
165+
return preg_match('#%a#i', $line) > 0;
166+
}
117167
}

0 commit comments

Comments
 (0)