test(encoding/utf8): add a spec-oracle QuickCheck suite - #4065
Merged
Conversation
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>
Contributor
There was a problem hiding this comment.
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.pkgto include themoonbitlang/core/quickchecktest 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.
Collaborator
Coverage Report for CI Build 6083Coverage remained the same at 90.714%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
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.
Pins
decode,decode_lossy, andencodeagainst an oracle transcribed from the Unicode 16.0 core spec rather than from the implementation:lead_classis Table 3-7 (the well-formed UTF-8 byte sequences);scanis 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_scalaris 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 platformTextDecoder/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:
C0/C1), every out-of-range lead (F5..FF), every bare continuation byte, every truncated 2-byte prefix;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
decodeanddecode_lossy;encode/decodeanddecode/encoderound-trips — the latter pins injectivity, which an accepted overlong form would break;decode_lossyoutput is always itself well-formed;ignore_bomare inverses, andignore_bomstrips only one leading BOM;Malformedoffset contract under truncation — the reported suffix must start at the lead byte, not where the scanner noticed;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