Skip to content

refactor(qc): replace moonbitlang/quickcheck with core's QuickCheck Mini - #122

Open
bobzhang wants to merge 2 commits into
mainfrom
core-quickcheck
Open

refactor(qc): replace moonbitlang/quickcheck with core's QuickCheck Mini#122
bobzhang wants to merge 2 commits into
mainfrom
core-quickcheck

Conversation

@bobzhang

@bobzhang bobzhang commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Evaluates and completes the switch from the third-party moonbitlang/quickcheck@0.14.0 test dependency to the QuickCheck framework that now ships in core (moonbitlang/core#3955, "QuickCheck Mini"). Verdict: core covers all of this repo's use cases — the dependency is dropped from moon.mod.

What mapped directly

  • Gen@qc.Generator (bindflat_map, fmapmap; one_of/frequency/int_range/sized/scale/array_with_size are identical, and int_range is half-open in both)
  • @qc.Shrink@shrink.Shrink — core ships the same String/Int64/Bool/Array instances the shrinkers used
  • forall_shrink + quick_check_silenceArbitrary/Shrink impls for SimpleDocument + @qc.check(count=2000, max_size=12) (deterministic, seeded)

What core lacks, and the workarounds

  • char_range/liftA2/liftA3: ~25 lines of local helpers in generator.mbt
  • counterexample: the property raises fail(message) carrying the rendered TOML; check reports it plus the Debug-printed shrunk counterexample
  • classify coverage stats: replaced by a separate fixed-seed snapshot test tallying the eight contains_* predicates over 2000 samples — distribution matches the old classification output (~33% tables vs 29%, ~67% arrays vs 61%)

Structurally, the generators/shrinkers moved from test-only files into the package proper (generator.mbt, shrink.mbt) so the trait impls satisfy coherence; qc.md is updated.

Caveat

Core's QuickCheck Mini merged 2026-08-03 and is marked WIP upstream — if the stable-channel CI toolchain predates it, the stable job will fail and this should wait for the next stable release.

Test plan

  • moon test internal/qc_model — 2/2 pass (2000-case round-trip via @qc.check + coverage snapshot)
  • moon test — 412 passed, 0 failed
  • moon check / moon fmt / moon info — clean

🤖 Generated with Claude Code

The QuickCheck framework that now ships in moonbitlang/core
(moonbitlang/core#3955) covers everything the round-trip test needs, so
the third-party moonbitlang/quickcheck@0.14.0 dependency is dropped.

The generators move from gen_test.mbt into generator.mbt as an Arbitrary
impl for SimpleDocument (plus small char_range/lift2/lift3 helpers core
does not provide), the shrinkers move from shrink_test.mbt into
shrink.mbt as a Shrink impl, and the property is driven by @qc.check.
Since the core driver has no classify combinator, generator coverage is
snapshotted separately from a fixed-seed sample; the distribution
matches the old classification stats (~33% tables, ~67% arrays).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bobzhang

bobzhang commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

CI confirms the caveat: nightly passes, stable fails with Cannot find import 'moonbitlang/core/quickcheck/shrink' — the stable toolchain's core predates moonbitlang/core#3955. The migration is complete and verified on nightly; merge once QuickCheck Mini reaches the stable channel (re-run CI to check).

Adapts the migration to what actually merged upstream:

- Local char_range/lift2/lift3 helpers are gone — generators now use
  @qc.char_range and the zip/zip_with/zip_with3 combinators (core#3977).
- The round-trip property and the coverage snapshot merge into a single
  @qc.report run using the observations API (core#3980): observe
  classifies every accepted case and the whole report is snapshotted,
  so pass status and structural coverage live in one deterministic
  check. The hand-rolled fixed-seed tally test is removed.
- With core#3979, generated strings now include multi-byte and non-BMP
  characters; all 2000 round-trip cases pass under the wider
  distribution.

Requires a core snapshot newer than the 2026-08-06 nightly (validated
locally via MOON_CORE_OVERRIDE against core main at 2da357d2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bobzhang

bobzhang commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto the APIs that actually landed in core main: local char_range/lift2/lift3 helpers are gone in favor of @qc.char_range + zip/zip_with/zip_with3 (core#3977), and the property test + coverage tally merged into a single @qc.report run using the observations API (core#3980) — observe classifies every accepted case and the whole report is snapshotted. With core#3979's Unicode strings, all 2000 round-trip cases still pass under the wider distribution.

Validated locally against core main (2da357d2) via MOON_CORE_OVERRIDE; CI stays red until a toolchain bundles a core snapshot ≥ 2026-08-06 (the current nightly predates the merges).

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.

1 participant