Skip to content

test(bytes): add QuickCheck property tests - #4042

Merged
bobzhang merged 5 commits into
mainfrom
agent/quickcheck-bytes
Aug 15, 2026
Merged

test(bytes): add QuickCheck property tests#4042
bobzhang merged 5 commits into
mainfrom
agent/quickcheck-bytes

Conversation

@bobzhang

Copy link
Copy Markdown
Contributor

Adds bytes/quickcheck_test.mbt: model-based QuickCheck property tests for immutable Bytes and BytesView, checked against plain Array[Byte] reference implementations. moonbitlang/core/quickcheck was already in the package's for "test" imports, so no moon.pkg change; .mbti is untouched.

Property families

  • Roundtripsfrom_array/to_array identity; makei agrees with its generating function; from_iter, iter, iter2, get/at all agree with indexed model reads (including out-of-bounds get returning None).
  • Concat / repeat(a + b).to_array() equals model concatenation; length is additive; associativity; b"" is a two-sided identity; repeat(n) equals n-fold concatenation.
  • View offset algebra (the focus) — for random valid (start, end) including empty/full/boundary slices: view contents equal the model slice element-for-element; data()/start_offset() bookkeeping is exact; view-of-view composes as b[i:j][k:l] == b[i+k : i+l] (verified both by Eq and against the model slice, plus a third nesting level); get_view mirrors the aborting slice on valid ranges and returns None on negative start / end past length / start > end; to_owned materializes exactly the viewed range; reading through views never disturbs the immutable parent.
  • Unsigned orderingCompare is shortlex and lexical_compare is pure lexicographic, both validated against unsigned-byte models with generation biased toward bytes >= 0x80 (where a signed-byte comparison bug would surface), plus an explicit 0x7F vs 0x80 boundary probe; a == b iff compare == 0 iff equal arrays; equal_to_bytes agrees with Eq.
  • Hash consistency — equal Bytes hash equally; bs.hash() == bs[:].hash() (exercises the specialized non-JS Bytes::hash fast path against the BytesView hasher path); equal-content views at different offsets in different backing buffers hash equally.
  • Searchfind/rev_find vs naive scans over a 0x7E..0x81 boundary alphabet, with needles sliced from the haystack (guaranteed hits), aliased needles viewing the same backing bytes, perturbed needles (model decides), searches inside offset views, empty-pattern semantics, and long needles over dense false-candidate haystacks to drive the long-scanner and Rabin–Karp fallback paths; has_prefix/has_suffix/chop_prefix/chop_suffix vs the model, including through nonzero-offset views.

Verification

  • moon check clean; moon info && moon fmt produce no .mbti or formatting diffs.
  • moon test -p moonbitlang/core/bytes passes on wasm-gc, wasm, js, native (148 tests each).
  • Locally swept seeds 1, 987654321, 20260814 at max_size=200 across backends before settling on the default seed — all passed.

Bugs found

None. The stressed areas (view offset bookkeeping under nesting and aliasing, unsigned comparison above 0x80, per-backend SIMD/scalar/Rabin–Karp search paths, hash fast-path consistency) all behaved per their documented semantics on every backend.

🤖 Generated with Claude Code

Property families:
- from_array/to_array/makei/from_iter/iter/iter2 roundtrips
- concat associativity, empty identity, additive length/contents; repeat
- views vs array-model slices, boundary/empty/full views, get_view
  rejection of invalid ranges, immutability of the parent
- view-of-view offset composition (b[i:j][k:l] == b[i+k:i+l])
- shortlex Compare and lexicographic lexical_compare vs unsigned models,
  biased toward bytes >= 0x80 to expose signed-byte comparison bugs
- hash consistency: equal Bytes, Bytes vs full view, equal-content views
  at different offsets in different backings
- find/rev_find vs naive scans over a 0x7E-0x81 boundary alphabet, on
  owned bytes, aliased needles, and offset views; long needles driving
  the long-scanner/Rabin-Karp paths
- has_prefix/has_suffix/chop_prefix/chop_suffix vs the model

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 14, 2026 08:17
@bobzhang
bobzhang enabled auto-merge (squash) August 14, 2026 08:17
@coveralls

coveralls commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 6040

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage remained the same at 90.29%

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: 17642
Covered Lines: 15929
Line Coverage: 90.29%
Coverage Strength: 133985.55 hits per line

💛 - Coveralls

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 new MoonBit QuickCheck test suite for Bytes / BytesView, using Array[Byte] as a reference model to validate a broad set of semantic properties (roundtrips, concatenation/repeat, view offset algebra, ordering/hash consistency, and search/prefix/suffix behavior) across backends.

Changes:

  • Introduces model helper functions (imod, pick_range, slice/ordering/search models) to define backend-independent expectations.
  • Adds property tests covering Bytes/BytesView construction, iteration/indexing, slicing/view composition, ordering semantics, hashing consistency, and search/prefix/suffix APIs.
  • Adds targeted generators (boundary-crossing alphabets and biased byte tweaks) to stress byte-sign boundaries and dense match patterns.

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

Comment thread bytes/quickcheck_test.mbt
Comment on lines +330 to +332
let mutated = needle.copy()
mutated[imod(a, mutated.length())] = b'\x00'
let mb = Bytes::from_array(mutated)
Comment thread bytes/quickcheck_test.mbt
let len = hay.length()
let ns = imod(a, len + 1)
// Prefer needles longer than the 32-byte short-pattern cutoff.
let ne = ns + imod(b, len - ns + 1)
@bobzhang
bobzhang merged commit 4ddaf53 into main Aug 15, 2026
19 checks passed
@bobzhang
bobzhang deleted the agent/quickcheck-bytes branch August 15, 2026 01:00
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