Skip to content

test(base64): add QuickCheck round-trip properties - #4007

Open
Yu-zh wants to merge 1 commit into
mainfrom
test/base64-quickcheck-roundtrip
Open

test(base64): add QuickCheck round-trip properties#4007
Yu-zh wants to merge 1 commit into
mainfrom
test/base64-quickcheck-roundtrip

Conversation

@Yu-zh

@Yu-zh Yu-zh commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Adds encoding/base64/quickcheck_test.mbt with five property-based round-trip tests, plus a test-only quickcheck import in encoding/base64/moon.pkg. No change to pkg.generated.mbti.

All properties are driven by arbitrary Bytes and anchored on the encoder's output:

property claim
encode then decode is the identity decode(encode(b)) == b
… without padding decode(encode(b, padding=false)) == b
padded and unpadded encodings decode alike padding is presentational
whitespace anywhere survives the round trip arbitrary ASCII whitespace spliced at arbitrary positions — including between the two = and after them — still decodes with ignore_whitespace=true
decode_lossy round trips through invalid characters arbitrary non-alphabet, non-padding, non-whitespace characters spliced in are skipped by decode_lossy

The last two use a splice helper driven by a generated Array[(Int, Int)] of (position, char-choice) pairs, so the number and placement of perturbations are themselves generated and shrink with the counterexample. Indices are wrapped into range with wrap_index rather than filtered, so the driver never discards a case.

Verification

  • moon test -p moonbitlang/core/encoding/base64 → 16 passed, 0 failed
  • moon check encoding/base64 → clean
  • moon info && moon fmt.mbti unchanged (test-only change)
  • Mutation check: temporarily dropping ignore_whitespace=true and adding a false bytes.length() < 3 conjunct made both properties fail with shrunk counterexamples ((<Bytes: []>, [(0, 0)], false) and a 3-byte case), confirming the properties are not vacuous and that the spliced edits really reach the decoder.

Five properties anchored on the encoder's output, all driven by
arbitrary `Bytes`:

- `decode(encode(b)) == b`, padded and unpadded
- padded and unpadded encodings decode to the same bytes
- arbitrary ASCII whitespace spliced at arbitrary positions (including
  between the two `=`) still decodes with `ignore_whitespace=true`
- arbitrary non-alphabet characters spliced in are skipped by
  `decode_lossy`

The perturbing splices are driven by a generated `Array[(Int, Int)]` of
(position, char-choice) pairs, so placement and count shrink along with
the input. Indices are wrapped into range rather than filtered, so no
case is discarded.
@coveralls

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 5849

Coverage increased (+0.03%) to 90.322%

Details

  • Coverage increased (+0.03%) from 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: 17669
Covered Lines: 15959
Line Coverage: 90.32%
Coverage Strength: 129215.99 hits per line

💛 - Coveralls

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.

2 participants