test(runtime): cover JSON escapes end to end and pin the i64 boundary pair - #3305
Open
nankingjing wants to merge 1 commit into
Open
nankingjing wants to merge 1 commit into
nankingjing wants to merge 1 commit into
Conversation
… pair Follow-up to the test groups promised on ultraworkers#3270. The earlier groups write escapes out and read values in, but never the two in the same test, which leaves both halves of the file narrower than they look. Escapes were only ever asserted against `render_string`. No test fed a backslash escape to the parser, so the parse side of `parse_escape` and `parse_unicode_escape` was unpinned: mutating `\n` or `\t` to any other character, stopping the unicode escape after three hex digits, switching `to_digit(16)` to base ten, or dropping the `\/` arm all left the suite green. The new groups drive those escapes through `parse`, assert the decoded value, and re-parse the rendered form so both directions are covered, including an object key, which goes through the same escaping. The only rejection test for out-of-range integers used a 25-digit literal. That is also satisfied by a much narrower integer type, so it pins "rejects absurdly large numbers" rather than "the accepted range is i64". The new boundary group asserts that i64::MAX and i64::MIN parse to their exact values and that one past either end is rejected, at both signs. Verified by mutation testing: each of the six mutations above fails the new tests, and the pre-existing tests stay green under all six.
|
The mutation-testing evidence is compelling — escapes only being asserted on renderer output while the parser never received a raw backslash escape is a genuine blind spot, and pinning the i64 boundary pair closes the other real gap. Thanks for making this a standalone branch off main. |
|
Thanks for the standalone second batch — mutation testing makes a strong case for both gaps. Just to confirm: the i64 boundary pair pins i64::MAX and i64::MIN specifically? And are u64/f64 boundaries covered elsewhere, or would those be a natural third batch? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3270, which added the first batch of
JsonValueparser/renderer unit tests. This is the second batch that was promised in review there: escape round-trips and the i64 boundary pair.It is a standalone branch off
mainand does not depend on #3270, #3273 or #3304.Why the earlier groups did not catch these
The two areas below were verified as real gaps by mutation testing against the current implementation on
main, not just by reading it.Escapes were only asserted on the renderer's output. Both existing tests that touch escapes (
escapes_control_characters, and the round-trip test in #3270) build their input fromrender_stringor from escape-free JSON, soparsewas never fed a single backslash escape. Mutating the parser left the whole suite green:mainparse_escape:'n' => '\n'becomes'n' => 'X'parse_escape:'t' => '\t'becomes't' => 'T'parse_escape:'/' => '/'becomes'/' => '!'parse_unicode_escape:0..4becomes0..3parse_unicode_escape:to_digit(16)becomesto_digit(10)parse_number:parse::<i64>()becomesparse::<i32>()widenedThe same six mutations also survive #3270's expanded 13-test suite, so this is not something that lands with that PR.
The rejection test did not pin the range.
parse_rejects_integers_outside_the_i64_rangeuses a 25-digit literal, which ani32-backed parser rejects too. It pins "rejects absurdly large numbers", not "the accepted range is i64".What is added
Four tests, appended to the existing module in
rust/crates/runtime/src/json.rs, same helpers and naming as the surrounding tests:parses_escape_sequences_inside_strings-\n,\t,\r,\b,\f,\/,\",\\throughparse, asserting the decoded value rather than the literal letter.parses_four_digit_unicode_escapes-Aandéin both hex cases, that all four digits belong to the escape, and that three digits, non-hex digits and a lone surrogate are rejected.round_trips_parsed_escapes_through_the_renderer- parse, render, re-parse and compare, over the escape set above, plus an object key carrying a newline so the key path is covered on both sides.parses_the_i64_boundary_and_rejects_the_values_past_it-9223372036854775807and-9223372036854775808parse toi64::MAXandi64::MINexactly, and9223372036854775808/-9223372036854775809are rejected.Verification
Local, on the GNU toolchain (
stable-x86_64-pc-windows-gnu, anaconda MinGW), against the implementation onmain:cargo test -p runtime --lib json::- 6 passed, 0 failed (2 pre-existing, 4 added).rustfmt --edition 2021 --checkclean;cargo clippy -p runtime --lib --testsreports nothing injson.rs.Two honest caveats:
action_requiredbehind the fork-approval gate - so this suite is not CI-verified. The results above are local only.mcp_stdio.rsandmcp_tool_bridge.rshave#[cfg(test)]modules usingstd::os::unix::fs::PermissionsExtwith nocfg(unix)gate, soset_modeis missing. To run the tests locally I applied a local-only, uncommitted no-opset_modeshim to those two files and reverted it afterwards; this PR touches none of that - it changes onlyrust/crates/runtime/src/json.rs, 116 added lines, 0 deletions.Because both this and #3270 append to the end of the same test module, the two will conflict textually if #3270 merges first. The resolution is to keep both blocks.