Artifact 57c6b62f1d20abb831aeb9e4b0072d48831fbc1de0f0b4579347ade7dcc634f8:
- File
internal/diff/diff_test.go
— part of check-in
[ac056aaa1d]
at
2026-08-23 21:06:14
on branch trunk
— check-in page: patch link, full hash + copy, tags, change stats
Stats are tallied at parse time so a windowed diff reports the whole file, and sit on the file header as well as the summary line. Tags come from checkin_tag, newest word per name winning. Copy falls back through execCommand to selection: navigator.clipboard is undefined on the plain-HTTP origins quarry is usually served from. (user: dmestas, size: 5933) [annotate] [blame] [check-ins using] [more...]
package diff import ( "strings" "testing" ) // The ticket's own motivating case, from pikchr a7f1c35b: one line // gains "(including" at its end. The reader must see THAT — a marked // range on each side — not two fully-coloured lines to diff by eye. func TestIntraLineMarks(t *testing.T) { u := `--- a/pikchr.y +++ b/pikchr.y @@ -5220,7 +5220,8 @@ static void x(void){ int i; - /* Remove leading and trailing whitespace from each argument. + /* Remove leading and trailing whitespace from each argument (including more; ` files := Parse(u) if len(files) != 1 || files[0].Path != "pikchr.y" || files[0].Change != Change { t.Fatalf("files = %+v", files) } h := files[0].Hunks[0] if h.OldStart != 5220 || h.NewStart != 5220 || h.Header != "static void x(void){" { t.Fatalf("hunk = %+v", h) } // Numbers: context carries both, deletion old-only, addition new-only. if h.Lines[0].OldNum != 5220 || h.Lines[0].NewNum != 5220 || h.Lines[1].OldNum != 5221 || h.Lines[1].NewNum != 0 || h.Lines[2].NewNum != 5221 || h.Lines[2].OldNum != 0 || h.Lines[3].OldNum != 5222 || h.Lines[3].NewNum != 5222 { t.Fatalf("numbering wrong: %+v", h.Lines) } Enrich(files) del, add := files[0].Hunks[0].Lines[1], files[0].Hunks[0].Lines[2] marked := func(l Line) string { var b strings.Builder for _, tok := range l.Tokens { if tok.Mark { b.WriteString(tok.Text) } } return b.String() } if got := marked(add); !strings.Contains(got, "(including") { t.Fatalf("addition marks = %q, want the added words", got) } if got := marked(add); strings.Contains(got, "Remove leading") { t.Fatalf("addition over-marked the unchanged text: %q", got) } if got := marked(del); strings.Contains(got, "Remove leading") { t.Fatalf("deletion over-marked: %q", got) } // Syntax classes arrived positionally (pikchr.y matches a lexer via // the .y suffix); reassembling all tokens must reproduce the text // exactly — the splice must never lose or duplicate a byte. var whole strings.Builder for _, tok := range add.Tokens { whole.WriteString(tok.Text) } if whole.String() != " /* Remove leading and trailing whitespace from each argument (including" { t.Fatalf("splice corrupted the text: %q", whole.String()) } } // A new file and a deleted file are CHANGE KINDS, not diffs against // /dev/null; and total rewrites mark nothing — a fully-marked pair // says less than plain colour. func TestChangeKindsAndRewriteGuard(t *testing.T) { u := `--- /dev/null +++ b/fresh.go @@ -0,0 +1,1 @@ +package fresh --- a/gone.go +++ /dev/null @@ -1,1 +0,0 @@ -package gone ` files := Parse(u) if len(files) != 2 || files[0].Change != Added || files[0].Path != "fresh.go" || files[1].Change != Deleted || files[1].Path != "gone.go" { t.Fatalf("kinds = %+v", files) } da, db := wordDiff("completely different text here", "nothing shared at all now") if da != nil || db != nil { t.Fatalf("a rewrite was word-marked: %v %v", da, db) } } // Per-line tokenization meets multi-line constructs it cannot close — // a raw string's backtick, a diff's @@ inside a string — and chroma // answers with Error tokens, which the stylesheet paints as red // warning boxes over healthy code. A lexer's shrug must render PLAIN. func TestLexerErrorsRenderPlain(t *testing.T) { u := "--- a/x.go\n+++ b/x.go\n@@ -1,1 +1,2 @@\n context\n+\tu := `--- embedded\n" files := Parse(u) Enrich(files) for _, l := range files[0].Hunks[0].Lines { for _, tok := range l.Tokens { if tok.Class == "err" { t.Fatalf("an Error token leaked a class: %+v", l.Tokens) } } } } // Found by the corpus sweep on sqlite: a deleted line whose text // begins "-- " (a SQL comment) prints as "---…" in unified output, and // prefix-matching alone misreads it as the next file's header, losing // the line. The @@ counts are the disambiguator: while a hunk's // promised lines remain, everything is content. func TestHeaderLookalikesInsideHunks(t *testing.T) { u := "--- a/q.sql\n+++ b/q.sql\n@@ -1,3 +1,2 @@\n context\n--- drop this SQL comment\n+++ and add this one\n" files := Parse(u) if len(files) != 1 { t.Fatalf("the lookalike split the file: %+v", files) } lines := files[0].Hunks[0].Lines if len(lines) != 3 { t.Fatalf("got %d lines, want 3: %+v", len(lines), lines) } if lines[1].Kind != "deletion" || lines[1].Tokens[0].Text != "-- drop this SQL comment" { t.Fatalf("the SQL-comment deletion was misread: %+v", lines[1]) } if lines[2].Kind != "addition" || lines[2].Tokens[0].Text != "++ and add this one" { t.Fatalf("the ++ addition was misread: %+v", lines[2]) } } // The counts are the FILE's, not the window's: a reader paging through // a long diff must see one total, not a total that shrinks per page. func TestParseCountsChangedLinesPerFileAndSurvivesWindowing(t *testing.T) { unified := "--- a/one.go\n+++ b/one.go\n" + "@@ -1,2 +1,3 @@\n ctx\n-gone\n+new\n+also\n" + "@@ -20,2 +21,2 @@\n ctx\n-old\n+fresh\n" + "--- /dev/null\n+++ b/two.go\n@@ -0,0 +1,2 @@\n+a\n+b\n" files := Parse(unified) if len(files) != 2 { t.Fatalf("want 2 files, got %d", len(files)) } if files[0].Added != 3 || files[0].Deleted != 2 { t.Errorf("one.go = +%d -%d, want +3 -2", files[0].Added, files[0].Deleted) } if files[1].Added != 2 || files[1].Deleted != 0 { t.Errorf("two.go = +%d -%d, want +2 -0", files[1].Added, files[1].Deleted) } // Cut to a window small enough to drop hunks, then ask again. Window(files, 0, 4) if files[0].Added != 3 || files[0].Deleted != 2 { t.Errorf("windowing changed the count to +%d -%d", files[0].Added, files[0].Deleted) } if len(files[0].Hunks) != 1 { t.Fatalf("window kept %d hunks, expected the count to outlive a cut", len(files[0].Hunks)) } } // A patch that opens mid-hunk with no file header must not panic the // tally. func TestParseTalliesSurviveAHeaderlessHunk(t *testing.T) { Parse("@@ -1,1 +1,1 @@\n-a\n+b\n") }