fork of go-gitdiff with jj support
at v0.7.1 17 kB view raw
1package gitdiff 2 3import ( 4 "testing" 5 "time" 6) 7 8func TestParsePatchIdentity(t *testing.T) { 9 tests := map[string]struct { 10 Input string 11 Output PatchIdentity 12 Err interface{} 13 }{ 14 "simple": { 15 Input: "Morton Haypenny <mhaypenny@example.com>", 16 Output: PatchIdentity{ 17 Name: "Morton Haypenny", 18 Email: "mhaypenny@example.com", 19 }, 20 }, 21 "extraWhitespace": { 22 Input: " Morton Haypenny <mhaypenny@example.com > ", 23 Output: PatchIdentity{ 24 Name: "Morton Haypenny", 25 Email: "mhaypenny@example.com", 26 }, 27 }, 28 "trailingCharacters": { 29 Input: "Morton Haypenny <mhaypenny@example.com> unrelated garbage", 30 Output: PatchIdentity{ 31 Name: "Morton Haypenny", 32 Email: "mhaypenny@example.com", 33 }, 34 }, 35 "missingName": { 36 Input: "<mhaypenny@example.com>", 37 Err: "invalid identity", 38 }, 39 "missingEmail": { 40 Input: "Morton Haypenny", 41 Err: "invalid identity", 42 }, 43 "unclosedEmail": { 44 Input: "Morton Haypenny <mhaypenny@example.com", 45 Err: "unclosed email", 46 }, 47 } 48 49 for name, test := range tests { 50 t.Run(name, func(t *testing.T) { 51 id, err := ParsePatchIdentity(test.Input) 52 if test.Err != nil { 53 assertError(t, test.Err, err, "parsing identity") 54 return 55 } 56 if err != nil { 57 t.Fatalf("unexpected error parsing identity: %v", err) 58 } 59 60 if test.Output != id { 61 t.Errorf("incorrect identity: expected %#v, actual %#v", test.Output, id) 62 } 63 }) 64 } 65} 66 67func TestParsePatchDate(t *testing.T) { 68 expected := time.Date(2020, 4, 9, 8, 7, 6, 0, time.UTC) 69 70 tests := map[string]struct { 71 Input string 72 Output time.Time 73 Err interface{} 74 }{ 75 "default": { 76 Input: "Thu Apr 9 01:07:06 2020 -0700", 77 Output: expected, 78 }, 79 "defaultLocal": { 80 Input: "Thu Apr 9 01:07:06 2020", 81 Output: time.Date(2020, 4, 9, 1, 7, 6, 0, time.Local), 82 }, 83 "iso": { 84 Input: "2020-04-09 01:07:06 -0700", 85 Output: expected, 86 }, 87 "isoStrict": { 88 Input: "2020-04-09T01:07:06-07:00", 89 Output: expected, 90 }, 91 "rfc": { 92 Input: "Thu, 9 Apr 2020 01:07:06 -0700", 93 Output: expected, 94 }, 95 "short": { 96 Input: "2020-04-09", 97 Output: time.Date(2020, 4, 9, 0, 0, 0, 0, time.Local), 98 }, 99 "raw": { 100 Input: "1586419626 -0700", 101 Output: expected, 102 }, 103 "unix": { 104 Input: "1586419626", 105 Output: expected, 106 }, 107 "unknownFormat": { 108 Input: "4/9/2020 01:07:06 PDT", 109 Err: "unknown date format", 110 }, 111 "empty": { 112 Input: "", 113 }, 114 } 115 116 for name, test := range tests { 117 t.Run(name, func(t *testing.T) { 118 d, err := ParsePatchDate(test.Input) 119 if test.Err != nil { 120 assertError(t, test.Err, err, "parsing date") 121 return 122 } 123 if err != nil { 124 t.Fatalf("unexpected error parsing date: %v", err) 125 } 126 if !test.Output.Equal(d) { 127 t.Errorf("incorrect parsed date: expected %v, actual %v", test.Output, d) 128 } 129 }) 130 } 131} 132 133func TestParsePatchHeader(t *testing.T) { 134 expectedSHA := "61f5cd90bed4d204ee3feb3aa41ee91d4734855b" 135 expectedIdentity := &PatchIdentity{ 136 Name: "Morton Haypenny", 137 Email: "mhaypenny@example.com", 138 } 139 expectedDate := time.Date(2020, 04, 11, 15, 21, 23, 0, time.FixedZone("PDT", -7*60*60)) 140 expectedTitle := "A sample commit to test header parsing" 141 expectedEmojiOneLineTitle := "🤖 Enabling auto-merging" 142 expectedEmojiMultiLineTitle := "[IA64] Put ia64 config files on the Uwe Kleine-König diet" 143 expectedBody := "The medium format shows the body, which\nmay wrap on to multiple lines.\n\nAnother body line." 144 expectedBodyAppendix := "CC: Joe Smith <joe.smith@company.com>" 145 146 tests := map[string]struct { 147 Input string 148 Options []PatchHeaderOption 149 Header PatchHeader 150 Err interface{} 151 }{ 152 "prettyShort": { 153 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 154Author: Morton Haypenny <mhaypenny@example.com> 155 156 A sample commit to test header parsing 157`, 158 Header: PatchHeader{ 159 SHA: expectedSHA, 160 Author: expectedIdentity, 161 Title: expectedTitle, 162 }, 163 }, 164 "prettyMedium": { 165 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 166Author: Morton Haypenny <mhaypenny@example.com> 167Date: Sat Apr 11 15:21:23 2020 -0700 168 169 A sample commit to test header parsing 170 171 The medium format shows the body, which 172 may wrap on to multiple lines. 173 174 Another body line. 175`, 176 Header: PatchHeader{ 177 SHA: expectedSHA, 178 Author: expectedIdentity, 179 AuthorDate: expectedDate, 180 Title: expectedTitle, 181 Body: expectedBody, 182 }, 183 }, 184 "prettyFull": { 185 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 186Author: Morton Haypenny <mhaypenny@example.com> 187Commit: Morton Haypenny <mhaypenny@example.com> 188 189 A sample commit to test header parsing 190 191 The medium format shows the body, which 192 may wrap on to multiple lines. 193 194 Another body line. 195`, 196 Header: PatchHeader{ 197 SHA: expectedSHA, 198 Author: expectedIdentity, 199 Committer: expectedIdentity, 200 Title: expectedTitle, 201 Body: expectedBody, 202 }, 203 }, 204 "prettyFuller": { 205 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 206Author: Morton Haypenny <mhaypenny@example.com> 207AuthorDate: Sat Apr 11 15:21:23 2020 -0700 208Commit: Morton Haypenny <mhaypenny@example.com> 209CommitDate: Sat Apr 11 15:21:23 2020 -0700 210 211 A sample commit to test header parsing 212 213 The medium format shows the body, which 214 may wrap on to multiple lines. 215 216 Another body line. 217`, 218 Header: PatchHeader{ 219 SHA: expectedSHA, 220 Author: expectedIdentity, 221 AuthorDate: expectedDate, 222 Committer: expectedIdentity, 223 CommitterDate: expectedDate, 224 Title: expectedTitle, 225 Body: expectedBody, 226 }, 227 }, 228 "prettyAppendix": { 229 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 230Author: Morton Haypenny <mhaypenny@example.com> 231AuthorDate: Sat Apr 11 15:21:23 2020 -0700 232Commit: Morton Haypenny <mhaypenny@example.com> 233CommitDate: Sat Apr 11 15:21:23 2020 -0700 234 235 A sample commit to test header parsing 236 237 The medium format shows the body, which 238 may wrap on to multiple lines. 239 240 Another body line. 241 --- 242 CC: Joe Smith <joe.smith@company.com> 243`, 244 Header: PatchHeader{ 245 SHA: expectedSHA, 246 Author: expectedIdentity, 247 AuthorDate: expectedDate, 248 Committer: expectedIdentity, 249 CommitterDate: expectedDate, 250 Title: expectedTitle, 251 Body: expectedBody + "\n---\n" + expectedBodyAppendix, 252 }, 253 }, 254 "mailbox": { 255 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001 256From: Morton Haypenny <mhaypenny@example.com> 257Date: Sat, 11 Apr 2020 15:21:23 -0700 258Subject: [PATCH] A sample commit to test header parsing 259 260The medium format shows the body, which 261may wrap on to multiple lines. 262 263Another body line. 264`, 265 Header: PatchHeader{ 266 SHA: expectedSHA, 267 Author: expectedIdentity, 268 AuthorDate: expectedDate, 269 Title: expectedTitle, 270 Body: expectedBody, 271 }, 272 }, 273 "mailboxPatchOnly": { 274 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001 275From: Morton Haypenny <mhaypenny@example.com> 276Date: Sat, 11 Apr 2020 15:21:23 -0700 277Subject: [PATCH] [BUG-123] A sample commit to test header parsing 278 279The medium format shows the body, which 280may wrap on to multiple lines. 281 282Another body line. 283`, 284 Options: []PatchHeaderOption{ 285 WithSubjectCleanMode(SubjectCleanPatchOnly), 286 }, 287 Header: PatchHeader{ 288 SHA: expectedSHA, 289 Author: expectedIdentity, 290 AuthorDate: expectedDate, 291 Title: "[BUG-123] " + expectedTitle, 292 Body: expectedBody, 293 }, 294 }, 295 "mailboxEmojiOneLine": { 296 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001 297From: Morton Haypenny <mhaypenny@example.com> 298Date: Sat, 11 Apr 2020 15:21:23 -0700 299Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Enabling=20auto-merging?= 300 301The medium format shows the body, which 302may wrap on to multiple lines. 303 304Another body line. 305`, 306 Header: PatchHeader{ 307 SHA: expectedSHA, 308 Author: expectedIdentity, 309 AuthorDate: expectedDate, 310 Title: expectedEmojiOneLineTitle, 311 Body: expectedBody, 312 }, 313 }, 314 "mailboxEmojiMultiLine": { 315 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001 316From: Morton Haypenny <mhaypenny@example.com> 317Date: Sat, 11 Apr 2020 15:21:23 -0700 318Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?= 319 =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?= 320 321The medium format shows the body, which 322may wrap on to multiple lines. 323 324Another body line. 325`, 326 Header: PatchHeader{ 327 SHA: expectedSHA, 328 Author: expectedIdentity, 329 AuthorDate: expectedDate, 330 Title: expectedEmojiMultiLineTitle, 331 Body: expectedBody, 332 }, 333 }, 334 "mailboxAppendix": { 335 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001 336From: Morton Haypenny <mhaypenny@example.com> 337Date: Sat, 11 Apr 2020 15:21:23 -0700 338Subject: [PATCH] A sample commit to test header parsing 339 340The medium format shows the body, which 341may wrap on to multiple lines. 342 343Another body line. 344--- 345CC: Joe Smith <joe.smith@company.com> 346`, 347 Header: PatchHeader{ 348 SHA: expectedSHA, 349 Author: expectedIdentity, 350 AuthorDate: expectedDate, 351 Title: expectedTitle, 352 Body: expectedBody, 353 BodyAppendix: expectedBodyAppendix, 354 }, 355 }, 356 "mailboxMinimalNoName": { 357 Input: `From: <mhaypenny@example.com> 358Subject: [PATCH] A sample commit to test header parsing 359 360The medium format shows the body, which 361may wrap on to multiple lines. 362 363Another body line. 364`, 365 Header: PatchHeader{ 366 Author: &PatchIdentity{expectedIdentity.Email, expectedIdentity.Email}, 367 Title: expectedTitle, 368 Body: expectedBody, 369 }, 370 }, 371 "mailboxMinimal": { 372 Input: `From: Morton Haypenny <mhaypenny@example.com> 373Subject: [PATCH] A sample commit to test header parsing 374 375The medium format shows the body, which 376may wrap on to multiple lines. 377 378Another body line. 379`, 380 Header: PatchHeader{ 381 Author: expectedIdentity, 382 Title: expectedTitle, 383 Body: expectedBody, 384 }, 385 }, 386 "unwrapTitle": { 387 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 388Author: Morton Haypenny <mhaypenny@example.com> 389Date: Sat Apr 11 15:21:23 2020 -0700 390 391 A sample commit to test header parsing with a long 392 title that is wrapped. 393`, 394 Header: PatchHeader{ 395 SHA: expectedSHA, 396 Author: expectedIdentity, 397 AuthorDate: expectedDate, 398 Title: expectedTitle + " with a long title that is wrapped.", 399 }, 400 }, 401 "normalizeBodySpace": { 402 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 403Author: Morton Haypenny <mhaypenny@example.com> 404Date: Sat Apr 11 15:21:23 2020 -0700 405 406 A sample commit to test header parsing 407 408 409 The medium format shows the body, which 410 may wrap on to multiple lines. 411 412 413 Another body line. 414 415 416`, 417 Header: PatchHeader{ 418 SHA: expectedSHA, 419 Author: expectedIdentity, 420 AuthorDate: expectedDate, 421 Title: expectedTitle, 422 Body: expectedBody, 423 }, 424 }, 425 "ignoreLeadingBlankLines": { 426 Input: ` 427 428` + " " + ` 429commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b 430Author: Morton Haypenny <mhaypenny@example.com> 431 432 A sample commit to test header parsing 433`, 434 Header: PatchHeader{ 435 SHA: expectedSHA, 436 Author: expectedIdentity, 437 Title: expectedTitle, 438 }, 439 }, 440 "emptyHeader": { 441 Input: "", 442 Header: PatchHeader{}, 443 }, 444 } 445 446 for name, test := range tests { 447 t.Run(name, func(t *testing.T) { 448 h, err := ParsePatchHeader(test.Input, test.Options...) 449 if test.Err != nil { 450 assertError(t, test.Err, err, "parsing patch header") 451 return 452 } 453 if err != nil { 454 t.Fatalf("unexpected error parsing patch header: %v", err) 455 } 456 if h == nil { 457 t.Fatalf("expected non-nil header, but got nil") 458 } 459 460 exp := test.Header 461 act := *h 462 463 if exp.SHA != act.SHA { 464 t.Errorf("incorrect parsed SHA: expected %q, actual %q", exp.SHA, act.SHA) 465 } 466 467 assertPatchIdentity(t, "author", exp.Author, act.Author) 468 if !exp.AuthorDate.Equal(act.AuthorDate) { 469 t.Errorf("incorrect parsed author date: expected %v, but got %v", exp.AuthorDate, act.AuthorDate) 470 } 471 472 assertPatchIdentity(t, "committer", exp.Committer, act.Committer) 473 if !exp.CommitterDate.Equal(act.CommitterDate) { 474 t.Errorf("incorrect parsed committer date: expected %v, but got %v", exp.CommitterDate, act.CommitterDate) 475 } 476 477 if exp.Title != act.Title { 478 t.Errorf("incorrect parsed title:\n expected: %q\n actual: %q", exp.Title, act.Title) 479 } 480 if exp.Body != act.Body { 481 t.Errorf("incorrect parsed body:\n expected: %q\n actual: %q", exp.Body, act.Body) 482 } 483 if exp.BodyAppendix != act.BodyAppendix { 484 t.Errorf("incorrect parsed body appendix:\n expected: %q\n actual: %q", 485 exp.BodyAppendix, act.BodyAppendix) 486 } 487 }) 488 } 489} 490 491func assertPatchIdentity(t *testing.T, kind string, exp, act *PatchIdentity) { 492 switch { 493 case exp == nil && act == nil: 494 case exp == nil && act != nil: 495 t.Errorf("incorrect parsed %s: expected nil, but got %+v", kind, act) 496 case exp != nil && act == nil: 497 t.Errorf("incorrect parsed %s: expected %+v, but got nil", kind, exp) 498 case exp.Name != act.Name || exp.Email != act.Email: 499 t.Errorf("incorrect parsed %s, expected %+v, bot got %+v", kind, exp, act) 500 } 501} 502 503func TestCleanSubject(t *testing.T) { 504 expectedSubject := "A sample commit to test header parsing" 505 506 tests := map[string]struct { 507 Input string 508 Mode SubjectCleanMode 509 Prefix string 510 Subject string 511 }{ 512 "CleanAll/noPrefix": { 513 Input: expectedSubject, 514 Mode: SubjectCleanAll, 515 Subject: expectedSubject, 516 }, 517 "CleanAll/patchPrefix": { 518 Input: "[PATCH] " + expectedSubject, 519 Mode: SubjectCleanAll, 520 Prefix: "[PATCH] ", 521 Subject: expectedSubject, 522 }, 523 "CleanAll/patchPrefixNoSpace": { 524 Input: "[PATCH]" + expectedSubject, 525 Mode: SubjectCleanAll, 526 Prefix: "[PATCH]", 527 Subject: expectedSubject, 528 }, 529 "CleanAll/patchPrefixContent": { 530 Input: "[PATCH 3/7] " + expectedSubject, 531 Mode: SubjectCleanAll, 532 Prefix: "[PATCH 3/7] ", 533 Subject: expectedSubject, 534 }, 535 "CleanAll/spacePrefix": { 536 Input: " " + expectedSubject, 537 Mode: SubjectCleanAll, 538 Subject: expectedSubject, 539 }, 540 "CleanAll/replyLowerPrefix": { 541 Input: "re: " + expectedSubject, 542 Mode: SubjectCleanAll, 543 Prefix: "re: ", 544 Subject: expectedSubject, 545 }, 546 "CleanAll/replyMixedPrefix": { 547 Input: "Re: " + expectedSubject, 548 Mode: SubjectCleanAll, 549 Prefix: "Re: ", 550 Subject: expectedSubject, 551 }, 552 "CleanAll/replyCapsPrefix": { 553 Input: "RE: " + expectedSubject, 554 Mode: SubjectCleanAll, 555 Prefix: "RE: ", 556 Subject: expectedSubject, 557 }, 558 "CleanAll/replyDoublePrefix": { 559 Input: "Re: re: " + expectedSubject, 560 Mode: SubjectCleanAll, 561 Prefix: "Re: re: ", 562 Subject: expectedSubject, 563 }, 564 "CleanAll/noPrefixSubjectHasRe": { 565 Input: "Reimplement parsing", 566 Mode: SubjectCleanAll, 567 Subject: "Reimplement parsing", 568 }, 569 "CleanAll/patchPrefixSubjectHasRe": { 570 Input: "[PATCH 1/2] Reimplement parsing", 571 Mode: SubjectCleanAll, 572 Prefix: "[PATCH 1/2] ", 573 Subject: "Reimplement parsing", 574 }, 575 "CleanAll/unclosedPrefix": { 576 Input: "[Just to annoy people", 577 Mode: SubjectCleanAll, 578 Subject: "[Just to annoy people", 579 }, 580 "CleanAll/multiplePrefix": { 581 Input: " Re:Re: [PATCH 1/2][DRAFT] " + expectedSubject + " ", 582 Mode: SubjectCleanAll, 583 Prefix: "Re:Re: [PATCH 1/2][DRAFT] ", 584 Subject: expectedSubject, 585 }, 586 "CleanPatchOnly/patchPrefix": { 587 Input: "[PATCH] " + expectedSubject, 588 Mode: SubjectCleanPatchOnly, 589 Prefix: "[PATCH] ", 590 Subject: expectedSubject, 591 }, 592 "CleanPatchOnly/mixedPrefix": { 593 Input: "[PATCH] [TICKET-123] " + expectedSubject, 594 Mode: SubjectCleanPatchOnly, 595 Prefix: "[PATCH] ", 596 Subject: "[TICKET-123] " + expectedSubject, 597 }, 598 "CleanPatchOnly/multiplePrefix": { 599 Input: "Re:Re: [PATCH 1/2][DRAFT] " + expectedSubject, 600 Mode: SubjectCleanPatchOnly, 601 Prefix: "Re:Re: [PATCH 1/2]", 602 Subject: "[DRAFT] " + expectedSubject, 603 }, 604 "CleanWhitespace/leadingSpace": { 605 Input: " [PATCH] " + expectedSubject, 606 Mode: SubjectCleanWhitespace, 607 Subject: "[PATCH] " + expectedSubject, 608 }, 609 "CleanWhitespace/trailingSpace": { 610 Input: "[PATCH] " + expectedSubject + " ", 611 Mode: SubjectCleanWhitespace, 612 Subject: "[PATCH] " + expectedSubject, 613 }, 614 } 615 616 for name, test := range tests { 617 t.Run(name, func(t *testing.T) { 618 prefix, subject := cleanSubject(test.Input, test.Mode) 619 if prefix != test.Prefix { 620 t.Errorf("incorrect prefix: expected %q, actual %q", test.Prefix, prefix) 621 } 622 if subject != test.Subject { 623 t.Errorf("incorrect subject: expected %q, actual %q", test.Subject, subject) 624 } 625 }) 626 } 627}