post inicial
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
package patch
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func roundTrip(t *testing.T, name, old, new string) {
|
||||
t.Helper()
|
||||
d, err := Unified("a/f", "b/f", old, new, 3)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Unified: %v", name, err)
|
||||
}
|
||||
if old == new {
|
||||
if d != "" {
|
||||
t.Errorf("%s: identical inputs must give empty diff", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
fps, err := Parse(d)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Parse: %v\n%s", name, err, d)
|
||||
}
|
||||
got, err := fps[0].Apply(old)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Apply: %v\n%s", name, err, d)
|
||||
}
|
||||
if got != new {
|
||||
t.Fatalf("%s: round trip mismatch\nwant %q\ngot %q\ndiff:\n%s", name, new, got, d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripCases(t *testing.T) {
|
||||
cases := map[string][2]string{
|
||||
"change middle": {"a\nb\nc\nd\ne\n", "a\nb\nX\nd\ne\n"},
|
||||
"insert start": {"a\nb\n", "new\na\nb\n"},
|
||||
"insert end": {"a\nb\n", "a\nb\nnew\n"},
|
||||
"delete all": {"a\nb\n", ""},
|
||||
"from empty": {"", "a\nb\n"},
|
||||
"drop final newline": {"a\nb\n", "a\nb"},
|
||||
"add final newline": {"a\nb", "a\nb\n"},
|
||||
"no newline change": {"a\nb", "a\nc"},
|
||||
"two distant hunks": {strings.Repeat("x\n", 20) + "a\n" + strings.Repeat("y\n", 20) + "b\n", strings.Repeat("x\n", 20) + "A\n" + strings.Repeat("y\n", 20) + "B\n"},
|
||||
"sshd port": {"# comment\nPort 22\nPermitRootLogin yes\n", "# comment\nPort 2222\nPermitRootLogin no\n"},
|
||||
"identical": {"a\n", "a\n"},
|
||||
"blank context lines": {"a\n\n\nb\n", "a\n\n\nc\n"},
|
||||
}
|
||||
for name, c := range cases {
|
||||
roundTrip(t, name, c[0], c[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripRandom(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(42))
|
||||
words := []string{"alpha", "beta", "gamma", "", "delta", "listen 80;", "}"}
|
||||
gen := func(n int) []string {
|
||||
var l []string
|
||||
for i := 0; i < n; i++ {
|
||||
l = append(l, words[r.Intn(len(words))])
|
||||
}
|
||||
return l
|
||||
}
|
||||
for i := 0; i < 300; i++ {
|
||||
a := gen(r.Intn(40))
|
||||
b := append([]string(nil), a...)
|
||||
for j := r.Intn(6); j > 0; j-- {
|
||||
switch op := r.Intn(3); {
|
||||
case op == 0 && len(b) > 0:
|
||||
k := r.Intn(len(b))
|
||||
b = append(b[:k], b[k+1:]...)
|
||||
case op == 1:
|
||||
k := r.Intn(len(b) + 1)
|
||||
b = append(b[:k], append([]string{words[r.Intn(len(words))]}, b[k:]...)...)
|
||||
case len(b) > 0:
|
||||
b[r.Intn(len(b))] = "changed"
|
||||
}
|
||||
}
|
||||
old, new := strings.Join(a, "\n"), strings.Join(b, "\n")
|
||||
if r.Intn(2) == 0 && old != "" {
|
||||
old += "\n"
|
||||
}
|
||||
if r.Intn(2) == 0 && new != "" {
|
||||
new += "\n"
|
||||
}
|
||||
roundTrip(t, "random", old, new)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyWithOffset(t *testing.T) {
|
||||
d, _ := Unified("a/f", "b/f", "a\nb\nc\n", "a\nB\nc\n", 1)
|
||||
fps, _ := Parse(d)
|
||||
// El archivo real tiene líneas extra al principio: el hunk se desplaza.
|
||||
got, err := fps[0].Apply("x\ny\nz\na\nb\nc\n")
|
||||
if err != nil || got != "x\ny\nz\na\nB\nc\n" {
|
||||
t.Fatalf("got %q err %v", got, err)
|
||||
}
|
||||
if _, err := fps[0].Apply("a\nDIFFERENT\nc\n"); err == nil {
|
||||
t.Error("context mismatch must fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGitDiffAndTargets(t *testing.T) {
|
||||
d := `diff --git a/src/main.c b/src/main.c
|
||||
index 83db48f..bf269f4 100644
|
||||
--- a/src/main.c 2026-09-27 10:00:00
|
||||
+++ b/src/main.c 2026-09-27 10:05:00
|
||||
@@ -1,3 +1,3 @@
|
||||
int main() {
|
||||
- return 1;
|
||||
+ return 0;
|
||||
}
|
||||
diff --git a/NEWS b/NEWS
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/NEWS
|
||||
@@ -0,0 +1 @@
|
||||
+fixed
|
||||
--- a/old.txt
|
||||
+++ /dev/null
|
||||
@@ -1 +0,0 @@
|
||||
-bye
|
||||
`
|
||||
fps, err := Parse(d)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fps) != 3 {
|
||||
t.Fatalf("files = %d", len(fps))
|
||||
}
|
||||
if fps[0].Target(-1) != "src/main.c" || fps[0].Target(0) != "b/src/main.c" || fps[0].Target(2) != "main.c" {
|
||||
t.Errorf("targets: %q %q %q", fps[0].Target(-1), fps[0].Target(0), fps[0].Target(2))
|
||||
}
|
||||
if !fps[1].IsNew || fps[1].Target(-1) != "NEWS" {
|
||||
t.Errorf("new file: %+v", fps[1])
|
||||
}
|
||||
if !fps[2].IsDelete || fps[2].Target(-1) != "old.txt" {
|
||||
t.Errorf("delete: %+v %q", fps[2], fps[2].Target(-1))
|
||||
}
|
||||
got, err := fps[0].Apply("int main() {\n return 1;\n}\n")
|
||||
if err != nil || !strings.Contains(got, "return 0;") {
|
||||
t.Errorf("apply: %q %v", got, err)
|
||||
}
|
||||
if got, err := fps[1].Apply(""); err != nil || got != "fixed\n" {
|
||||
t.Errorf("new file: %q %v", got, err)
|
||||
}
|
||||
if got, err := fps[2].Apply("bye\n"); err != nil || got != "" {
|
||||
t.Errorf("delete: %q %v", got, err)
|
||||
}
|
||||
if _, err := fps[1].Apply("exists\n"); err == nil {
|
||||
t.Error("creating an existing file must fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrors(t *testing.T) {
|
||||
for _, d := range []string{
|
||||
"no diff here",
|
||||
"--- a/f\n+++ b/f\n@@ -1,2 +1,2 @@\n a\n", // truncado
|
||||
"--- a/f\n+++ b/f\n@@ bad @@\n",
|
||||
"--- a/f\n+++ b/f\n", // sin hunks
|
||||
"--- a/f\n+++ b/f\n@@ -1 +1 @@\n?weird\n",
|
||||
} {
|
||||
if _, err := Parse(d); err == nil {
|
||||
t.Errorf("expected error for %q", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibilidad con diff(1) y patch(1) del sistema, si están instalados.
|
||||
func TestSystemToolsCompatibility(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
old := "server {\n listen 80;\n server_name a;\n}\n"
|
||||
new := "server {\n listen 443 ssl;\n server_name a;\n ssl_certificate x;\n}\n"
|
||||
oldPath, newPath := filepath.Join(dir, "old"), filepath.Join(dir, "new")
|
||||
os.WriteFile(oldPath, []byte(old), 0o644)
|
||||
os.WriteFile(newPath, []byte(new), 0o644)
|
||||
|
||||
if _, err := exec.LookPath("diff"); err == nil {
|
||||
out, _ := exec.Command("diff", "-u", oldPath, newPath).Output()
|
||||
fps, err := Parse(string(out))
|
||||
if err != nil {
|
||||
t.Fatalf("parse diff -u output: %v", err)
|
||||
}
|
||||
if got, err := fps[0].Apply(old); err != nil || got != new {
|
||||
t.Errorf("apply diff -u output: %q %v", got, err)
|
||||
}
|
||||
}
|
||||
if _, err := exec.LookPath("patch"); err == nil {
|
||||
d, _ := Unified("old", "old", old, new, 3)
|
||||
cmd := exec.Command("patch", "-s", oldPath)
|
||||
cmd.Stdin = strings.NewReader(d)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("patch(1) rejected our diff: %v %s", err, out)
|
||||
}
|
||||
if got, _ := os.ReadFile(oldPath); string(got) != new {
|
||||
t.Errorf("patch(1) result mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user