fork of go-gitdiff with jj support
1package gitdiff
2
3import (
4 "io"
5 "reflect"
6 "testing"
7)
8
9func TestParseTextFragmentHeader(t *testing.T) {
10 tests := map[string]struct {
11 Input string
12 Output *TextFragment
13 Err bool
14 }{
15 "shortest": {
16 Input: "@@ -1 +1 @@\n",
17 Output: &TextFragment{
18 OldPosition: 1,
19 OldLines: 1,
20 NewPosition: 1,
21 NewLines: 1,
22 },
23 },
24 "standard": {
25 Input: "@@ -21,5 +28,9 @@\n",
26 Output: &TextFragment{
27 OldPosition: 21,
28 OldLines: 5,
29 NewPosition: 28,
30 NewLines: 9,
31 },
32 },
33 "trailingComment": {
34 Input: "@@ -21,5 +28,9 @@ func test(n int) {\n",
35 Output: &TextFragment{
36 Comment: "func test(n int) {",
37 OldPosition: 21,
38 OldLines: 5,
39 NewPosition: 28,
40 NewLines: 9,
41 },
42 },
43 "incomplete": {
44 Input: "@@ -12,3 +2\n",
45 Err: true,
46 },
47 "badNumbers": {
48 Input: "@@ -1a,2b +3c,4d @@\n",
49 Err: true,
50 },
51 }
52
53 for name, test := range tests {
54 t.Run(name, func(t *testing.T) {
55 p := newTestParser(test.Input, true)
56
57 frag, err := p.ParseTextFragmentHeader()
58 if test.Err {
59 if err == nil || err == io.EOF {
60 t.Fatalf("expected error parsing header, but got %v", err)
61 }
62 return
63 }
64 if err != nil {
65 t.Fatalf("error parsing header: %v", err)
66 }
67
68 if !reflect.DeepEqual(test.Output, frag) {
69 t.Errorf("incorrect fragment\nexpected: %+v\nactual: %+v", test.Output, frag)
70 }
71 })
72 }
73}
74
75func TestParseTextChunk(t *testing.T) {
76 tests := map[string]struct {
77 Input string
78 Fragment TextFragment
79
80 Output *TextFragment
81 Err bool
82 }{
83 "addWithContext": {
84 Input: ` context line
85+new line 1
86+new line 2
87 context line
88`,
89 Fragment: TextFragment{
90 OldLines: 2,
91 NewLines: 4,
92 },
93 Output: &TextFragment{
94 OldLines: 2,
95 NewLines: 4,
96 Lines: []Line{
97 {OpContext, "context line\n"},
98 {OpAdd, "new line 1\n"},
99 {OpAdd, "new line 2\n"},
100 {OpContext, "context line\n"},
101 },
102 LinesAdded: 2,
103 LeadingContext: 1,
104 TrailingContext: 1,
105 },
106 },
107 "deleteWithContext": {
108 Input: ` context line
109-old line 1
110-old line 2
111 context line
112`,
113 Fragment: TextFragment{
114 OldLines: 4,
115 NewLines: 2,
116 },
117 Output: &TextFragment{
118 OldLines: 4,
119 NewLines: 2,
120 Lines: []Line{
121 {OpContext, "context line\n"},
122 {OpDelete, "old line 1\n"},
123 {OpDelete, "old line 2\n"},
124 {OpContext, "context line\n"},
125 },
126 LinesDeleted: 2,
127 LeadingContext: 1,
128 TrailingContext: 1,
129 },
130 },
131 "replaceWithContext": {
132 Input: ` context line
133-old line 1
134+new line 1
135 context line
136`,
137 Fragment: TextFragment{
138 OldLines: 3,
139 NewLines: 3,
140 },
141 Output: &TextFragment{
142 OldLines: 3,
143 NewLines: 3,
144 Lines: []Line{
145 {OpContext, "context line\n"},
146 {OpDelete, "old line 1\n"},
147 {OpAdd, "new line 1\n"},
148 {OpContext, "context line\n"},
149 },
150 LinesDeleted: 1,
151 LinesAdded: 1,
152 LeadingContext: 1,
153 TrailingContext: 1,
154 },
155 },
156 "middleContext": {
157 Input: ` context line
158-old line 1
159 context line
160+new line 1
161 context line
162`,
163 Fragment: TextFragment{
164 OldLines: 4,
165 NewLines: 4,
166 },
167 Output: &TextFragment{
168 OldLines: 4,
169 NewLines: 4,
170 Lines: []Line{
171 {OpContext, "context line\n"},
172 {OpDelete, "old line 1\n"},
173 {OpContext, "context line\n"},
174 {OpAdd, "new line 1\n"},
175 {OpContext, "context line\n"},
176 },
177 LinesDeleted: 1,
178 LinesAdded: 1,
179 LeadingContext: 1,
180 TrailingContext: 1,
181 },
182 },
183 "deleteFinalNewline": {
184 Input: ` context line
185-old line 1
186+new line 1
187\ No newline at end of file
188`,
189 Fragment: TextFragment{
190 OldLines: 2,
191 NewLines: 2,
192 },
193 Output: &TextFragment{
194 OldLines: 2,
195 NewLines: 2,
196 Lines: []Line{
197 {OpContext, "context line\n"},
198 {OpDelete, "old line 1\n"},
199 {OpAdd, "new line 1"},
200 },
201 LinesDeleted: 1,
202 LinesAdded: 1,
203 LeadingContext: 1,
204 },
205 },
206 "addFinalNewline": {
207 Input: ` context line
208-old line 1
209\ No newline at end of file
210+new line 1
211`,
212 Fragment: TextFragment{
213 OldLines: 2,
214 NewLines: 2,
215 },
216 Output: &TextFragment{
217 OldLines: 2,
218 NewLines: 2,
219 Lines: []Line{
220 {OpContext, "context line\n"},
221 {OpDelete, "old line 1"},
222 {OpAdd, "new line 1\n"},
223 },
224 LinesDeleted: 1,
225 LinesAdded: 1,
226 LeadingContext: 1,
227 },
228 },
229 "addAll": {
230 Input: `+new line 1
231+new line 2
232+new line 3
233`,
234 Fragment: TextFragment{
235 OldLines: 0,
236 NewLines: 3,
237 },
238 Output: &TextFragment{
239 OldLines: 0,
240 NewLines: 3,
241 Lines: []Line{
242 {OpAdd, "new line 1\n"},
243 {OpAdd, "new line 2\n"},
244 {OpAdd, "new line 3\n"},
245 },
246 LinesAdded: 3,
247 },
248 },
249 "deleteAll": {
250 Input: `-old line 1
251-old line 2
252-old line 3
253`,
254 Fragment: TextFragment{
255 OldLines: 3,
256 NewLines: 0,
257 },
258 Output: &TextFragment{
259 OldLines: 3,
260 NewLines: 0,
261 Lines: []Line{
262 {OpDelete, "old line 1\n"},
263 {OpDelete, "old line 2\n"},
264 {OpDelete, "old line 3\n"},
265 },
266 LinesDeleted: 3,
267 },
268 },
269 "emptyContextLine": {
270 Input: ` context line
271
272+new line
273 context line
274`,
275 Fragment: TextFragment{
276 OldLines: 3,
277 NewLines: 4,
278 },
279 Output: &TextFragment{
280 OldLines: 3,
281 NewLines: 4,
282 Lines: []Line{
283 {OpContext, "context line\n"},
284 {OpContext, "\n"},
285 {OpAdd, "new line\n"},
286 {OpContext, "context line\n"},
287 },
288 LinesAdded: 1,
289 LeadingContext: 2,
290 TrailingContext: 1,
291 },
292 },
293 "emptyChunk": {
294 Input: "",
295 Err: true,
296 },
297 "invalidOperation": {
298 Input: ` context line
299?wat line
300 context line
301`,
302 Fragment: TextFragment{
303 OldLines: 3,
304 NewLines: 3,
305 },
306 Err: true,
307 },
308 "unbalancedHeader": {
309 Input: ` context line
310-old line 1
311+new line 1
312 context line
313`,
314 Fragment: TextFragment{
315 OldLines: 2,
316 NewLines: 5,
317 },
318 Err: true,
319 },
320 "onlyContext": {
321 Input: ` context line
322 context line
323`,
324 Fragment: TextFragment{
325 OldLines: 2,
326 NewLines: 2,
327 },
328 Err: true,
329 },
330 "unexpectedNoNewlineMarker": {
331 Input: `\ No newline at end of file`,
332 Fragment: TextFragment{
333 OldLines: 1,
334 NewLines: 1,
335 },
336 Err: true,
337 },
338 }
339
340 for name, test := range tests {
341 t.Run(name, func(t *testing.T) {
342 p := newTestParser(test.Input, true)
343
344 frag := test.Fragment
345 err := p.ParseTextChunk(&frag)
346 if test.Err {
347 if err == nil || err == io.EOF {
348 t.Fatalf("expected error parsing text chunk, but got %v", err)
349 }
350 return
351 }
352 if err != nil {
353 t.Fatalf("error parsing text chunk: %v", err)
354 }
355
356 if !reflect.DeepEqual(test.Output, &frag) {
357 t.Errorf("incorrect fragment\nexpected: %+v\nactual: %+v", test.Output, &frag)
358 }
359 })
360 }
361}
362
363func TestParseTextFragments(t *testing.T) {
364 tests := map[string]struct {
365 Input string
366 File File
367
368 Fragments []*TextFragment
369 Err bool
370 }{
371 "multipleChanges": {
372 Input: `@@ -1,3 +1,2 @@
373 context line
374-old line 1
375 context line
376@@ -8,3 +7,3 @@
377 context line
378-old line 2
379+new line 1
380 context line
381@@ -15,3 +14,4 @@
382 context line
383-old line 3
384+new line 2
385+new line 3
386 context line
387`,
388 Fragments: []*TextFragment{
389 {
390 OldPosition: 1,
391 OldLines: 3,
392 NewPosition: 1,
393 NewLines: 2,
394 Lines: []Line{
395 {OpContext, "context line\n"},
396 {OpDelete, "old line 1\n"},
397 {OpContext, "context line\n"},
398 },
399 LinesDeleted: 1,
400 LeadingContext: 1,
401 TrailingContext: 1,
402 },
403 {
404 OldPosition: 8,
405 OldLines: 3,
406 NewPosition: 7,
407 NewLines: 3,
408 Lines: []Line{
409 {OpContext, "context line\n"},
410 {OpDelete, "old line 2\n"},
411 {OpAdd, "new line 1\n"},
412 {OpContext, "context line\n"},
413 },
414 LinesDeleted: 1,
415 LinesAdded: 1,
416 LeadingContext: 1,
417 TrailingContext: 1,
418 },
419 {
420 OldPosition: 15,
421 OldLines: 3,
422 NewPosition: 14,
423 NewLines: 4,
424 Lines: []Line{
425 {OpContext, "context line\n"},
426 {OpDelete, "old line 3\n"},
427 {OpAdd, "new line 2\n"},
428 {OpAdd, "new line 3\n"},
429 {OpContext, "context line\n"},
430 },
431 LinesDeleted: 1,
432 LinesAdded: 2,
433 LeadingContext: 1,
434 TrailingContext: 1,
435 },
436 },
437 },
438 "badNewFile": {
439 Input: `@@ -1 +1,2 @@
440-old line 1
441+new line 1
442+new line 2
443`,
444 File: File{
445 IsNew: true,
446 },
447 Err: true,
448 },
449 "badDeletedFile": {
450 Input: `@@ -1,2 +1 @@
451-old line 1
452 context line
453`,
454 File: File{
455 IsDelete: true,
456 },
457 Err: true,
458 },
459 }
460
461 for name, test := range tests {
462 t.Run(name, func(t *testing.T) {
463 p := newTestParser(test.Input, true)
464
465 file := test.File
466 n, err := p.ParseTextFragments(&file)
467 if test.Err {
468 if err == nil || err == io.EOF {
469 t.Fatalf("expected error parsing text fragments, but got %v", err)
470 }
471 return
472 }
473 if err != nil {
474 t.Fatalf("error parsing text fragments: %v", err)
475 }
476
477 if len(test.Fragments) != n {
478 t.Fatalf("incorrect number of added fragments: expected %d, actual %d", len(test.Fragments), n)
479 }
480
481 for i, frag := range test.Fragments {
482 if !reflect.DeepEqual(frag, file.TextFragments[i]) {
483 t.Errorf("incorrect fragment at position %d\nexpected: %+v\nactual: %+v", i, frag, file.TextFragments[i])
484 }
485 }
486 })
487 }
488}