54 lines
1.7 KiB
Go
54 lines
1.7 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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)
|
|
}
|
|
}
|
|
})
|
|
}
|