Files
bluekeyes-go-gitdiff/gitdiff/patch_header_test.go
T
Billy Keyes f0da09b10f Add ParsePatchHeader and related types (#6)
This function parses patch headers (the preamble returned by the
existing Parse function) to extract information about the commit that
generated the patch. This is useful when patches are an interchange
format and this library is applying commits generated elsewhere.

Because of the variety of header formats, parsing is fairly lenient and
best-effort, although certain invalid input does cause errors.

This also extracts some test utilities from the apply tests for reuse.
2020-04-18 22:21:55 -07:00

387 lines
9.7 KiB
Go

package gitdiff
import (
"testing"
"time"
)
func TestParsePatchIdentity(t *testing.T) {
tests := map[string]struct {
Input string
Output PatchIdentity
Err interface{}
}{
"simple": {
Input: "Morton Haypenny <mhaypenny@example.com>",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"extraWhitespace": {
Input: " Morton Haypenny <mhaypenny@example.com > ",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"trailingCharacters": {
Input: "Morton Haypenny <mhaypenny@example.com> unrelated garbage",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"missingName": {
Input: "<mhaypenny@example.com>",
Err: "invalid identity",
},
"missingEmail": {
Input: "Morton Haypenny",
Err: "invalid identity",
},
"unclosedEmail": {
Input: "Morton Haypenny <mhaypenny@example.com",
Err: "unclosed email",
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
id, err := ParsePatchIdentity(test.Input)
if test.Err != nil {
assertError(t, test.Err, err, "parsing identity")
return
}
if err != nil {
t.Fatalf("unexpected error parsing identity: %v", err)
}
if test.Output != id {
t.Errorf("incorrect identity: expected %#v, actual %#v", test.Output, id)
}
})
}
}
func TestParsePatchDate(t *testing.T) {
expected := time.Date(2020, 04, 11, 22, 21, 23, 0, time.UTC)
tests := map[string]struct {
Input string
Output PatchDate
}{
"default": {
Input: "Sat Apr 11 15:21:23 2020 -0700",
Output: PatchDate{
Parsed: expected,
Raw: "Sat Apr 11 15:21:23 2020 -0700",
},
},
"defaultLocal": {
Input: "Sat Apr 11 15:21:23 2020",
Output: PatchDate{
Parsed: time.Date(2020, 04, 11, 15, 21, 23, 0, time.Local),
Raw: "Sat Apr 11 15:21:23 2020",
},
},
"iso": {
Input: "2020-04-11 15:21:23 -0700",
Output: PatchDate{
Parsed: expected,
Raw: "2020-04-11 15:21:23 -0700",
},
},
"isoStrict": {
Input: "2020-04-11T15:21:23-07:00",
Output: PatchDate{
Parsed: expected,
Raw: "2020-04-11T15:21:23-07:00",
},
},
"rfc": {
Input: "Sat, 11 Apr 2020 15:21:23 -0700",
Output: PatchDate{
Parsed: expected,
Raw: "Sat, 11 Apr 2020 15:21:23 -0700",
},
},
"short": {
Input: "2020-04-11",
Output: PatchDate{
Parsed: time.Date(2020, 04, 11, 0, 0, 0, 0, time.Local),
Raw: "2020-04-11",
},
},
"raw": {
Input: "1586643683 -0700",
Output: PatchDate{
Parsed: expected,
Raw: "1586643683 -0700",
},
},
"unix": {
Input: "1586643683",
Output: PatchDate{
Parsed: expected,
Raw: "1586643683",
},
},
"unknownFormat": {
Input: "4/11/2020 15:21:23 PDT",
Output: PatchDate{
Raw: "4/11/2020 15:21:23 PDT",
},
},
"empty": {
Input: "",
Output: PatchDate{},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
d := ParsePatchDate(test.Input)
if test.Output.Raw != d.Raw {
t.Errorf("incorrect raw date: expected %q, actual %q", test.Output.Raw, d.Raw)
}
if !test.Output.Parsed.Equal(d.Parsed) {
t.Errorf("incorrect parsed date: expected %v, actual %v", test.Output.Parsed, d.Parsed)
}
})
}
}
func TestParsePatchHeader(t *testing.T) {
expectedSHA := "61f5cd90bed4d204ee3feb3aa41ee91d4734855b"
expectedIdentity := &PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
}
expectedDate := &PatchDate{
Parsed: time.Date(2020, 04, 11, 15, 21, 23, 0, time.FixedZone("PDT", -7*60*60)),
Raw: "Sat Apr 11 15:21:23 2020 -0700",
}
expectedTitle := "A sample commit to test header parsing"
expectedMsg := "The medium format shows the body, which\nmay wrap on to multiple lines.\n\nAnother body line."
tests := map[string]struct {
Input string
Header PatchHeader
Err interface{}
}{
"prettyShort": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Title: expectedTitle,
},
},
"prettyMedium": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Message: expectedMsg,
},
},
"prettyFull": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Commit: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Committer: expectedIdentity,
Title: expectedTitle,
Message: expectedMsg,
},
},
"prettyFuller": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
AuthorDate: Sat Apr 11 15:21:23 2020 -0700
Commit: Morton Haypenny <mhaypenny@example.com>
CommitDate: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Committer: expectedIdentity,
CommitterDate: expectedDate,
Title: expectedTitle,
Message: expectedMsg,
},
},
"mailbox": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: &PatchDate{
Parsed: expectedDate.Parsed,
Raw: "Sat, 11 Apr 2020 15:21:23 -0700",
},
Title: "[PATCH] " + expectedTitle,
Message: expectedMsg,
},
},
"unwrapTitle": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing with a long
title that is wrapped.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle + " with a long title that is wrapped.",
},
},
"normalizeBodySpace": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Message: expectedMsg,
},
},
"ignoreLeadingBlankLines": {
Input: `
` + " " + `
commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Title: expectedTitle,
},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
h, err := ParsePatchHeader(test.Input)
if test.Err != nil {
assertError(t, test.Err, err, "parsing patch header")
return
}
if err != nil {
t.Fatalf("unexpected error parsing patch header: %v", err)
}
if h == nil {
t.Fatalf("expected non-nil header, but got nil")
}
exp := test.Header
act := *h
if exp.SHA != act.SHA {
t.Errorf("incorrect parsed SHA: expected %q, actual %q", exp.SHA, act.SHA)
}
assertPatchIdentity(t, "author", exp.Author, act.Author)
assertPatchDate(t, "author", exp.AuthorDate, act.AuthorDate)
assertPatchIdentity(t, "committer", exp.Committer, act.Committer)
assertPatchDate(t, "committer", exp.CommitterDate, act.CommitterDate)
if exp.Title != act.Title {
t.Errorf("incorrect parsed title:\n expected: %q\n actual: %q", exp.Title, act.Title)
}
if exp.Message != act.Message {
t.Errorf("incorrect parsed message:\n expected: %q\n actual: %q", exp.Message, act.Message)
}
})
}
}
func assertPatchIdentity(t *testing.T, kind string, exp, act *PatchIdentity) {
switch {
case exp == nil && act == nil:
case exp == nil && act != nil:
t.Errorf("incorrect parsed %s: expected nil, but got %+v", kind, act)
case exp != nil && act == nil:
t.Errorf("incorrect parsed %s: expected %+v, but got nil", kind, exp)
case exp.Name != act.Name || exp.Email != act.Email:
t.Errorf("incorrect parsed %s, expected %+v, bot got %+v", kind, exp, act)
}
}
func assertPatchDate(t *testing.T, kind string, exp, act *PatchDate) {
switch {
case exp == nil && act == nil:
case exp == nil && act != nil:
t.Errorf("incorrect parsed %s date: expected nil, but got %+v", kind, act)
case exp != nil && act == nil:
t.Errorf("incorrect parsed %s date: expected %+v, but got nil", kind, exp)
case exp.Raw != act.Raw || !exp.Parsed.Equal(act.Parsed):
t.Errorf("incorrect parsed %s date, expected %+v, bot got %+v", kind, exp, act)
}
}