diff options
| author | Anton Medvedev <anton@medv.io> | 2025-11-30 12:46:34 +0100 |
|---|---|---|
| committer | Anton Medvedev <anton@medv.io> | 2025-11-30 12:46:34 +0100 |
| commit | f6b0f38af648d028422a7494378b5dabdc90573f (patch) | |
| tree | 3c26cfc269c021300a2d1e4e02623dd440c20226 /pkg/gitdiff/text_test.go | |
First commit
Diffstat (limited to 'pkg/gitdiff/text_test.go')
| -rw-r--r-- | pkg/gitdiff/text_test.go | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/pkg/gitdiff/text_test.go b/pkg/gitdiff/text_test.go new file mode 100644 index 0000000..990b3bc --- /dev/null +++ b/pkg/gitdiff/text_test.go @@ -0,0 +1,488 @@ +package gitdiff + +import ( + "io" + "reflect" + "testing" +) + +func TestParseTextFragmentHeader(t *testing.T) { + tests := map[string]struct { + Input string + Output *TextFragment + Err bool + }{ + "shortest": { + Input: "@@ -1 +1 @@\n", + Output: &TextFragment{ + OldPosition: 1, + OldLines: 1, + NewPosition: 1, + NewLines: 1, + }, + }, + "standard": { + Input: "@@ -21,5 +28,9 @@\n", + Output: &TextFragment{ + OldPosition: 21, + OldLines: 5, + NewPosition: 28, + NewLines: 9, + }, + }, + "trailingComment": { + Input: "@@ -21,5 +28,9 @@ func test(n int) {\n", + Output: &TextFragment{ + Comment: "func test(n int) {", + OldPosition: 21, + OldLines: 5, + NewPosition: 28, + NewLines: 9, + }, + }, + "incomplete": { + Input: "@@ -12,3 +2\n", + Err: true, + }, + "badNumbers": { + Input: "@@ -1a,2b +3c,4d @@\n", + Err: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + frag, err := p.ParseTextFragmentHeader() + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing header, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("error parsing header: %v", err) + } + + if !reflect.DeepEqual(test.Output, frag) { + t.Errorf("incorrect fragment\nexpected: %+v\nactual: %+v", test.Output, frag) + } + }) + } +} + +func TestParseTextChunk(t *testing.T) { + tests := map[string]struct { + Input string + Fragment TextFragment + + Output *TextFragment + Err bool + }{ + "addWithContext": { + Input: ` context line ++new line 1 ++new line 2 + context line +`, + Fragment: TextFragment{ + OldLines: 2, + NewLines: 4, + }, + Output: &TextFragment{ + OldLines: 2, + NewLines: 4, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpAdd, "new line 1\n"}, + {OpAdd, "new line 2\n"}, + {OpContext, "context line\n"}, + }, + LinesAdded: 2, + LeadingContext: 1, + TrailingContext: 1, + }, + }, + "deleteWithContext": { + Input: ` context line +-old line 1 +-old line 2 + context line +`, + Fragment: TextFragment{ + OldLines: 4, + NewLines: 2, + }, + Output: &TextFragment{ + OldLines: 4, + NewLines: 2, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1\n"}, + {OpDelete, "old line 2\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 2, + LeadingContext: 1, + TrailingContext: 1, + }, + }, + "replaceWithContext": { + Input: ` context line +-old line 1 ++new line 1 + context line +`, + Fragment: TextFragment{ + OldLines: 3, + NewLines: 3, + }, + Output: &TextFragment{ + OldLines: 3, + NewLines: 3, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1\n"}, + {OpAdd, "new line 1\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 1, + LinesAdded: 1, + LeadingContext: 1, + TrailingContext: 1, + }, + }, + "middleContext": { + Input: ` context line +-old line 1 + context line ++new line 1 + context line +`, + Fragment: TextFragment{ + OldLines: 4, + NewLines: 4, + }, + Output: &TextFragment{ + OldLines: 4, + NewLines: 4, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1\n"}, + {OpContext, "context line\n"}, + {OpAdd, "new line 1\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 1, + LinesAdded: 1, + LeadingContext: 1, + TrailingContext: 1, + }, + }, + "deleteFinalNewline": { + Input: ` context line +-old line 1 ++new line 1 +\ No newline at end of file +`, + Fragment: TextFragment{ + OldLines: 2, + NewLines: 2, + }, + Output: &TextFragment{ + OldLines: 2, + NewLines: 2, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1\n"}, + {OpAdd, "new line 1"}, + }, + LinesDeleted: 1, + LinesAdded: 1, + LeadingContext: 1, + }, + }, + "addFinalNewline": { + Input: ` context line +-old line 1 +\ No newline at end of file ++new line 1 +`, + Fragment: TextFragment{ + OldLines: 2, + NewLines: 2, + }, + Output: &TextFragment{ + OldLines: 2, + NewLines: 2, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1"}, + {OpAdd, "new line 1\n"}, + }, + LinesDeleted: 1, + LinesAdded: 1, + LeadingContext: 1, + }, + }, + "addAll": { + Input: `+new line 1 ++new line 2 ++new line 3 +`, + Fragment: TextFragment{ + OldLines: 0, + NewLines: 3, + }, + Output: &TextFragment{ + OldLines: 0, + NewLines: 3, + Lines: []Line{ + {OpAdd, "new line 1\n"}, + {OpAdd, "new line 2\n"}, + {OpAdd, "new line 3\n"}, + }, + LinesAdded: 3, + }, + }, + "deleteAll": { + Input: `-old line 1 +-old line 2 +-old line 3 +`, + Fragment: TextFragment{ + OldLines: 3, + NewLines: 0, + }, + Output: &TextFragment{ + OldLines: 3, + NewLines: 0, + Lines: []Line{ + {OpDelete, "old line 1\n"}, + {OpDelete, "old line 2\n"}, + {OpDelete, "old line 3\n"}, + }, + LinesDeleted: 3, + }, + }, + "emptyContextLine": { + Input: ` context line + ++new line + context line +`, + Fragment: TextFragment{ + OldLines: 3, + NewLines: 4, + }, + Output: &TextFragment{ + OldLines: 3, + NewLines: 4, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpContext, "\n"}, + {OpAdd, "new line\n"}, + {OpContext, "context line\n"}, + }, + LinesAdded: 1, + LeadingContext: 2, + TrailingContext: 1, + }, + }, + "emptyChunk": { + Input: "", + Err: true, + }, + "invalidOperation": { + Input: ` context line +?wat line + context line +`, + Fragment: TextFragment{ + OldLines: 3, + NewLines: 3, + }, + Err: true, + }, + "unbalancedHeader": { + Input: ` context line +-old line 1 ++new line 1 + context line +`, + Fragment: TextFragment{ + OldLines: 2, + NewLines: 5, + }, + Err: true, + }, + "onlyContext": { + Input: ` context line + context line +`, + Fragment: TextFragment{ + OldLines: 2, + NewLines: 2, + }, + Err: true, + }, + "unexpectedNoNewlineMarker": { + Input: `\ No newline at end of file`, + Fragment: TextFragment{ + OldLines: 1, + NewLines: 1, + }, + Err: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + frag := test.Fragment + err := p.ParseTextChunk(&frag) + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing text chunk, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("error parsing text chunk: %v", err) + } + + if !reflect.DeepEqual(test.Output, &frag) { + t.Errorf("incorrect fragment\nexpected: %+v\nactual: %+v", test.Output, &frag) + } + }) + } +} + +func TestParseTextFragments(t *testing.T) { + tests := map[string]struct { + Input string + File File + + Fragments []*TextFragment + Err bool + }{ + "multipleChanges": { + Input: `@@ -1,3 +1,2 @@ + context line +-old line 1 + context line +@@ -8,3 +7,3 @@ + context line +-old line 2 ++new line 1 + context line +@@ -15,3 +14,4 @@ + context line +-old line 3 ++new line 2 ++new line 3 + context line +`, + Fragments: []*TextFragment{ + { + OldPosition: 1, + OldLines: 3, + NewPosition: 1, + NewLines: 2, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 1\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 1, + LeadingContext: 1, + TrailingContext: 1, + }, + { + OldPosition: 8, + OldLines: 3, + NewPosition: 7, + NewLines: 3, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 2\n"}, + {OpAdd, "new line 1\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 1, + LinesAdded: 1, + LeadingContext: 1, + TrailingContext: 1, + }, + { + OldPosition: 15, + OldLines: 3, + NewPosition: 14, + NewLines: 4, + Lines: []Line{ + {OpContext, "context line\n"}, + {OpDelete, "old line 3\n"}, + {OpAdd, "new line 2\n"}, + {OpAdd, "new line 3\n"}, + {OpContext, "context line\n"}, + }, + LinesDeleted: 1, + LinesAdded: 2, + LeadingContext: 1, + TrailingContext: 1, + }, + }, + }, + "badNewFile": { + Input: `@@ -1 +1,2 @@ +-old line 1 ++new line 1 ++new line 2 +`, + File: File{ + IsNew: true, + }, + Err: true, + }, + "badDeletedFile": { + Input: `@@ -1,2 +1 @@ +-old line 1 + context line +`, + File: File{ + IsDelete: true, + }, + Err: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + p := newTestParser(test.Input, true) + + file := test.File + n, err := p.ParseTextFragments(&file) + if test.Err { + if err == nil || err == io.EOF { + t.Fatalf("expected error parsing text fragments, but got %v", err) + } + return + } + if err != nil { + t.Fatalf("error parsing text fragments: %v", err) + } + + if len(test.Fragments) != n { + t.Fatalf("incorrect number of added fragments: expected %d, actual %d", len(test.Fragments), n) + } + + for i, frag := range test.Fragments { + if !reflect.DeepEqual(frag, file.TextFragments[i]) { + t.Errorf("incorrect fragment at position %d\nexpected: %+v\nactual: %+v", i, frag, file.TextFragments[i]) + } + } + }) + } +} |
