golang.org/x/tools@v0.21.0/internal/diff/diff_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package diff_test 6 7 import ( 8 "bytes" 9 "math/rand" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "reflect" 14 "strings" 15 "testing" 16 "unicode/utf8" 17 18 "golang.org/x/tools/internal/diff" 19 "golang.org/x/tools/internal/diff/difftest" 20 "golang.org/x/tools/internal/testenv" 21 ) 22 23 func TestApply(t *testing.T) { 24 for _, tc := range difftest.TestCases { 25 t.Run(tc.Name, func(t *testing.T) { 26 got, err := diff.Apply(tc.In, tc.Edits) 27 if err != nil { 28 t.Fatalf("Apply(Edits) failed: %v", err) 29 } 30 if got != tc.Out { 31 t.Errorf("Apply(Edits): got %q, want %q", got, tc.Out) 32 } 33 if tc.LineEdits != nil { 34 got, err := diff.Apply(tc.In, tc.LineEdits) 35 if err != nil { 36 t.Fatalf("Apply(LineEdits) failed: %v", err) 37 } 38 if got != tc.Out { 39 t.Errorf("Apply(LineEdits): got %q, want %q", got, tc.Out) 40 } 41 } 42 }) 43 } 44 } 45 46 func TestNEdits(t *testing.T) { 47 for _, tc := range difftest.TestCases { 48 edits := diff.Strings(tc.In, tc.Out) 49 got, err := diff.Apply(tc.In, edits) 50 if err != nil { 51 t.Fatalf("Apply failed: %v", err) 52 } 53 if got != tc.Out { 54 t.Fatalf("%s: got %q wanted %q", tc.Name, got, tc.Out) 55 } 56 if len(edits) < len(tc.Edits) { // should find subline edits 57 t.Errorf("got %v, expected %v for %#v", edits, tc.Edits, tc) 58 } 59 } 60 } 61 62 func TestNRandom(t *testing.T) { 63 rand.Seed(1) 64 for i := 0; i < 1000; i++ { 65 a := randstr("abω", 16) 66 b := randstr("abωc", 16) 67 edits := diff.Strings(a, b) 68 got, err := diff.Apply(a, edits) 69 if err != nil { 70 t.Fatalf("Apply failed: %v", err) 71 } 72 if got != b { 73 t.Fatalf("%d: got %q, wanted %q, starting with %q", i, got, b, a) 74 } 75 } 76 } 77 78 // $ go test -fuzz=FuzzRoundTrip ./internal/diff 79 func FuzzRoundTrip(f *testing.F) { 80 f.Fuzz(func(t *testing.T, a, b string) { 81 if !utf8.ValidString(a) || !utf8.ValidString(b) { 82 return // inputs must be text 83 } 84 edits := diff.Strings(a, b) 85 got, err := diff.Apply(a, edits) 86 if err != nil { 87 t.Fatalf("Apply failed: %v", err) 88 } 89 if got != b { 90 t.Fatalf("applying diff(%q, %q) gives %q; edits=%v", a, b, got, edits) 91 } 92 }) 93 } 94 95 func TestLineEdits(t *testing.T) { 96 for _, tc := range difftest.TestCases { 97 t.Run(tc.Name, func(t *testing.T) { 98 want := tc.LineEdits 99 if want == nil { 100 want = tc.Edits // already line-aligned 101 } 102 got, err := diff.LineEdits(tc.In, tc.Edits) 103 if err != nil { 104 t.Fatalf("LineEdits: %v", err) 105 } 106 if !reflect.DeepEqual(got, want) { 107 t.Errorf("in=<<%s>>\nout=<<%s>>\nraw edits=%s\nline edits=%s\nwant: %s", 108 tc.In, tc.Out, tc.Edits, got, want) 109 } 110 // make sure that applying the edits gives the expected result 111 fixed, err := diff.Apply(tc.In, got) 112 if err != nil { 113 t.Error(err) 114 } 115 if fixed != tc.Out { 116 t.Errorf("Apply(LineEdits): got %q, want %q", fixed, tc.Out) 117 } 118 }) 119 } 120 } 121 122 func TestToUnified(t *testing.T) { 123 testenv.NeedsTool(t, "patch") 124 for _, tc := range difftest.TestCases { 125 t.Run(tc.Name, func(t *testing.T) { 126 unified, err := diff.ToUnified(difftest.FileA, difftest.FileB, tc.In, tc.Edits, diff.DefaultContextLines) 127 if err != nil { 128 t.Fatal(err) 129 } 130 if unified == "" { 131 return 132 } 133 orig := filepath.Join(t.TempDir(), "original") 134 err = os.WriteFile(orig, []byte(tc.In), 0644) 135 if err != nil { 136 t.Fatal(err) 137 } 138 temp := filepath.Join(t.TempDir(), "patched") 139 err = os.WriteFile(temp, []byte(tc.In), 0644) 140 if err != nil { 141 t.Fatal(err) 142 } 143 cmd := exec.Command("patch", "-p0", "-u", "-s", "-o", temp, orig) 144 cmd.Stdin = strings.NewReader(unified) 145 cmd.Stdout = new(bytes.Buffer) 146 cmd.Stderr = new(bytes.Buffer) 147 if err = cmd.Run(); err != nil { 148 t.Fatalf("%v: %q (%q) (%q)", err, cmd.String(), 149 cmd.Stderr, cmd.Stdout) 150 } 151 got, err := os.ReadFile(temp) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if string(got) != tc.Out { 156 t.Errorf("applying unified failed: got\n%q, wanted\n%q unified\n%q", 157 got, tc.Out, unified) 158 } 159 160 }) 161 } 162 } 163 164 func TestRegressionOld001(t *testing.T) { 165 a := "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage diff_test\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"golang.org/x/tools/gopls/internal/lsp/diff\"\n\t\"golang.org/x/tools/internal/diff/difftest\"\n\t\"golang.org/x/tools/gopls/internal/span\"\n)\n" 166 167 b := "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage diff_test\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/safehtml/template\"\n\t\"golang.org/x/tools/gopls/internal/lsp/diff\"\n\t\"golang.org/x/tools/internal/diff/difftest\"\n\t\"golang.org/x/tools/gopls/internal/span\"\n)\n" 168 diffs := diff.Strings(a, b) 169 got, err := diff.Apply(a, diffs) 170 if err != nil { 171 t.Fatalf("Apply failed: %v", err) 172 } 173 if got != b { 174 i := 0 175 for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ { 176 } 177 t.Errorf("oops %vd\n%q\n%q", diffs, got, b) 178 t.Errorf("\n%q\n%q", got[i:], b[i:]) 179 } 180 } 181 182 func TestRegressionOld002(t *testing.T) { 183 a := "n\"\n)\n" 184 b := "n\"\n\t\"golang.org/x//nnal/stack\"\n)\n" 185 diffs := diff.Strings(a, b) 186 got, err := diff.Apply(a, diffs) 187 if err != nil { 188 t.Fatalf("Apply failed: %v", err) 189 } 190 if got != b { 191 i := 0 192 for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ { 193 } 194 t.Errorf("oops %vd\n%q\n%q", diffs, got, b) 195 t.Errorf("\n%q\n%q", got[i:], b[i:]) 196 } 197 } 198 199 // return a random string of length n made of characters from s 200 func randstr(s string, n int) string { 201 src := []rune(s) 202 x := make([]rune, n) 203 for i := 0; i < n; i++ { 204 x[i] = src[rand.Intn(len(src))] 205 } 206 return string(x) 207 }