Skip to content

Commit db1ebf7

Browse files
committed
internal/lsp: move unified diff testing to the diff interface
This now checks at the diff.TextEdit layer rather than myers.Op Change-Id: I706657a6c5705f0ad7109aa81f4dce174dda5f2b Reviewed-on: https://go-review.googlesource.com/c/tools/+/198380 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
1 parent b917058 commit db1ebf7

File tree

2 files changed

+180
-209
lines changed

2 files changed

+180
-209
lines changed

internal/lsp/diff/difftest/difftest.go

Lines changed: 180 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,209 @@
88
package difftest
99

1010
import (
11+
"flag"
12+
"fmt"
13+
"io/ioutil"
14+
"os"
15+
"os/exec"
16+
"strings"
1117
"testing"
1218

1319
"golang.org/x/tools/internal/lsp/diff"
1420
"golang.org/x/tools/internal/span"
1521
)
1622

23+
const (
24+
fileA = "from"
25+
fileB = "to"
26+
unifiedPrefix = "--- " + fileA + "\n+++ " + fileB + "\n"
27+
)
28+
29+
var verifyDiff = flag.Bool("verify-diff", false, "Check that the unified diff output matches `diff -u`")
30+
1731
func DiffTest(t *testing.T, compute diff.ComputeEdits) {
1832
t.Helper()
19-
for _, test := range []struct{ name, in, out, unified string }{{
33+
for _, test := range []struct {
34+
name, in, out, unified string
35+
nodiff bool
36+
}{{
2037
name: "empty",
2138
in: "",
2239
out: "",
2340
}, {
2441
name: "no_diff",
25-
in: "gargantuan",
26-
out: "gargantuan",
42+
in: "gargantuan\n",
43+
out: "gargantuan\n",
44+
}, {
45+
name: "replace_all",
46+
in: "gord\n",
47+
out: "gourd\n",
48+
unified: unifiedPrefix + `
49+
@@ -1 +1 @@
50+
-gord
51+
+gourd
52+
`[1:],
2753
}, {
2854
name: "insert_rune",
29-
in: "gord",
30-
out: "gourd",
55+
in: "gord\n",
56+
out: "gourd\n",
57+
unified: unifiedPrefix + `
58+
@@ -1 +1 @@
59+
-gord
60+
+gourd
61+
`[1:],
3162
}, {
3263
name: "delete_rune",
33-
in: "groat",
34-
out: "goat",
64+
in: "groat\n",
65+
out: "goat\n",
66+
unified: unifiedPrefix + `
67+
@@ -1 +1 @@
68+
-groat
69+
+goat
70+
`[1:],
3571
}, {
3672
name: "replace_rune",
37-
in: "loud",
38-
out: "lord",
73+
in: "loud\n",
74+
out: "lord\n",
75+
unified: unifiedPrefix + `
76+
@@ -1 +1 @@
77+
-loud
78+
+lord
79+
`[1:],
3980
}, {
4081
name: "insert_line",
4182
in: "one\nthree\n",
4283
out: "one\ntwo\nthree\n",
43-
}} {
44-
edits := compute(span.FileURI("/"+test.name), test.in, test.out)
45-
got := diff.ApplyEdits(test.in, edits)
46-
if got != test.out {
47-
t.Logf("test %v had diff:%v\n", test.name, diff.ToUnified(test.name+".orig", test.name, test.in, edits))
48-
t.Errorf("diff %v got:\n%v\nexpected:\n%v", test.name, got, test.out)
84+
unified: unifiedPrefix + `
85+
@@ -1,2 +1,3 @@
86+
one
87+
+two
88+
three
89+
`[1:],
90+
}, {
91+
name: "replace_no_newline",
92+
in: "A",
93+
out: "B",
94+
unified: unifiedPrefix + `
95+
@@ -1 +1 @@
96+
-A
97+
\ No newline at end of file
98+
+B
99+
\ No newline at end of file
100+
`[1:],
101+
}, {
102+
name: "delete_front",
103+
in: "A\nB\nC\nA\nB\nB\nA\n",
104+
out: "C\nB\nA\nB\nA\nC\n",
105+
unified: unifiedPrefix + `
106+
@@ -1,7 +1,6 @@
107+
-A
108+
-B
109+
C
110+
+B
111+
A
112+
B
113+
-B
114+
A
115+
+C
116+
`[1:],
117+
nodiff: true, // diff algorithm produces different delete/insert pattern
118+
},
119+
{
120+
name: "replace_last_line",
121+
in: "A\nB\n",
122+
out: "A\nC\n\n",
123+
unified: unifiedPrefix + `
124+
@@ -1,2 +1,3 @@
125+
A
126+
-B
127+
+C
128+
+
129+
`[1:],
130+
},
131+
{
132+
name: "mulitple_replace",
133+
in: "A\nB\nC\nD\nE\nF\nG\n",
134+
out: "A\nH\nI\nJ\nE\nF\nK\n",
135+
unified: unifiedPrefix + `
136+
@@ -1,7 +1,7 @@
137+
A
138+
-B
139+
-C
140+
-D
141+
+H
142+
+I
143+
+J
144+
E
145+
F
146+
-G
147+
+K
148+
`[1:],
149+
}} {
150+
t.Run(test.name, func(t *testing.T) {
151+
t.Helper()
152+
edits := compute(span.FileURI("/"+test.name), test.in, test.out)
153+
got := diff.ApplyEdits(test.in, edits)
154+
unified := diff.ToUnified("from", "to", test.in, edits)
155+
if got != test.out {
156+
t.Errorf("got patched:\n%v\nfrom diff:\n%v\nexpected:\n%v", got, unified, test.out)
157+
}
158+
if unified != test.unified {
159+
t.Errorf("got diff:\n%v\nexpected:\n%v", unified, test.unified)
160+
}
161+
if *verifyDiff && !test.nodiff {
162+
diff, err := getDiffOutput(test.in, test.out)
163+
if err != nil {
164+
t.Fatal(err)
165+
}
166+
if len(diff) > 0 {
167+
diff = unifiedPrefix + diff
168+
}
169+
if diff != test.unified {
170+
t.Errorf("unified:\n%q\ndiff -u:\n%q", test.unified, diff)
171+
}
172+
}
173+
})
174+
}
175+
}
176+
177+
func getDiffOutput(a, b string) (string, error) {
178+
fileA, err := ioutil.TempFile("", "myers.in")
179+
if err != nil {
180+
return "", err
181+
}
182+
defer os.Remove(fileA.Name())
183+
if _, err := fileA.Write([]byte(a)); err != nil {
184+
return "", err
185+
}
186+
if err := fileA.Close(); err != nil {
187+
return "", err
188+
}
189+
fileB, err := ioutil.TempFile("", "myers.in")
190+
if err != nil {
191+
return "", err
192+
}
193+
defer os.Remove(fileB.Name())
194+
if _, err := fileB.Write([]byte(b)); err != nil {
195+
return "", err
196+
}
197+
if err := fileB.Close(); err != nil {
198+
return "", err
199+
}
200+
cmd := exec.Command("diff", "-u", fileA.Name(), fileB.Name())
201+
out, err := cmd.CombinedOutput()
202+
if err != nil {
203+
if _, ok := err.(*exec.ExitError); !ok {
204+
return "", fmt.Errorf("failed to run diff -u %v %v: %v\n%v", fileA.Name(), fileB.Name(), err, string(out))
49205
}
50206
}
207+
diff := string(out)
208+
if len(diff) <= 0 {
209+
return diff, nil
210+
}
211+
bits := strings.SplitN(diff, "\n", 3)
212+
if len(bits) != 3 {
213+
return "", fmt.Errorf("diff output did not have file prefix:\n%s", diff)
214+
}
215+
return bits[2], nil
51216
}

0 commit comments

Comments
 (0)