post inicial

This commit is contained in:
lastdragon
2026-09-27 16:04:02 -06:00
parent d84587e1f3
commit 52864794f5
67 changed files with 15023 additions and 2 deletions
+202
View File
@@ -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")
}
}
}