Skip to content

QuickCheck Mini (WIP) - #3955

Merged
CAIMEOX merged 16 commits into
moonbitlang:mainfrom
CAIMEOX:caimeo/qcmini
Aug 3, 2026
Merged

QuickCheck Mini (WIP)#3955
CAIMEOX merged 16 commits into
moonbitlang:mainfrom
CAIMEOX:caimeo/qcmini

Conversation

@CAIMEOX

@CAIMEOX CAIMEOX commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator
  • Introduce a mini property-based testing framework
  • trait Shrink and instances for common used data types
  • support simple property and filter

Copilot AI review requested due to automatic review settings July 29, 2026 02:50
@CAIMEOX
CAIMEOX marked this pull request as draft July 29, 2026 02:50

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 core building blocks for a “QuickCheck Mini (WIP)” in moonbitlang/core, introducing a shrinker API and a size-aware generator API, plus tuple/list instances and corresponding package wiring.

Changes:

  • Introduces @quickcheck/shrink with a Shrink trait, primitive/composite shrinkers, and tests.
  • Adds @quickcheck/gen providing a Gen[T] generator type with common combinators and tests.
  • Extends tuple/list packages with Shrink instances, updates tuple Arbitrary coverage to 8/9-tuples, and updates moon.pkg / generated .mbti interfaces accordingly.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tuple/tuple_shrink.mbt Adds @shrink.Shrink impls for tuples (2–9).
tuple/tuple_shrink_test.mbt Adds snapshot tests validating tuple shrinking behavior.
tuple/tuple_arbitrary.mbt Adds @quickcheck.Arbitrary impls for 8- and 9-tuples.
tuple/pkg.generated.mbti Exposes new tuple Shrink impls and imports quickcheck/shrink.
tuple/moon.pkg Adds moonbitlang/core/quickcheck/shrink dependency.
tuple/extends.mbt Adds deprecated extend entries to avoid promoting Shrink methods as inherent tuple methods.
quickcheck/shrink/utils.mbt Adds shared helpers for shrinkers (array chunk removal + decimal shrinking).
quickcheck/shrink/shrink.mbt Defines Shrink trait plus primitive shrinker impls and docs.
quickcheck/shrink/shrink_test.mbt Adds tests for primitive shrinkers.
quickcheck/shrink/composite.mbt Adds composite shrinkers for Option, Result, Array, and Iter.
quickcheck/shrink/composite_test.mbt Adds tests for composite shrinkers.
quickcheck/shrink/pkg.generated.mbti Generated interface for the new quickcheck/shrink package.
quickcheck/shrink/moon.pkg Declares quickcheck/shrink package imports and warning config.
quickcheck/gen/gen.mbt Implements Gen[T] and generator combinators (map, flat_map, one_of, frequency, etc.).
quickcheck/gen/gen_test.mbt Adds tests for generator behavior and combinators.
quickcheck/gen/moon.pkg Declares quickcheck/gen package imports.
quickcheck/gen/pkg.generated.mbti Generated interface for the new quickcheck/gen package.
list/shrink.mbt Adds @shrink.Shrink impl for List[T].
list/pkg.generated.mbti Exposes new list Shrink impl and imports quickcheck/shrink.
list/moon.pkg Adds moonbitlang/core/quickcheck/shrink dependency.
list/list_test.mbt Adds snapshot test for list shrinking.
list/extends.mbt Adds deprecated extend entry to avoid promoting Shrink methods as inherent list methods.
Comments suppressed due to low confidence (2)

list/list_test.mbt:1220

  • After fixing list shrinking to include suffix removals, shrinking [1,2,3,4,5,6] should also include removing the last chunk of size 3 (<List: [1, 2, 3]>).
      #|  <List: [4, 5, 6]>,

quickcheck/shrink/composite_test.mbt:74

  • After fixing array chunk removal to allow suffix removal, shrinking [1,2,3,4,5,6] should also include removing the last chunk of size 3 ([1, 2, 3]).
      #|  [0, 2, 3, 4, 5, 6],

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread quickcheck/shrink/shrink.mbt Outdated
Comment thread tuple/tuple_shrink_test.mbt Outdated
Comment thread list/shrink.mbt Outdated
Comment thread quickcheck/shrink/utils.mbt Outdated
Comment thread list/list_test.mbt Outdated
Comment thread quickcheck/shrink/composite_test.mbt Outdated
@CAIMEOX
CAIMEOX requested a review from Copilot July 30, 2026 03:30

This comment was marked as low quality.

@CAIMEOX
CAIMEOX marked this pull request as ready for review July 30, 2026 10:39
@CAIMEOX
CAIMEOX requested a review from bobzhang July 31, 2026 02:37
Comment thread quickcheck/pkg.generated.mbti Outdated
Comment thread quickcheck/pkg.generated.mbti Outdated
Comment thread quickcheck/pkg.generated.mbti Outdated
Comment thread quickcheck/pkg.generated.mbti Outdated
Comment thread quickcheck/gen/pkg.generated.mbti Outdated
@CAIMEOX
CAIMEOX requested a review from bobzhang August 3, 2026 02:34
@CAIMEOX
CAIMEOX merged commit 0037923 into moonbitlang:main Aug 3, 2026
18 of 19 checks passed
@bobzhang

bobzhang commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Feedback from migrating a real project to this package: moonbit-community/toml-parser#122 replaced all of its moonbitlang/quickcheck@0.14.0 usage (custom generators, custom shrinkers, a 2000-case round-trip property with coverage classification) with this package. The core design held up well — the typeclass-driven check/report driver is cleaner than explicit forall_shrink plumbing, deterministic seeding is snapshot-friendly, and the Shrink instances covered everything we used. That said, the migration surfaced some gaps worth addressing while the API is still WIP:

  1. No coverage classification. Classic QuickCheck's classify/label is how you notice that a generator quietly stopped exercising the interesting regions of the input space. We had to hand-roll a fixed-seed sample tally to keep that safety net. Suggestion: a classify? : Array[(String, (A) -> Bool)] parameter on report whose counts come back in Passed(labels~).

  2. QuickCheckReport is abstract outside the package. report returns a structured result that callers cannot destructure — the only way to consume it is via its Debug rendering, and even this package's own black-box tests resort to debug_inspect everywhere. Making the enum pub (matchable, not constructible) would let tests assert on Passed/Falsified structurally.

  3. No way to attach counterexample context. The property can only return Bool. Attaching human-readable context (in our case, the rendered TOML document) requires raising fail(msg), which lands in Raised and conflates "property errored" with "property failed, with context". Either a counterexample-style mechanism or documenting the raise pattern as the blessed idiom would help.

  4. Missing small combinators. char_range and applicative combines (zip/zip_with/zip_with3) are absent; every nontrivial generator ends up re-deriving them from flat_map/map (our migration needed them ~15 times).

  5. Arbitrary for Char is ASCII-only, and String inherits that. For property testing, multi-byte and non-BMP characters are exactly where string-handling bugs hide; an ASCII-biased distribution that regularly emits arbitrary Unicode scalar values (never surrogates) would be a better default. Relatedly, the internal unsafe_to_char uses could be replaced by validated construction.

  6. Minor: getting a Generator[T] from an Arbitrary instance (the old Gen::spawn) is possible via Generator(Arbitrary::arbitrary) but non-obvious — worth a documented helper.

A patch implementing 1, 2, 4, and 5 is prepared (all 7015 core tests pass with seeded snapshots updated) and will be submitted as a follow-up PR.

🤖 Generated with Claude Code

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