Ctrl K
ComunidadGuías…

vlib/json2/checker_depth_test.v

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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
}