1
0
mirror of https://github.com/golang/go synced 2024-11-06 19:36:30 -07:00
go/internal/lsp/diff/difftest/difftest.go
Ian Cottrell cc9bfb3eec internal/lsp: rewrite the unified diff handling
It is now in the diff package written in terms of edits, instead of the myers package.
This also means that the unified handling is no longer pluggable because it does
not need to be.

Change-Id: I7141b023e95ed0c1d21cbc81c7420c117fc5ef1a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/198517
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-10-04 03:45:17 +00:00

217 lines
4.1 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package difftest supplies a set of tests that will operate on any
// implementation of a diff algorithm as exposed by
// "golang.org/x/tools/internal/lsp/diff"
package difftest
import (
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"testing"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/span"
)
const (
fileA = "from"
fileB = "to"
unifiedPrefix = "--- " + fileA + "\n+++ " + fileB + "\n"
)
var verifyDiff = flag.Bool("verify-diff", false, "Check that the unified diff output matches `diff -u`")
func DiffTest(t *testing.T, compute diff.ComputeEdits) {
t.Helper()
for _, test := range []struct {
name, in, out, unified string
nodiff bool
}{{
name: "empty",
in: "",
out: "",
}, {
name: "no_diff",
in: "gargantuan\n",
out: "gargantuan\n",
}, {
name: "replace_all",
in: "gord\n",
out: "gourd\n",
unified: unifiedPrefix + `
@@ -1 +1 @@
-gord
+gourd
`[1:],
}, {
name: "insert_rune",
in: "gord\n",
out: "gourd\n",
unified: unifiedPrefix + `
@@ -1 +1 @@
-gord
+gourd
`[1:],
}, {
name: "delete_rune",
in: "groat\n",
out: "goat\n",
unified: unifiedPrefix + `
@@ -1 +1 @@
-groat
+goat
`[1:],
}, {
name: "replace_rune",
in: "loud\n",
out: "lord\n",
unified: unifiedPrefix + `
@@ -1 +1 @@
-loud
+lord
`[1:],
}, {
name: "insert_line",
in: "one\nthree\n",
out: "one\ntwo\nthree\n",
unified: unifiedPrefix + `
@@ -1,2 +1,3 @@
one
+two
three
`[1:],
}, {
name: "replace_no_newline",
in: "A",
out: "B",
unified: unifiedPrefix + `
@@ -1 +1 @@
-A
\ No newline at end of file
+B
\ No newline at end of file
`[1:],
}, {
name: "delete_front",
in: "A\nB\nC\nA\nB\nB\nA\n",
out: "C\nB\nA\nB\nA\nC\n",
unified: unifiedPrefix + `
@@ -1,7 +1,6 @@
-A
-B
C
+B
A
B
-B
A
+C
`[1:],
nodiff: true, // diff algorithm produces different delete/insert pattern
},
{
name: "replace_last_line",
in: "A\nB\n",
out: "A\nC\n\n",
unified: unifiedPrefix + `
@@ -1,2 +1,3 @@
A
-B
+C
+
`[1:],
},
{
name: "mulitple_replace",
in: "A\nB\nC\nD\nE\nF\nG\n",
out: "A\nH\nI\nJ\nE\nF\nK\n",
unified: unifiedPrefix + `
@@ -1,7 +1,7 @@
A
-B
-C
-D
+H
+I
+J
E
F
-G
+K
`[1:],
}} {
t.Run(test.name, func(t *testing.T) {
t.Helper()
edits := compute(span.FileURI("/"+test.name), test.in, test.out)
got := diff.ApplyEdits(test.in, edits)
unified := fmt.Sprint(diff.ToUnified("from", "to", test.in, edits))
if got != test.out {
t.Errorf("got patched:\n%v\nfrom diff:\n%v\nexpected:\n%v", got, unified, test.out)
}
if unified != test.unified {
t.Errorf("got diff:\n%v\nexpected:\n%v", unified, test.unified)
}
if *verifyDiff && !test.nodiff {
diff, err := getDiffOutput(test.in, test.out)
if err != nil {
t.Fatal(err)
}
if len(diff) > 0 {
diff = unifiedPrefix + diff
}
if diff != test.unified {
t.Errorf("unified:\n%q\ndiff -u:\n%q", test.unified, diff)
}
}
})
}
}
func getDiffOutput(a, b string) (string, error) {
fileA, err := ioutil.TempFile("", "myers.in")
if err != nil {
return "", err
}
defer os.Remove(fileA.Name())
if _, err := fileA.Write([]byte(a)); err != nil {
return "", err
}
if err := fileA.Close(); err != nil {
return "", err
}
fileB, err := ioutil.TempFile("", "myers.in")
if err != nil {
return "", err
}
defer os.Remove(fileB.Name())
if _, err := fileB.Write([]byte(b)); err != nil {
return "", err
}
if err := fileB.Close(); err != nil {
return "", err
}
cmd := exec.Command("diff", "-u", fileA.Name(), fileB.Name())
out, err := cmd.CombinedOutput()
if err != nil {
if _, ok := err.(*exec.ExitError); !ok {
return "", fmt.Errorf("failed to run diff -u %v %v: %v\n%v", fileA.Name(), fileB.Name(), err, string(out))
}
}
diff := string(out)
if len(diff) <= 0 {
return diff, nil
}
bits := strings.SplitN(diff, "\n", 3)
if len(bits) != 3 {
return "", fmt.Errorf("diff output did not have file prefix:\n%s", diff)
}
return bits[2], nil
}