Skip to content

test(fuzz): v2 lazy value-equality differential + Phase 2 walker #72

@membphis

Description

@membphis

Background

Part of the qjson fuzz harness (#63). #63 lands the shared fuzz/ crate
scaffolding, the nightly CI job, and fuzz_parse_eager (accept/reject
differential). This issue is the highest-payoff follow-up.

Phase 1 (scan + validation) already has differential coverage
(scanner_crosscheck.rs, json_test_suite.rs, rfc8259_compliance.rs).
Phase 2 — cursor walk, skip cache, lazy decode — has no property /
differential / fuzz coverage
, and it is the most offset-arithmetic-heavy
code in the crate (find_value_span, scalar_byte_range, nth_object_entry,
the +1/+2/+3 index conventions, the cold/warm skip-cache paths).

Depends on #63 (needs the fuzz/ crate + nightly CI from that issue).

Goal

Put a correctness oracle on Phase 2 via value-equality differential testing
against serde_json
. For every input that serde_json accepts, decode the
entire qjson document through the public cursor API and assert deep equality
with serde_json::Value.

This oracle is strictly stronger than v1's accept/reject: it catches
"accepted but decoded wrong" (off-by-one in a span, a mis-decoded escape, a
wrong number) that accept/reject can never see.

Scope (this issue = one PR)

  • Add fuzz_parse_lazy fuzz target.
  • Implement a doc-walker that recursively visits every path and decodes
    each leaf (string / number / bool / null) into a serde_json::Value
    using the cursor API (qjson_cursor_* / object_entry_at).
  • Compare the reconstructed Value against serde_json::from_slice
    with deep equality; numbers compared bit-exact as f64.
  • Exercise both skip-cache paths: do repeated, varied-order sibling
    lookups per container so the warm (cached) path is covered, not just
    the cold (populate) path.
  • Extend the shared corpus with Phase-2-exercising seeds.

Acceptance Criteria

  • cargo +nightly fuzz run fuzz_parse_lazy builds and runs under
    -max_total_time=60.
  • On serde-accepted inputs, the reconstructed value equals
    serde_json::Value (modulo documented duplicate-key / number
    normalization, mirroring test: fuzz harness (cargo-fuzz / libFuzzer) #63's allowlist).
  • The walker provably exercises the warm skip-cache path (varied-order
    re-lookups), not only the cold path.

Notes

  • Lazy mode deliberately skips value-level validation, so accept/reject
    differential does not apply here — only value comparison on
    serde-accepted inputs.
  • Duplicate-key value semantics must be aligned with serde (last-wins vs
    first-wins) or normalized before comparison.

Affected files

  • fuzz/fuzz_targets/fuzz_parse_lazy.rs (new)
  • fuzz/ shared walker helper if factored out

Part of #63. Depends on #63.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions