Files
toml/fuzz_test.go
2026-07-13 02:45:49 -04:00

88 lines
2.0 KiB
Go

package toml
import (
"reflect"
"testing"
"unicode/utf8"
)
func FuzzParse(f *testing.F) {
for _, s := range []string{
"",
`key = "value"`,
"[table]\nk = 1",
"[[arr]]\nx = 1.5\n[[arr]]\nx = 2e3",
"t = { a = 1, b = { c = [1, 2] } }",
`s = "esc \u00E9 \t"`,
"n = -0x10\nb = 0b101\no = 0o17",
} {
f.Add([]byte(s))
}
f.Fuzz(func(t *testing.T, data []byte) {
p := NewParser(data)
_, _ = p.Parse() // property: no panic, no hang
})
}
// normalize collapses the parser's dual slice representations
// ([]map[string]any for [[headers]], []any for inline arrays) so the
// fixpoint compares values, not construction syntax.
func normalize(v any) any {
switch t := v.(type) {
case map[string]any:
for k, vv := range t {
t[k] = normalize(vv)
}
return t
case []map[string]any:
out := make([]any, len(t))
for i, m := range t {
out[i] = normalize(m)
}
return out
case []any:
for i, vv := range t {
t[i] = normalize(vv)
}
return t
}
return v
}
func FuzzRoundTrip(f *testing.F) {
for _, s := range []string{
"a = 1\nb = 2.5\nc = true\nd = \"x\"",
"[t]\nk = [1, 2, 3]",
"[[s]]\nn = \"a\"\n[[s]]\nn = \"b\"",
`m = { x = 1, y = { z = "q" } }`,
`u = "日本語 \u00E9"`,
"big = 1e100\nsmall = 1e-100",
} {
f.Add([]byte(s))
}
f.Fuzz(func(t *testing.T, data []byte) {
// Encoder normalizes invalid UTF-8 to U+FFFD (WriteRune); byte-exact
// round trip is only a property of valid input.
if !utf8.Valid(data) {
t.Skip()
}
m1, err := NewParser(data).Parse()
if err != nil {
t.Skip()
}
out, err := Marshal(m1)
if err != nil {
// Parser accepts values the encoder cannot represent
// (mixed table/scalar arrays). Documented asymmetry.
t.Skip()
}
m2, err := NewParser(out).Parse()
if err != nil {
t.Fatalf("emitted TOML failed to re-parse: %v\nemitted:\n%s", err, out)
}
if !reflect.DeepEqual(normalize(m1), normalize(m2)) {
t.Fatalf("fixpoint violation:\nm1: %#v\nm2: %#v\nemitted:\n%s", m1, m2, out)
}
})
}