fork of go-gitdiff with jj support
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 Header PatchHeader
149 Err interface{}
150 }{
151 "prettyShort": {
152 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
153Author: Morton Haypenny <mhaypenny@example.com>
154
155 A sample commit to test header parsing
156`,
157 Header: PatchHeader{
158 SHA: expectedSHA,
159 Author: expectedIdentity,
160 Title: expectedTitle,
161 },
162 },
163 "prettyMedium": {
164 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
165Author: Morton Haypenny <mhaypenny@example.com>
166Date: Sat Apr 11 15:21:23 2020 -0700
167
168 A sample commit to test header parsing
169
170 The medium format shows the body, which
171 may wrap on to multiple lines.
172
173 Another body line.
174`,
175 Header: PatchHeader{
176 SHA: expectedSHA,
177 Author: expectedIdentity,
178 AuthorDate: expectedDate,
179 Title: expectedTitle,
180 Body: expectedBody,
181 },
182 },
183 "prettyFull": {
184 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
185Author: Morton Haypenny <mhaypenny@example.com>
186Commit: Morton Haypenny <mhaypenny@example.com>
187
188 A sample commit to test header parsing
189
190 The medium format shows the body, which
191 may wrap on to multiple lines.
192
193 Another body line.
194`,
195 Header: PatchHeader{
196 SHA: expectedSHA,
197 Author: expectedIdentity,
198 Committer: expectedIdentity,
199 Title: expectedTitle,
200 Body: expectedBody,
201 },
202 },
203 "prettyFuller": {
204 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
205Author: Morton Haypenny <mhaypenny@example.com>
206AuthorDate: Sat Apr 11 15:21:23 2020 -0700
207Commit: Morton Haypenny <mhaypenny@example.com>
208CommitDate: Sat Apr 11 15:21:23 2020 -0700
209
210 A sample commit to test header parsing
211
212 The medium format shows the body, which
213 may wrap on to multiple lines.
214
215 Another body line.
216`,
217 Header: PatchHeader{
218 SHA: expectedSHA,
219 Author: expectedIdentity,
220 AuthorDate: expectedDate,
221 Committer: expectedIdentity,
222 CommitterDate: expectedDate,
223 Title: expectedTitle,
224 Body: expectedBody,
225 },
226 },
227 "prettyAppendix": {
228 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
229Author: Morton Haypenny <mhaypenny@example.com>
230AuthorDate: Sat Apr 11 15:21:23 2020 -0700
231Commit: Morton Haypenny <mhaypenny@example.com>
232CommitDate: Sat Apr 11 15:21:23 2020 -0700
233
234 A sample commit to test header parsing
235
236 The medium format shows the body, which
237 may wrap on to multiple lines.
238
239 Another body line.
240 ---
241 CC: Joe Smith <joe.smith@company.com>
242`,
243 Header: PatchHeader{
244 SHA: expectedSHA,
245 Author: expectedIdentity,
246 AuthorDate: expectedDate,
247 Committer: expectedIdentity,
248 CommitterDate: expectedDate,
249 Title: expectedTitle,
250 Body: expectedBody + "\n---\n" + expectedBodyAppendix,
251 },
252 },
253 "mailbox": {
254 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
255From: Morton Haypenny <mhaypenny@example.com>
256Date: Sat, 11 Apr 2020 15:21:23 -0700
257Subject: [PATCH] A sample commit to test header parsing
258
259The medium format shows the body, which
260may wrap on to multiple lines.
261
262Another body line.
263`,
264 Header: PatchHeader{
265 SHA: expectedSHA,
266 Author: expectedIdentity,
267 AuthorDate: expectedDate,
268 Title: expectedTitle,
269 Body: expectedBody,
270 },
271 },
272 "mailboxEmojiOneLine": {
273 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
274From: Morton Haypenny <mhaypenny@example.com>
275Date: Sat, 11 Apr 2020 15:21:23 -0700
276Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Enabling=20auto-merging?=
277
278The medium format shows the body, which
279may wrap on to multiple lines.
280
281Another body line.
282`,
283 Header: PatchHeader{
284 SHA: expectedSHA,
285 Author: expectedIdentity,
286 AuthorDate: expectedDate,
287 Title: expectedEmojiOneLineTitle,
288 Body: expectedBody,
289 },
290 },
291 "mailboxEmojiMultiLine": {
292 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
293From: Morton Haypenny <mhaypenny@example.com>
294Date: Sat, 11 Apr 2020 15:21:23 -0700
295Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?=
296 =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?=
297
298The medium format shows the body, which
299may wrap on to multiple lines.
300
301Another body line.
302`,
303 Header: PatchHeader{
304 SHA: expectedSHA,
305 Author: expectedIdentity,
306 AuthorDate: expectedDate,
307 Title: expectedEmojiMultiLineTitle,
308 Body: expectedBody,
309 },
310 },
311 "mailboxAppendix": {
312 Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
313From: Morton Haypenny <mhaypenny@example.com>
314Date: Sat, 11 Apr 2020 15:21:23 -0700
315Subject: [PATCH] A sample commit to test header parsing
316
317The medium format shows the body, which
318may wrap on to multiple lines.
319
320Another body line.
321---
322CC: Joe Smith <joe.smith@company.com>
323`,
324 Header: PatchHeader{
325 SHA: expectedSHA,
326 Author: expectedIdentity,
327 AuthorDate: expectedDate,
328 Title: expectedTitle,
329 Body: expectedBody,
330 BodyAppendix: expectedBodyAppendix,
331 },
332 },
333 "mailboxMinimalNoName": {
334 Input: `From: <mhaypenny@example.com>
335Subject: [PATCH] A sample commit to test header parsing
336
337The medium format shows the body, which
338may wrap on to multiple lines.
339
340Another body line.
341`,
342 Header: PatchHeader{
343 Author: &PatchIdentity{expectedIdentity.Email, expectedIdentity.Email},
344 Title: expectedTitle,
345 Body: expectedBody,
346 },
347 },
348 "mailboxMinimal": {
349 Input: `From: Morton Haypenny <mhaypenny@example.com>
350Subject: [PATCH] A sample commit to test header parsing
351
352The medium format shows the body, which
353may wrap on to multiple lines.
354
355Another body line.
356`,
357 Header: PatchHeader{
358 Author: expectedIdentity,
359 Title: expectedTitle,
360 Body: expectedBody,
361 },
362 },
363 "unwrapTitle": {
364 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
365Author: Morton Haypenny <mhaypenny@example.com>
366Date: Sat Apr 11 15:21:23 2020 -0700
367
368 A sample commit to test header parsing with a long
369 title that is wrapped.
370`,
371 Header: PatchHeader{
372 SHA: expectedSHA,
373 Author: expectedIdentity,
374 AuthorDate: expectedDate,
375 Title: expectedTitle + " with a long title that is wrapped.",
376 },
377 },
378 "normalizeBodySpace": {
379 Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
380Author: Morton Haypenny <mhaypenny@example.com>
381Date: Sat Apr 11 15:21:23 2020 -0700
382
383 A sample commit to test header parsing
384
385
386 The medium format shows the body, which
387 may wrap on to multiple lines.
388
389
390 Another body line.
391
392
393`,
394 Header: PatchHeader{
395 SHA: expectedSHA,
396 Author: expectedIdentity,
397 AuthorDate: expectedDate,
398 Title: expectedTitle,
399 Body: expectedBody,
400 },
401 },
402 "ignoreLeadingBlankLines": {
403 Input: `
404
405` + " " + `
406commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
407Author: Morton Haypenny <mhaypenny@example.com>
408
409 A sample commit to test header parsing
410`,
411 Header: PatchHeader{
412 SHA: expectedSHA,
413 Author: expectedIdentity,
414 Title: expectedTitle,
415 },
416 },
417 }
418
419 for name, test := range tests {
420 t.Run(name, func(t *testing.T) {
421 h, err := ParsePatchHeader(test.Input)
422 if test.Err != nil {
423 assertError(t, test.Err, err, "parsing patch header")
424 return
425 }
426 if err != nil {
427 t.Fatalf("unexpected error parsing patch header: %v", err)
428 }
429 if h == nil {
430 t.Fatalf("expected non-nil header, but got nil")
431 }
432
433 exp := test.Header
434 act := *h
435
436 if exp.SHA != act.SHA {
437 t.Errorf("incorrect parsed SHA: expected %q, actual %q", exp.SHA, act.SHA)
438 }
439
440 assertPatchIdentity(t, "author", exp.Author, act.Author)
441 if !exp.AuthorDate.Equal(act.AuthorDate) {
442 t.Errorf("incorrect parsed author date: expected %v, but got %v", exp.AuthorDate, act.AuthorDate)
443 }
444
445 assertPatchIdentity(t, "committer", exp.Committer, act.Committer)
446 if !exp.CommitterDate.Equal(act.CommitterDate) {
447 t.Errorf("incorrect parsed committer date: expected %v, but got %v", exp.CommitterDate, act.CommitterDate)
448 }
449
450 if exp.Title != act.Title {
451 t.Errorf("incorrect parsed title:\n expected: %q\n actual: %q", exp.Title, act.Title)
452 }
453 if exp.Body != act.Body {
454 t.Errorf("incorrect parsed body:\n expected: %q\n actual: %q", exp.Body, act.Body)
455 }
456 if exp.BodyAppendix != act.BodyAppendix {
457 t.Errorf("incorrect parsed body appendix:\n expected: %q\n actual: %q",
458 exp.BodyAppendix, act.BodyAppendix)
459 }
460 })
461 }
462}
463
464func assertPatchIdentity(t *testing.T, kind string, exp, act *PatchIdentity) {
465 switch {
466 case exp == nil && act == nil:
467 case exp == nil && act != nil:
468 t.Errorf("incorrect parsed %s: expected nil, but got %+v", kind, act)
469 case exp != nil && act == nil:
470 t.Errorf("incorrect parsed %s: expected %+v, but got nil", kind, exp)
471 case exp.Name != act.Name || exp.Email != act.Email:
472 t.Errorf("incorrect parsed %s, expected %+v, bot got %+v", kind, exp, act)
473 }
474}
475
476func TestCleanupSubject(t *testing.T) {
477 exp := "A sample commit to test header parsing"
478 tests := map[string]string{
479 "plain": "",
480 "patch": "[PATCH] ",
481 "patchv5": "[PATCH v5] ",
482 "patchrfc": "[PATCH RFC] ",
483 "patchnospace": "[PATCH]",
484 "space": " ",
485 "re": "re: ",
486 "Re": "Re: ",
487 "RE": "rE: ",
488 "rere": "re: re: ",
489 }
490
491 for name, prefix := range tests {
492 gotprefix, gottitle := parseSubject(prefix + exp)
493 if gottitle != exp {
494 t.Errorf("%s: Incorrect parsing of prefix %s: got title %s, wanted %s",
495 name, prefix, gottitle, exp)
496 }
497 if gotprefix != prefix {
498 t.Errorf("%s: Incorrect parsing of prefix %s: got prefix %s",
499 name, prefix, gotprefix)
500 }
501 }
502
503 moretests := map[string]struct {
504 in, eprefix, etitle string
505 }{
506 "Reimplement": {"Reimplement something", "", "Reimplement something"},
507 "patch-reimplement": {"[PATCH v5] Reimplement something", "[PATCH v5] ", "Reimplement something"},
508 "Openbracket": {"[Just to annoy people", "", "[Just to annoy people"},
509 }
510
511 for name, test := range moretests {
512 prefix, title := parseSubject(test.in)
513 if title != test.etitle {
514 t.Errorf("%s: Incorrect parsing of %s: got title %s, wanted %s",
515 name, test.in, title, test.etitle)
516 }
517 if prefix != test.eprefix {
518 t.Errorf("%s: Incorrect parsing of %s: got prefix %s, wanted %s",
519 name, test.in, title, test.etitle)
520 }
521 }
522}