// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package format import ( "os" "path/filepath" "testing" "sourcedock.dev/petrbalvin/gasm-sdk/parser" ) // FuzzFormatIdempotency hammers the formatter with arbitrary input. The // contract: formatting twice equals formatting once, and input that parses // cleanly still parses cleanly after formatting. The seed corpus carries the // repository's kernels, so a plain `go test` run replays every seed as a // regression case and CI exercises them without any fuzzing budget. func FuzzFormatIdempotency(f *testing.F) { for _, pattern := range []string{ "../testdata/*.s", "../testdata/verify/*.s", } { files, _ := filepath.Glob(pattern) for _, path := range files { if b, err := os.ReadFile(path); err == nil { f.Add(string(b)) } } } f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tMOVQ AX, BX\n\tRET\n") f.Add("TEXT ·f(SB),NOSPLIT,$0\n\tMOVQ AX,BX\n\n\n\tRET\n") f.Add("garbage ### ???\n") // Line-ending whitespace at the edge of a comment: a CR followed by more // trailing whitespace once survived the first pass and disappeared on // re-lexing, so formatting was not idempotent. f.Add("//\r ") f.Add("// loop \r\t\nMOVQ AX, BX\n") f.Add("TEXT ·f(SB), NOSPLIT, $0 // tail\r\n\tMOVQ AX, BX\r\n\tRET\r\n") f.Fuzz(func(t *testing.T, src string) { once := Source(src) twice := Source(once) if once != twice { t.Fatalf("formatting is not idempotent:\nfirst: %q\nsecond: %q", once, twice) } if _, errs := parser.Parse("in.s", src); len(errs) == 0 { if _, errs := parser.Parse("out.s", once); len(errs) > 0 { t.Fatalf("formatted output of clean input does not parse: %v\n%s", errs[0], once) } } }) }