1use 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
86pub trait FuzzTestFunction<T, FT: ?Sized, ImplId> {
94 type NormalizedFunction: for<'a> Fn(&'a T) -> bool;
95 fn test_function(self) -> Self::NormalizedFunction;
96}
97
98pub enum ReturnBool {}
100pub enum ReturnVoid {}
102pub 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
145pub 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
164pub 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
178pub 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
196pub 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
219pub 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#[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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#[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#[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#[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#[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
815pub 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 #[coverage(off)]
843 pub fn finish(self) -> (S, P) {
844 (self.sensor, self.pool)
845 }
846}
847
848impl SensorAndPoolBuilder<BasicSensor, BasicPool> {
849 #[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 #[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 #[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}