test: add FuzzMarshal, golden messages, synctest cancellation and cross smoke
Test / test (push) Successful in 1m31s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-22 01:03:27 +02:00
parent 3e741e7790
commit bef1d3fbd9
10 changed files with 242 additions and 1 deletions
+68
View File
@@ -121,3 +121,71 @@ func tomlEqual(a, b any) bool {
return reflect.DeepEqual(a, b)
}
}
// FuzzMarshal drives the encoder with generated Go values and holds it to
// the same round-trip invariant FuzzParse holds the parser to: a value built
// only of encodable kinds must marshal, the document must re-parse, and the
// tree must equal the value it came from.
func FuzzMarshal(f *testing.F) {
seeds := [][]byte{
{},
{0, 0, 1, 2},
{1, 1, 2, 3, 2, 2, 3, 4},
{0, 3, 1, 9, 3, 3, 2, 8, 1, 0, 1, 7},
}
for _, s := range seeds {
f.Add(s)
}
f.Fuzz(func(t *testing.T, data []byte) {
v := fuzzValue(data)
out, err := Marshal(v)
if err != nil {
t.Fatalf("marshal of an encodable value failed: %v\nvalue: %#v", err, v)
}
tree, err := ParseMap(out)
if err != nil {
t.Fatalf("re-parse of the emitted document failed: %v\ndoc:\n%s", err, out)
}
if !tomlEqual(v, tree) {
t.Fatalf("round-trip changed the value\nvalue: %#v\ndoc:\n%s\ntree: %#v", v, out, tree)
}
})
}
// fuzzKeys is the fixed key pool the generated values draw from, so keys are
// always valid bare keys and repeat often.
var fuzzKeys = []string{"alpha", "beta", "gamma", "delta"}
// fuzzValue builds a map[string]any of encodable kinds from data: integers,
// positive floats, short strings, nested tables and scalar arrays. The bytes
// decide the shape deterministically.
func fuzzValue(data []byte) map[string]any {
root := map[string]any{}
cur := root
depth := 0
for i := 0; i+3 < len(data); i += 4 {
key := fuzzKeys[int(data[i])%len(fuzzKeys)]
switch data[i+1] % 5 {
case 0:
cur[key] = int64(data[i+2])<<8 | int64(data[i+3])
case 1:
cur[key] = float64(int(data[i+2])%1000)/8.0 + 0.125
case 2:
cur[key] = string(rune('a' + int(data[i+2])%26))
case 3:
cur[key] = []any{
int64(data[i+2]),
float64(int(data[i+3])%100)/4.0 + 0.25,
string(rune('a' + int(data[i+3])%26)),
}
case 4:
if depth < 6 {
next := map[string]any{}
cur[key] = next
cur = next
depth++
}
}
}
return root
}