diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 19:39:32 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 19:39:32 +0200 |
| commit | eab84ab42f51beab5debc03b8049f0929c5e8904 (patch) | |
| tree | 803e95b9b950f65ff3d1c9b5006d92b9bfb8756d /internal/journal/fuzz_test.go | |
| parent | 5b22b8e3a7d1ae9a3fcc4e894ae2e9d142bf8086 (diff) | |
| download | krino-eab84ab42f51beab5debc03b8049f0929c5e8904.tar.gz krino-eab84ab42f51beab5debc03b8049f0929c5e8904.zip | |
plan 8: fuzz the log's escaping and round trip
Diffstat (limited to 'internal/journal/fuzz_test.go')
| -rw-r--r-- | internal/journal/fuzz_test.go | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/internal/journal/fuzz_test.go b/internal/journal/fuzz_test.go new file mode 100644 index 0000000..0090d5c --- /dev/null +++ b/internal/journal/fuzz_test.go @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package journal + +import ( + "path/filepath" + "strings" + "testing" + "time" +) + +// FuzzEscapeRoundTrip: escape never yields a tab or a newline, which would +// split a log line, and unescape returns exactly the bytes escaped - for +// any string, invalid UTF-8 included. Undo reads every path back through +// this pair. +func FuzzEscapeRoundTrip(f *testing.F) { + for _, s := range []string{"plain.pdf", "tab\there", "new\nline", `back\slash`, `\x41`, "\xff\xfe", `\\n`, "zażółć gęślą jaźń", "\x00\x1b[2K"} { + f.Add(s) + } + f.Fuzz(func(t *testing.T, s string) { + e := escape(s) + if strings.ContainsAny(e, "\t\n") { + t.Fatalf("escape(%q) = %q holds a tab or a newline", s, e) + } + if got := unescape(e); got != s { + t.Fatalf("unescape(escape(%q)) = %q", s, got) + } + }) +} + +// FuzzParseLine: a log line of any content is rejected or parsed, never a +// panic - the log is a file a crash can truncate or a person can edit. +func FuzzParseLine(f *testing.F) { + f.Add("2026-09-11T10:02:03+02:00\tR\tdl\ta.pdf\t1\tmove\tok\tacme\t/a\t/b\t3\t2026-09-11T10:02:03+02:00\t") + f.Add("\t\t\t\t\t\t\t\t\t\t\t\t") + f.Add("") + f.Add("2026-09-11T10:02:03+02:00\tR\tdl\t\\x\t99999999999999999999\tmove") + f.Fuzz(func(t *testing.T, line string) { + parseLine(line) + }) +} + +// FuzzEntryRoundTrip: a step written with Append comes back from Entries +// field for field, whatever its file name, rule, paths and detail hold. +func FuzzEntryRoundTrip(f *testing.F) { + f.Add("a.pdf", "acme", "/dl/a.pdf", "/w/a.pdf", "") + f.Add("new\nline\t.pdf", "r", `/dl/\x`, "", "bad\xffbyte") + f.Fuzz(func(t *testing.T, file, rule, src, dst, detail string) { + path := filepath.Join(t.TempDir(), "krino.log") + w, err := Open(path) + if err != nil { + t.Fatal(err) + } + at := time.Date(2026, 9, 11, 10, 2, 3, 0, time.FixedZone("CEST", 2*3600)) + want := Entry{Time: at, Run: "R", Dir: "dl", File: file, Step: 1, Action: "move", Status: "ok", + Rule: rule, Src: src, Dst: dst, Size: 3, ModTime: at, Detail: detail} + for _, e := range []Entry{ + {Time: at, Run: "R", Action: "run-start", Status: "ok"}, + want, + {Time: at, Run: "R", Action: "run-end", Status: "ok"}, + } { + if err := w.Append(e); err != nil { + t.Fatal(err) + } + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + entries, err := Entries(path, "R") + if err != nil { + t.Fatal(err) + } + var got []Entry + for _, e := range entries { + if e.Action == "move" { + got = append(got, e) + } + } + if len(got) != 1 { + t.Fatalf("read back %d move entries, want 1: %+v", len(got), entries) + } + g := got[0] + if !g.Time.Equal(want.Time) || !g.ModTime.Equal(want.ModTime) { + t.Fatalf("times changed: %v %v", g.Time, g.ModTime) + } + g.Time, g.ModTime = want.Time, want.ModTime + if g != want { + t.Fatalf("read back\n%+v\nwant\n%+v", g, want) + } + }) +} |
