bolero_hydro/
lib.rs

1use bolero_generator::{
2    combinator::{AndThenGenerator, FilterGenerator, FilterMapGenerator, MapGenerator},
3    TypeValueGenerator,
4};
5use core::{fmt::Debug, marker::PhantomData, time::Duration};
6
7cfg_if::cfg_if! {
8    if #[cfg(fuzzing_libfuzzer)] {
9        /// The default engine used when defining a test target
10        pub use bolero_libfuzzer::LibFuzzerEngine as DefaultEngine;
11    } else if #[cfg(fuzzing_afl)] {
12        /// The default engine used when defining a test target
13        pub use bolero_afl::AflEngine as DefaultEngine;
14    } else if #[cfg(fuzzing_honggfuzz)] {
15        /// The default engine used when defining a test target
16        pub use bolero_honggfuzz::HonggfuzzEngine as DefaultEngine;
17    } else if #[cfg(kani)] {
18        pub use bolero_kani::KaniEngine as DefaultEngine;
19    } else {
20        mod test;
21
22        /// The default engine used when defining a test target
23        pub use crate::test::TestEngine as DefaultEngine;
24    }
25}
26
27/// Re-export of [`bolero_generator`]
28pub mod generator {
29    pub use bolero_generator::{self, prelude::*};
30}
31
32// For users' sake, re-expose the prelude functions straight under bolero::
33pub use bolero_generator::prelude::*;
34
35#[doc(hidden)]
36pub use bolero_engine::{self, TargetLocation, __item_path__};
37
38pub use bolero_engine::{Driver, Engine, Test};
39
40#[cfg(test)]
41mod tests;
42
43/// Execute tests for a given target
44///
45/// This should be executed in a separate test target, for example
46/// `tests/my_test_target/main.rs`.
47///
48/// # Examples
49///
50/// By default, `input` is a `&[u8]`.
51///
52/// This mode is generally used when testing an implementation that
53/// handles raw bytes, e.g. a parser.
54///
55/// ```rust
56/// use bolero::check;
57///
58/// check!().for_each(|input| {
59///     // implement checks here
60/// });
61/// ```
62///
63/// Calling `with_type::<Type>()` will generate random values of `Type`
64/// to be tested. `Type` is required to implement [`generator::TypeGenerator`]
65/// in order to use this method.
66///
67/// This mode is used for testing an implementation that requires
68/// structured input.
69///
70/// ```rust
71/// use bolero::check;
72///
73/// check!()
74///     .with_type::<(u8, u16)>()
75///     .for_each(|(a, b)| {
76///         // implement checks here
77///     });
78/// ```
79///
80/// The function `with_generator::<Generator>(generator)` will use the provided `Generator`,
81/// which implements [`generator::ValueGenerator`], to generate input
82/// values of type `Generator::Output`.
83///
84/// This mode is used for testing an implementation that requires
85/// structured input with specific constraints applied to the type.
86/// In the following example, we are only interested in generating
87/// two values, one being between 0 and 100, the other: 10 and 50.
88///
89/// ```rust
90/// use bolero::check;
91///
92/// check!()
93///     .with_generator((0..100, 10..50))
94///     .for_each(|(a, b)| {
95///         // implement checks here
96///     });
97/// ```
98///
99/// For compatibility purposes, `bolero` also supports the same interface as
100/// [rust-fuzz/afl.rs](https://github.com/rust-fuzz/afl.rs). This usage
101/// has a few downsides:
102///
103/// * The test cannot be configured
104/// * The test code will be contained inside a macro which can trip up
105///   some editors and IDEs.
106///
107/// ```rust
108/// use bolero::check;
109///
110/// check!(|input| {
111///     // implement checks here
112/// });
113/// ```
114#[macro_export]
115macro_rules! check {
116    () => {{
117        let location = $crate::TargetLocation {
118            package_name: env!("CARGO_PKG_NAME"),
119            manifest_dir: env!("CARGO_MANIFEST_DIR"),
120            module_path: module_path!(),
121            file: file!(),
122            line: line!(),
123            item_path: $crate::__item_path__!(),
124            test_name: None,
125        };
126
127        if !location.should_run() {
128            return;
129        }
130
131        $crate::test(location)
132    }};
133    ($fun:path) => {
134        $crate::check!(|input| { $fun(input) })
135    };
136    (| $input:ident $(: &[u8])? | $impl:expr) => {
137        $crate::check!().for_each(|$input: &[u8]| $impl)
138    };
139    (| $input:ident : $ty:ty | $impl:expr) => {
140        $crate::check!().with_type().for_each(|$input: $ty| $impl)
141    };
142    (name = $target_name:expr) => {{
143        let location = $crate::TargetLocation {
144            package_name: env!("CARGO_PKG_NAME"),
145            manifest_dir: env!("CARGO_MANIFEST_DIR"),
146            module_path: module_path!(),
147            file: file!(),
148            line: line!(),
149            item_path: $crate::__item_path__!(),
150            test_name: Some(format!("{}", $target_name)),
151        };
152
153        if !location.should_run() {
154            return;
155        }
156
157        $crate::test(location)
158    }};
159}
160
161#[macro_export]
162#[deprecated = "`fuzz!` has been deprecated in favor of `check!`."]
163macro_rules! fuzz {
164    ($($arg:tt)*) => {
165        $crate::check!($($arg)*)
166    }
167}
168
169/// Configuration for a test target
170pub struct TestTarget<Generator, Engine, InputOwnership> {
171    generator: Generator,
172    driver_options: bolero_generator::driver::Options,
173    engine: Engine,
174    input_ownership: PhantomData<InputOwnership>,
175}
176
177#[doc(hidden)]
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
179pub struct BorrowedInput;
180
181#[doc(hidden)]
182#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183pub struct ClonedInput;
184
185#[doc(hidden)]
186pub fn test(
187    location: TargetLocation,
188) -> TestTarget<ByteSliceGenerator, DefaultEngine, BorrowedInput> {
189    TestTarget::new(DefaultEngine::new(location))
190}
191
192/// Default generator for byte slices
193#[derive(Copy, Clone, Default, PartialEq, Eq)]
194pub struct ByteSliceGenerator;
195
196impl<Engine> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
197    /// Create a `TestTarget` with the given `Engine`
198    pub fn new(engine: Engine) -> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
199        Self {
200            generator: ByteSliceGenerator,
201            engine,
202            driver_options: Default::default(),
203            input_ownership: PhantomData,
204        }
205    }
206}
207
208impl<G, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
209    /// Set the value generator for the `TestTarget`
210    ///
211    /// The function `with_generator::<Generator>(generator)` will use the provided `Generator`,
212    /// which implements [`generator::ValueGenerator`], to generate input
213    /// values of type `Generator::Output`.
214    ///
215    /// This mode is used for testing an implementation that requires
216    /// structured input with specific constraints applied to the type.
217    pub fn with_generator<Generator: generator::ValueGenerator>(
218        self,
219        generator: Generator,
220    ) -> TestTarget<Generator, Engine, InputOwnership>
221    where
222        Generator::Output: Debug,
223    {
224        TestTarget {
225            generator,
226            engine: self.engine,
227            driver_options: self.driver_options,
228            input_ownership: self.input_ownership,
229        }
230    }
231
232    /// Set the type generator for the `TestTarget`
233    ///
234    /// Calling `with_type::<Type>()` will generate random values of `Type`
235    /// to be tested. `Type` is required to implement [`generator::TypeGenerator`]
236    /// in order to use this method.
237    ///
238    /// This mode is used for testing an implementation that requires
239    /// structured input.
240    pub fn with_type<T: Debug + generator::TypeGenerator>(
241        self,
242    ) -> TestTarget<TypeValueGenerator<T>, Engine, InputOwnership> {
243        TestTarget {
244            generator: generator::produce(),
245            engine: self.engine,
246            driver_options: self.driver_options,
247            input_ownership: self.input_ownership,
248        }
249    }
250
251    /// Set the generator for the `TestTarget` as being `arbitrary`-based
252    ///
253    /// Calling `with_arbitrary::<Type>()` will generate random values of `Type`
254    /// to be tested. `Type` is required to implement [`arbitrary::Arbitrary`]
255    /// in order to use this method.
256    ///
257    /// This mode is used for testing an implementation that requires
258    /// structured input.
259    #[cfg(feature = "arbitrary")]
260    pub fn with_arbitrary<T>(
261        self,
262    ) -> TestTarget<bolero_generator::arbitrary::ArbitraryGenerator<T>, Engine, InputOwnership>
263    where
264        T: 'static + Debug,
265        T: for<'a> bolero_generator::arbitrary::Arbitrary<'a>,
266    {
267        TestTarget {
268            generator: generator::gen_arbitrary(),
269            engine: self.engine,
270            driver_options: self.driver_options,
271            input_ownership: self.input_ownership,
272        }
273    }
274
275    /// Set the amount of time that will be spent shrinking an input on failure
276    ///
277    /// Engines can optionally shrink inputs on failures to make it easier to debug
278    /// and identify the failure. Increasing this time can potentially lead to smaller
279    /// values.
280    pub fn with_shrink_time(mut self, shrink_time: Duration) -> Self {
281        self.driver_options.set_shrink_time(shrink_time);
282        self
283    }
284
285    /// Exhaustively iterates over all of the possible inputs
286    ///
287    /// Note that if the search space is large this can take a long time.
288    pub fn exhaustive(mut self) -> Self {
289        self.driver_options.set_exhaustive(true);
290        self
291    }
292}
293
294impl<G: generator::ValueGenerator, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
295    /// Map the value of the generator
296    pub fn map<F: Fn(G::Output) -> T, T: Debug>(
297        self,
298        map: F,
299    ) -> TestTarget<MapGenerator<G, F>, Engine, InputOwnership> {
300        TestTarget {
301            generator: self.generator.map_gen(map),
302            engine: self.engine,
303            driver_options: self.driver_options,
304            input_ownership: self.input_ownership,
305        }
306    }
307
308    /// Map the value of the generator with a new generator
309    pub fn and_then<F: Fn(G::Output) -> T, T: generator::ValueGenerator>(
310        self,
311        map: F,
312    ) -> TestTarget<AndThenGenerator<G, F>, Engine, InputOwnership>
313    where
314        T::Output: Debug,
315    {
316        TestTarget {
317            generator: self.generator.and_then_gen(map),
318            engine: self.engine,
319            driver_options: self.driver_options,
320            input_ownership: self.input_ownership,
321        }
322    }
323
324    /// Filter the value of the generator
325    pub fn filter<F: Fn(&G::Output) -> bool>(
326        self,
327        filter: F,
328    ) -> TestTarget<FilterGenerator<G, F>, Engine, InputOwnership> {
329        TestTarget {
330            generator: self.generator.filter_gen(filter),
331            engine: self.engine,
332            driver_options: self.driver_options,
333            input_ownership: self.input_ownership,
334        }
335    }
336
337    /// Filter the value of the generator and map it to something else
338    pub fn filter_map<F: Fn(G::Output) -> Option<T>, T>(
339        self,
340        filter_map: F,
341    ) -> TestTarget<FilterMapGenerator<G, F>, Engine, InputOwnership> {
342        TestTarget {
343            generator: self.generator.filter_map_gen(filter_map),
344            engine: self.engine,
345            driver_options: self.driver_options,
346            input_ownership: self.input_ownership,
347        }
348    }
349
350    /// Set the driver mode for the test target
351    #[deprecated = "Driver mode is no longer being used by generator implementations"]
352    #[allow(deprecated)]
353    pub fn with_driver_mode(self, mode: DriverMode) -> Self {
354        let _ = mode;
355        self
356    }
357
358    /// Set the maximum number of recursive types that can be generated.
359    ///
360    /// The default value is `5`.
361    pub fn with_max_depth(mut self, max_depth: usize) -> Self {
362        self.driver_options.set_max_depth(max_depth);
363        self
364    }
365}
366
367cfg_if::cfg_if! {
368    if #[cfg(any(fuzzing, kani))] {
369        impl<G, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
370            /// Set the maximum runtime of the tests
371            pub fn with_test_time(self, test_time: Duration) -> Self {
372                let _ = test_time;
373                self
374            }
375
376            /// Set the number of iterations executed
377            pub fn with_iterations(self, iterations: usize) -> Self {
378                let _ = iterations;
379                self
380            }
381
382            /// Set the maximum length of the generated bytes
383            pub fn with_max_len(mut self, max_len: usize) -> Self {
384                self.driver_options.set_max_len(max_len);
385                self
386            }
387        }
388    } else {
389        impl<G, InputOwnership> TestTarget<G, crate::test::TestEngine, InputOwnership> {
390            /// Set the maximum runtime of the tests
391            pub fn with_test_time(mut self, test_time: Duration) -> Self {
392                self.engine.with_test_time(test_time);
393                self
394            }
395
396            /// Set the number of iterations executed
397            pub fn with_iterations(mut self, iterations: usize) -> Self {
398                self.engine.with_iterations(iterations);
399                self
400            }
401
402            /// Set the maximum length of the generated bytes
403            pub fn with_max_len(mut self, max_len: usize) -> Self {
404                self.driver_options.set_max_len(max_len);
405                self.engine.with_max_len(max_len);
406                self
407            }
408        }
409    }
410}
411
412impl<G, Engine> TestTarget<G, Engine, BorrowedInput>
413where
414    G: generator::ValueGenerator,
415    <G as generator::ValueGenerator>::Output: Clone,
416{
417    /// Use a cloned value for the test input
418    ///
419    /// Cloning the test inputs will force a call to [`Clone::clone`]
420    /// on each input value, and therefore, will be less
421    /// efficient than using a reference.
422    pub fn cloned(self) -> TestTarget<G, Engine, ClonedInput> {
423        TestTarget {
424            generator: self.generator,
425            engine: self.engine,
426            driver_options: self.driver_options,
427            input_ownership: PhantomData,
428        }
429    }
430}
431
432impl<G, E> TestTarget<G, E, BorrowedInput>
433where
434    G: generator::ValueGenerator,
435{
436    /// Iterate over all of the inputs and check the `TestTarget`
437    pub fn for_each<F>(self, test: F) -> E::Output
438    where
439        E: Engine<bolero_engine::BorrowedGeneratorTest<F, G, G::Output>>,
440        bolero_engine::BorrowedGeneratorTest<F, G, G::Output>: Test,
441    {
442        let test = bolero_engine::BorrowedGeneratorTest::new(test, self.generator);
443        self.engine.run(test, self.driver_options)
444    }
445}
446
447impl<G, E> TestTarget<G, E, ClonedInput>
448where
449    G: generator::ValueGenerator,
450{
451    /// Iterate over all of the inputs and check the `TestTarget`
452    pub fn for_each<F>(self, test: F) -> E::Output
453    where
454        E: Engine<bolero_engine::ClonedGeneratorTest<F, G, G::Output>>,
455        bolero_engine::ClonedGeneratorTest<F, G, G::Output>: Test,
456    {
457        let test = bolero_engine::ClonedGeneratorTest::new(test, self.generator);
458        self.engine.run(test, self.driver_options)
459    }
460}
461
462impl<E> TestTarget<ByteSliceGenerator, E, BorrowedInput> {
463    /// Iterate over all of the inputs and check the `TestTarget`
464    pub fn for_each<T>(self, test: T) -> E::Output
465    where
466        E: Engine<bolero_engine::BorrowedSliceTest<T>>,
467        bolero_engine::BorrowedSliceTest<T>: Test,
468    {
469        let test = bolero_engine::BorrowedSliceTest::new(test);
470        self.engine.run(test, self.driver_options)
471    }
472
473    /// Iterate over all of the inputs and check the `TestTarget`
474    #[cfg(feature = "std")]
475    pub fn run<T, R>(self, mut test: T) -> E::Output
476    where
477        T: FnMut() -> R + core::panic::RefUnwindSafe,
478        R: bolero_engine::IntoResult,
479        E: bolero_engine::ScopedEngine,
480    {
481        self.engine.run(move |_| test(), self.driver_options)
482    }
483
484    #[cfg(feature = "std")]
485    pub fn run_with_replay<T, R>(self, test: T) -> E::Output
486    where
487        T: FnMut(bool) -> R + core::panic::RefUnwindSafe,
488        R: bolero_engine::IntoResult,
489        E: bolero_engine::ScopedEngine,
490    {
491        self.engine.run(test, self.driver_options.with_replay_on_fail(true))
492    }
493}
494
495impl<E> TestTarget<ByteSliceGenerator, E, ClonedInput> {
496    /// Iterate over all of the inputs and check the `TestTarget`
497    pub fn for_each<T>(self, test: T) -> E::Output
498    where
499        E: Engine<bolero_engine::ClonedSliceTest<T>>,
500        bolero_engine::ClonedSliceTest<T>: Test,
501    {
502        let test = bolero_engine::ClonedSliceTest::new(test);
503        self.engine.run(test, self.driver_options)
504    }
505}