Skip to content

test(encoding/utf8): add a spec-oracle QuickCheck suite - #4065

Merged
bobzhang merged 1 commit into
mainfrom
agent/utf8-spec-quickcheck
Aug 15, 2026
Merged

test(encoding/utf8): add a spec-oracle QuickCheck suite#4065
bobzhang merged 1 commit into
mainfrom
agent/utf8-spec-quickcheck

Conversation

@bobzhang

Copy link
Copy Markdown
Contributor

Pins decode, decode_lossy, and encode against an oracle transcribed from the Unicode 16.0 core spec rather than from the implementation:

  • lead_class is Table 3-7 (the well-formed UTF-8 byte sequences);
  • scan is D93b, the maximal subpart of an ill-formed subsequence rule — it fixes both how many U+FFFD a lossy decoder must emit and where a strict decoder must report the failure;
  • push_scalar is the closed-form encoder from the same chapter.

An independent oracle matters here because the package carries two implementations selected by target — a hand-written scanner (decode_nonjs.mbt) and the platform TextDecoder/TextEncoder (decode_js.mbt) — and the scanner and encoder are additionally #intrinsic, so a backend may substitute its own code generation for the MoonBit body entirely. The spec is the only thing all of those must agree with.

Coverage

Exhaustive where the input space allows:

  • every 1- and 2-byte string (256 + 65536 inputs), which settles Table 3-7's first two columns outright: every overlong lead (C0/C1), every out-of-range lead (F5..FF), every bare continuation byte, every truncated 2-byte prefix;
  • every 3- and 4-byte string over a boundary alphabet containing all of Table 3-7's range boundaries, so an off-by-one bound — or a forgotten surrogate (ED 80..9F) or overlong (E0 A0..BF, F0 90..BF) carve-out — fails.

Property-based elsewhere, with a generator biased toward near misses (boundary bytes, class-edge scalars, truncated encodings). Uniform random bytes would almost never form a valid multi-byte sequence and would therefore exercise only the reject-immediately path.

Properties

  • oracle agreement for both decode and decode_lossy;
  • encode/decode and decode/encode round-trips — the latter pins injectivity, which an accepted overlong form would break;
  • decode_lossy output is always itself well-formed;
  • lossy agrees with strict wherever strict succeeds;
  • per-scalar byte lengths, over every boundary scalar plus a strided sweep of the whole range;
  • encoding is a homomorphism from concatenation;
  • BOM emission and ignore_bom are inverses, and ignore_bom strips only one leading BOM;
  • the Malformed offset contract under truncation — the reported suffix must start at the lead byte, not where the scanner noticed;
  • view-offset independence.

Result

All 13 tests pass on wasm, wasm-gc, js and native. No divergence found — the suite is a lock, not a bug report.

🤖 Generated with Claude Code

Pins `decode`, `decode_lossy`, and `encode` against an oracle
transcribed from the Unicode 16.0 core spec rather than from the
implementation: `lead_class` is Table 3-7 (the well-formed UTF-8 byte
sequences), `scan` is D93b (the maximal-subpart rule that fixes how
many U+FFFD a lossy decoder emits and where a strict decoder reports
the failure), and `push_scalar` is the closed-form encoder.

An independent oracle matters here because the package carries two
implementations selected by target -- a hand-written scanner
(decode_nonjs.mbt) and the platform TextDecoder/TextEncoder
(decode_js.mbt) -- and the scanner and encoder are additionally
`#intrinsic`, so a backend may substitute its own code generation for
the MoonBit body. The spec is the only thing all of them must agree
with.

Coverage is exhaustive where the input space allows -- every 1- and
2-byte string (settling every overlong lead, every out-of-range lead,
every bare continuation byte and every truncated 2-byte prefix), and
every 3- and 4-byte string over an alphabet containing all of Table
3-7's range boundaries. Elsewhere it is property-based, with a
generator biased toward near misses (boundary bytes, class-edge
scalars, truncated encodings) rather than uniform random bytes, which
would almost never form a valid multi-byte sequence and so would
exercise only the reject-immediately path.

Properties: oracle agreement for both decoders; encode/decode and
decode/encode round-trips (the latter pins injectivity, which an
accepted overlong form would break); decode_lossy output is always
itself well-formed; lossy agrees with strict wherever strict succeeds;
per-scalar byte lengths; concatenation homomorphism; BOM emission and
ignore_bom as inverses; the `Malformed` offset contract under
truncation; and view-offset independence.

All 13 tests pass on wasm, wasm-gc, js and native.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 15, 2026 07:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a comprehensive UTF-8 specification-oracle test suite that validates @utf8.decode, @utf8.decode_lossy, and @utf8.encode against rules transcribed from the Unicode 16.0 core specification (Table 3-7 + maximal-subpart rule), ensuring consistent behavior across the JS/non-JS implementations and any backend intrinsic substitutions.

Changes:

  • Introduces encoding/utf8/quickcheck_test.mbt, combining an independent spec oracle with both exhaustive boundary enumeration and property-based generation.
  • Adds round-trip, lossy/strict consistency, BOM semantics, truncation offset-contract, and view-offset-independence properties.
  • Updates encoding/utf8/moon.pkg to include the moonbitlang/core/quickcheck test dependency.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
encoding/utf8/quickcheck_test.mbt New Unicode-spec-driven oracle + exhaustive and QuickCheck properties for UTF-8 decode/encode behavior across strict/lossy/BOM/truncation/view-offset rules.
encoding/utf8/moon.pkg Adds moonbitlang/core/quickcheck as a test import to support the new suite.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@coveralls

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 6083

Coverage remained the same at 90.714%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 17650
Covered Lines: 16011
Line Coverage: 90.71%
Coverage Strength: 155168.58 hits per line

💛 - Coveralls

@bobzhang
bobzhang merged commit c5772c8 into main Aug 15, 2026
20 checks passed
@bobzhang
bobzhang deleted the agent/utf8-spec-quickcheck branch August 15, 2026 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants