mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-11 10:19:02 +00:00
strings.Split produces an empty trailing element when the git diff output ends with \n. The empty element was written into the last file's buffer, and TrimSuffix only removed one \n — leaving the last Diff field with an extra trailing newline that earlier files didn't have. Switch to TrimRight so all Diff values are consistently clean.
262 lines
6.1 KiB
Go
262 lines
6.1 KiB
Go
package diff
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestParseDiffText_Rename guards against issue #99: a renamed file must be
|
|
// recognized via the "rename from"/"rename to" extended header lines so that
|
|
// the parser reads content at the NEW path instead of warning about the old
|
|
// path ("cannot read file ... exit status 128").
|
|
func TestParseDiffText_Rename(t *testing.T) {
|
|
diffText := `diff --git a/pkg/old name.go b/pkg/new name.go
|
|
similarity index 95%
|
|
rename from pkg/old name.go
|
|
rename to pkg/new name.go
|
|
index 1234567..89abcde 100644
|
|
--- a/pkg/old name.go
|
|
+++ b/pkg/new name.go
|
|
@@ -1,3 +1,3 @@
|
|
line1
|
|
-line2
|
|
+line2 changed
|
|
line3
|
|
`
|
|
diffs, err := ParseDiffText(context.Background(), diffText, t.TempDir(), "", nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseDiffText: %v", err)
|
|
}
|
|
if len(diffs) != 1 {
|
|
t.Fatalf("expected 1 diff, got %d", len(diffs))
|
|
}
|
|
d := diffs[0]
|
|
if !d.IsRenamed {
|
|
t.Errorf("IsRenamed = false, want true")
|
|
}
|
|
if d.OldPath != "pkg/old name.go" {
|
|
t.Errorf("OldPath = %q, want %q", d.OldPath, "pkg/old name.go")
|
|
}
|
|
if d.NewPath != "pkg/new name.go" {
|
|
t.Errorf("NewPath = %q, want %q", d.NewPath, "pkg/new name.go")
|
|
}
|
|
if d.IsNew || d.IsDeleted {
|
|
t.Errorf("IsNew/IsDeleted = %v/%v, want false/false", d.IsNew, d.IsDeleted)
|
|
}
|
|
}
|
|
|
|
// TestParseDiffText_PureRename covers a 100% similarity rename, which carries
|
|
// no hunks and no ---/+++ lines at all.
|
|
func TestParseDiffText_PureRename(t *testing.T) {
|
|
diffText := `diff --git a/old.go b/new.go
|
|
similarity index 100%
|
|
rename from old.go
|
|
rename to new.go
|
|
`
|
|
diffs, err := ParseDiffText(context.Background(), diffText, t.TempDir(), "", nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseDiffText: %v", err)
|
|
}
|
|
if len(diffs) != 1 {
|
|
t.Fatalf("expected 1 diff, got %d", len(diffs))
|
|
}
|
|
d := diffs[0]
|
|
if !d.IsRenamed || d.OldPath != "old.go" || d.NewPath != "new.go" {
|
|
t.Errorf("got IsRenamed=%v OldPath=%q NewPath=%q, want true/old.go/new.go",
|
|
d.IsRenamed, d.OldPath, d.NewPath)
|
|
}
|
|
}
|
|
|
|
// TestParseDiffText_DeletedFile guards the /dev/null detection: git emits
|
|
// "+++ /dev/null" WITHOUT the b/ prefix, which the old regexes required, so
|
|
// deletions were misclassified and triggered a doomed `git show ref:path`.
|
|
func TestParseDiffText_DeletedFile(t *testing.T) {
|
|
diffText := `diff --git a/gone.go b/gone.go
|
|
deleted file mode 100644
|
|
index 1234567..0000000
|
|
--- a/gone.go
|
|
+++ /dev/null
|
|
@@ -1,2 +0,0 @@
|
|
-line1
|
|
-line2
|
|
`
|
|
diffs, err := ParseDiffText(context.Background(), diffText, t.TempDir(), "", nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseDiffText: %v", err)
|
|
}
|
|
if len(diffs) != 1 {
|
|
t.Fatalf("expected 1 diff, got %d", len(diffs))
|
|
}
|
|
d := diffs[0]
|
|
if !d.IsDeleted {
|
|
t.Errorf("IsDeleted = false, want true")
|
|
}
|
|
if d.NewPath != "/dev/null" {
|
|
t.Errorf("NewPath = %q, want /dev/null", d.NewPath)
|
|
}
|
|
if d.OldPath != "gone.go" {
|
|
t.Errorf("OldPath = %q, want gone.go", d.OldPath)
|
|
}
|
|
}
|
|
|
|
// TestParseDiffText_NewFile covers "--- /dev/null" (no a/ prefix).
|
|
func TestParseDiffText_NewFile(t *testing.T) {
|
|
diffText := `diff --git a/fresh.go b/fresh.go
|
|
new file mode 100644
|
|
index 0000000..1234567
|
|
--- /dev/null
|
|
+++ b/fresh.go
|
|
@@ -0,0 +1,2 @@
|
|
+line1
|
|
+line2
|
|
`
|
|
repo := t.TempDir()
|
|
diffs, err := ParseDiffText(context.Background(), diffText, repo, "", nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseDiffText: %v", err)
|
|
}
|
|
if len(diffs) != 1 {
|
|
t.Fatalf("expected 1 diff, got %d", len(diffs))
|
|
}
|
|
d := diffs[0]
|
|
if !d.IsNew {
|
|
t.Errorf("IsNew = false, want true")
|
|
}
|
|
if d.IsDeleted {
|
|
t.Errorf("IsDeleted = true, want false")
|
|
}
|
|
if d.Insertions != 2 {
|
|
t.Errorf("Insertions = %d, want 2", d.Insertions)
|
|
}
|
|
}
|
|
|
|
func TestParseDiffText_NoTrailingNewlines(t *testing.T) {
|
|
diffText := `diff --git a/a.go b/a.go
|
|
index 1234567..89abcde 100644
|
|
--- a/a.go
|
|
+++ b/a.go
|
|
@@ -1,3 +1,3 @@
|
|
line1
|
|
-line2
|
|
+line2 changed
|
|
line3
|
|
diff --git a/b.json b/b.json
|
|
new file mode 100644
|
|
index 0000000..1234567
|
|
--- /dev/null
|
|
+++ b/b.json
|
|
@@ -0,0 +1,3 @@
|
|
+{
|
|
+ "key": "value"
|
|
+}
|
|
`
|
|
diffs, err := ParseDiffText(context.Background(), diffText, t.TempDir(), "", nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseDiffText: %v", err)
|
|
}
|
|
if len(diffs) != 2 {
|
|
t.Fatalf("expected 2 diffs, got %d", len(diffs))
|
|
}
|
|
for i, d := range diffs {
|
|
if strings.HasSuffix(d.Diff, "\n") {
|
|
t.Errorf("diffs[%d] (%s) has trailing newline in Diff field", i, d.NewPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStripDiffHeaders(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{
|
|
name: "standard diff with index line",
|
|
in: strings.Join([]string{
|
|
"diff --git a/foo.go b/foo.go",
|
|
"index 1234567..89abcde 100644",
|
|
"--- a/foo.go",
|
|
"+++ b/foo.go",
|
|
"@@ -1,3 +1,3 @@",
|
|
" line1",
|
|
"-line2",
|
|
"+line2 changed",
|
|
" line3",
|
|
}, "\n"),
|
|
want: strings.Join([]string{
|
|
"--- a/foo.go",
|
|
"+++ b/foo.go",
|
|
"@@ -1,3 +1,3 @@",
|
|
" line1",
|
|
"-line2",
|
|
"+line2 changed",
|
|
" line3",
|
|
}, "\n"),
|
|
},
|
|
{
|
|
name: "rename diff preserves extended headers",
|
|
in: strings.Join([]string{
|
|
"diff --git a/old.go b/new.go",
|
|
"similarity index 95%",
|
|
"rename from old.go",
|
|
"rename to new.go",
|
|
"index 1234567..89abcde 100644",
|
|
"--- a/old.go",
|
|
"+++ b/new.go",
|
|
"@@ -1,2 +1,2 @@",
|
|
" line1",
|
|
"-old",
|
|
"+new",
|
|
}, "\n"),
|
|
want: strings.Join([]string{
|
|
"similarity index 95%",
|
|
"rename from old.go",
|
|
"rename to new.go",
|
|
"--- a/old.go",
|
|
"+++ b/new.go",
|
|
"@@ -1,2 +1,2 @@",
|
|
" line1",
|
|
"-old",
|
|
"+new",
|
|
}, "\n"),
|
|
},
|
|
{
|
|
name: "content line starting with index prefix not stripped",
|
|
in: strings.Join([]string{
|
|
"diff --git a/x.go b/x.go",
|
|
"index aaa..bbb 100644",
|
|
"--- a/x.go",
|
|
"+++ b/x.go",
|
|
"@@ -1,2 +1,3 @@",
|
|
" line1",
|
|
"+index foo..bar is data",
|
|
}, "\n"),
|
|
want: strings.Join([]string{
|
|
"--- a/x.go",
|
|
"+++ b/x.go",
|
|
"@@ -1,2 +1,3 @@",
|
|
" line1",
|
|
"+index foo..bar is data",
|
|
}, "\n"),
|
|
},
|
|
{
|
|
name: "empty string",
|
|
in: "",
|
|
want: "",
|
|
},
|
|
{
|
|
name: "no trailing newline preserved",
|
|
in: "diff --git a/f b/f\n--- a/f\n+++ b/f",
|
|
want: "--- a/f\n+++ b/f",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := StripDiffHeaders(tt.in)
|
|
if got != tt.want {
|
|
t.Errorf("StripDiffHeaders() =\n%s\nwant:\n%s", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|