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