monkey_test/
lib.rs

1#![doc(
2    issue_tracker_base_url = "https://github.com/jockbert/monkey_test/issues/"
3)]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/jockbert/monkey_test/main/assets/doc/logo-256.png"
6)]
7#![doc(
8    html_favicon_url = "https://raw.githubusercontent.com/jockbert/monkey_test/main/assets/doc/logo.ico"
9)]
10#![warn(missing_docs)]
11#![doc = include_str!("../DOCUMENTATION.md")]
12
13mod config;
14mod convenience_traits;
15pub mod gens;
16mod internal;
17mod runner;
18pub mod shrinks;
19
20#[cfg(test)]
21mod testing;
22
23use std::ops::RangeInclusive;
24
25// Re-export details from some modules for easier access.
26pub use config::*;
27pub use convenience_traits::*;
28
29/// Main entry point for writing property based tests using the monkey-test
30/// tool.
31///
32/// # Example
33/// ```should_panic
34/// use monkey_test::*;
35///
36/// monkey_test()
37///   .with_generator(gens::u8::any())
38///   .assert_true(|x| x < 15);
39/// ```
40pub fn monkey_test() -> Conf {
41    Conf::default()
42}
43
44/// A boxed iterator of example type `E`
45pub type BoxIter<E> = Box<dyn Iterator<Item = E>>;
46
47/// A boxed shrinker of example type `E`
48pub type BoxShrink<E> = Box<dyn Shrink<E>>;
49
50/// A boxed generator of example type `E`
51pub type BoxGen<E> = Box<dyn Gen<E>>;
52
53/// A property is something that should hold, for all given examples.
54pub type Property<E> = fn(E) -> bool;
55
56/// Type alias for size range used when generating examples. It is an inclusive
57/// range so it can encompass all values including usize::MAX.
58pub type ExampleSize = RangeInclusive<usize>;
59
60/// Type alias for a randomization seed used when generating random examples in
61/// a generator.
62pub type Seed = u64;
63
64impl<E> core::fmt::Debug for dyn Gen<E> {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.write_str(std::any::type_name::<Self>())
67    }
68}
69
70/// The generator trait, for producing example values to test in a property.
71pub trait Gen<E: Clone + 'static>: CloneGen<E> {
72    /// Produce a example iterator from the generator, given a randomization
73    /// seed and (if applicable) size of examples to produce.
74    fn examples(&self, seed: Seed, size: ExampleSize) -> BoxIter<E>;
75
76    /// Returns a predefined shrinker, or a empty shrinker if no suitable
77    /// exists.
78    ///
79    /// This enables distributing a default shrinker with given generator,
80    /// reducing the need to explicitly configure a shrinker at place of use.
81    ///
82    /// When implementing a [Gen], you can return a empty [shrinks::none]
83    /// shrinker, if that makes the implementation easier, but when you will not
84    /// get any shrinking functionality applied to failing example.
85    fn shrinker(&self) -> BoxShrink<E>;
86
87    /// Bind another shrinker to generator. See [gens::other_shrinker].
88    fn with_shrinker(&self, other_shrink: BoxShrink<E>) -> BoxGen<E> {
89        gens::other_shrinker(self.clone_box(), other_shrink)
90    }
91
92    /// Concatenate together two generators. See [gens::chain].
93    fn chain(&self, other_gen: BoxGen<E>) -> BoxGen<E> {
94        gens::chain(self.clone_box(), other_gen)
95    }
96}
97
98/// The shrinker trait, for shrinking a failed example values into smaller ones.
99/// What is determined as a smaller value can be subjective and is up to author
100/// or tester to determine, but as a rule of thumb a smaller value should be
101/// easier to interpret, when a property is proven wrong.
102pub trait Shrink<E>: CloneShrink<E>
103where
104    E: Clone,
105{
106    /// Returns a series of smaller examples, given an original example.
107    fn candidates(&self, original: E) -> BoxIter<E>;
108}
109
110// Doctest the readme file
111#[doc = include_str!("../README.md")]
112#[cfg(doctest)]
113pub struct ReadmeDoctests;