vlib/json2/checker_depth_test.v
Rama master · réplica de lectura del repositorio original.
module json2
// These tests guard against a regression to the stack-overflow abort of the
// checker pass: before max_nesting_depth, deeply nested input killed the
// whole process (`V panic: stack overflow`) at a few thousand `[`/`{` in a
// row instead of returning an error.
fn test_nesting_at_max_nesting_depth_still_parses() {
arr := '['.repeat(max_nesting_depth) + ']'.repeat(max_nesting_depth)
a := parse(arr, DecoderOptions{}) or {
panic('an array nested exactly max_nesting_depth deep must parse, got: ${err.msg()}')
}
assert a.str().len > 0
obj := '{"a":'.repeat(max_nesting_depth) + '0' + '}'.repeat(max_nesting_depth)
o := parse(obj, DecoderOptions{}) or {
panic('an object nested exactly max_nesting_depth deep must parse, got: ${err.msg()}')
}
assert o.str().len > 0
}
fn test_nesting_over_max_nesting_depth_errors_instead_of_crashing() {
arr_bad := '['.repeat(max_nesting_depth + 1) + ']'.repeat(max_nesting_depth + 1)
if a := parse(arr_bad, DecoderOptions{}) {
// a successful parse here would mean the nesting guard regressed
// into a potential stack-overflow again
println('over-limit array parsed: ${a.str().len} chars')
assert false
} else {
assert err.msg().contains('maximum nesting depth')
}
obj_bad := '{"a":'.repeat(max_nesting_depth + 1) + '0' + '}'.repeat(max_nesting_depth + 1)
if a := parse(obj_bad, DecoderOptions{}) {
println('over-limit object parsed: ${a.str().len} chars')
assert false
} else {
assert err.msg().contains('maximum nesting depth')
}
}
// nested values inside a document guarded on either side, deepest last
fn test_deeply_nested_value_inside_a_document_is_rejected() {
prefix := '{"key": [1, 2, '
suffix := ', 3]}'
body := '['.repeat(max_nesting_depth + 1) + ']'.repeat(max_nesting_depth + 1)
doc := prefix + body + suffix
if a := parse(doc, DecoderOptions{}) {
println('deep nested document parsed: ${a.str().len} chars')
assert false
} else {
assert err.msg().contains('maximum nesting depth')
}
}
// mixed_nesting_string alternates array and object containers so the shared
// counter counts both forms in one chain.
fn mixed_nesting_string(levels int) string {
if levels <= 0 {
return '0'
}
mut parts := []string{}
for i in 0 .. levels {
parts << if i % 2 == 0 { '[' } else { '{"k":' }
}
parts << '0'
for i := levels - 1; i >= 0; i-- {
parts << if i % 2 == 0 { ']' } else { '}' }
}
return parts.join('')
}
fn test_mixed_array_and_object_nesting_at_both_boundaries() {
at_limit := parse(mixed_nesting_string(max_nesting_depth), DecoderOptions{}) or {
panic('mixed array/object nesting exactly at the limit must parse: ${err.msg()}')
}
assert at_limit.str().len > 0
if a := parse(mixed_nesting_string(max_nesting_depth + 1), DecoderOptions{}) {
println('over-limit mixed nesting parsed: ${a.str().len} chars')
assert false
} else {
assert err.msg().contains('maximum nesting depth')
}
}
// sibling containers sit at the same level: the deferred decrements must return
// the counter between siblings, or every sibling after the first would be
// rejected. Each of the three reaches exactly max_nesting_depth.
fn test_sibling_containers_do_not_accumulate_depth() {
sub := '['.repeat(max_nesting_depth - 1) + '0' + ']'.repeat(max_nesting_depth - 1)
a := parse('[${sub},${sub},${sub}]', DecoderOptions{}) or {
panic('three maximal siblings must parse without depth accumulation: ${err.msg()}')
}
assert a.str().len > 0
}
// a bounded nesting error must not poison the reused buffer of decode_reuse
fn test_decode_reuse_recovers_after_a_depth_error() {
mut buffer := DecodeBuffer{}
over := mixed_nesting_string(max_nesting_depth + 1)
if v := decode_reuse[Any](over, mut buffer, DecoderOptions{}) {
println('over-limit reuse input parsed: ${v.str().len} chars')
assert false
} else {
assert err.msg().contains('maximum nesting depth')
}
shallow := '{"a":[1,{"b":[0]}]}'
norm := decode_reuse[Any](shallow, mut buffer, DecoderOptions{}) or {
panic('a reused buffer must recover after a bounded error: ${err.msg()}')
}
assert norm.str().replace(' ', '') == shallow
}