11
11
12
12
use const DIRECTORY_SEPARATOR ;
13
13
use const PHP_EOL ;
14
+ use function array_slice ;
15
+ use function array_splice ;
16
+ use function count ;
14
17
use function explode ;
15
18
use function implode ;
16
19
use function preg_match ;
@@ -60,30 +63,68 @@ protected function failureDescription(mixed $other): string
60
63
return 'string matches format description ' ;
61
64
}
62
65
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
+ */
63
78
protected function additionalFailureDescription (mixed $ other ): string
64
79
{
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 ;
67
85
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
+ }
71
96
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
+ }
74
113
}
75
114
}
115
+ $ eIndex ++;
76
116
}
117
+ $ expectedString = implode ("\n" , $ expected );
118
+ $ outputString = implode ("\n" , $ output );
77
119
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 );
82
121
}
83
122
84
123
private function regularExpressionForFormatDescription (string $ string ): string
85
124
{
86
- $ string = strtr (
125
+ // only add the end of string check ($) for single line comparisons
126
+ $ endOfLine = $ this ->isMultilineMatch ($ string ) ? '' : '$ ' ;
127
+ $ string = strtr (
87
128
preg_quote ($ string , '/ ' ),
88
129
[
89
130
'%% ' => '% ' ,
@@ -102,7 +143,7 @@ private function regularExpressionForFormatDescription(string $string): string
102
143
],
103
144
);
104
145
105
- return '/^ ' . $ string . ' $ /s ' ;
146
+ return '/^ ' . $ string . $ endOfLine . ' /s ' ;
106
147
}
107
148
108
149
private function convertNewlines (string $ text ): string
@@ -114,4 +155,9 @@ private function differ(): Differ
114
155
{
115
156
return new Differ (new UnifiedDiffOutputBuilder ("--- Expected \n+++ Actual \n" ));
116
157
}
158
+
159
+ private function isMultilineMatch (string $ line ): bool
160
+ {
161
+ return preg_match ('#%a#i ' , $ line ) > 0 ;
162
+ }
117
163
}
0 commit comments