fuzzcheck/
builder.rs

1/*!
2Builders used to set up a fuzz test.
3
4This module contains 5 types to build a fuzz test: `FuzzerBuilder[1–5]`.
5
6The idea is to help you specify each part of the fuzzer progressively:
71. the function to fuzz
82. the [mutator](crate::Mutator) to generate arguments to the test function (called “inputs” or “test cases”)
93. the [serializer](crate::Serializer) to save test cases to the file system
104. the [sensor](crate::Sensor) to provide feedback after running the test function, and the [pool](crate::Pool) to interpret the feedback from the sensor
115. [other settings](crate::Arguments) for the fuzzer, such as the maximum allowed complexity for the test cases, where to save the corpora or artifacts on the file system, etc.
12
13In most cases, you don't need to manually specify all these components. If the argument type of the function has a [default mutator](crate::DefaultMutator) and is serializable with serde, then you can write:
14```no_run
15# fn test_function(x: &bool) {}
16let _ = fuzzcheck::fuzz_test(test_function) // FuzzerBuilder1
17    .default_options() // FuzzerBuilder5!  we use the default values for stages 2 to 5
18    .launch();
19
20```
21This is equivalent to:
22```no_run
23# use fuzzcheck::DefaultMutator;
24# fn test_function(x: &bool) {}
25#
26# fn fuzz() {
27let _ = fuzzcheck::fuzz_test(test_function)
28    .default_mutator()      // the default is `<T as DefaultMutator>::default_mutator()`
29    .serde_serializer()   // the default is `SerdeSerializer::new()`
30    .default_sensor_and_pool() // the default is `default_sensor_and_pool().finish()`
31    .arguments_from_cargo_fuzzcheck()
32    .launch();
33# }
34```
35If you'd like to use a custom mutator, serializer, sensor and pool, or arguments, you can write:
36```no_run
37# use fuzzcheck::DefaultMutator;
38# use fuzzcheck::builder::default_sensor_and_pool;
39# use fuzzcheck::Arguments;
40# fn test_function(x: &bool) {}
41#
42# fn fuzz() {
43# let my_mutator = bool::default_mutator();
44# let my_serializer = fuzzcheck::SerdeSerializer::default();
45# let (sensor, pool) = default_sensor_and_pool().finish();
46# let arguments: Arguments = todo!();
47let _ = fuzzcheck::fuzz_test(test_function)
48    .mutator(my_mutator)         // the default is `<T as DefaultMutator>::default_mutator()`
49    .serializer(my_serializer)   // the default is `SerdeSerializer::new()`
50    .sensor_and_pool(sensor, pool)
51    .arguments(arguments)
52    .launch();
53# }
54```
55
56To build a custom sensor and pool, you may want to look at the [`Sensor`], [`Pool`](crate::Pool), and [`CompatibleWithObservations`] traits.
57You can also look at the types provided in the [`sensors_and_pools`](crate::sensors_and_pools) module. But the easiest way to customize them
58is to use the [`SensorAndPoolBuilder`], although it only offers a couple limited options.
59*/
60
61use std::borrow::Borrow;
62use std::marker::PhantomData;
63use std::path::Path;
64use std::result::Result;
65use std::time::Duration;
66
67use fuzzcheck_common::arg::{
68    options_parser, Arguments, ArgumentsError, FuzzerCommand, COMMAND_FUZZ, COMMAND_MINIFY_INPUT, INPUT_FILE_FLAG,
69};
70
71use crate::code_coverage_sensor::CodeCoverageSensor;
72use crate::fuzzer::{Fuzzer, FuzzingResult};
73use crate::sensors_and_pools::{
74    AndPool, DifferentObservations, MaximiseEachCounterPool, MaximiseObservationPool, MostNDiversePool,
75    SameObservations, SimplestToActivateCounterPool, WrapperSensor,
76};
77#[cfg(feature = "serde_ron_serializer")]
78use crate::SerdeRonSerializer;
79#[cfg(feature = "serde_json_serializer")]
80use crate::SerdeSerializer;
81use crate::{
82    split_string_by_whitespace, CompatibleWithObservations, DefaultMutator, Mutator, PoolExt, Sensor, SensorExt,
83    Serializer,
84};
85
86/** A function that can be fuzz-tested.
87
88Strictly speaking, fuzzcheck can only test functions of type `Fn(&T) -> bool`.
89Using this trait, we can convert other types of functions to `Fn(&T) -> bool`
90automatically. For example, a function `fn foo(x: &u8) -> Result<T, E>` can be
91wrapped in a closure that returns `true` iff `foo(x)` is `Ok(..)`.
92*/
93pub trait FuzzTestFunction<T, FT: ?Sized, ImplId> {
94    type NormalizedFunction: for<'a> Fn(&'a T) -> bool;
95    fn test_function(self) -> Self::NormalizedFunction;
96}
97
98/// Marker type for a function of type `Fn(&T) -> bool`
99pub enum ReturnBool {}
100/// Marker type for a function of type `Fn(&T)`
101pub enum ReturnVoid {}
102/// Marker type for a function of type `Fn(&T) -> Result<V, E>`
103pub enum ReturnResult {}
104
105impl<T, FT: ?Sized, F> FuzzTestFunction<T, FT, ReturnBool> for F
106where
107    T: Borrow<FT>,
108    F: Fn(&FT) -> bool,
109{
110    type NormalizedFunction = impl Fn(&T) -> bool;
111    #[coverage(off)]
112    fn test_function(self) -> Self::NormalizedFunction {
113        #[coverage(off)]
114        move |x| (self)(x.borrow())
115    }
116}
117impl<T, FT: ?Sized, F> FuzzTestFunction<T, FT, ReturnVoid> for F
118where
119    T: Borrow<FT>,
120    F: Fn(&FT),
121{
122    type NormalizedFunction = impl Fn(&T) -> bool;
123    #[coverage(off)]
124    fn test_function(self) -> Self::NormalizedFunction {
125        #[coverage(off)]
126        move |x| {
127            self(x.borrow());
128            true
129        }
130    }
131}
132
133impl<T, FT: ?Sized, F, S, E> FuzzTestFunction<T, FT, ReturnResult> for F
134where
135    T: Borrow<FT>,
136    F: Fn(&FT) -> Result<E, S>,
137{
138    type NormalizedFunction = impl Fn(&T) -> bool;
139    #[coverage(off)]
140    fn test_function(self) -> Self::NormalizedFunction {
141        move |x| self(x.borrow()).is_ok()
142    }
143}
144
145/// A fuzz-test builder that knows the function to fuzz-test. It is created by calling [`fuzz_test(..)`](fuzz_test).
146///
147/// Use [`self.mutator(..)`](FuzzerBuilder1::mutator) to specify the [mutator](Mutator)
148/// and obtain a [`FuzzerBuilder2`]. If the function argument’s type implements [`DefaultMutator`],
149/// you can also use [`self.default_mutator()`](FuzzerBuilder1::default_mutator).
150///
151/// Alternatively, use [`self.default_options()`](FuzzerBuilder1::default_options)
152/// to use the default mutator, serializer, sensor, pool, and arguments, and obtain a [`FuzzerBuilder5`].
153/// This method is only available if the argument of the test function implements [`DefaultMutator`]
154/// and is serializable with serde.
155pub struct FuzzerBuilder1<T, F>
156where
157    T: ?Sized,
158    F: Fn(&T) -> bool + 'static,
159{
160    test_function: F,
161    _phantom: PhantomData<*const T>,
162}
163
164/// A fuzz-test builder that knows the function to fuzz-test and the mutator.
165///
166/// Use [`self.serializer(..)`](FuzzerBuilder2::serializer) to specify the [serializer](Serializer) and obtain a [`FuzzerBuilder3`].
167pub struct FuzzerBuilder2<F, M, V>
168where
169    F: Fn(&V) -> bool + 'static,
170    V: Clone + 'static,
171    M: Mutator<V>,
172{
173    test_function: F,
174    mutator: M,
175    _phantom: PhantomData<*const V>,
176}
177
178/// A fuzz-test builder that knows the function to fuzz-test, the mutator, and the serializer.
179///
180/// Use [`self.sensor_and_pool(..)`](FuzzerBuilder3::sensor_and_pool) to specify the [sensor](Sensor) and [pool](crate::Pool) and obtain a [FuzzerBuilder4].
181///
182/// Alternatively, use [`self.default_sensor_and_pool(..)`](FuzzerBuilder3::default_sensor_and_pool) to use fuzzcheck’s
183/// default sensor, which monitors code coverage.
184pub struct FuzzerBuilder3<F, M, V>
185where
186    F: Fn(&V) -> bool + 'static,
187    V: Clone + 'static,
188    M: Mutator<V>,
189{
190    test_function: F,
191    mutator: M,
192    serializer: Box<dyn Serializer<Value = V>>,
193    _phantom: PhantomData<*const V>,
194}
195
196/// A fuzz-test builder that knows the function to fuzz-test, the mutator, the serializer, the sensor, and the pool.
197///
198/// Use [`self.arguments(..)`] to specify the [arguments](Arguments) and obtain a [`FuzzerBuilder5`].
199///
200/// If you are using the `cargo-fuzzcheck` command line tool (and you should), use
201/// [`self.arguments_from_cargo_fuzzcheck()`](FuzzerBuilder4::arguments_from_cargo_fuzzcheck)
202/// to use the arguments specified by this tool, which is easier.
203pub struct FuzzerBuilder4<F, M, V, Sens, P>
204where
205    F: Fn(&V) -> bool + 'static,
206    V: Clone + 'static,
207    M: Mutator<V>,
208    Sens: Sensor,
209    P: CompatibleWithObservations<Sens::Observations>,
210{
211    test_function: F,
212    mutator: M,
213    serializer: Box<dyn Serializer<Value = V>>,
214    sensor: Sens,
215    pool: P,
216    _phantom: PhantomData<*const V>,
217}
218
219/// A fuzz-test builder that knows every necessary detail to start fuzzing.
220///
221/// Use [`self.launch()`](FuzzerBuilder5::launch) to start fuzzing.
222///
223/// You can also override some arguments using:
224/// * [`self.command(..)`](FuzzerBuilder5::command)
225/// * [`self.in_corpus(..)`](FuzzerBuilder5::in_corpus)
226/// * [`self.out_corpus(..)`](FuzzerBuilder5::out_corpus)
227/// * [`self.artifacts_folder(..)`](FuzzerBuilder5::artifacts_folder)
228/// * [`self.maximum_complexity(..)`](FuzzerBuilder5::maximum_complexity)
229/// * [`self.stop_after_iterations(..)`](FuzzerBuilder5::stop_after_iterations)
230/// * [`self.stop_after_duration(..)`](FuzzerBuilder5::stop_after_duration)
231/// * [`self.stop_after_first_test_failure(..)`](FuzzerBuilder5::stop_after_first_test_failure)
232pub struct FuzzerBuilder5<F, M, V, Sens, P>
233where
234    F: Fn(&V) -> bool + 'static,
235    V: Clone + 'static,
236    M: Mutator<V>,
237    Sens: Sensor,
238    P: CompatibleWithObservations<Sens::Observations>,
239{
240    test_function: F,
241    mutator: M,
242    serializer: Box<dyn Serializer<Value = V>>,
243    sensor: Sens,
244    pool: P,
245    arguments: Arguments,
246    _phantom: PhantomData<*const V>,
247}
248
249/**
250    Build a fuzz test for the given function!
251
252    The returned value is a [`FuzzerBuilder1`]. See the [module/crate documentation](crate::builder)
253    for a full example of how to build a fuzz test.
254
255    There are currently three kinds of functions that can be passed as arguments:
256
257    1. `Fn(&T)` : the fuzzer will only report a failure when the given function crashes
258    2. `Fn(&T) -> Bool` : the fuzzer will report a failure when the output is `false`
259    3. `Fn(&T) -> Result<_,_>` : the fuzzer will report a failure when the output is `Err(..)`
260*/
261#[coverage(off)]
262pub fn fuzz_test<T, F, TestFunctionKind>(test_function: F) -> FuzzerBuilder1<T::Owned, F::NormalizedFunction>
263where
264    T: ?Sized + ToOwned + 'static,
265    T::Owned: Clone,
266    F: FuzzTestFunction<T::Owned, T, TestFunctionKind>,
267{
268    FuzzerBuilder1 {
269        test_function: test_function.test_function(),
270        _phantom: PhantomData,
271    }
272}
273
274#[cfg(feature = "serde_json_serializer")]
275impl<T, F> FuzzerBuilder1<T, F>
276where
277    T: ?Sized + ToOwned + 'static,
278    T::Owned: Clone + serde::Serialize + for<'e> serde::Deserialize<'e> + DefaultMutator,
279    <T::Owned as DefaultMutator>::Mutator: 'static,
280    F: Fn(&T) -> bool,
281    F: FuzzTestFunction<T::Owned, T, ReturnBool>,
282{
283    /// Use the default mutator, serializer, sensor, pool, and arguments.
284    #[doc(cfg(feature = "serde_json_serializer"))]
285    #[coverage(off)]
286    pub fn default_options(
287        self,
288    ) -> FuzzerBuilder5<
289        F::NormalizedFunction,
290        <T::Owned as DefaultMutator>::Mutator,
291        T::Owned,
292        impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
293        BasicAndDiverseAndMaxHitsPool,
294    > {
295        self.mutator(<T::Owned as DefaultMutator>::default_mutator())
296            .serializer(SerdeSerializer::default())
297            .default_sensor_and_pool()
298            .arguments_from_cargo_fuzzcheck()
299    }
300}
301
302impl<T, F> FuzzerBuilder1<T, F>
303where
304    T: ?Sized + ToOwned + 'static,
305    T::Owned: Clone + DefaultMutator,
306    <T::Owned as DefaultMutator>::Mutator: 'static,
307    F: Fn(&T) -> bool,
308    F: FuzzTestFunction<T::Owned, T, ReturnBool>,
309{
310    /// Use the [`DefaultMutator`] trait to specify the mutator that produces input values for the tested function.
311    #[coverage(off)]
312    pub fn default_mutator(
313        self,
314    ) -> FuzzerBuilder2<F::NormalizedFunction, <T::Owned as DefaultMutator>::Mutator, T::Owned> {
315        self.mutator(<T::Owned as DefaultMutator>::default_mutator())
316    }
317}
318impl<T, F> FuzzerBuilder1<T, F>
319where
320    T: ?Sized,
321    F: Fn(&T) -> bool,
322{
323    /**
324        Specify the mutator that produces input values for the tested function.
325
326        For example, if the test function is:
327        ```
328        fn foo(xs: &[u8]) {
329            // ..
330        }
331        ```
332        Then the given mutator should produces values that can be borrowed as `[u8]`.
333        We can write:
334        ```
335        use fuzzcheck::DefaultMutator;
336        use fuzzcheck::mutators::vector::VecMutator;
337        fn foo(xs: &[u8]) {
338            // ..
339        }
340        fn fuzz_test() {
341            fuzzcheck::fuzz_test(foo)
342                .mutator(VecMutator::new(u8::default_mutator(), 2 ..= 10))
343                // ..
344                # ;
345        }
346        ```
347        Alternatively, if you would like to use the argument type’s [default mutator](DefaultMutator), you can use
348        [`.default_mutator()`](FuzzerBuilder1::default_mutator), as follows:
349        ```
350        use fuzzcheck::DefaultMutator;
351        fn foo(xs: &[u8]) {
352            // ..
353        }
354        fn fuzz_test() {
355            fuzzcheck::fuzz_test(foo)
356                .default_mutator()
357                // ..
358                # ;
359        }
360        ```
361    */
362    #[coverage(off)]
363    pub fn mutator<M, V>(self, mutator: M) -> FuzzerBuilder2<F::NormalizedFunction, M, V>
364    where
365        V: Clone + Borrow<T>,
366        F: FuzzTestFunction<V, T, ReturnBool>,
367        M: Mutator<V>,
368    {
369        FuzzerBuilder2 {
370            test_function: self.test_function.test_function(),
371            mutator,
372            _phantom: PhantomData,
373        }
374    }
375}
376
377impl<F, M, V> FuzzerBuilder2<F, M, V>
378where
379    F: Fn(&V) -> bool,
380    V: Clone + 'static,
381    M: Mutator<V>,
382{
383    /**
384        Specify the serializer to use when saving the interesting test cases to the file system.
385
386        The serializer must implement the [`Serializer`](crate::Serializer) trait. If you wish
387        to use `serde`, you can use [`.serde_serializer()`](FuzzerBuilder2::serde_serializer) as follows:
388        ```
389        # use fuzzcheck::DefaultMutator;
390        # fn foo(x: &bool) {}
391        fuzzcheck::fuzz_test(foo)
392            .mutator(
393                # bool::default_mutator()
394                /* .. */
395            )
396            .serde_serializer()
397            # ;
398        ```
399    */
400    #[coverage(off)]
401    pub fn serializer<S>(self, serializer: S) -> FuzzerBuilder3<F, M, V>
402    where
403        S: Serializer<Value = V> + 'static,
404    {
405        FuzzerBuilder3 {
406            test_function: self.test_function,
407            mutator: self.mutator,
408            serializer: Box::new(serializer),
409            _phantom: PhantomData,
410        }
411    }
412}
413
414#[cfg(feature = "serde_json_serializer")]
415impl<F, M, V> FuzzerBuilder2<F, M, V>
416where
417    F: Fn(&V) -> bool,
418    V: Clone + serde::Serialize + for<'e> serde::Deserialize<'e> + 'static,
419    M: Mutator<V>,
420{
421    /// Specify [`SerdeSerializer`] as the serializer to use when saving the interesting test cases to the file system.
422    #[coverage(off)]
423    pub fn serde_serializer(self) -> FuzzerBuilder3<F, M, V> {
424        FuzzerBuilder3 {
425            test_function: self.test_function,
426            mutator: self.mutator,
427            serializer: Box::new(SerdeSerializer::<V>::default()),
428            _phantom: PhantomData,
429        }
430    }
431}
432
433#[cfg(feature = "serde_ron_serializer")]
434impl<F, M, V> FuzzerBuilder2<F, M, V>
435where
436    F: Fn(&V) -> bool,
437    V: Clone + serde::Serialize + for<'e> serde::Deserialize<'e> + 'static,
438    M: Mutator<V>,
439{
440    /// Specify [`SerdeRonSerializer`] as the serializer to use when saving the
441    /// interesting test cases to the file system.
442    #[coverage(off)]
443    pub fn serde_ron_serializer(self) -> FuzzerBuilder3<F, M, V> {
444        FuzzerBuilder3 {
445            test_function: self.test_function,
446            mutator: self.mutator,
447            serializer: Box::new(SerdeRonSerializer::<V>::default()),
448            _phantom: PhantomData,
449        }
450    }
451}
452
453impl<F, M, V> FuzzerBuilder3<F, M, V>
454where
455    F: Fn(&V) -> bool,
456    V: Clone + 'static,
457    M: Mutator<V>,
458{
459    #[coverage(off)]
460    pub fn default_sensor_and_pool_with_custom_filter(
461        self,
462        keep: impl Fn(&Path, &str) -> bool,
463    ) -> FuzzerBuilder4<
464        F,
465        M,
466        V,
467        impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
468        BasicAndDiverseAndMaxHitsPool,
469    > {
470        let (sensor, pool) = default_sensor_and_pool_with_custom_filter(keep).finish();
471        FuzzerBuilder4 {
472            test_function: self.test_function,
473            mutator: self.mutator,
474            serializer: self.serializer,
475            sensor,
476            pool,
477            _phantom: PhantomData,
478        }
479    }
480
481    #[coverage(off)]
482    /// Uses the default sensor and pool. For most cases this is desirable, but
483    /// sometimes you might want to instead use
484    /// [`FuzzerBuilder3::sensor_and_pool`]. This is especially true because the
485    /// default sensor and pool tries to find test cases which take a long time
486    /// to execute - this slows down the fuzzer.
487    pub fn default_sensor_and_pool(
488        self,
489    ) -> FuzzerBuilder4<
490        F,
491        M,
492        V,
493        impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
494        BasicAndDiverseAndMaxHitsPool,
495    > {
496        let (sensor, pool) = default_sensor_and_pool().finish();
497        FuzzerBuilder4 {
498            test_function: self.test_function,
499            mutator: self.mutator,
500            serializer: self.serializer,
501            sensor,
502            pool,
503            _phantom: PhantomData,
504        }
505    }
506    #[coverage(off)]
507    pub fn sensor_and_pool<Sens: Sensor, P: CompatibleWithObservations<Sens::Observations>>(
508        self,
509        sensor: Sens,
510        pool: P,
511    ) -> FuzzerBuilder4<F, M, V, Sens, P> {
512        FuzzerBuilder4 {
513            test_function: self.test_function,
514            mutator: self.mutator,
515            serializer: self.serializer,
516            sensor,
517            pool,
518            _phantom: PhantomData,
519        }
520    }
521}
522
523impl<F, M, V, Sens, P> FuzzerBuilder4<F, M, V, Sens, P>
524where
525    F: Fn(&V) -> bool,
526    V: Clone + 'static,
527    M: Mutator<V>,
528    Sens: Sensor,
529    P: CompatibleWithObservations<Sens::Observations>,
530{
531    #[coverage(off)]
532    pub fn arguments(self, arguments: Arguments) -> FuzzerBuilder5<F, M, V, Sens, P> {
533        FuzzerBuilder5 {
534            test_function: self.test_function,
535            mutator: self.mutator,
536            serializer: self.serializer,
537            sensor: self.sensor,
538            pool: self.pool,
539            arguments,
540            _phantom: self._phantom,
541        }
542    }
543    #[coverage(off)]
544    pub fn arguments_from_cargo_fuzzcheck(self) -> FuzzerBuilder5<F, M, V, Sens, P> {
545        let parser = options_parser();
546        let mut help = format!(
547            r#""
548fuzzcheck <SUBCOMMAND> [OPTIONS]
549
550SUBCOMMANDS:
551    {fuzz}    Run the fuzz test
552    {minify}    Minify a crashing test input, requires --{input_file}
553"#,
554            fuzz = COMMAND_FUZZ,
555            minify = COMMAND_MINIFY_INPUT,
556            input_file = INPUT_FILE_FLAG,
557        );
558        help += parser.usage("").as_str();
559        help += format!(
560            r#""
561## Examples:
562
563fuzzcheck {fuzz}
564    Launch the fuzzer with default options.
565
566fuzzcheck {minify} --{input_file} "artifacts/crash.json"
567
568    Minify the test input defined in the file "artifacts/crash.json".
569    It will put minified inputs in the folder artifacts/crash.minified/
570    and name them {{complexity}}-{{hash}}.json. 
571    For example, artifacts/crash.minified/4213--8cd7777109b57b8c.json
572    is a minified input of complexity 42.13.
573"#,
574            fuzz = COMMAND_FUZZ,
575            minify = COMMAND_MINIFY_INPUT,
576            input_file = INPUT_FILE_FLAG,
577        )
578        .as_str();
579
580        let arguments = std::env::var("FUZZCHECK_ARGS").unwrap();
581        let arguments = split_string_by_whitespace(&arguments);
582        let matches = parser.parse(arguments).map_err(ArgumentsError::from);
583        let arguments = match matches.and_then(
584            #[coverage(off)]
585            |matches| Arguments::from_matches(&matches, false),
586        ) {
587            Ok(r) => r,
588            Err(e) => {
589                println!("{}\n\n{}", e, help);
590                std::process::exit(1);
591            }
592        };
593        FuzzerBuilder5 {
594            test_function: self.test_function,
595            mutator: self.mutator,
596            serializer: self.serializer,
597            sensor: self.sensor,
598            pool: self.pool,
599            arguments,
600            _phantom: PhantomData,
601        }
602    }
603}
604
605impl<F, M, V, Sens, P> FuzzerBuilder5<F, M, V, Sens, P>
606where
607    F: Fn(&V) -> bool + 'static,
608    V: Clone + 'static,
609    M: Mutator<V>,
610    Sens: Sensor + 'static,
611    P: CompatibleWithObservations<Sens::Observations> + 'static,
612    Fuzzer<V, M>: 'static,
613{
614    #[must_use]
615    #[coverage(off)]
616    pub fn command(self, command: FuzzerCommand) -> Self {
617        let mut x = self;
618        x.arguments.command = command;
619        x
620    }
621    #[must_use]
622    #[coverage(off)]
623    pub fn in_corpus(self, path: Option<&Path>) -> Self {
624        let mut x = self;
625        x.arguments.corpus_in = path.map(Path::to_path_buf);
626        x
627    }
628    #[must_use]
629    #[coverage(off)]
630    pub fn out_corpus(self, path: Option<&Path>) -> Self {
631        let mut x = self;
632        x.arguments.corpus_out = path.map(Path::to_path_buf);
633        x
634    }
635    #[must_use]
636    #[coverage(off)]
637    pub fn artifacts_folder(self, path: Option<&Path>) -> Self {
638        let mut x = self;
639        x.arguments.artifacts_folder = path.map(Path::to_path_buf);
640        x
641    }
642    #[must_use]
643    #[coverage(off)]
644    pub fn maximum_complexity(self, max_input_cplx: f64) -> Self {
645        let mut x = self;
646        x.arguments.max_input_cplx = max_input_cplx;
647        x
648    }
649    #[must_use]
650    #[coverage(off)]
651    pub fn stop_after_iterations(self, number_of_iterations: usize) -> Self {
652        let mut x = self;
653        x.arguments.maximum_iterations = number_of_iterations;
654        x
655    }
656    #[must_use]
657    #[coverage(off)]
658    pub fn stop_after_duration(self, duration: Duration) -> Self {
659        let mut x = self;
660        x.arguments.maximum_duration = duration;
661        x
662    }
663    #[must_use]
664    #[coverage(off)]
665    pub fn stop_after_first_test_failure(self, stop_after_first_test_failure: bool) -> Self {
666        let mut x = self;
667        x.arguments.stop_after_first_failure = stop_after_first_test_failure;
668        x
669    }
670    /// Launch the fuzz test!
671    #[coverage(off)]
672    pub fn launch(self) -> FuzzingResult<V> {
673        let FuzzerBuilder5 {
674            test_function,
675            mutator,
676            serializer,
677            pool,
678            sensor,
679            arguments,
680            _phantom,
681        } = self;
682
683        mutator.initialize();
684
685        crate::fuzzer::launch(
686            Box::new(test_function),
687            mutator,
688            serializer,
689            Box::new((sensor, pool)),
690            arguments,
691        )
692    }
693}
694
695pub type BasicSensor = CodeCoverageSensor;
696pub type DiverseSensor = impl WrapperSensor<
697    Wrapped = CodeCoverageSensor,
698    Observations = (<CodeCoverageSensor as Sensor>::Observations, usize),
699>;
700pub type MaxHitsSensor = impl WrapperSensor<
701    Wrapped = CodeCoverageSensor,
702    Observations = (<CodeCoverageSensor as Sensor>::Observations, u64),
703>;
704pub type BasicAndMaxHitsSensor = impl WrapperSensor<
705    Wrapped = CodeCoverageSensor,
706    Observations = (<CodeCoverageSensor as Sensor>::Observations, u64),
707>;
708
709pub type BasicPool = SimplestToActivateCounterPool;
710pub type DiversePool = AndPool<MostNDiversePool, MaximiseObservationPool<u64>, DifferentObservations>;
711pub type MaxHitsPool = AndPool<MaximiseEachCounterPool, MaximiseObservationPool<u64>, DifferentObservations>;
712pub type BasicAndDiversePool = AndPool<
713    AndPool<SimplestToActivateCounterPool, MostNDiversePool, SameObservations>,
714    MaximiseObservationPool<usize>,
715    DifferentObservations,
716>;
717pub type BasicAndMaxHitsPool = AndPool<
718    AndPool<SimplestToActivateCounterPool, MaximiseEachCounterPool, SameObservations>,
719    MaximiseObservationPool<u64>,
720    DifferentObservations,
721>;
722
723pub type BasicAndDiverseAndMaxHitsPool = AndPool<
724    AndPool<
725        AndPool<SimplestToActivateCounterPool, MostNDiversePool, SameObservations>,
726        MaximiseEachCounterPool,
727        SameObservations,
728    >,
729    AndPool<MaximiseObservationPool<usize>, MaximiseObservationPool<u64>, DifferentObservations>,
730    DifferentObservations,
731>;
732
733#[coverage(off)]
734pub fn max_cov_hits_sensor_and_pool() -> SensorAndPoolBuilder<MaxHitsSensor, MaxHitsPool> {
735    let sensor = CodeCoverageSensor::observing_only_files_from_current_dir();
736    let nbr_counters = sensor.count_instrumented;
737    let sensor = sensor.map(
738        #[coverage(off)]
739        |o| {
740            let sum = o
741                .iter()
742                .map(
743                    #[coverage(off)]
744                    |(_, count)| count,
745                )
746                .sum::<u64>();
747            (o, sum)
748        },
749    );
750    let pool = MaximiseEachCounterPool::new("max_each_cov_hits", nbr_counters).and(
751        MaximiseObservationPool::new("max_total_cov_hits"),
752        Some(0.1),
753        DifferentObservations,
754    );
755    SensorAndPoolBuilder { sensor, pool }
756}
757
758/// Create the initial [sensor and pool builder](SensorAndPoolBuilder)
759///
760/// Use [`.find_most_diverse_set_of_test_cases()`](SensorAndPoolBuilder::<BasicPool>::find_most_diverse_set_of_test_cases)
761/// or [`.find_test_cases_repeatedly_hitting_coverage_counters()`](SensorAndPoolBuilder::<BasicPool>::find_test_cases_repeatedly_hitting_coverage_counters)
762/// on the result to augment the pool. Or use [`.finish()`](SensorAndPoolBuilder::finish) to obtain the concrete sensor and pool.
763#[coverage(off)]
764pub fn basic_sensor_and_pool() -> SensorAndPoolBuilder<BasicSensor, BasicPool> {
765    let sensor = CodeCoverageSensor::observing_only_files_from_current_dir();
766    let nbr_counters = sensor.count_instrumented;
767    SensorAndPoolBuilder {
768        sensor,
769        pool: SimplestToActivateCounterPool::new("simplest_cov", nbr_counters),
770    }
771}
772
773/// Like [`basic_sensor_and_pool`], but uses a closure to determine which function should
774/// be observed by the code coverage sensor.
775#[coverage(off)]
776pub fn basic_sensor_and_pool_with_custom_filter(
777    keep: impl Fn(&Path, &str) -> bool,
778) -> SensorAndPoolBuilder<BasicSensor, BasicPool> {
779    let sensor = CodeCoverageSensor::new(keep);
780    let nbr_counters = sensor.count_instrumented;
781    SensorAndPoolBuilder {
782        sensor,
783        pool: SimplestToActivateCounterPool::new("simplest_cov", nbr_counters),
784    }
785}
786
787/// Create the [sensor and pool builder](SensorAndPoolBuilder) that is used by default by fuzzcheck
788///
789/// Currently, the result cannot be augmented any further. Thus, the only action you can take on the result is to
790/// use [`.finish()`](SensorAndPoolBuilder::finish) to obtain the concrete sensor and pool.
791#[coverage(off)]
792pub fn default_sensor_and_pool() -> SensorAndPoolBuilder<
793    impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
794    BasicAndDiverseAndMaxHitsPool,
795> {
796    basic_sensor_and_pool()
797        .find_most_diverse_set_of_test_cases(20)
798        .find_test_cases_repeatedly_hitting_coverage_counters()
799}
800
801/// Like [`default_sensor_and_pool`], but uses a closure to determine which function should
802/// be observed by the code coverage sensor.
803#[coverage(off)]
804pub fn default_sensor_and_pool_with_custom_filter(
805    keep: impl Fn(&Path, &str) -> bool,
806) -> SensorAndPoolBuilder<
807    impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
808    BasicAndDiverseAndMaxHitsPool,
809> {
810    basic_sensor_and_pool_with_custom_filter(keep)
811        .find_most_diverse_set_of_test_cases(20)
812        .find_test_cases_repeatedly_hitting_coverage_counters()
813}
814
815/// A builder to create a [sensor](Sensor) and [pool](crate::Pool) that can be given as argument to
816/// [`FuzzerBuilder3::sensor_and_pool`].
817///
818/// # Usage
819/// ```no_run
820/// use fuzzcheck::builder::basic_sensor_and_pool;
821///
822/// let (sensor, pool) = basic_sensor_and_pool()
823///     .find_most_diverse_set_of_test_cases(10) // optional
824///     .find_test_cases_repeatedly_hitting_coverage_counters() // optional
825///     .finish(); // mandatory
826/// ```
827pub struct SensorAndPoolBuilder<S, P>
828where
829    S: Sensor,
830    P: CompatibleWithObservations<S::Observations>,
831{
832    sensor: S,
833    pool: P,
834}
835
836impl<S, P> SensorAndPoolBuilder<S, P>
837where
838    S: Sensor,
839    P: CompatibleWithObservations<S::Observations>,
840{
841    /// Obtain the sensor and pool from the builder
842    #[coverage(off)]
843    pub fn finish(self) -> (S, P) {
844        (self.sensor, self.pool)
845    }
846}
847
848impl SensorAndPoolBuilder<BasicSensor, BasicPool> {
849    /// Augment the current pool such that it also tries to find a fixed-length set of test cases which, together,
850    /// trigger the most code coverage.
851    ///
852    /// ### Argument
853    /// `size` : the size of the set of test cases to find
854    #[coverage(off)]
855    pub fn find_most_diverse_set_of_test_cases(
856        self,
857        size: usize,
858    ) -> SensorAndPoolBuilder<DiverseSensor, BasicAndDiversePool> {
859        let nbr_counters = self.sensor.count_instrumented;
860        let sensor = self.sensor.map(
861            #[coverage(off)]
862            |o| {
863                let len = o.len();
864                (o, len)
865            },
866        );
867        let pool = self
868            .pool
869            .and(
870                MostNDiversePool::new(&format!("diverse_cov_{}", size), size, nbr_counters),
871                Some(0.1),
872                SameObservations,
873            )
874            .and(
875                MaximiseObservationPool::<usize>::new("diverse_cov_1"),
876                Some(0.01),
877                DifferentObservations,
878            );
879        SensorAndPoolBuilder { sensor, pool }
880    }
881    /// Augment the current pool such that it also tries to find test cases repeatedly hitting the same regions of code.
882    #[coverage(off)]
883    pub fn find_test_cases_repeatedly_hitting_coverage_counters(
884        self,
885    ) -> SensorAndPoolBuilder<BasicAndMaxHitsSensor, BasicAndMaxHitsPool> {
886        let nbr_counters = self.sensor.count_instrumented;
887        let sensor = self.sensor.map(
888            #[coverage(off)]
889            |o| {
890                let sum = o
891                    .iter()
892                    .map(
893                        #[coverage(off)]
894                        |(_, count)| count,
895                    )
896                    .sum::<u64>();
897                (o, sum)
898            },
899        );
900        let pool = self
901            .pool
902            .and(
903                MaximiseEachCounterPool::new("max_each_cov_hits", nbr_counters),
904                Some(0.1),
905                SameObservations,
906            )
907            .and(
908                MaximiseObservationPool::<u64>::new("max_total_cov_hits"),
909                Some(0.01),
910                DifferentObservations,
911            );
912        SensorAndPoolBuilder { sensor, pool }
913    }
914}
915
916impl<T> SensorAndPoolBuilder<T, BasicAndDiversePool>
917where
918    T: WrapperSensor<
919        Wrapped = CodeCoverageSensor,
920        Observations = (<CodeCoverageSensor as Sensor>::Observations, usize),
921    >,
922{
923    /// Augment the current pool such that it also tries to find test cases repeatedly hitting the same regions of code.
924    #[coverage(off)]
925    pub fn find_test_cases_repeatedly_hitting_coverage_counters(
926        self,
927    ) -> SensorAndPoolBuilder<
928        impl Sensor<Observations = (<CodeCoverageSensor as Sensor>::Observations, (usize, u64))>,
929        BasicAndDiverseAndMaxHitsPool,
930    > {
931        let nbr_counters = self.sensor.wrapped().count_instrumented;
932
933        let sensor = self.sensor.map(
934            #[coverage(off)]
935            |o| {
936                let sum =
937                    o.0.iter()
938                        .map(
939                            #[coverage(off)]
940                            |(_, count)| count,
941                        )
942                        .sum::<u64>();
943                (o.0, (o.1, sum))
944            },
945        );
946
947        let pool = self
948            .pool
949            .p1
950            .and(
951                MaximiseEachCounterPool::new("max_each_cov_hits", nbr_counters),
952                Some(0.1),
953                SameObservations,
954            )
955            .and(
956                self.pool.p2.and(
957                    MaximiseObservationPool::<u64>::new("max_total_cov_hits"),
958                    Some(0.01),
959                    DifferentObservations,
960                ),
961                None,
962                DifferentObservations,
963            );
964        SensorAndPoolBuilder { sensor, pool }
965    }
966}