commit: PR desc parser and writer
This commit is contained in:
parent
77751ecc46
commit
db766bacc3
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -54,7 +55,7 @@ func parsePrLine(line string) (BasicPR, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractAssociatedDescriptionAndPRs(data *bufio.Scanner) (string, []BasicPR) {
|
func ExtractPRsFromDescription(data *bufio.Scanner) (string, []BasicPR) {
|
||||||
prs := make([]BasicPR, 0, 1)
|
prs := make([]BasicPR, 0, 1)
|
||||||
var desc strings.Builder
|
var desc strings.Builder
|
||||||
|
|
||||||
@ -74,15 +75,40 @@ func ExtractAssociatedDescriptionAndPRs(data *bufio.Scanner) (string, []BasicPR)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prToLine(writer io.Writer, pr BasicPR) {
|
func prToLine(writer io.Writer, pr BasicPR) {
|
||||||
fmt.Fprintf(writer, PrPattern, pr.org, pr.repo, pr.num)
|
|
||||||
writer.Write([]byte("\n"))
|
writer.Write([]byte("\n"))
|
||||||
|
fmt.Fprintf(writer, PrPattern, pr.org, pr.repo, pr.num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns:
|
||||||
|
// <0 for a<b
|
||||||
|
// >0 for a>b
|
||||||
|
// =0 when equal
|
||||||
|
func compareBasicPRs(a BasicPR, b BasicPR) int {
|
||||||
|
if c := strings.Compare(a.org, b.org); c != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if c := strings.Compare(a.repo, b.repo); c != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.num > b.num {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if a.num < b.num {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppendPRsToDescription(desc string, prs []BasicPR) string {
|
func AppendPRsToDescription(desc string, prs []BasicPR) string {
|
||||||
var out strings.Builder
|
var out strings.Builder
|
||||||
|
|
||||||
out.WriteString(strings.TrimSpace(desc))
|
out.WriteString(strings.TrimSpace(desc))
|
||||||
out.WriteString("\n\n")
|
out.WriteString("\n")
|
||||||
|
|
||||||
|
slices.SortFunc(prs, compareBasicPRs)
|
||||||
|
prs = slices.Compact(prs)
|
||||||
|
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
prToLine(&out, pr)
|
prToLine(&out, pr)
|
||||||
|
@ -12,181 +12,136 @@ func newStringScanner(s string) *bufio.Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAssociatedPRScanner(t *testing.T) {
|
func TestAssociatedPRScanner(t *testing.T) {
|
||||||
t.Run("No PRs", func(t *testing.T) {
|
testTable := []struct {
|
||||||
if _, out := ExtractAssociatedDescriptionAndPRs(newStringScanner("")); len(out) != 0 {
|
name string
|
||||||
t.Error("Unexpected output", out)
|
input string
|
||||||
}
|
prs []BasicPR
|
||||||
})
|
desc string
|
||||||
|
}{
|
||||||
t.Run("Single PR", func(t *testing.T) {
|
{
|
||||||
const singlePRText = `Some header of the issue
|
"No PRs",
|
||||||
|
"",
|
||||||
Followed by some description
|
[]BasicPR{},
|
||||||
PR: test/foo#4
|
"",
|
||||||
`
|
},
|
||||||
_, out := ExtractAssociatedDescriptionAndPRs(newStringScanner(singlePRText))
|
{
|
||||||
if len(out) != 1 {
|
"Single PRs",
|
||||||
t.Error("Unexpected output", out)
|
"Some header of the issue\n\nFollowed by some description\n\nPR: test/foo#4\n",
|
||||||
return
|
[]BasicPR{{org: "test", repo: "foo", num: 4}},
|
||||||
}
|
"Some header of the issue\n\nFollowed by some description",
|
||||||
|
},
|
||||||
expected := BasicPR{
|
{
|
||||||
|
"Multiple PRs",
|
||||||
|
"Some header of the issue\n\nFollowed by some description\nPR: test/foo#4\n\nPR: test/goo#5\n",
|
||||||
|
[]BasicPR{
|
||||||
|
{org: "test", repo: "foo", num: 4},
|
||||||
|
{org: "test", repo: "goo", num: 5},
|
||||||
|
},
|
||||||
|
"Some header of the issue\n\nFollowed by some description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Multiple PRs with whitespace",
|
||||||
|
"Some header of the issue\n\n\tPR: test/goo#5\n\n Followed by some description\n \t PR: test/foo#4\n",
|
||||||
|
[]BasicPR{
|
||||||
|
{org: "test", repo: "foo", num: 4},
|
||||||
|
{org: "test", repo: "goo", num: 5},
|
||||||
|
},
|
||||||
|
"Some header of the issue\n\n\n Followed by some description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Multiple PRs with missing names and other special cases to ignore",
|
||||||
|
"Some header of the issue\n\n\n\t PR: foobar#5 \n\t PR: rd/goo5 \n\t PR: test/#5 \n" +
|
||||||
|
"\t PR: /goo#5 \n\t PR: test/goo# \n\t PR: test / goo # 10 \n\tPR: test/gool# 10 \n" +
|
||||||
|
"\t PR: test/goo#5 \n\t\n Followed by some description\n\t PR: test/foo#4 \n\t\n\n",
|
||||||
|
[]BasicPR{
|
||||||
|
{
|
||||||
org: "test",
|
org: "test",
|
||||||
repo: "foo",
|
repo: "foo",
|
||||||
num: 4,
|
num: 4,
|
||||||
}
|
},
|
||||||
if out[0] != expected {
|
{
|
||||||
t.Error("Unexpected", out, "Expected", expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Multiple PRs", func(t *testing.T) {
|
|
||||||
const multiplePRText = `Some header of the issue
|
|
||||||
|
|
||||||
Followed by some description
|
|
||||||
PR: test/foo#4
|
|
||||||
|
|
||||||
PR: test/goo#5
|
|
||||||
`
|
|
||||||
|
|
||||||
_, out := ExtractAssociatedDescriptionAndPRs(newStringScanner(multiplePRText))
|
|
||||||
if len(out) != 2 {
|
|
||||||
t.Error("Unexpected output", out)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
expected1 := BasicPR{
|
|
||||||
org: "test",
|
|
||||||
repo: "foo",
|
|
||||||
num: 4,
|
|
||||||
}
|
|
||||||
expected2 := BasicPR{
|
|
||||||
org: "test",
|
org: "test",
|
||||||
repo: "goo",
|
repo: "goo",
|
||||||
num: 5,
|
num: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Some header of the issue\n\n\n\t PR: foobar#5 \n\t PR: rd/goo5 \n\t PR: test/#5 \n" +
|
||||||
|
"\t PR: /goo#5 \n\t PR: test/goo# \n\t PR: test / goo # 10 \n\tPR: test/gool# 10 \n" +
|
||||||
|
"\t\n Followed by some description",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Contains(out, expected1) {
|
for _, test := range testTable {
|
||||||
t.Error("Unexpected", out, "Expected", expected1)
|
t.Run(test.name, func(t *testing.T) {
|
||||||
}
|
desc, prs := ExtractPRsFromDescription(newStringScanner(test.input))
|
||||||
if !slices.Contains(out, expected2) {
|
if len(prs) != len(test.prs) {
|
||||||
t.Error("Unexpected", out, "Expected", expected2)
|
t.Error("Unexpected length:", len(prs), "expected:", len(test.prs))
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Multiple PRs with whitespace", func(t *testing.T) {
|
|
||||||
const whitespacePRText = `Some header of the issue
|
|
||||||
|
|
||||||
PR: test/goo#5
|
|
||||||
|
|
||||||
Followed by some description
|
|
||||||
PR: test/foo#4
|
|
||||||
`
|
|
||||||
|
|
||||||
desc, out := ExtractAssociatedDescriptionAndPRs(newStringScanner(whitespacePRText))
|
|
||||||
if len(out) != 2 {
|
|
||||||
t.Error("Unexpected output", out)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedDesc = `Some header of the issue
|
for _, p := range test.prs {
|
||||||
|
if !slices.Contains(prs, p) {
|
||||||
|
t.Error("missing expected PR", p)
|
||||||
Followed by some description`
|
|
||||||
expected1 := BasicPR{
|
|
||||||
org: "test",
|
|
||||||
repo: "foo",
|
|
||||||
num: 4,
|
|
||||||
}
|
}
|
||||||
expected2 := BasicPR{
|
|
||||||
org: "test",
|
|
||||||
repo: "goo",
|
|
||||||
num: 5,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Contains(out, expected1) {
|
if desc != test.desc {
|
||||||
t.Error("Unexpected", out, "Expected", expected1)
|
t.Error("Desc output", len(desc), "!=", len(test.desc), ":", desc)
|
||||||
}
|
|
||||||
if !slices.Contains(out, expected2) {
|
|
||||||
t.Error("Unexpected", out, "Expected", expected2)
|
|
||||||
}
|
|
||||||
if desc != expectedDesc {
|
|
||||||
t.Error("unexpected desc", desc)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("Multiple PRs with missing names and other special cases to ignore", func(t *testing.T) {
|
func TestAppendingPRsToDescription(t *testing.T) {
|
||||||
const whitespacePRText = `Some header of the issue
|
testTable := []struct {
|
||||||
|
name string
|
||||||
PR: foobar#5
|
desc string
|
||||||
PR: rd/goo5
|
PRs []BasicPR
|
||||||
PR: test/#5
|
output string
|
||||||
PR: /goo#5
|
}{
|
||||||
PR: test/goo#
|
{
|
||||||
PR: test / goo # 10
|
"Append single PR to end of description",
|
||||||
PR: test/gool# 10
|
"something",
|
||||||
PR: test/goo#5
|
[]BasicPR{
|
||||||
|
{org: "a", repo: "b", num: 100},
|
||||||
Followed by some description
|
},
|
||||||
PR: test/foo#4
|
"something\n\nPR: a/b#100",
|
||||||
|
},
|
||||||
|
{
|
||||||
`
|
"Append multiple PR to end of description",
|
||||||
|
"something",
|
||||||
desc, out := ExtractAssociatedDescriptionAndPRs(newStringScanner(whitespacePRText))
|
[]BasicPR{
|
||||||
if len(out) != 2 {
|
{org: "a1", repo: "b", num: 100},
|
||||||
t.Error("Unexpected output", out)
|
{org: "a1", repo: "c", num: 100},
|
||||||
return
|
{org: "a1", repo: "c", num: 101},
|
||||||
|
{org: "b", repo: "b", num: 100},
|
||||||
|
{org: "c", repo: "b", num: 100},
|
||||||
|
},
|
||||||
|
"something\n\nPR: a1/b#100\nPR: a1/c#100\nPR: a1/c#101\nPR: b/b#100\nPR: c/b#100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Append multiple sorted PR to end of description and remove dups",
|
||||||
|
"something",
|
||||||
|
[]BasicPR{
|
||||||
|
{org: "a1", repo: "c", num: 101},
|
||||||
|
{org: "a1", repo: "c", num: 100},
|
||||||
|
{org: "c", repo: "b", num: 100},
|
||||||
|
{org: "b", repo: "b", num: 100},
|
||||||
|
{org: "a1", repo: "c", num: 101},
|
||||||
|
{org: "a1", repo: "c", num: 101},
|
||||||
|
{org: "a1", repo: "b", num: 100},
|
||||||
|
},
|
||||||
|
"something\n\nPR: a1/b#100\nPR: a1/c#100\nPR: a1/c#101\nPR: b/b#100\nPR: c/b#100",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedDesc = `Some header of the issue
|
for _, test := range testTable {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
PR: foobar#5
|
d := AppendPRsToDescription(test.desc, test.PRs)
|
||||||
PR: rd/goo5
|
if d != test.output {
|
||||||
PR: test/#5
|
t.Error(len(d), "vs", len(test.output))
|
||||||
PR: /goo#5
|
|
||||||
PR: test/goo#
|
|
||||||
PR: test / goo # 10
|
|
||||||
PR: test/gool# 10
|
|
||||||
|
|
||||||
Followed by some description`
|
|
||||||
|
|
||||||
if desc != expectedDesc {
|
|
||||||
t.Error(len(desc), "vs", len(expectedDesc))
|
|
||||||
t.Error("description doesn't match expected. ", desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected1 := BasicPR{
|
|
||||||
org: "test",
|
|
||||||
repo: "foo",
|
|
||||||
num: 4,
|
|
||||||
}
|
|
||||||
expected2 := BasicPR{
|
|
||||||
org: "test",
|
|
||||||
repo: "goo",
|
|
||||||
num: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !slices.Contains(out, expected1) {
|
|
||||||
t.Error("Unexpected", out, "Expected", expected1)
|
|
||||||
}
|
|
||||||
if !slices.Contains(out, expected2) {
|
|
||||||
t.Error("Unexpected", out, "Expected", expected2)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Append PRs to end of description", func(t *testing.T) {
|
|
||||||
d := AppendPRsToDescription("something", []BasicPR{
|
|
||||||
BasicPR{org: "a", repo: "b", num: 100},
|
|
||||||
})
|
|
||||||
|
|
||||||
const expectedDesc = `something
|
|
||||||
|
|
||||||
PR: a/b#100
|
|
||||||
`
|
|
||||||
if d != expectedDesc {
|
|
||||||
t.Error(len(d), "vs", len(expectedDesc))
|
|
||||||
t.Error("unpected output", d)
|
t.Error("unpected output", d)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user