123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 | package diffview
import (
"strconv"
"strings"
"testing"
"github.com/charmbracelet/x/ansi"
)
func TestRenderDiff(t *testing.T) {
if from, to := changed(`say("hello there")`, `say("hello")`); from != 10 || to != 16 {
t.Errorf("changed = %d, %d; want 10, 16", from, to)
}
if from, to := changed("abc", "xyz"); from != to {
t.Errorf("a wholly changed line should have no emphasis, got %d, %d", from, to)
}
long := "+" + strings.Repeat("x", 100)
out := Render("@@ -1 +1 @@\n-old\n"+long+"\n context", 0, 40, Dark)
lines := strings.Split(out, "\n")
if len(lines) != 6 {
t.Fatalf("got %d lines, want 6:\n%s", len(lines), out)
}
for _, l := range lines[1:5] {
if w := ansi.StringWidth(l); w != 40 {
t.Errorf("row %q is %d wide, want 40", ansi.Strip(l), w)
}
}
if got := strings.Join([]string{ansi.Strip(lines[2]), ansi.Strip(lines[3]), ansi.Strip(lines[4])}, ""); strings.ReplaceAll(got, " ", "") != long {
t.Errorf("the wrapped line lost text: %q", got)
}
}
// Inside a hunk, "---" is a removed line starting "--" (an SQL comment),
// not a file header, and is shown as removed.
func TestRenderDiffDashLines(t *testing.T) {
out := Render("--- f\n+++ f\n@@ -1 +1 @@\n--- drop table\n+++ added\n", 0, 40, Dark)
lines := strings.Split(out, "\n")
if len(lines) != 5 {
t.Fatalf("got %d lines:\n%s", len(lines), out)
}
for i, l := range lines {
// Changed lines are rows padded to the width; headers are not.
if row := ansi.StringWidth(l) == 40; row != (i >= 3) {
t.Errorf("line %d (%q) shown as a row: %v", i, ansi.Strip(l), row)
}
}
}
// A second file's headers, after a hunk that ends in removed lines, are
// headers, not changed lines.
func TestRenderSecondFile(t *testing.T) {
out := Render("--- a\n+++ a\n@@ -1,2 +1,1 @@\n x\n-y\n--- b\n+++ b\n@@ -1 +1 @@\n-old\n+new\n\\ No newline at end of file\n", 0, 40, Dark)
lines := strings.Split(out, "\n")
if len(lines) != 11 {
t.Fatalf("got %d lines:\n%s", len(lines), out)
}
for i, l := range lines {
// Changed lines are rows padded to the width; headers are not.
if row := ansi.StringWidth(l) == 40; row != (i == 4 || i == 8 || i == 9) {
t.Errorf("line %d (%q) shown as a row: %v", i, ansi.Strip(l), row)
}
}
}
func TestVisible(t *testing.T) {
cases := map[string]string{
"ls\x1b]0;rm -rf ~\x07": `ls\x1b]0;rm -rf ~\a`,
"a\rb\r\nc": `a\rb\r` + "\nc", // CRLF shows: a diff may change only that
"bidi\u202eevil": `bidi\u202eevil`,
"zero\u200bwidth": `zero\u200bwidth`,
"c1\u009bcontrol": `c1\u009bcontrol`,
"tab\there é ✓": "tab here é ✓",
}
for in, want := range cases {
if got := Visible(in); got != want {
t.Errorf("Visible(%q) = %q, want %q", in, got, want)
}
}
}
// Visible output is printable characters and newlines only, line by line.
func FuzzVisible(f *testing.F) {
for _, s := range []string{
"plain\ntext", "visible\x1b]0;x hidden\nline2", "c1\u009b31m \u009d0;t\u009c",
"\x9b\xff invalid", "bidi\u202eevil\u2066x\u200f", "tag\U000e0041", "ㅤx͏️⠀",
} {
f.Add(s)
}
f.Fuzz(func(t *testing.T, s string) {
out := Visible(s)
for _, r := range out {
if r != '\n' && (!strconv.IsPrint(r) || blank(r)) {
t.Fatalf("Visible(%q) = %q: holds %U", s, out, r)
}
}
if strings.Count(out, "\n") != strings.Count(s, "\n") {
t.Fatalf("Visible(%q) = %q: lines changed", s, out)
}
})
}
|