Skip to content

Commit 82f74ce

Browse files
committed
Improve bless tests script to minimize diffs
Compute the diff between the old EXPECTF and the new output and don't touch lines that still match the old EXPECTF. This reduces the amount of manual fixup necessary after running bless_tests.php.
1 parent edace5f commit 82f74ce

File tree

1 file changed

+166
-2
lines changed

1 file changed

+166
-2
lines changed

scripts/dev/bless_tests.php

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,26 @@
1919
}
2020

2121
$phpt = file_get_contents($path);
22+
$out = file_get_contents($outPath);
23+
2224
if (false !== strpos($phpt, '--XFAIL--')) {
2325
// Don't modify expected output of XFAIL tests
2426
continue;
2527
}
2628

27-
$out = file_get_contents($outPath);
28-
$out = normalizeOutput($out);
29+
// Don't update EXPECTREGEX tests
30+
if (!preg_match('/--EXPECT(F?)--(.*)$/s', $phpt, $matches)) {
31+
continue;
32+
}
33+
34+
$oldExpect = trim($matches[2]);
35+
$isFormat = $matches[1] == 'F';
36+
if ($isFormat) {
37+
$out = generateMinimallyDifferingOutput($out, $oldExpect);
38+
} else {
39+
$out = normalizeOutput($out);
40+
}
41+
2942
$phpt = insertOutput($phpt, $out);
3043
file_put_contents($path, $phpt);
3144
}
@@ -62,9 +75,160 @@ function normalizeOutput(string $out): string {
6275
return $out;
6376
}
6477

78+
function formatToRegex(string $format): string {
79+
$result = preg_quote($format, '/');
80+
$result = str_replace('%d', '\d+', $result);
81+
$result = str_replace('%s', '[^\r\n]+', $result);
82+
return "/^$result$/s";
83+
}
84+
85+
function generateMinimallyDifferingOutput(string $out, string $oldExpect) {
86+
$outLines = explode("\n", $out);
87+
$oldExpectLines = explode("\n", $oldExpect);
88+
$differ = new Differ(function($oldExpect, $new) {
89+
if (strpos($oldExpect, '%') === false) {
90+
return $oldExpect === $new;
91+
}
92+
return preg_match(formatToRegex($oldExpect), $new);
93+
});
94+
$diff = $differ->diff($oldExpectLines, $outLines);
95+
96+
$result = [];
97+
foreach ($diff as $elem) {
98+
if ($elem->type == DiffElem::TYPE_KEEP) {
99+
$result[] = $elem->old;
100+
} else if ($elem->type == DiffElem::TYPE_ADD) {
101+
$result[] = normalizeOutput($elem->new);
102+
}
103+
}
104+
return implode("\n", $result);
105+
}
106+
65107
function insertOutput(string $phpt, string $out): string {
66108
return preg_replace_callback('/--EXPECTF?--.*$/s', function($matches) use($out) {
67109
$F = strpos($out, '%') !== false ? 'F' : '';
68110
return "--EXPECT$F--\n" . $out . "\n";
69111
}, $phpt);
70112
}
113+
114+
/**
115+
* Implementation of the the Myers diff algorithm.
116+
*
117+
* Myers, Eugene W. "An O (ND) difference algorithm and its variations."
118+
* Algorithmica 1.1 (1986): 251-266.
119+
*/
120+
121+
class DiffElem
122+
{
123+
const TYPE_KEEP = 0;
124+
const TYPE_REMOVE = 1;
125+
const TYPE_ADD = 2;
126+
127+
/** @var int One of the TYPE_* constants */
128+
public $type;
129+
/** @var mixed Is null for add operations */
130+
public $old;
131+
/** @var mixed Is null for remove operations */
132+
public $new;
133+
134+
public function __construct(int $type, $old, $new) {
135+
$this->type = $type;
136+
$this->old = $old;
137+
$this->new = $new;
138+
}
139+
}
140+
141+
class Differ
142+
{
143+
private $isEqual;
144+
145+
/**
146+
* Create differ over the given equality relation.
147+
*
148+
* @param callable $isEqual Equality relation with signature function($a, $b) : bool
149+
*/
150+
public function __construct(callable $isEqual) {
151+
$this->isEqual = $isEqual;
152+
}
153+
154+
/**
155+
* Calculate diff (edit script) from $old to $new.
156+
*
157+
* @param array $old Original array
158+
* @param array $new New array
159+
*
160+
* @return DiffElem[] Diff (edit script)
161+
*/
162+
public function diff(array $old, array $new) {
163+
list($trace, $x, $y) = $this->calculateTrace($old, $new);
164+
return $this->extractDiff($trace, $x, $y, $old, $new);
165+
}
166+
167+
private function calculateTrace(array $a, array $b) {
168+
$n = \count($a);
169+
$m = \count($b);
170+
$max = $n + $m;
171+
$v = [1 => 0];
172+
$trace = [];
173+
for ($d = 0; $d <= $max; $d++) {
174+
$trace[] = $v;
175+
for ($k = -$d; $k <= $d; $k += 2) {
176+
if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
177+
$x = $v[$k+1];
178+
} else {
179+
$x = $v[$k-1] + 1;
180+
}
181+
182+
$y = $x - $k;
183+
while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) {
184+
$x++;
185+
$y++;
186+
}
187+
188+
$v[$k] = $x;
189+
if ($x >= $n && $y >= $m) {
190+
return [$trace, $x, $y];
191+
}
192+
}
193+
}
194+
throw new \Exception('Should not happen');
195+
}
196+
197+
private function extractDiff(array $trace, int $x, int $y, array $a, array $b) {
198+
$result = [];
199+
for ($d = \count($trace) - 1; $d >= 0; $d--) {
200+
$v = $trace[$d];
201+
$k = $x - $y;
202+
203+
if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
204+
$prevK = $k + 1;
205+
} else {
206+
$prevK = $k - 1;
207+
}
208+
209+
$prevX = $v[$prevK];
210+
$prevY = $prevX - $prevK;
211+
212+
while ($x > $prevX && $y > $prevY) {
213+
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]);
214+
$x--;
215+
$y--;
216+
}
217+
218+
if ($d === 0) {
219+
break;
220+
}
221+
222+
while ($x > $prevX) {
223+
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null);
224+
$x--;
225+
}
226+
227+
while ($y > $prevY) {
228+
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]);
229+
$y--;
230+
}
231+
}
232+
return array_reverse($result);
233+
}
234+
}

0 commit comments

Comments
 (0)