Download
87 lines 2.3 KiBGoModified 2026-10-26 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
package diff

import (
	"strings"
	"testing"
)

// A hunk that only adds lines, once placed by its header after an earlier
// hunk removed lines, sliced at a negative index and crashed blinker.
func TestApplyAdditionBeforeEarlierHunk(t *testing.T) {
	text := "1\n2\n3\n4\n5\n6\n7\n8\n"
	for _, patch := range []string{
		"@@ -1,6 +1,1 @@\n 1\n-2\n-3\n-4\n-5\n-6\n@@ -3,0 +4,1 @@\n+X\n",
		"@@ -9223372036854775807,0 +1 @@\n+X\n",
	} {
		if _, err := Apply(text, patch); err == nil {
			t.Errorf("Apply(%q) succeeded", patch)
		}
	}
}

// The final newline is part of the text: changing it shows in the diff,
// and applying that diff changes it.
func TestFinalNewline(t *testing.T) {
	for _, c := range [][2]string{
		{"a\n", "a"},
		{"a", "a\n"},
		{"a\nb", "a\nc"},
		{"x\na", "y\na"},
		{"", "a"},
		{"a", ""},
	} {
		u := Unified("f", c[0], c[1], 3)
		if u == "" {
			t.Errorf("%q → %q: no diff", c[0], c[1])
			continue
		}
		if got, err := Apply(c[0], u); err != nil || got != c[1] {
			t.Errorf("%q → %q: Apply = %q, %v\n%s", c[0], c[1], got, err, u)
		}
	}
	if u := Unified("f", "a\n", "a", 3); !strings.Contains(u, "-a\n+a\n"+noNewline+"\n") {
		t.Errorf("diff:\n%s", u)
	}
}

// Applying the diff between two texts gives the second, unless a hunk's
// context occurs more than once, which Apply refuses rather than guess.
func FuzzApplyUnified(f *testing.F) {
	f.Add("a\nb\nc\n", "a\nB\nc\n", 3)
	f.Add("a\nb", "a\nb\n", 1)
	f.Add("x\nx\nx\n", "x\ny\nx\n", 1)
	f.Add("", "new\n", 0)
	f.Fuzz(func(t *testing.T, a, b string, context int) {
		context = 1 + (context%5+5)%5
		u := Unified("f", a, b, context)
		if a == b {
			if u != "" {
				t.Fatalf("diff of equal texts:\n%s", u)
			}
			return
		}
		if u == "" {
			t.Fatal("no diff of different texts")
		}
		got, err := Apply(a, u)
		if err != nil {
			if strings.Contains(err.Error(), "matches the file in") {
				return
			}
			t.Fatalf("Apply: %v\n%s", err, u)
		}
		if got != b {
			t.Fatalf("Apply = %q, want %q\n%s", got, b, u)
		}
	})
}

// No patch, however malformed, panics.
func FuzzApply(f *testing.F) {
	f.Add("1\n2\n3\n", "@@ -1,2 +1,1 @@\n 1\n-2\n@@ -1,0 +1 @@\n+x\n")
	f.Add("a", "\\ No newline at end of file\n-a\n+b\n")
	f.Add("", "@@ -0,0 +1 @@\n+x")
	f.Fuzz(func(t *testing.T, text, patch string) {
		Apply(text, patch)
	})
}