Files
2026-07-13 13:00:08 +08:00

72 lines
2.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package diff
import (
"fmt"
"runtime"
"strings"
"testing"
"time"
)
// TestLargeRewriteBoundedCost proves a full rewrite of a large file no longer
// pays O(N²): the edit-distance cap skips the line-by-line render, so memory and
// time stay bounded while the tallies and an omitted-diff marker still report the
// change. Before the cap this allocated ~565 MB for 3000 lines (≈6 GB at 10k).
func TestLargeRewriteBoundedCost(t *testing.T) {
var oldB, newB strings.Builder
const n = 6000
for i := 0; i < n; i++ {
fmt.Fprintf(&oldB, "old line %d\n", i)
fmt.Fprintf(&newB, "totally different new line %d\n", i)
}
var m0, m1 runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&m0)
start := time.Now()
c := Build("big.txt", oldB.String(), newB.String(), Modify)
elapsed := time.Since(start)
runtime.ReadMemStats(&m1)
allocMB := float64(m1.TotalAlloc-m0.TotalAlloc) / (1 << 20)
t.Logf("2×%d-line rewrite: %v, %.1f MB, +%d/-%d", n, elapsed, allocMB, c.Added, c.Removed)
if allocMB > 150 {
t.Errorf("allocated %.1f MB — the edit-distance cap should bound this", allocMB)
}
if elapsed > time.Second {
t.Errorf("took %v — should be bounded", elapsed)
}
if c.Added != n || c.Removed != n {
t.Errorf("tallies wrong: +%d/-%d, want +%d/-%d", c.Added, c.Removed, n, n)
}
if !strings.Contains(c.Diff, "too large") {
t.Errorf("expected an omitted-diff marker, got %q", c.Diff)
}
}
// TestSmallEditOnLargeFileStillDiffs proves the cap doesn't punish the common
// case: a tiny edit in a big file converges far below the cap, so it keeps a real
// line-by-line diff regardless of file size.
func TestSmallEditOnLargeFileStillDiffs(t *testing.T) {
var oldB strings.Builder
const n = 8000
for i := 0; i < n; i++ {
fmt.Fprintf(&oldB, "line %d\n", i)
}
old := oldB.String()
updated := strings.Replace(old, "line 4000\n", "line 4000 EDITED\n", 1)
c := Build("big.txt", old, updated, Modify)
if c.Added != 1 || c.Removed != 1 {
t.Errorf("tallies = +%d/-%d, want +1/-1", c.Added, c.Removed)
}
if !strings.Contains(c.Diff, "line 4000 EDITED") || strings.Contains(c.Diff, "too large") {
t.Errorf("small edit on a large file should keep a real diff, got %q", firstN(c.Diff, 200))
}
}
func firstN(s string, n int) string {
if len(s) > n {
return s[:n]
}
return s
}