1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
use crate::{SaveToStatsFolder, Sensor};
/// A custom sensor whose observations are given by a mutable static value.
///
/// In the example below, we use a `StaticValueSensor` to maximise
/// the value of a variable used in the test function.
/// ```
/// use fuzzcheck::sensors_and_pools::{StaticValueSensor, MaximiseObservationPool};
/// use fuzzcheck::{Arguments, ReasonForStopping};
///
/// // the “COUNT” variable must be a static item
/// static mut COUNT: usize = 0;
///
/// fn test_function(xs: &[u8]) -> bool {
/// if xs.len() == 6 {
/// let mut number_correct_guesses = 0;
/// if xs[0] == 98 { number_correct_guesses += 1 }
/// if xs[1] == 18 { number_correct_guesses += 1 }
/// if xs[2] == 9 { number_correct_guesses += 1 }
/// if xs[3] == 203 { number_correct_guesses += 1 }
/// if xs[4] == 45 { number_correct_guesses += 1 }
/// if xs[5] == 165 { number_correct_guesses += 1 }
///
/// // here, record the value of number_correct_guesses in COUNT
/// unsafe { COUNT = number_correct_guesses; }
///
/// number_correct_guesses != 6
/// } else {
/// true
/// }
/// }
/// // You can create the sensor as follows.
/// // It is unsafe because of the access to the global mutable variable.
/// // After each run of the test function, the sensor resets `COUNT`
/// // to the second argument (here: 0). It is best if you don't access
/// // `COUNT` outside of the test function.
/// let sensor = unsafe { StaticValueSensor::new(&mut COUNT, 0) };
///
/// // The sensor can be paired with any pool which is compatible with
/// // observations of type `usize`. For example, we can use:
/// let pool = MaximiseObservationPool::<usize>::new("maximise_count");
///
/// // then launch fuzzcheck with this sensor and pool
/// let result = fuzzcheck::fuzz_test(test_function)
/// .default_mutator()
/// .serde_serializer()
/// .sensor_and_pool(sensor, pool)
/// .arguments(Arguments::for_internal_documentation_test())
/// .stop_after_first_test_failure(true)
/// .launch();
///
/// assert!(matches!(
/// result.reason_for_stopping,
/// ReasonForStopping::TestFailure(x)
/// if matches!(
/// x.as_slice(),
/// [98, 18, 9, 203, 45, 165]
/// )
/// ));
/// ```
pub struct StaticValueSensor<T>
where
T: 'static + Clone,
{
value: *mut T,
default_value: T,
}
impl<T> StaticValueSensor<T>
where
T: 'static + Clone,
{
pub fn new(value: &'static mut T, default_value: T) -> Self {
Self { value, default_value }
}
}
impl<T> SaveToStatsFolder for StaticValueSensor<T>
where
T: 'static + Clone,
{
fn save_to_stats_folder(&self) -> Vec<(std::path::PathBuf, Vec<u8>)> {
vec![]
}
}
impl<T: 'static> Sensor for StaticValueSensor<T>
where
T: Clone,
{
type Observations = T;
fn start_recording(&mut self) {
unsafe { *self.value = self.default_value.clone() };
}
fn stop_recording(&mut self) {
// unsafe {
// self.recorded_value = (*self.value).clone();
// }
}
fn get_observations(&mut self) -> Self::Observations {
unsafe { (*self.value).clone() }
}
}