test(format): fuzz the expansion round-trip
A second formatter target holds the assembly path's contract: beyond the
token view, a file the expander reads cleanly must expand to the same
statement sequence after formatting, because the macro language draws
distinctions from layout (the adjacency of a #define name and its '(',
continuation bodies) that a token count cannot see.
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
e600fc00e7
commit
59637d35b1
1 file changed
+62
@@ -204,6 +204,68 @@ func dump(v any) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// FuzzFormatExpansionRoundTrip hammers the formatter against the assembly
|
||||
// path's reading of the file: macro expansion and include splicing. The
|
||||
// token view pins every token the formatter carries over, and beyond that a
|
||||
// file the expander reads cleanly must expand to exactly the same statement
|
||||
// sequence after formatting, because distinctions the macro language draws
|
||||
// from layout (the adjacency of a #define name and its '(', the backslash
|
||||
// continuations that split a body into statements) live in spacing a pure
|
||||
// token count cannot see. The seed corpus carries the repository's kernels,
|
||||
// so a plain `go test` run replays every seed as a regression case.
|
||||
func FuzzFormatExpansionRoundTrip(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("#define A(x) x+1\nTEXT ·f(SB), $0\n\tA(2)\n\tRET\n")
|
||||
f.Add("#define A (x) x+1\nA\n")
|
||||
f.Add("#define M (BX)\nTEXT ·f(SB), $0\n\tMOVL M, AX\n\tRET\n")
|
||||
f.Add("#define M() \\\n\tADD $1, R0 \\\n\tSUB $2, R1\nTEXT ·f(SB), $0\n\tM()\n\tRET\n")
|
||||
f.Add("#define PAIR(v) \\\n\tADD $v, R0; \\\n\tSUB $v, R1\nTEXT ·f(SB), $0\n\tPAIR(3)\n\tRET\n")
|
||||
f.Add("#define A A\nA\n") // self-reference
|
||||
f.Add("#define A(x) ((x))\nA(A(A(1)))\n") // nested invocation
|
||||
f.Add("#define A 1\n#define B A\nB\n") // chained object macros
|
||||
f.Add("#ifdef X\n#else\n#endif\n") // conditionals
|
||||
f.Add("#define A(x) x x\nA(A(A(1)))\n") // amplifying body
|
||||
f.Add("#include \"textflag.h\"\n") // the never-spliced header
|
||||
f.Add("#include \"no/such/header.h\"\n") // unresolvable include
|
||||
f.Add("#define A(x)\nA()\n") // empty body
|
||||
f.Add("TEXT ·f(SB), $0\n\tM\n\tRET\n") // undefined name, no table entry
|
||||
|
||||
f.Fuzz(func(t *testing.T, src string) {
|
||||
out := Source(src)
|
||||
in, o := tokenView(src), tokenView(out)
|
||||
if !sameView(in, o) {
|
||||
t.Fatalf("formatting changed the token stream:\ninput: %v\noutput: %v\n%s", viewString(in), viewString(o), out)
|
||||
}
|
||||
if hasStrayIllegal(src) {
|
||||
// The formatter documents dropping stray runes; their files have
|
||||
// no expansion to preserve, and the token view above is a weaker
|
||||
// contract that already held.
|
||||
return
|
||||
}
|
||||
before, errs := parser.ParseWithOptions("in.s", src, parser.Options{Expand: true})
|
||||
if len(errs) > 0 {
|
||||
return
|
||||
}
|
||||
after, errs := parser.ParseWithOptions("out.s", out, parser.Options{Expand: true})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("formatted output of expandable input no longer expands: %v\n%s", errs[0], out)
|
||||
}
|
||||
if got, want := stmtSignature(after), stmtSignature(before); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("formatting changed the expansion:\nbefore: %v\nafter: %v\n%s", want, got, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func dumpValue(v reflect.Value, b *strings.Builder) {
|
||||
switch v.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
|
||||
Reference in new issue
Block a user