package toml import ( "reflect" "strings" "testing" ) 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", "e = \"\\e\\x41\"\r\n# c\r\n[[a]]\r\n", "a = 1 # \x1b[8m\nb = 2", "[ [a] ]\nk =\r1", } { 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", `"a.b" = { "c d" = 1 }`, `mixed = [1, {value = "x"}, [{nested = true}]]`, "[[outer]]\n[outer.\"inner.key\"]\nx = \"\\b\\f\"\n[[outer]]\nx = 2", } { f.Add([]byte(s)) } f.Fuzz(func(t *testing.T, data []byte) { m1, err := NewParser(data).Parse() if err != nil { t.Skip() } out, err := Marshal(m1) if err != nil { // Separate dotted paths may form a tree deeper than the encoder's // documented whole-value bound. No other encode failure is allowed. if strings.Contains(err.Error(), "nesting exceeds") { t.Skip() } t.Fatalf("parsed value cannot be encoded: %v", err) } 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) } }) }