master
  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}