github.com/v2fly/tools@v0.100.0/internal/lsp/diff/difftest/difftest.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 difftest supplies a set of tests that will operate on any 6 // implementation of a diff algorithm as exposed by 7 // "github.com/v2fly/tools/internal/lsp/diff" 8 package difftest 9 10 import ( 11 "fmt" 12 "testing" 13 14 "github.com/v2fly/tools/internal/lsp/diff" 15 "github.com/v2fly/tools/internal/span" 16 ) 17 18 const ( 19 FileA = "from" 20 FileB = "to" 21 UnifiedPrefix = "--- " + FileA + "\n+++ " + FileB + "\n" 22 ) 23 24 var TestCases = []struct { 25 Name, In, Out, Unified string 26 Edits, LineEdits []diff.TextEdit 27 NoDiff bool 28 }{{ 29 Name: "empty", 30 In: "", 31 Out: "", 32 }, { 33 Name: "no_diff", 34 In: "gargantuan\n", 35 Out: "gargantuan\n", 36 }, { 37 Name: "replace_all", 38 In: "fruit\n", 39 Out: "cheese\n", 40 Unified: UnifiedPrefix + ` 41 @@ -1 +1 @@ 42 -fruit 43 +cheese 44 `[1:], 45 Edits: []diff.TextEdit{{Span: newSpan(0, 5), NewText: "cheese"}}, 46 LineEdits: []diff.TextEdit{{Span: newSpan(0, 6), NewText: "cheese\n"}}, 47 }, { 48 Name: "insert_rune", 49 In: "gord\n", 50 Out: "gourd\n", 51 Unified: UnifiedPrefix + ` 52 @@ -1 +1 @@ 53 -gord 54 +gourd 55 `[1:], 56 Edits: []diff.TextEdit{{Span: newSpan(2, 2), NewText: "u"}}, 57 LineEdits: []diff.TextEdit{{Span: newSpan(0, 5), NewText: "gourd\n"}}, 58 }, { 59 Name: "delete_rune", 60 In: "groat\n", 61 Out: "goat\n", 62 Unified: UnifiedPrefix + ` 63 @@ -1 +1 @@ 64 -groat 65 +goat 66 `[1:], 67 Edits: []diff.TextEdit{{Span: newSpan(1, 2), NewText: ""}}, 68 LineEdits: []diff.TextEdit{{Span: newSpan(0, 6), NewText: "goat\n"}}, 69 }, { 70 Name: "replace_rune", 71 In: "loud\n", 72 Out: "lord\n", 73 Unified: UnifiedPrefix + ` 74 @@ -1 +1 @@ 75 -loud 76 +lord 77 `[1:], 78 Edits: []diff.TextEdit{{Span: newSpan(2, 3), NewText: "r"}}, 79 LineEdits: []diff.TextEdit{{Span: newSpan(0, 5), NewText: "lord\n"}}, 80 }, { 81 Name: "replace_partials", 82 In: "blanket\n", 83 Out: "bunker\n", 84 Unified: UnifiedPrefix + ` 85 @@ -1 +1 @@ 86 -blanket 87 +bunker 88 `[1:], 89 Edits: []diff.TextEdit{ 90 {Span: newSpan(1, 3), NewText: "u"}, 91 {Span: newSpan(6, 7), NewText: "r"}, 92 }, 93 LineEdits: []diff.TextEdit{{Span: newSpan(0, 8), NewText: "bunker\n"}}, 94 }, { 95 Name: "insert_line", 96 In: "1: one\n3: three\n", 97 Out: "1: one\n2: two\n3: three\n", 98 Unified: UnifiedPrefix + ` 99 @@ -1,2 +1,3 @@ 100 1: one 101 +2: two 102 3: three 103 `[1:], 104 Edits: []diff.TextEdit{{Span: newSpan(7, 7), NewText: "2: two\n"}}, 105 }, { 106 Name: "replace_no_newline", 107 In: "A", 108 Out: "B", 109 Unified: UnifiedPrefix + ` 110 @@ -1 +1 @@ 111 -A 112 \ No newline at end of file 113 +B 114 \ No newline at end of file 115 `[1:], 116 Edits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "B"}}, 117 }, { 118 Name: "add_end", 119 In: "A", 120 Out: "AB", 121 Unified: UnifiedPrefix + ` 122 @@ -1 +1 @@ 123 -A 124 \ No newline at end of file 125 +AB 126 \ No newline at end of file 127 `[1:], 128 Edits: []diff.TextEdit{{Span: newSpan(1, 1), NewText: "B"}}, 129 LineEdits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "AB"}}, 130 }, { 131 Name: "add_newline", 132 In: "A", 133 Out: "A\n", 134 Unified: UnifiedPrefix + ` 135 @@ -1 +1 @@ 136 -A 137 \ No newline at end of file 138 +A 139 `[1:], 140 Edits: []diff.TextEdit{{Span: newSpan(1, 1), NewText: "\n"}}, 141 LineEdits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "A\n"}}, 142 }, { 143 Name: "delete_front", 144 In: "A\nB\nC\nA\nB\nB\nA\n", 145 Out: "C\nB\nA\nB\nA\nC\n", 146 Unified: UnifiedPrefix + ` 147 @@ -1,7 +1,6 @@ 148 -A 149 -B 150 C 151 +B 152 A 153 B 154 -B 155 A 156 +C 157 `[1:], 158 Edits: []diff.TextEdit{ 159 {Span: newSpan(0, 4), NewText: ""}, 160 {Span: newSpan(6, 6), NewText: "B\n"}, 161 {Span: newSpan(10, 12), NewText: ""}, 162 {Span: newSpan(14, 14), NewText: "C\n"}, 163 }, 164 NoDiff: true, // diff algorithm produces different delete/insert pattern 165 }, 166 { 167 Name: "replace_last_line", 168 In: "A\nB\n", 169 Out: "A\nC\n\n", 170 Unified: UnifiedPrefix + ` 171 @@ -1,2 +1,3 @@ 172 A 173 -B 174 +C 175 + 176 `[1:], 177 Edits: []diff.TextEdit{{Span: newSpan(2, 3), NewText: "C\n"}}, 178 LineEdits: []diff.TextEdit{{Span: newSpan(2, 4), NewText: "C\n\n"}}, 179 }, 180 { 181 Name: "multiple_replace", 182 In: "A\nB\nC\nD\nE\nF\nG\n", 183 Out: "A\nH\nI\nJ\nE\nF\nK\n", 184 Unified: UnifiedPrefix + ` 185 @@ -1,7 +1,7 @@ 186 A 187 -B 188 -C 189 -D 190 +H 191 +I 192 +J 193 E 194 F 195 -G 196 +K 197 `[1:], 198 Edits: []diff.TextEdit{ 199 {Span: newSpan(2, 8), NewText: "H\nI\nJ\n"}, 200 {Span: newSpan(12, 14), NewText: "K\n"}, 201 }, 202 NoDiff: true, // diff algorithm produces different delete/insert pattern 203 }, 204 } 205 206 func init() { 207 // expand all the spans to full versions 208 // we need them all to have their line number and column 209 for _, tc := range TestCases { 210 c := span.NewContentConverter("", []byte(tc.In)) 211 for i := range tc.Edits { 212 tc.Edits[i].Span, _ = tc.Edits[i].Span.WithAll(c) 213 } 214 for i := range tc.LineEdits { 215 tc.LineEdits[i].Span, _ = tc.LineEdits[i].Span.WithAll(c) 216 } 217 } 218 } 219 220 func DiffTest(t *testing.T, compute diff.ComputeEdits) { 221 t.Helper() 222 for _, test := range TestCases { 223 t.Run(test.Name, func(t *testing.T) { 224 t.Helper() 225 edits, err := compute(span.URIFromPath("/"+test.Name), test.In, test.Out) 226 if err != nil { 227 t.Fatal(err) 228 } 229 got := diff.ApplyEdits(test.In, edits) 230 unified := fmt.Sprint(diff.ToUnified(FileA, FileB, test.In, edits)) 231 if got != test.Out { 232 t.Errorf("got patched:\n%v\nfrom diff:\n%v\nexpected:\n%v", got, unified, test.Out) 233 } 234 if !test.NoDiff && unified != test.Unified { 235 t.Errorf("got diff:\n%v\nexpected:\n%v", unified, test.Unified) 236 } 237 }) 238 } 239 } 240 241 func newSpan(start, end int) span.Span { 242 return span.New("", span.NewPoint(0, 0, start), span.NewPoint(0, 0, end)) 243 }