changelog generator & diff tool
stormlightlabs.github.io/git-storm/
changelog
changeset
markdown
golang
git
1package diff
2
3import (
4 "strings"
5 "testing"
6)
7
8func TestSplitDiff_Diff(t *testing.T) {
9 tests := []struct {
10 name string
11 oldContent string
12 newContent string
13 width int
14 showLineNum bool
15 expectFunc func(result DiffResult) bool
16 }{
17 {
18 name: "empty files",
19 oldContent: "",
20 newContent: "",
21 width: 80,
22 showLineNum: true,
23 expectFunc: func(result DiffResult) bool {
24 return result.View == ViewSplit && strings.Contains(result.Content, "No changes")
25 },
26 },
27 {
28 name: "identical files",
29 oldContent: "line1\nline2\nline3",
30 newContent: "line1\nline2\nline3",
31 width: 100,
32 showLineNum: true,
33 expectFunc: func(result DiffResult) bool {
34 return result.View == ViewSplit &&
35 strings.Contains(result.Content, "line1") &&
36 strings.Contains(result.Content, "line2") &&
37 strings.Contains(result.Content, "line3")
38 },
39 },
40 {
41 name: "simple insertion",
42 oldContent: "line1\nline3",
43 newContent: "line1\nline2\nline3",
44 width: 100,
45 showLineNum: true,
46 expectFunc: func(result DiffResult) bool {
47 return result.View == ViewSplit &&
48 strings.Contains(result.Content, "line1") &&
49 strings.Contains(result.Content, "line2") &&
50 strings.Contains(result.Content, "line3") &&
51 strings.Contains(result.Content, SymbolAdd)
52 },
53 },
54 {
55 name: "simple deletion",
56 oldContent: "line1\nline2\nline3",
57 newContent: "line1\nline3",
58 width: 100,
59 showLineNum: true,
60 expectFunc: func(result DiffResult) bool {
61 return result.View == ViewSplit &&
62 strings.Contains(result.Content, "line1") &&
63 strings.Contains(result.Content, "line2") &&
64 strings.Contains(result.Content, "line3") &&
65 strings.Contains(result.Content, SymbolDeleteLine)
66 },
67 },
68 {
69 name: "replacement",
70 oldContent: "github.com/foo/bar v1.0.0",
71 newContent: "github.com/foo/bar v2.0.0",
72 width: 120,
73 showLineNum: true,
74 expectFunc: func(result DiffResult) bool {
75 return result.View == ViewSplit &&
76 strings.Contains(result.Content, "v1.0.0") &&
77 strings.Contains(result.Content, "v2.0.0") &&
78 strings.Contains(result.Content, SymbolChange)
79 },
80 },
81 {
82 name: "without line numbers",
83 oldContent: "old line",
84 newContent: "new line",
85 width: 100,
86 showLineNum: false,
87 expectFunc: func(result DiffResult) bool {
88 return result.View == ViewSplit &&
89 strings.Contains(result.Content, "old line") &&
90 strings.Contains(result.Content, "new line")
91 },
92 },
93 }
94
95 for _, tt := range tests {
96 t.Run(tt.name, func(t *testing.T) {
97 splitter := &SplitDiff{
98 TerminalWidth: tt.width,
99 ShowLineNumbers: tt.showLineNum,
100 Expanded: true,
101 }
102
103 result, err := splitter.Diff(
104 strings.NewReader(tt.oldContent),
105 strings.NewReader(tt.newContent),
106 ViewSplit,
107 )
108
109 if err != nil {
110 t.Fatalf("unexpected error: %v", err)
111 }
112
113 if !tt.expectFunc(result) {
114 t.Errorf("result did not meet expectations.\nGot:\n%s", result.Content)
115 }
116 })
117 }
118}
119
120func TestUnifiedDiff_Diff(t *testing.T) {
121 tests := []struct {
122 name string
123 oldContent string
124 newContent string
125 width int
126 showLineNum bool
127 expectFunc func(result DiffResult) bool
128 }{
129 {
130 name: "empty files",
131 oldContent: "",
132 newContent: "",
133 width: 80,
134 showLineNum: true,
135 expectFunc: func(result DiffResult) bool {
136 return result.View == ViewUnified && strings.Contains(result.Content, "No changes")
137 },
138 },
139 {
140 name: "identical files",
141 oldContent: "line1\nline2\nline3",
142 newContent: "line1\nline2\nline3",
143 width: 100,
144 showLineNum: true,
145 expectFunc: func(result DiffResult) bool {
146 return result.View == ViewUnified &&
147 strings.Contains(result.Content, "line1") &&
148 strings.Contains(result.Content, "line2") &&
149 strings.Contains(result.Content, "line3")
150 },
151 },
152 {
153 name: "simple insertion",
154 oldContent: "line1\nline3",
155 newContent: "line1\nline2\nline3",
156 width: 100,
157 showLineNum: true,
158 expectFunc: func(result DiffResult) bool {
159 return result.View == ViewUnified &&
160 strings.Contains(result.Content, "line1") &&
161 strings.Contains(result.Content, "+line2") &&
162 strings.Contains(result.Content, "line3")
163 },
164 },
165 {
166 name: "simple deletion",
167 oldContent: "line1\nline2\nline3",
168 newContent: "line1\nline3",
169 width: 100,
170 showLineNum: true,
171 expectFunc: func(result DiffResult) bool {
172 return result.View == ViewUnified &&
173 strings.Contains(result.Content, "line1") &&
174 strings.Contains(result.Content, "-line2") &&
175 strings.Contains(result.Content, "line3")
176 },
177 },
178 {
179 name: "replacement",
180 oldContent: "github.com/foo/bar v1.0.0",
181 newContent: "github.com/foo/bar v2.0.0",
182 width: 120,
183 showLineNum: true,
184 expectFunc: func(result DiffResult) bool {
185 return result.View == ViewUnified &&
186 strings.Contains(result.Content, "-github.com/foo/bar v1.0.0") &&
187 strings.Contains(result.Content, "+github.com/foo/bar v2.0.0")
188 },
189 },
190 {
191 name: "without line numbers",
192 oldContent: "old line",
193 newContent: "new line",
194 width: 100,
195 showLineNum: false,
196 expectFunc: func(result DiffResult) bool {
197 return result.View == ViewUnified &&
198 strings.Contains(result.Content, "-old line") &&
199 strings.Contains(result.Content, "+new line")
200 },
201 },
202 }
203
204 for _, tt := range tests {
205 t.Run(tt.name, func(t *testing.T) {
206 unifier := &UnifiedDiff{
207 TerminalWidth: tt.width,
208 ShowLineNumbers: tt.showLineNum,
209 Expanded: true,
210 }
211
212 result, err := unifier.Diff(
213 strings.NewReader(tt.oldContent),
214 strings.NewReader(tt.newContent),
215 ViewUnified,
216 )
217
218 if err != nil {
219 t.Fatalf("unexpected error: %v", err)
220 }
221
222 if !tt.expectFunc(result) {
223 t.Errorf("result did not meet expectations.\nGot:\n%s", result.Content)
224 }
225 })
226 }
227}
228
229func TestSplitLines(t *testing.T) {
230 tests := []struct {
231 name string
232 input string
233 expected []string
234 }{
235 {
236 name: "empty string",
237 input: "",
238 expected: []string{},
239 },
240 {
241 name: "single line no newline",
242 input: "hello",
243 expected: []string{"hello"},
244 },
245 {
246 name: "single line with newline",
247 input: "hello\n",
248 expected: []string{"hello"},
249 },
250 {
251 name: "multiple lines",
252 input: "line1\nline2\nline3",
253 expected: []string{"line1", "line2", "line3"},
254 },
255 {
256 name: "multiple lines with trailing newline",
257 input: "line1\nline2\nline3\n",
258 expected: []string{"line1", "line2", "line3"},
259 },
260 {
261 name: "empty lines preserved",
262 input: "line1\n\nline3",
263 expected: []string{"line1", "", "line3"},
264 },
265 {
266 name: "only newlines",
267 input: "\n\n\n",
268 expected: []string{"", "", ""},
269 },
270 }
271
272 for _, tt := range tests {
273 t.Run(tt.name, func(t *testing.T) {
274 result := splitLines(tt.input)
275
276 if len(result) != len(tt.expected) {
277 t.Fatalf("expected %d lines, got %d", len(tt.expected), len(result))
278 }
279
280 for i := range result {
281 if result[i] != tt.expected[i] {
282 t.Errorf("line %d: expected %q, got %q", i, tt.expected[i], result[i])
283 }
284 }
285 })
286 }
287}
288
289func TestDiffViewKind_String(t *testing.T) {
290 tests := []struct {
291 kind DiffViewKind
292 expected string
293 }{
294 {ViewUnified, "Unified"},
295 {ViewSplit, "Split"},
296 {ViewHunk, "Hunk"},
297 {ViewInline, "Inline"},
298 {ViewRich, "Rich"},
299 {ViewSource, "Source"},
300 {DiffViewKind(999), "Unknown"},
301 }
302
303 for _, tt := range tests {
304 t.Run(tt.expected, func(t *testing.T) {
305 result := tt.kind.String()
306 if result != tt.expected {
307 t.Errorf("expected %q, got %q", tt.expected, result)
308 }
309 })
310 }
311}
312
313func TestSplitDiff_CompressedView(t *testing.T) {
314 oldLines := make([]string, 50)
315 newLines := make([]string, 50)
316 for i := range 50 {
317 oldLines[i] = "unchanged line"
318 newLines[i] = "unchanged line"
319 }
320
321 newLines[25] = "changed line"
322
323 oldContent := strings.Join(oldLines, "\n")
324 newContent := strings.Join(newLines, "\n")
325
326 splitter := &SplitDiff{
327 TerminalWidth: 100,
328 ShowLineNumbers: true,
329 Expanded: false,
330 }
331
332 result, err := splitter.Diff(
333 strings.NewReader(oldContent),
334 strings.NewReader(newContent),
335 ViewSplit,
336 )
337
338 if err != nil {
339 t.Fatalf("unexpected error: %v", err)
340 }
341
342 if !strings.Contains(result.Content, "unchanged lines") {
343 t.Errorf("expected compression indicator in output")
344 }
345
346 if !strings.Contains(result.Content, "changed line") {
347 t.Errorf("expected changed line in output")
348 }
349}
350
351func TestUnifiedDiff_CompressedView(t *testing.T) {
352 oldLines := make([]string, 50)
353 newLines := make([]string, 50)
354 for i := range 50 {
355 oldLines[i] = "unchanged line"
356 newLines[i] = "unchanged line"
357 }
358
359 newLines[25] = "changed line"
360
361 oldContent := strings.Join(oldLines, "\n")
362 newContent := strings.Join(newLines, "\n")
363
364 unifier := &UnifiedDiff{
365 TerminalWidth: 100,
366 ShowLineNumbers: true,
367 Expanded: false, // Enable compression
368 }
369
370 result, err := unifier.Diff(
371 strings.NewReader(oldContent),
372 strings.NewReader(newContent),
373 ViewUnified,
374 )
375
376 if err != nil {
377 t.Fatalf("unexpected error: %v", err)
378 }
379
380 if !strings.Contains(result.Content, "unchanged lines") {
381 t.Errorf("expected compression indicator in output")
382 }
383
384 if !strings.Contains(result.Content, "+changed line") {
385 t.Errorf("expected changed line with + prefix in output")
386 }
387}