Skip to content

Commit 29e97f8

Browse files
committed
gopls/internal/marker: make checkDiffs less fragile
The marker tests make over 500 calls to checkDiffs, which currently works by computing line diffs between the before code and the after code and comparing these with the unified diffs that are the golden contents in the test .txt file. This is fragile, as a different diff algorithm would produce different diffs, and we would like to remove the dependency on myers.ComputeEdit. This CL instead just checks that the unified golden diffs correctly convert the before code into the after code. (Note that this is a complete test of the new code on all existing uses in marker tests.) A subsequent CL will replace myers.ComputeEdit. Change-Id: Iabf6659a646bd69730d8ae19b1d46885a4903d90 Reviewed-on: https://go-review.googlesource.com/c/tools/+/724660 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent 1f3446a commit 29e97f8

File tree

2 files changed

+77
-23
lines changed

2 files changed

+77
-23
lines changed

‎gopls/internal/test/marker/marker_test.go‎

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,46 +1458,50 @@ func checkChangedFiles(mark marker, changed map[string][]byte, golden *Golden) {
14581458
}
14591459
}
14601460

1461-
// checkDiffs computes unified diffs for each changed file, and compares with
1462-
// the diff content stored in the given golden directory.
1461+
// checkDiffs checks that the diff content stored in the given golden directory
1462+
// converts the orginal contents into the changed contents.
1463+
// (This logic is strange because hundreds of existing marker tests were created
1464+
// containing a modified version of unified diffs.)
14631465
func checkDiffs(mark marker, changed map[string][]byte, golden *Golden) {
1464-
diffs := make(map[string]string)
14651466
for name, after := range changed {
14661467
before := mark.run.env.FileContent(name)
1467-
// TODO(golang/go#64023): switch back to diff.Strings.
1468-
// The attached issue is only one obstacle to switching.
1469-
// Another is that different diff algorithms produce
1470-
// different results, so if we commit diffs in test
1471-
// expectations, then we need to either (1) state
1472-
// which diff implementation they use and never change
1473-
// it, or (2) don't compare diffs, but instead apply
1474-
// the "want" diff and check that it produces the
1475-
// "got" output. Option 2 is more robust, as it allows
1476-
// the test expectation to use any valid diff.
14771468
edits := myers.ComputeEdits(before, string(after))
14781469
d, err := diff.ToUnified("before", "after", before, edits, 0)
14791470
if err != nil {
14801471
// Can't happen: edits are consistent.
14811472
log.Fatalf("internal error in diff.ToUnified: %v", err)
14821473
}
14831474
// Trim the unified header from diffs, as it is unnecessary and repetitive.
1475+
// (an artifact of ToUnified, but not stored in the marker files)
14841476
difflines := strings.Split(d, "\n")
1477+
var got string
14851478
if len(difflines) >= 2 && strings.HasPrefix(difflines[1], "+++") {
1486-
diffs[name] = strings.Join(difflines[2:], "\n")
1479+
got = strings.Join(difflines[2:], "\n")
14871480
} else {
1488-
diffs[name] = d
1481+
got = d // "" in packagedecl.txt:147
14891482
}
1490-
}
1491-
// Check changed files match expectations.
1492-
for filename, got := range diffs {
1493-
if want, ok := golden.Get(mark.T(), filename, []byte(got)); !ok {
1483+
// the call to Get is so that the -update flag will update the test.
1484+
// normally it just returns 'got'.
1485+
if tdiffs, ok := golden.Get(mark.T(), name, []byte(got)); !ok {
14941486
mark.errorf("%s: unexpected change to file %s; got diff:\n%s",
1495-
mark.note.Name, filename, got)
1496-
} else if got != string(want) {
1497-
mark.errorf("%s: wrong diff for %s:\n\ngot:\n%s\n\nwant:\n%s\n",
1498-
mark.note.Name, filename, got, want)
1487+
mark.note.Name, name, got)
1488+
return
1489+
} else {
1490+
// restore the ToUnifed header lines deleted above
1491+
// before calling ApplyUnified
1492+
diffsFromTest := "--- \n+++ \n" + string(tdiffs)
1493+
want, err := diff.ApplyUnified(diffsFromTest, before)
1494+
if err != nil {
1495+
mark.errorf("%s: ApplyUnified(%q,%q) failed: %v",
1496+
mark.note.Name, before, after, err)
1497+
}
1498+
if want != string(after) {
1499+
mark.errorf("%s: got\n%q expected\n%q",
1500+
mark.note.Name, want, string(after))
1501+
}
14991502
}
15001503
}
1504+
15011505
// Report unmet expectations.
15021506
for filename := range golden.data {
15031507
if _, ok := changed[filename]; !ok {

‎internal/diff/unified.go‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package diff
77
import (
88
"fmt"
99
"log"
10+
"regexp"
11+
"strconv"
1012
"strings"
1113
)
1214

@@ -249,3 +251,51 @@ func (u unified) String() string {
249251
}
250252
return b.String()
251253
}
254+
255+
// ApplyUnified applies the unified diffs.
256+
func ApplyUnified(udiffs, bef string) (string, error) {
257+
before := strings.Split(bef, "\n")
258+
unif := strings.Split(udiffs, "\n")
259+
var got []string
260+
left := 0
261+
// parse and apply the unified diffs
262+
for _, l := range unif {
263+
if len(l) == 0 {
264+
continue // probably the last line (from Split)
265+
}
266+
switch l[0] {
267+
case '@': // The @@ line
268+
m := atregexp.FindStringSubmatch(l)
269+
fromLine, err := strconv.Atoi(m[1])
270+
if err != nil {
271+
return "", fmt.Errorf("missing line number in %q", l)
272+
}
273+
// before is a slice, so0-based; fromLine is 1-based
274+
for ; left < fromLine-1; left++ {
275+
got = append(got, before[left])
276+
}
277+
case '+': // add this line
278+
if strings.HasPrefix(l, "+++ ") {
279+
continue
280+
}
281+
got = append(got, l[1:])
282+
case '-': // delete this line
283+
if strings.HasPrefix(l, "--- ") {
284+
continue
285+
}
286+
left++
287+
case ' ':
288+
return "", fmt.Errorf("unexpected line %q", l)
289+
default:
290+
return "", fmt.Errorf("impossible unified %q", udiffs)
291+
}
292+
}
293+
// copy any remaining lines
294+
for ; left < len(before); left++ {
295+
got = append(got, before[left])
296+
}
297+
return strings.Join(got, "\n"), nil
298+
}
299+
300+
// The first number in the @@ lines is the line number in the 'before' data
301+
var atregexp = regexp.MustCompile(`@@ -(\d+).* @@`)

0 commit comments

Comments
 (0)