qcheck
QuickCheck-inspired property-based testing with integrated shrinking
Overview
Rather than specifying test cases manually, you describe the invariants that values of a given type must satisfy (“properties”). Then, generators generate lots of values (test cases) on which the properties are checked. Finally, if a value is found for which a given property does not hold, that value is “shrunk” in order to find an nice, informative counter-example that is presented to you.
This module has functions for running and configuring property tests as well as generating random values (with shrinking) to drive those tests.
For full usage examples, see the project README.
API
Running Tests
Configuring and Seeding
Low-Level Construction
Combinators and Other Utilities
- return (and constant)
- bind (and then)
- apply
- parameter
- map
- map2
- map3
- map4
- map5
- map6
- from_generators
- from_weighted_generators
- sized_from
Generator Categories
There are a few different “categories” of generator.
- Some types have generators named after the type.
- These give a distribution of values that is a reasonable default for test generation.
- E.g.,
string
,float
,bit_array
.
- Generic generators
- These are fully specified, or “generic”, and you must provide generators for values and sizes.
- E.g.,
generic_string
,generic_list
.
- Fixed size/length generators
- These take a size or length parameter as appropriate for the type, and generate values of that size (when possible).
- These generators use the default value generator.
- E.g.,
fixed_length_string
,fixed_length_list
- Non-empty generators
- These generate collections with length or size of at least one
- E.g.,
non_empty_string
,non_empty_bit_array
- From other generators
- The
_from
suffix means that another generator is used to generate values - E.g.,
string_from
,list_from
- The
- Mixing and matching
- Some generators mix and match the above categories
- E.g.,
fixed_length_list_from
,non_empty_string_from
Numeric Generators
Ints
Floats
Codepoint and String Generators
The main purpose of codepoint generators is to use them to generate strings.
Codepoints
ASCII Codepoints
- uppercase_ascii_codepoint
- lowercase_ascii_codepoint
- ascii_digit_codepoint
- alphabetic_ascii_codepoint
- alphanumeric_ascii_codepoint
- printable_ascii_codepoint
- ascii_whitespace_codepoint
- uniform_printable_ascii_codepoint
Strings
String generators are built from codepoint generators.
Bit Array Generators
Bit array values come from integers, and handle sizes and shrinking in a reasonable way given that values in the bit array are connected to the size of the bit array in certain situations.
These functions will generate bit arrays that cause runtime crashes when targeting JavaScript.
Byte-aligned bit arrays
Byte-aligned bit arrays always have a size that is a multiple of 8.
These bit arrays work on the JavaScript target.
- byte_aligned_bit_array
- non_empty_byte_aligned_bit_array
- fixed_size_byte_aligned_bit_array
- fixed_size_byte_aligned_bit_array_from
- generic_byte_aligned_bit_array
UTF-8 Encoded Bit Arrays
Bit arrays where the values are always valid utf-8 encoded bytes.
These bit arrays work on the JavaScript target.
- utf8_bit_array
- non_empty_utf8_bit_array
- fixed_size_utf8_bit_array
- fixed_size_utf8_bit_array_from
- generic_utf8_bit_array
Collection Generators
Lists
Dictionaries
Sets
Tuples
Other Generators
Debug Generators
These functions aren’t meant to be used directly in your tests. They are provided to help debug or investigate what values and shrinks that a generator produces.
Notes
The exact distributions of individual generators are considered an
implementation detail, and may change without a major version update.
For example, if the option
generator currently produced None
approximately 25% of the time, but that distribution was changed to produce
None
50% of the time instead, that would not be considered a breaking
change.
Types
A generator for producing random values of type a
.
While the type is not opaque, it’s recommended to use the provided
generators, and the provided combinators like map
and bind
. Direct
generator construction should be reserved for special use-cases.
pub type Generator(a) {
Generator(fn(Seed) -> #(Tree(a), Seed))
}
Constructors
-
Generator(fn(Seed) -> #(Tree(a), Seed))
Functions
pub fn alphabetic_ascii_codepoint() -> Generator(UtfCodepoint)
Generate alphabetic ASCII characters.
Returns
A generator that produces alphabetic ASCII characters as codepoints
Example
string_from(alphabetic_ascii_codepoint())
pub fn alphanumeric_ascii_codepoint() -> Generator(UtfCodepoint)
Generate alphanumeric ASCII characters.
Returns
A generator that produces alphanumeric ASCII characters as codepoints
Example
string_from(alphanumeric_ascii_codepoint())
pub fn apply(
f: Generator(fn(a) -> b),
x: Generator(a),
) -> Generator(b)
Support for constructing generators in an applicative style.
Example
type Box {
Box(x: Int, y: Int, w: Int, h: Int)
}
fn box_generator() {
return({
use x <- parameter
use y <- parameter
use w <- parameter
use h <- parameter
Box(x:, y:, w:, h:)
})
|> apply(bounded_int(-100, 100))
|> apply(bounded_int(-100, 100))
|> apply(bounded_int(1, 100))
|> apply(bounded_int(1, 100))
}
pub fn ascii_digit_codepoint() -> Generator(UtfCodepoint)
Generate ASCII digits as codepoints.
Returns
A generator that produces ASCII digits from 0
to 9
as codepoints
Example
string_from(ascii_digit_codepoint())
pub fn ascii_whitespace_codepoint() -> Generator(UtfCodepoint)
Generate ASCII whitespace as codepoints.
Returns
A generator that produces ASCII whitespace as codepoints
Example
let whitespace_generator = string_from(ascii_whitespace_codepoint())
pub fn bind(
generator: Generator(a),
f: fn(a) -> Generator(b),
) -> Generator(b)
Transform a generator by applying a function that returns another generator to each generated value.
Unlike map
, this allows for a dependency on the resulting generator and
the original generated values.
Arguments
generator
: A generator that creates a value of typea
f
: A function that takes a value of typea
and returns a generator of typeb
Returns
A generator that first generates a value of type a
, then uses that value
to generate a value of type b
Examples
Say you wanted to generate a valid date in string form, like "2025-01-30"
.
In order to generate a valid day, you need both the month (some months have
31 days, other have fewer) and also the year (since the year affects the max
days in February). So, before you can generate a valid day, you must first
generate a year and a month. You could imagine a set of functions like this:
fn date_generator() -> Generator(String) {
use #(year, month) <- bind(tuple2(year_generator(), month_generator()))
use day <- map(day_generator(year:, month:))
int.to_string(year)
<> "-"
<> int.to_string(month)
<> "-"
<> int.to_string(day)
}
// Note how the day generator depends on the value of `year` and `month`.
fn day_generator(year year, month month) -> Generator(Int) {
todo
}
fn year_generator() -> Generator(Int) {
todo
}
fn month_generator() -> Generator(Int) {
todo
}
Another situation in which you would need bind
is if you needed to
generate departure and arrival times. We will say a pair of departure and
arrival times is valid if the departure time is before the arrival time.
That means we cannot generate an arrival time without first generating a
departure time. Here is how that might look:
fn departure_and_arrival_generator() {
use departure_time <- bind(departure_time_generator())
use arrival_time <- map(arrival_time_generator(departure_time))
#(departure_time, arrival_time)
}
fn departure_time_generator() {
todo
}
fn arrival_time_generator(departure_time) {
todo
}
pub fn bit_array() -> Generator(BitArray)
Generate bit arrays.
Warning
This function will generate bit arrays that cause runtime crashes when targeting JavaScript.
pub fn bool() -> Generator(Bool)
Generate boolean values.
Returns
A generator that generates boolean values and shrinks towards False
pub fn bounded_codepoint(
from low: Int,
to high: Int,
) -> Generator(UtfCodepoint)
Generate Unicode codepoints uniformly distributed within a specified range.
Arguments
from
: Minimum codepoint value (inclusive)to
: Maximum codepoint value (inclusive)
Returns
A generator that produces Unicode codepoints within the specified range.
Notes
- If the range is invalid, it will be automatically adjusted to a valid range
- Shrinks towards an origin codepoint (typically lowercase ‘a’)
- Mainly used for string generation
Example
let cyrillic_character = bounded_codepoint(from: 0x0400, to: 0x04FF)
pub fn bounded_float(
from low: Float,
to high: Float,
) -> Generator(Float)
Generate floats uniformly distributed between from
and to
, inclusive.
Arguments
from
: Lower bound of the range (inclusive)to
: Upper bound of the range (inclusive)
Returns
A generator producing floats within the specified range.
Behavior
- Shrinks towards
0
, but won’t shrink outside of the range[from, to]
- Automatically orders parameters if
from
>to
Example
Generate floats between -10 and 10.
bounded_float(-10, 10)
pub fn bounded_int(from low: Int, to high: Int) -> Generator(Int)
Generate integers uniformly distributed between from
and to
, inclusive.
Arguments
from
: Lower bound of the range (inclusive)to
: Upper bound of the range (inclusive)
Returns
A generator producing integers within the specified range.
Behavior
- Shrinks towards
0
, but won’t shrink outside of the range[from, to]
- Automatically orders parameters if
from
>to
Example
Generate integers between -10 and 10.
bounded_int(-10, 10)
pub fn byte_aligned_bit_array() -> Generator(BitArray)
Generate byte-aligned bit arrays.
pub fn codepoint() -> Generator(UtfCodepoint)
Generate Unicode codepoints with a decent distribution that is good for generating genreal strings.
Returns
A generator that produces Unicode codepoints
Example
The decent default string generator could be writen something like this:
generic_string(codepoint(), small_non_negative_int())
pub fn codepoint_from_ints(
first: Int,
rest: List(Int),
) -> Generator(UtfCodepoint)
Generate a codepoint from a list of codepoints represented as integers.
Splitting up the arguments in this way ensures some value is always generated by preventing you from passing in an empty list.
Arguments
first
: First codepoint to choose fromrest
: Additional codepoints to choose from
Returns
A Generator
that produces codepoints from the provided values
Example
let ascii_whitespace_generator = codepoint_from_ints(
// Horizontal tab
9,
[
// Line feed
10,
// Vertical tab
11,
// Form feed
12,
// Carriage return
13,
// Space
32,
],
)
pub fn codepoint_from_strings(
first: String,
rest: List(String),
) -> Generator(UtfCodepoint)
Generate a codepoint from a list of strings.
Arguments
first
: First character to choose fromrest
: Additional characters to choose from
Returns
A Generator
that produces codepoints from the provided values
Notes
- Splitting up the arguments in this way ensures some value is always generated by preventing you from passing in an empty list.
- Only the first codepoint is taken from each of the provided strings
Example
let quadrant_generator = codepoint_from_strings("▙", ["▛", "▜", "▟"])
pub fn config(
test_count test_count: Int,
max_retries max_retries: Int,
seed seed: Seed,
) -> Config
Create a new Config
with specified test count, max retries, and seed.
Arguments
test_count
: Number of test cases to generatemax_retries
: Maximum retries to test a shrunk input candidate. Values > 1 can be useful for testing non-deterministic code.seed
: Random seed for deterministic test generation
Returns
A Config
with the provided settings, using defaults for any invalid arguments
Example
let config = config(test_count: 10_000, max_retries: 1, seed: seed(47))
pub fn constant(a: a) -> Generator(a)
Create a generator that always returns the same value and does not shrink.
Arguments
a
: The value to be consistently generated
Returns
A Generator
that produces the same input for all test cases
Example
use string <- given(constant("Gleam"))
string == "Gleam"
Notes
This function is an alias for return
.
pub fn default_config() -> Config
Create a default configuration for property-based testing.
Returns
- A
Config
with default settings for test count, max retries, and seed
Example
let config = default_config()
pub fn fixed_length_list_from(
element_generator: Generator(a),
length: Int,
) -> Generator(List(a))
Generate fixed-length lists with elements from the given generator.
Arguments
element_generator
: Generates list elementslength
: The length of the generated lists
Returns
A generator that produces fixed-length lists with elements from the given generator.
Shrinking
Shrinks first on list length, then on list elements, ensuring shrunk lists remain within length generator’s range.
Example
fixed_length_list_from(string(), 5)
pub fn fixed_length_string_from(
generator: Generator(UtfCodepoint),
length: Int,
) -> Generator(String)
Generate a fixed-length string from the given codepoint generator.
Arguments
generator
: A generator for codepointslength
: Number of graphemes in the generated string
Returns
A generator that produces strings with the specified number of graphemes
Example
fixed_length_string_from(codepoint(), 5)
pub fn fixed_size_bit_array(size: Int) -> Generator(BitArray)
Generate fixed-size bit arrays.
Warning
This function will generate bit arrays that cause runtime crashes when targeting JavaScript.
pub fn fixed_size_bit_array_from(
value_generator: Generator(Int),
bit_size: Int,
) -> Generator(BitArray)
Generate fixed-size bit arrays where elements are generated using the provided integer generator.
Arguments
value_generator
: Generators bit array valuesbit_size
: Number of bits in the generated bit array
Returns
A generator of fixed-size bit arrays
Notes
Shrinks on values, not on size
Example
fixed_size_bit_array_from(bounded_int(0, 255), 64)
Warning
This function will generate bit arrays that cause runtime crashes when targeting JavaScript unless the bit size is a multiple of 8.
pub fn fixed_size_byte_aligned_bit_array(
num_bytes: Int,
) -> Generator(BitArray)
Generate byte-aligned bit arrays of the given number of bytes
Arguments
num_bytes
: Number of bytes for the generated bit array
Returns
A generator that produces bit arrays with the specified number of bytes
Example
Generate 4-byte bit arrays:
fixed_size_byte_aligned_bit_array(4)
pub fn fixed_size_byte_aligned_bit_array_from(
value_generator: Generator(Int),
byte_size: Int,
) -> Generator(BitArray)
Generate byte-aligned bit arrays of the given number of bytes from the given value generator
Arguments
value_generator
: Generates the values of the bit arraynum_bytes
: Number of bytes for the generated bit array
Returns
A generator that produces bit arrays with the specified number of bytes according to the given value generator
Example
Generate 4-byte bit arrays:
fixed_size_byte_aligned_bit_array(bounded_int(0, 255), 16)
pub fn fixed_size_utf8_bit_array(
num_codepoints: Int,
) -> Generator(BitArray)
Generate a fixed-sized bit array of valid UTF-8 encoded bytes with the given number of codepoints.
Arguments
num_codepoints
: The number of Unicode codepoints represented by the generated bit arrays
Returns
A generator that produces of fixed-sized bit arrays of UTF-8 encoded bytes
Details
- The size is determined by the number of Unicode codepoints, not bytes or bits.
- If a negative number is provided, it is converted to zero.
pub fn fixed_size_utf8_bit_array_from(
codepoint_generator: Generator(UtfCodepoint),
num_codepoints: Int,
) -> Generator(BitArray)
Generate a fixed-sized bit array of valid UTF-8 encoded bytes with the given number of codepoints and values generated from the given codepoint generator.
Arguments
codepoint_generator
: Generates the valuesnum_codepoints
: The number of Unicode codepoints represented by the generated bit arrays
Returns
A generator that produces of fixed-sized bit arrays of UTF-8 encoded bytes
Details
- The size is determined by the number of Unicode codepoints, not bytes or bits.
- If a negative number is provided, it is converted to zero.
pub fn float() -> Generator(Float)
Generate floats with a bias towards smaller values.
Shrinks towards 0.0
.
Returns
A generator that produces floating-point numbers
pub fn from_generators(
generator: Generator(a),
generators: List(Generator(a)),
) -> Generator(a)
Choose a generator from a list of generators, then generate a value from the selected generator.
Arguments
generator
: Initial generator to include in the choicegenerators
: Additional generators to choose from
Returns
A generator that selects and uses one of the provided generators
Notes
- Will always produce values since at least one generator is required
Example
fn mostly_ascii_characters_generator() {
from_generators(uppercase_ascii_codepoint(), [
lowercase_ascii_codepoint(),
uniform_codepoint(),
])
}
pub fn from_weighted_generators(
generator: #(Int, Generator(a)),
generators: List(#(Int, Generator(a))),
) -> Generator(a)
Choose a generator from a list of weighted generators, then generate a value from the selected generator.
Arguments
generator
: Initial weighted generator (weight and generator)generators
: Additional weighted generators
Returns
A generator that selects and generates values based on the provided weights
Example
from_weighted_generators(#(26, uppercase_ascii_codepoint()), [
#(26, lowercase_ascii_codepoint()),
#(10, ascii_digit_codepoint()),
])
pub fn generate(
generator: Generator(a),
number_to_generate: Int,
seed: Seed,
) -> #(List(a), Seed)
Generate a fixed number of random values from a generator.
Arguments
generator
: The generator to use for creating valuesnumber_to_generate
: Number of values to generateseed
: Random seed for value generation
Returns
A list of generated values, without their associated shrinks
Notes
Primarily useful for debugging generator behavior
pub fn generate_tree(
generator: Generator(a),
seed: Seed,
) -> #(Tree(a), Seed)
Generate a single value and its shrink tree from a generator.
Arguments
generator
: The generator to use for creating the valueseed
: Random seed for value generation
Returns
A tuple containing the generated value’s shrink tree and the next seed
Notes
Primarily useful for debugging generator behavior
pub fn generator(
random_generator: Generator(a),
tree: fn(a) -> Tree(a),
) -> qcheck.Generator(a)
Create a new generator from a random generator and a shrink tree function.
Arguments
random_generator
: Produces random values of typea
tree
: Function that creates a shrink tree for generated values
Returns
A new Generator(a)
that combines random generation and shrinking
Notes
This is a low-level function for building custom generators. Prefer using
built-in generators or combinators like map
, bind
, etc.
pub fn generic_bit_array(
values_from value_generator: Generator(Int),
bit_size_from bit_size_generator: Generator(Int),
) -> Generator(BitArray)
Generate bit arrays with configurable values and bit sizes.
Arguments
values_from
: Generator for bit array contentsbit_size_from
: Generator for bit array size
Returns
A bit array generator
Example
let generator = generic_bit_array(
value_generator: bounded_int(0, 255),
bit_size_generator: bounded_int(32, 64)
)
Warning
This function will generate bit arrays that cause runtime crashes when targeting JavaScript.
pub fn generic_byte_aligned_bit_array(
values_from value_generator: Generator(Int),
byte_size_from byte_size_generator: Generator(Int),
) -> Generator(BitArray)
Generate byte-aligned bit arrays according to the given value generator and byte size generator.
Arguments
value_generator
: Generates the values of the bit arraybyte_size_generator
: Generates the number of bytes of the bit array
Returns
A byte-aligned bit array generator
pub fn generic_dict(
keys_from key_generator: Generator(a),
values_from value_generator: Generator(b),
size_from size_generator: Generator(Int),
) -> Generator(Dict(a, b))
Generates dictionaries with keys from a key generator, values from a value generator, and sizes from a size generator.
Arguments
keys_from
: Generator for dictionary keysvalues_from
: Generator for dictionary valuessize_from
: Generator for dictionary size
Returns
A generator that produces dictionaries
Notes
- The actual size may be less than the generated size due to potential key duplicates
- Shrinks on size first, then on individual elements
Example
generic_dict(
key_generator: uniform_int(),
value_generator: string(),
size_generator: small_strictly_positive_int()
)
pub fn generic_list(
elements_from element_generator: Generator(a),
length_from length_generator: Generator(Int),
) -> Generator(List(a))
Generate lists with elements from one generator and lengths from another.
Arguments
elements_from
: Generates list elementslength_from
: Generates list lengths
Returns
A generator that produces lists with:
- Elements from
elements_from
- Lengths from
length_from
Shrinking
Shrinks first on list length, then on list elements, ensuring shrunk lists remain within length generator’s range.
Example
generic_list(string(), small_non_negative_int())
pub fn generic_set(
elements_from element_generator: Generator(a),
size_from size_generator: Generator(Int),
) -> Generator(Set(a))
Generates sets with values from an element generator, and sizes from a size generator.
Arguments
elements_from
: Generator for set elementssize_from
: Generator for set size
Returns
A generator that produces sets
Notes
- The actual size may be less than the generated size due to potential duplicates
- Shrinks on size first, then on individual elements
Example
generic_set(
value_generator: string(),
size_generator: small_strictly_positive_int()
)
pub fn generic_string(
codepoints_from codepoint_generator: Generator(UtfCodepoint),
length_from length_generator: Generator(Int),
) -> Generator(String)
Generate a string from the given codepoint generator and the given length generator.
Arguments
codepoint_generator
: A generator for codepointslength_generator
: A generator to determine number of graphemes in the generated strings
Returns
A string generator
Example
generic_string(ascii_digit_codepoint(), bounded_int(8, 15))
pub fn generic_utf8_bit_array(
codepoints_from codepoint_generator: Generator(UtfCodepoint),
codepoint_size_from num_codepoints_generator: Generator(Int),
) -> Generator(Generator(List(UtfCodepoint)))
Generate bit arrays of UTF-8 encoded bytes with configurable values and number of codepoints.
Arguments
codepoints_from
: Generates the codepoint values of the resulting bit arrayscodepoint_size_from
: Generates sizes in number of codepoints represented by the resulting bit arrays
Returns
A generator of bit arrays of valid UTF-8 encoded bytes
pub fn given(
generator: Generator(a),
property: fn(a) -> Nil,
) -> Nil
Test a property against generated test cases using the default configuration.
Arguments
generator
: Creates test inputsproperty
: The property to verify
Returns
Nil
if all test cases pass (the property returnsNil
)- Panics if any test case fails (the property panics)
pub fn list_from(
element_generator: Generator(a),
) -> Generator(List(a))
Generate lists with elements from the given generator and the default length generator.
Arguments
element_generator
: Generates list elements
Returns
A generator that produces lists with elements from the given generator
Shrinking
Shrinks first on list length, then on list elements.
Example
list_from(string())
pub fn lowercase_ascii_codepoint() -> Generator(UtfCodepoint)
Generate lowercase ASCII letters.
Returns
A generator that produces lowercase letters from a
to z
as codepoints
Example
string_from(lowercase_ascii_codepoint())
pub fn map(
generator: Generator(a),
f: fn(a) -> b,
) -> Generator(b)
Transform a generator by applying a function to each generated value.
Arguments
generator
: The original generator to transformf
: Function to apply to each generated value
Returns
A new generator that produces values transformed by f
, with shrinking
behavior derived from the original generator
Examples
let even_number_generator = map(uniform_int(), fn(n) { 2 * n })
With use
:
let even_number_generator = {
use n <- map(uniform_int())
2 * n
}
pub fn map2(
g1: Generator(a),
g2: Generator(b),
f: fn(a, b) -> c,
) -> Generator(c)
Transform two generators by applying a function to their generated values.
Arguments
g1
: First generator to provide inputg2
: Second generator to provide inputf
: Function to apply to generated values fromg1
andg2
Returns
A new generator that produces values by applying f
to values from g1
and g2
Example
use year, month <- map2(bounded_int(0, 9999), bounded_int(1, 12))
int.to_string(year) <> "-" <> int.to_string(month)
pub fn map3(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
f: fn(a, b, c) -> d,
) -> Generator(d)
Transform three generators by applying a function to their generated values.
See docs for map2
.
pub fn map4(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
f: fn(a, b, c, d) -> e,
) -> Generator(e)
Transform four generators by applying a function to their generated values.
See docs for map2
.
pub fn map5(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
g5: Generator(e),
f: fn(a, b, c, d, e) -> f,
) -> Generator(f)
Transform five generators by applying a function to their generated values.
See docs for map2
.
pub fn map6(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
g5: Generator(e),
g6: Generator(f),
f: fn(a, b, c, d, e, f) -> g,
) -> Generator(g)
Transform six generators by applying a function to their generated values.
See docs for map2
.
pub fn nil() -> Generator(Nil)
Generate a constant Nil
value.
Returns
A Generator
that always returns Nil
and does not shrink
pub fn non_empty_bit_array() -> Generator(BitArray)
Generate non-empty bit arrays.
Returns
A generator of non-empty bit arrays
Warning
This function will generate bit arrays that cause runtime crashes when targeting JavaScript.
pub fn non_empty_byte_aligned_bit_array() -> Generator(BitArray)
Generate non-empty byte-aligned bit arrays.
pub fn non_empty_string() -> Generator(String)
Generate non-empty strings with the default codepoint and length generators.
Example
use string <- given(string())
string.length(string) > 0
pub fn non_empty_string_from(
codepoint_generator: Generator(UtfCodepoint),
) -> Generator(String)
Generate non-empty strings with the given codepoint generator and default length generator.
Example
non_empty_string_from(alphanumeric_ascii_codepoint())
pub fn non_empty_utf8_bit_array() -> Generator(BitArray)
Generate non-empty bit arrays of valid UTF-8 bytes.
pub fn option_from(
generator: Generator(a),
) -> Generator(Option(a))
Create a generator for Option
values.
Arguments
generator
: Generator for the inner value type
Returns
A generator that produces Option
values, shrinking towards None
first,
then towards the shrinks of the input generator
Example
option_from(string())
pub fn parameter(f: fn(a) -> b) -> fn(a) -> b
Support for constructing curried functions for the applicative style of generator composition.
Example
type Box {
Box(x: Int, y: Int, w: Int, h: Int)
}
fn box_generator() {
return({
use x <- parameter
use y <- parameter
use w <- parameter
use h <- parameter
Box(x:, y:, w:, h:)
})
|> apply(bounded_int(-100, 100))
|> apply(bounded_int(-100, 100))
|> apply(bounded_int(1, 100))
|> apply(bounded_int(1, 100))
}
pub fn printable_ascii_codepoint() -> Generator(UtfCodepoint)
Generate printable ASCII characters with a bias towards alphanumeric characters.
Returns
A generator that produces printable ASCII characters as codepoints
Example
string_from(printable_ascii_codepoint())
pub fn random_seed() -> Seed
Create a new randomly-generated seed.
Returns
A Seed
value that can be used to configure non-deterministic test generation
Example
let config = config(test_count: 10_000, max_retries: 1, seed: random_seed())
pub fn return(a: a) -> Generator(a)
Create a generator that always returns the same value and does not shrink.
Arguments
a
: The value to be consistently generated
Returns
A Generator
that produces the same input for all test cases
Example
use string <- given(return("Gleam"))
string == "Gleam"
pub fn run(
config: Config,
generator: Generator(a),
property: fn(a) -> Nil,
) -> Nil
Test a property against generated test cases using the provided configuration.
Arguments
config
: Settings for test executiongenerator
: Creates test inputsproperty
: The property to verify
Returns
Nil
if all test cases pass (the property returnsNil
)- Panics if any test case fails (the property panics)
pub fn seed(n: Int) -> Seed
Create a new seed from a provided integer.
Arguments
n
: Integer to create the seed from
Returns
A Seed
value that can be used to configure deterministic test generation
Example
let config = default_config() |> with_seed(seed(124))
pub fn sized_from(
sized_generator: fn(Int) -> Generator(a),
size_generator: Generator(Int),
) -> Generator(a)
Creates a generator by first generating a size using the provided
size_generator
, then passing that size to the sized_generator
to
produce a value.
Shrinks on the size first, then on the generator.
Arguments
sized_generator
: A generator function that takes a size and produces a valuesize_generator
: A generator for creating the size input
Returns
A generator that first produces a size, then uses that size to generate a value
Example
Create a bit arrays whose bit size is from 10 to 20.
fixed_size_bit_array() |> sized_from(bounded_int(10, 20))
pub fn small_non_negative_int() -> Generator(Int)
Generate small non-negative integers, well-suited for modeling sized elements like lists or strings.
Shrinks towards 0
.
Returns
A generator for small, non-negative integers
Example
generic_string(bounded_codepoint(0, 255), small_non_negative_int())
pub fn small_strictly_positive_int() -> Generator(Int)
Generate small, strictly positive integers, well-suited for modeling sized elements like lists or strings.
Shrinks towards 0
.
Returns
A generator for small, strictly positive integers
Example
generic_string(bounded_codepoint(0, 255), small_strictly_positive_int())
pub fn string() -> Generator(String)
Generate strings with the default codepoint and length generators.
Example
use string <- given(string())
string.length(string) == string.length(string <> "!") + 1
pub fn string_from(
codepoint_generator: Generator(UtfCodepoint),
) -> Generator(String)
Generate strings with the given codepoint generator and default length generator.
Example
string_from(ascii_digit_codepoint())
pub fn then(
generator: Generator(a),
f: fn(a) -> Generator(b),
) -> Generator(b)
Transform a generator by applying a function that returns another generator to each generated value.
Unlike map
, this allows for a dependency on the resulting generator and
the original generated values.
(then
is an alias for bind
.)
Arguments
generator
: A generator that creates a value of typea
f
: A function that takes a value of typea
and returns a generator of typeb
Returns
A generator that first generates a value of type a
, then uses that value
to generate a value of type b
Examples
Say you wanted to generate a valid date in string form, like "2025-01-30"
.
In order to generate a valid day, you need both the month (some months have
31 days, other have fewer) and also the year (since the year affects the max
days in February). So, before you can generate a valid day, you must first
generate a year and a month. You could imagine a set of functions like this:
fn date_generator() -> Generator(String) {
use #(year, month) <- then(tuple2(year_generator(), month_generator()))
use day <- map(day_generator(year:, month:))
int.to_string(year)
<> "-"
<> int.to_string(month)
<> "-"
<> int.to_string(day)
}
// Note how the day generator depends on the value of `year` and `month`.
fn day_generator(year year, month month) -> Generator(Int) {
todo
}
fn year_generator() -> Generator(Int) {
todo
}
fn month_generator() -> Generator(Int) {
todo
}
Another situation in which you would need then
is if you needed to
generate departure and arrival times. We will say a pair of departure and
arrival times is valid if the departure time is before the arrival time.
That means we cannot generate an arrival time without first generating a
departure time. Here is how that might look:
fn departure_and_arrival_generator() {
use departure_time <- then(departure_time_generator())
use arrival_time <- map(arrival_time_generator(departure_time))
#(departure_time, arrival_time)
}
fn departure_time_generator() {
todo
}
fn arrival_time_generator(departure_time) {
todo
}
pub fn tuple2(
g1: Generator(a),
g2: Generator(b),
) -> Generator(#(a, b))
Generate a tuple of two values using the provided generators.
Arguments
g1
: Generator for the first tuple elementg2
: Generator for the second tuple element
Returns
A generator that produces a tuple of two values, one from each input generator
Example
let point_generator = tuple2(float(), float())
pub fn tuple3(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
) -> Generator(#(a, b, c))
Generate a tuple of three values using the provided generators.
See docs for tuple2
.
pub fn tuple4(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
) -> Generator(#(a, b, c, d))
Generate a tuple of four values using the provided generators.
See docs for tuple2
.
pub fn tuple5(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
g5: Generator(e),
) -> Generator(#(a, b, c, d, e))
Generate a tuple of five values using the provided generators.
See docs for tuple2
.
pub fn tuple6(
g1: Generator(a),
g2: Generator(b),
g3: Generator(c),
g4: Generator(d),
g5: Generator(e),
g6: Generator(f),
) -> Generator(#(a, b, c, d, e, f))
Generate a tuple of six values using the provided generators.
See docs for tuple2
.
pub fn uniform_codepoint() -> Generator(UtfCodepoint)
Generate Unicode codepoints.
Returns
A generator that creates Unicode codepoints across the valid range
Notes
- Generates codepoints from U+0000 to U+10FFFF
- Uses ASCII lowercase ‘a’ as a shrink target
Example
string_from(uniform_codepoint())
pub fn uniform_int() -> Generator(Int)
Generate uniformly distributed integers across a large range.
Details
- Shrinks generated values towards
0
- Not likely to hit interesting or corner cases
Returns
A generator of integers with uniform distribution
Example
let positive_int_generator = {
use n <- map(uniform_int())
int.absolute_value(n)
}
pub fn uniform_printable_ascii_codepoint() -> Generator(
UtfCodepoint,
)
Uniformly generate printable ASCII characters.
Returns
A generator that produces printable ASCII characters as codepoints
Example
string_from(uniform_printable_ascii_codepoint())
pub fn uppercase_ascii_codepoint() -> Generator(UtfCodepoint)
Generate uppercase ASCII letters.
Returns
A generator that produces uppercase letters from A
to Z
as codepoints
Example
string_from(uppercase_ascii_codepoint())
pub fn utf8_bit_array() -> Generator(BitArray)
Generate bit arrays of valid UTF-8 bytes.
pub fn with_max_retries(
config: Config,
max_retries: Int,
) -> Config
Set the maximum number of retries for a property test.
Arguments
config
: The current configurationmax_retries
: Maximum number of retries allowed. Ifmax_retries < 0
, uses the default max retries.
Returns
A new Config
with the specified maximum retries
Example
let config = default_config() |> with_max_retries(100)
pub fn with_seed(config: Config, seed: Seed) -> Config
Set the random seed for reproducible test case generation.
Arguments
config
: The current configurationseed
: Seed value for random number generation
Returns
A new Config
with the specified random seed
Example
let config = default_config() |> with_seed(seed(124))
pub fn with_test_count(config: Config, test_count: Int) -> Config
Set the number of test cases to run in a property test.
Arguments
config
: The current configurationtest_count
: Number of test cases to generate. Iftest_count <= 0
, uses the default test count.
Returns
A new Config
with the specified test count
Example
let config = default_config() |> with_test_count(10_000)