changelog generator & diff tool stormlightlabs.github.io/git-storm/
changelog changeset markdown golang git
at main 9.5 kB view raw
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}