{"id":28581249,"url":"https://github.com/moonbitlang/quickcheck","last_synced_at":"2026-01-30T22:36:40.077Z","repository":{"id":250855982,"uuid":"835636037","full_name":"moonbitlang/quickcheck","owner":"moonbitlang","description":"Automatic testing of MoonBit programs","archived":false,"fork":false,"pushed_at":"2026-01-21T10:07:26.000Z","size":339,"stargazers_count":28,"open_issues_count":6,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-21T14:42:32.916Z","etag":null,"topics":["property-based-testing","test"],"latest_commit_sha":null,"homepage":"","language":"MoonBit","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moonbitlang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-07-30T08:29:13.000Z","updated_at":"2026-01-21T10:07:29.000Z","dependencies_parsed_at":"2025-12-09T08:07:55.339Z","dependency_job_id":null,"html_url":"https://github.com/moonbitlang/quickcheck","commit_stats":null,"previous_names":["moonbitlang/quickcheck"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/moonbitlang/quickcheck","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moonbitlang%2Fquickcheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moonbitlang%2Fquickcheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moonbitlang%2Fquickcheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moonbitlang%2Fquickcheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moonbitlang","download_url":"https://codeload.github.com/moonbitlang/quickcheck/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moonbitlang%2Fquickcheck/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28921393,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T22:32:35.345Z","status":"ssl_error","status_checked_at":"2026-01-30T22:32:31.927Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["property-based-testing","test"],"created_at":"2025-06-11T04:16:33.886Z","updated_at":"2026-01-30T22:36:40.061Z","avatar_url":"https://github.com/moonbitlang.png","language":"MoonBit","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MoonBit QuickCheck\n\n## Introduction\n\nThe idea of QuickCheck was originally introduced in John's paper [_QuickCheck: a lightweight tool for random testing of Haskell programs_](https://doi.org/10.1145/351240.351266) and its Haskell derivation QuickCheck, which aimed at simplifying the writing of tests by generating them. The core is to automatically generate tests based on a the signature of the **specification / theorem** (i.e. properties which functions should satisfy) and QuickCheck then tests that the properties hold. The idea spread to other languages and is now implemented in **MoonBit**. Because of the differences in type systems between Haskell and MoonBit, the original idea morphed into something different. There are several differences to this approach:\n\n- We have discarded many ideas in Haskell QuickCheck that have been proven inappropriate by time.\n- Some features requires a very fancy type system were removed (Or not considered currently). For instance, the generation of functions requires GADT but MoonBit does not.\n- MoonBit QuickCheck brings many modern academic ideas into industrial practice (listed [below](#references)).\n\n---\n\n## Table of Contents\n\n- [MoonBit QuickCheck](#moonbit-quickcheck)\n  - [Introduction](#introduction)\n  - [Table of Contents](#table-of-contents)\n  - [Quick Start](#quick-start)\n    - [Installation](#installation)\n    - [First Look](#first-look)\n    - [Failure Diagnosis](#failure-diagnosis)\n    - [Traits](#traits)\n      - [Testable](#testable)\n      - [Arbitrary](#arbitrary)\n      - [Shrink](#shrink)\n  - [Advanced Topics](#advanced-topics)\n    - [QuickCheck Invalid Case](#quickcheck-invalid-case)\n    - [Generators](#generators)\n      - [Choosing between alternatives](#choosing-between-alternatives)\n      - [Size parameter](#size-parameter)\n      - [Default Generators](#default-generators)\n    - [Custom Generator](#custom-generator)\n    - [Conditional Properties](#conditional-properties)\n    - [Classifying Data](#classifying-data)\n  - [Application](#application)\n  - [Roadmap and Future Work](#roadmap-and-future-work)\n    - [Data Generation](#data-generation)\n    - [Property Verification](#property-verification)\n    - [Shrinking](#shrinking)\n  - [References](#references)\n\n## Quick Start\n\n### Installation\n\nFirstly, you need to install the MoonBit QuickCheck library. \nYou can do this by executing the following command:\n\n```sh\nmoon add moonbitlang/quickcheck\nmoon install\n```\n\nTo use the library, you need to import it in your `moon.pkg.json` file,\nand for convenience we give an alias `qc` to the library:\n\n```json\n{\n  \"import\": [{ \"path\": \"moonbitlang/quickcheck\", \"alias\": \"qc\" }]\n}\n```\n\n### First Look\n\nLet's start with something very simple. Suppose that we just wrote a function `reverse` which takes an array as argument and returns its reverse. We want to test its functionality by writing unit tests:\n\n```moonbit\ntest \"reverse\" {\n  inspect!(reverse(([] : Array[Int])), content=\"[]\")\n  inspect!(reverse([1, 2, 3]), content=\"[3, 2, 1]\")\n}\n```\n\nIs this enough to prove that this function is correct? No, the bugs may lies in the remaining untested code. But if we want more rigorous verification, we may need to write more test cases. For the complicated program, there would never be time to. How can we reduce the cost of testing? The key to doing so must be to generalize the test functions, so that each function covers not only one test case, but many. This is where QuickCheck comes in. We can write a property that `reverse` should hold for any array, and QuickCheck will generate a large number of random array and check that the property holds for all of them.\n\nBut what property should we write? We may notice that the reverse of the reverse of an array is the original array. So we can write a **property** (you can roughly think of it as a function `(T) -\u003e Bool`) that `reverse` should hold for any array:\n\n```moonbit\nfn prop_reverse_identity(arr : Array[Int]) -\u003e Bool {\n  reverse(reverse(arr)) == arr\n}\n```\n\nWe may consider the code logically as:\n\n$$\n\\forall x: \\text{Array[T]}.\\space \\text{reverse}(\\text{reverse}(x)) = x\n$$\n\nLooks good, now we can use QuickCheck to test this property,\nthis is easily archived by the `quick_check_fn` function:\n\n```moonbit\ntest {\n  @qc.quick_check_fn!(prop_reverse_identity)\n  // equivalent to quick_check!(Arrow(prop_reverse_identity))\n}\n```\n\nThe `quick_check_fn` function takes a `f: (T) -\u003e Bool`,\nwhere `T` satisfies some constraints (We will discuss this later),\nthe function first generates a bunch of random values with\ntype `T` and then calling the property function `f` with these values.\nThe results are then collected and will be reported later.\nIn this example, the input value `T = Array[Int]` and the boolean\nvalue `true` implies that the property holds for some particular values,\nand the `false` implies that the property does not hold for some values.\nWhen finding a counterexample, the QuickCheck will try to simplify it\nand then print it to the console. If all the tests passed, the following\nsentence will be printed to the console:\n\n```\n+++ [100/0/100] Ok, passed!\n```\n\nThe three number in the brackets are the number of passed, discarded and total tests\n(the default value of total tests is `100`).\n\n### Failure Diagnosis\n\nNow let's see another example: suppose we want to write a function\n`remove(arr : Array[Int], x : Int) -\u003e Array[Int]` that takes\nan array and an element and returns a new array with **all**\noccurrences of `x` removed. The intuitive implementation is to\nsearch for `x` in the array and remove it if found:\n\n```moonbit\nfn remove(arr : Array[Int], x : Int) -\u003e Array[Int] {\n  match arr.search(x) {\n    Some(i) =\u003e arr.remove(i) |\u003e ignore\n    None =\u003e ()\n  }\n  arr\n}\n```\n\nNow let's use QuickCheck to test this function.\nThe first thing we need to do is to write a property.\nWe may consider the following properties:\n\n- If an element was removed from the array, the length of the array should be less than or equal to the original array\n\n$$\n\\forall x:\\text{T}, a:\\text{Array[T]} . \\space\n\\text{length}(\\text{remove}(a,x)) \\leq \\text{length}(a)\n$$\n\n- If an element was removed from the array, the element should not exist in the array anymore\n\n$$\n\\forall x:\\text{T}, a:\\text{Array[T]} . \\space\nx\\not\\in\\text{remove}(a,x)\n$$\n\nNow we translate the first property into a MoonBit function (Note that the property function should have exactly **one** argument, but we can use tuple to pass multiple arguments):\n\n```moonbit\nfn prop_length_is_not_greater(iarr : (Int, Array[Int])) -\u003e Bool {\n  let (x, arr) = iarr\n  let len = arr.length()\n  remove(arr, x).length() \u003c= len\n}\n```\n\nRun QuickCheck, all tests passed. However, this property is not considered a _good_ property because it is can be fulfilled easily and hence the test is not very meaningful. Most Bugs may still exist in the function.\n\n```moonbit\ntest {\n  @qc.quick_check_fn!(prop_length_is_not_greater)\n}\n\n// +++ [100/0/100] Ok, passed!\n```\n\nThe later property is considered better,\nsimilarly we implement it in MoonBit:\n\n```moonbit\nfn prop_remove_not_presence(iarr : (Int, Array[Int])) -\u003e Bool {\n  let (x, arr) = iarr\n  remove(arr, x).contains(x).not()\n}\n\ntest {\n  @qc.quick_check_fn!(prop_remove_not_presence)\n}\n```\n\nThe QuickCheck reports a failure with a counterexample in the second line after 8 tests:\n\n```\n*** [8/0/100] Failed! Falsified.\n(0, [0, 0])\n```\n\nWhen `x = 0` and `arr = [0, 0]` the property does not hold.\nThe `remove` function should remove all occurrences of\n`x` in the array, but it only removes the first one.\nQuickCheck finds us a succinct counterexample that\ndirectly reveals the core of the problem.\nHow does it do this?\nIn practice QuickCheck generates a lot of complex samples\nand inputs them into the property function to test them,\nand if it finds a counterexample it **doesn't** report it immediately,\nbut **shrinks** accordingly to try to find a simpler counterexample.\nIn this example, as in many cases, the randomly generated test case\ncontains junk values that have nothing to do with the test failure itself.\nWhen the counterexample is found, qc will apply some predefined shrinkers\nto this counterexample and try to explore downwards for smaller counterexamples.\n\n### Traits\n\nWe've already discussed generation and shrinking,\nand this section will discuss how these functions are attached to a type.\nRemind the `quick_check_fn` function we used before, it is defined as follows\nin the MoonBit QuickCheck library:\n\n```moonbit\n///| path: src/driver.mbt\npub fn quick_check_fn[A : Arbitrary + Shrink + Show, B : Testable](\n  f : (A) -\u003e B\n) -\u003e Unit!Failure {\n  quick_check!(Arrow(f))\n}\n```\n\nLet's look at the type signature first.\nThe `quick_check_fn` function takes a function `f: (A) -\u003e B` as argument,\nwhere `A` should have implemented the `Arbitrary`, `Shrink` and `Show` traits,\nand `B` should have implemented the `Testable` trait.\nIn our examples `B` is fixed to a `Bool`, this is because Booleans\nimplement the `Testable` trait, which can actually be more flexible,\nwe will discuss this later, for now we focus on the constraints of `A`:\n\n- `Arbitrary` is a trait that generates random values of a type.\n- `Shrink` is a trait that allows a value of particular type to be shrunk to simpler ones.\n- `Show` is used for printing the counterexample when a test fails.\n\nThe function body\nIt takes a value of type that implemented `Testable` as argument and runs the test. This is an effectful function that may throw an failure to the MoonBit test driver when a test fails (Or give up). If success, it simply returns `Unit` with the sentence `Ok, passed!` printed to the console.\n\nThe body of the function contains a call to `quick_check` and wraps `f` in `Arrow`.\nAccording to the definition of `quick_check`, it requires a value `prop` of a type\n`P` that implements `Testable` as an argument, and under certain conditions:\nFor any type `P` implementing `Testable`,\nand `A` implementing `Arbitrary`, `Shrink` and `Show`,\nthe `Arrow[A, P]` type can be implemented as `Testable` as well.\nThe reader may wonder why we don't directly implement `(A) -\u003e P` as `Testable`\nbut instead use `Arrow[A, P]` as a wrapper.\nThe reason is simple: The MoonBit type system does not allow us to do so.\n\n```moonbit\npub fn quick_check[P : Testable](prop : P) -\u003e Unit!Failure\n\ntype Arrow[A, P] (A) -\u003e P\n\nimpl[P : Testable, A : Arbitrary + Shrink + Show] Testable for Arrow[A, P]\n```\n\n#### Testable\n\nAnother advantage of using `Arrow` is that it constructs a `Testable`,\nand QuickCheck provides a number of `Testable` combinators that can be\nused to modify a test's properties, such as `with_max_success`\nto modify the maximum number of successes:\n\n```moonbit\ntest {\n  @qc.quick_check!(\n    @qc.Arrow(prop_remove_not_presence) |\u003e @qc.with_max_success(1000),\n  )\n}\n\n///| Output: +++ [1000/0/1000] Ok, passed!\n```\n\nThere are many other combinators that can be used to modify the properties of a\ntest, check the document for `src/testable.mbt` for more details.\n\n#### Arbitrary\n\n`Arbitrary` is a trait indicating that a type has the ability\nto generate random values for given size and random number generator,\nwhich is defined in the MoonBit Core library with compiler level support.\nIt has a method `arbitrary` that takes a size and a random number\ngenerator and then returns a random value of the type.\n\n```moonbit\npub trait Arbitrary {\n  arbitrary(Int, RandomState) -\u003e Self\n}\n```\n\nThe vast majority of types in the core library already implement the\n`Arbitrary` trait, and users can also write specific instance for\ntheir own defined types or use the compiler's automatic derivation.\nFor example, we can derive the `Arbitrary` trait for our own `Nat` type:\n\n```moonbit\nenum Nat {\n  Zero\n  Succ(Nat)\n} derive(Arbitrary, Show)\n\ntest {\n  let nat_gen : @qc.Gen[Nat] = @qc.Gen::spawn()\n  let nats = nat_gen.samples(size=4)\n  inspect!(\n    nats,\n    content=\"[Succ(Succ(Succ(Zero))), Succ(Succ(Succ(Succ(Zero)))), Succ(Zero), Succ(Succ(Succ(Zero)))]\",\n  )\n}\n```\n\nFor any type that implements `Arbitrary`,\nyou can use the `Gen::spawn` function to get its generator.\nCalling the generator's method `samples` will generate some values of that type.\nNote that `Gen` is an internal type of QuickCheck,\nwhich will be automatically called by the `quick_check` function,\nnormally do not require the user to access it,\nhere we use it to show the behavior of `Arbitrary`.\n\n#### Shrink\n\n`Shrink` is a trait that shrinks a value to a simpler one.\nIt has a method `shrink` that takes a value and returns a iter\nof simpler values (lazily).\n\n```moonbit\npub trait Shrink {\n  shrink(Self) -\u003e Iter[Self]\n}\n```\n\nIf QuickCheck finds a set of values that fails a given property,\nit will try to make that value simpler than the original value by\ngetting the shrinks for the value and trying each one in turn to\ncheck that the property is still false. If it is, the smaller\nvalue becomes the new counterexample and the shrinking process\ncontinues with that value.\n\n## Advanced Topics\n\n### QuickCheck Invalid Case\n\nThis section summarizes some common mistakes in using QuickCheck:\n\n- Testing with external effects: If a property requires an external state, for example, the user's input, the global mutable and etc, you should pass the state as an argument to the property function. Otherwise, the property is not a pure function and the test is not repeatable.\n- Mutable arguments was unexpectedly modified: You should keep the arguments **immutable** in the property function. If you need to modify the arguments, you should make a copy of them.\n\n### Generators\n\nTest data is produced by test data generators. QuickCheck defines default generators for some often used types, but you can use your own, and will need to define your own generators for any new types you introduce (Or for simple types we can use the trivial definition by `derive(Arbitrary)`).\n\nGenerators have types of the form `Gen[T]`, which is a generator for values of type `T`. It was defined as a struct, it contains single field named `gen`, with type `(Int, RandomState) -\u003e T` (The first parameter is the size of the generated value, and the second is the random number generator), notice that this is similar to the `arbitrary` method in the `Arbitrary` trait:\n\n```moonbit\nstruct Gen[T] {\n  gen : (Int, RandomState) -\u003e T\n}\n```\n\nQuickCheck defines a series of useful methods for `Gen[T]`, for the most basic ones, we can use `Gen::new(f: (Int, RandomState) -\u003e T)` to create a generator from a function and run it by invoking the `run` method:\n\n```moonbit\nlet g : Gen[Int] = Gen::new(..) // Suppose we have a generator for Int\nlet x : Int = g.run(100, RandomState::new()) // Generate a random Int at size 100\n```\n\n`Gen[T]` was implemented as functor, applicative and monad which means you can compose it in many ways.\n\n```moonbit\nfn pure[T](val : T) -\u003e Gen[T]\nfn fmap[T, U](self : Gen[T], f : (T) -\u003e U) -\u003e Gen[U]\nfn ap[T, U](self : Gen[(T) -\u003e U], v : Gen[T]) -\u003e Gen[U]\nfn bind[T, U](self : Gen[T], f : (T) -\u003e Gen[U]) -\u003e Gen[U]\n```\n\nFor instance, you can use the `fmap` method to transform the generated value:\n\n```moonbit\nlet g : Gen[Int] = Gen::new(..)\nlet g2 : Gen[Int] = g.fmap(fn(x) { x + 1 })\nlet g3 : Gen[String] = g.fmap(fn(x) { x.to_string() })\n```\n\nOr create a dependent generator:\n\n```moonbit\nlet g : Gen[Int] = Gen::new(..)\nlet g2 : Gen[Int] = g.bind(fn(x : Int) {\n  if x == 0 {\n    Gen::pure(100)\n  } else {\n    Gen::pure(200)\n  }\n})\n```\n\nThe following documents explains some useful combinators for `Gen[T]`.\n\n#### Choosing between alternatives\n\nA generator may take the form `one_of` which chooses among the generators in the array with equal probability. For example, this generates a random boolean which is true with probability one half:\n\n```moonbit\nlet gen_bool : Gen[Bool] = one_of([pure(true), pure(false)])\n```\n\nIf you want to control the distribution of results using frequency instead. We have `frequency` which chooses a generator from the array randomly, but weighs the probability of choosing each alternative by the factor given. For example, this generates true in the probability of $4/5$.\n\n```moonbit\nlet gen_freq : Gen[Bool] = frequency([(4, pure(true)), (1, pure(false))])\n```\n\n#### Size parameter\n\nWe have pointed that test data generators have an size parameter. QuickCheck begins by generating small test cases, and gradually increases the size as testing progresses. Different test data generators interpret the size parameter in different ways: some _ignore_ it, some interprets it as an upper bound on the size of containers. Most generators defined in `Arbitrary` trait depends on the size parameter but most separated generator combinators does not.\n\nYou can obtain the value of the size parameter using `sized` combinator.\n\n```moonbit\npub fn sized[T](f : (Int) -\u003e Gen[T]) -\u003e Gen[T]\n```\n\nFor example, we can make a trivial generator for a list of integers with a given length:\n\n```moonbit\nlet gen : Gen[Int] = sized(fn { size =\u003e pure(size) })\nlet arr = Array::makei(20, fn { i =\u003e gen.sample(size=i) })\ninspect!(arr, content=\"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\")\n```\n\nThe purpose of size control is to ensure that test cases are large enough to reveal errors, while remaining small enough to test fast. Sometimes the default size control does not achieve this. So we have provided some tool functions like `resize` and `scale` (check the document for details) for this purpose.\n\n#### Default Generators\n\nQuickCheck defines default test data generators and shrinkers for some often used types (By trait `Arbitrary` and `Shrink`). You do not need to define or apply these explicitly for every property because QuickCheck can provide a property with appropriate generators and shrinkers for the property's arguments. But if you are required to do so, you can use the `forall` for explicitly universal quantification.\n\n```moonbit\nquick_check!(forall(spawn(), fn(x : List[Int]) { x.rev().rev() == x }))\n```\n\nNote that the `spawn` function is useful for creating `Gen[T]` from its arbitrary instance, in this example the type checker infers the type of the first argument in `forall` to be `Gen[List[T]]` from the type of the property function.\n\n```moonbit\nfn Gen::spawn[T : Arbitrary]() -\u003e Gen[T]\n```\n\n### Custom Generator\n\nRecall the `remove` function we want to test before. The QuickCheck do report an error for us, but the generator is quite random and the tuple element `(x, arr): (Int, Array[Int])` is independent. In most cases, the `x` is not in the array, so the test is not very meaningful (falls to the branch `None =\u003e ()`). Now we use the `one_of_array` combinator to generate a random element from a generated non-empty array, which makes the test more meaningful.\n\n```moonbit\ntest {\n  quick_check!(\n    forall(\n      spawn(),\n      fn(a : Array[Int]) {\n        forall(one_of_array(a),\n          fn(y : Int) { remove(a, y).contains(y).not() })\n        |\u003e filter(a.length() != 0)\n      },\n    ),\n  )\n}\n```\n\nWe admit the following facts:\n\n- The first `spawn` evaluates to a generator of `Array[Int]`.\n- The `forall` function can be nested.\n- There is a `filter` function that filters out the test cases that do not satisfy the condition, non-empty array is required in this case.\n- The `one_of_array` function selects a random element from a non-empty array.\n\n```\n*** [4/33/100] Failed! Falsified.\n[0, 0]\n0\n```\n\nWe find that there are 33 cases that do not satisfy the condition, and the counterexample is `[0, 0]` and `0`, which points out the issue clearly. The `one_of_array` function selects a random element from the array, so the `y` is always `0` in this case. The `remove` function only removes the first one, so the property does not hold.\n\n### Conditional Properties\n\nNow let's further verify our hypothesis: The function `remove` only removes the first one but not all. So if we instead formulate a conditional property which restricts the input array contains no duplicated elements, then our tests should pass. Use the function `filter` can filter out unwanted test cases:\n\n```moonbit\ntest {\n  fn no_duplicate(x : Array[Int]) -\u003e Bool {\n    @sorted_set.from_iter(x.iter()).size() == x.length().to_int64()\n  }\n\n  quick_check!(\n    forall(\n      spawn(),\n      fn(iarr : (Int, Array[Int])) {\n        let (x, arr) = iarr\n        filter(remove(arr.copy(), x).contains(x).not(), no_duplicate(arr))\n      },\n    ),\n  )\n}\n```\n\n- Notice that we use `arr.copy()` to make a copy of the array, because the `remove` function may modify the argument array.\n- The `no_duplicate` function checks whether the input array contains no duplicated elements.\n- As discussed above, the `filter` combinator filters out the test cases that do not satisfy the condition.\n\nRunning this test, we find that all tests passed. Now we have strong evidence that the `remove` function only removes the first one but not all.\n\n### Classifying Data\n\nWe may interest in the distribution of the generated data: sometimes the generator may produce trivial data that does not help us to find the bugs. We want to find out what data is generated and how often in order to improve the generator. QuickCheck provides functions like `label`, `collect` and `classify` to achieve this.\n\n```moonbit\ntest \"classes\" {\n  quick_check_fn!(\n    fn(x : List[Int]) {\n      Arrow(prop_rev)\n      |\u003e classify(x.length() \u003e 5, \"long list\")\n      |\u003e classify(x.length() \u003c= 5, \"short list\")\n    },\n  )\n}\n```\n\nThe `classify` function takes a boolean and a string, and if the boolean is true, the test case is classified with the string.\n\n```\n+++ [100/0/100] Ok, passed!\n22% : short list\n78% : long list\n```\n\nThe `label` function takes a string and classifies the test case with the string.\n\n```moonbit\ntest \"label\" {\n  quick_check_fn!(\n    fn(x : List[Int]) {\n      Arrow(prop_rev)\n      |\u003e label(if x.is_empty() { \"trivial\" } else { \"non-trivial\" })\n    }\n  )\n}\n```\n\nThe result is as follows:\n\n```\n+++ [100/0/100] Ok, passed!\n8% : trivial\n92% : non-trivial\n```\n\nThe `collect` function is a generalization of `label`. The difference is that the `collect` takes an argument implementing the `Show` trait, and the `label` takes a string directly. The `collect` function is useful when you want to classify the test case with a complex value.\n\n## Application\n\nWe have applied MoonBit QuickCheck to test the correctness of the MoonBit core. For now we have found several bugs in the core, including:\n\n- [Escaped characters in JSON](https://github.com/moonbitlang/core/pull/812)\n- [Invalid JSON in inspect function](https://github.com/moonbitlang/core/issues/819)\n- [BigInt.op_sub edge cases](https://github.com/moonbitlang/core/issues/860)\n- [Unterminated moon test](https://github.com/moonbitlang/core/issues/875)\n- [Incorrect BigInt.op_div due to the ill normalization step](https://github.com/moonbitlang/core/issues/942)\n\n## Roadmap and Future Work\n\n### Data Generation\n\n- [x] Functional Enumeration\n- [x] Random Generation\n  - Currently use the SplitMix algorithms\n- [ ] Falsify (with free shrinkers)\n  - [x] Basic Implementation\n  - [x] Test driver adaption\n\n### Property Verification\n\n- [x] Boolean Property\n- [x] Test Invariants\n- [x] Operation Invariance\n- [x] `Testable` Trait\n- [ ] Existential Quantification\n\n### Shrinking\n\n- [x] Linear Shrinking\n- [x] Internal Shrinking\n- [ ] Integrated Shrinking\n\n## References\n\n- [OCaml QCheck](https://github.com/c-cube/qcheck)\n- [Haskell QuickCheck](https://hackage.haskell.org/package/QuickCheck)\n- [Feat: functional enumeration of algebraic types](https://doi.org/10.1145/2430532.2364515)\n- [QuickCheck: a lightweight tool for random testing of Haskell programs](https://doi.org/10.1145/351240.351266)\n- [SmallCheck and Lazy SmallCheck automatic exhaustive testing for small values](https://doi.org/10.1145/1411286.1411292)\n- [Automatic Testing of Operation Invariance](https://ceur-ws.org/Vol-1335/wflp2014_paper9.pdf)\n- [Fast Splittable Pseudorandom Number Generators](https://doi.org/10.1145/2660193.2660195)\n- [falsify: Internal Shrinking Reimagined for Haskell](https://doi.org/10.1145/3609026.3609733)\n- [Selective applicative functors](https://doi.org/10.1145/3342521)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoonbitlang%2Fquickcheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoonbitlang%2Fquickcheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoonbitlang%2Fquickcheck/lists"}