use criterion::{BenchmarkGroup, BenchmarkId, Criterion, measurement::WallTime};
use protest::{Generator, config::GeneratorConfig};
use rand::SeedableRng;
use rand::rngs::StdRng;
pub trait PropertyBencher {
fn bench_function_over_inputs<I, G, F>(
&mut self,
name: &str,
bench_fn: F,
generator: G,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&mut criterion::Bencher, &I);
fn bench_property<I, G, P>(
&mut self,
name: &str,
generator: G,
property: P,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
P: Fn(&I) + 'static;
}
impl PropertyBencher for Criterion {
fn bench_function_over_inputs<I, G, F>(
&mut self,
name: &str,
mut bench_fn: F,
generator: G,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&mut criterion::Bencher, &I),
{
let mut rng = StdRng::from_entropy();
let config = GeneratorConfig::default();
let inputs: Vec<I> = (0..sample_count)
.map(|_| generator.generate(&mut rng, &config))
.collect();
let mut group = self.benchmark_group(name);
for (idx, input) in inputs.iter().enumerate() {
group.bench_with_input(BenchmarkId::from_parameter(idx), input, &mut bench_fn);
}
group.finish();
}
fn bench_property<I, G, P>(
&mut self,
name: &str,
generator: G,
property: P,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
P: Fn(&I) + 'static,
{
let mut rng = StdRng::from_entropy();
let config = GeneratorConfig::default();
let inputs: Vec<I> = (0..sample_count)
.map(|_| generator.generate(&mut rng, &config))
.collect();
let mut group = self.benchmark_group(name);
for (idx, input) in inputs.iter().enumerate() {
group.bench_with_input(BenchmarkId::from_parameter(idx), &input, |b, input| {
b.iter(|| property(input));
});
}
group.finish();
}
}
pub trait PropertyBenchmarkGroup<'a> {
fn bench_generated<I, G, F>(&mut self, id: &str, generator: G, f: F)
where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&I) + 'static;
}
impl<'a> PropertyBenchmarkGroup<'a> for BenchmarkGroup<'a, WallTime> {
fn bench_generated<I, G, F>(&mut self, id: &str, generator: G, mut f: F)
where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&I) + 'static,
{
self.bench_function(id, move |b| {
let mut rng = StdRng::from_entropy();
let config = GeneratorConfig::default();
b.iter_batched(
|| generator.generate(&mut rng, &config),
|input| f(&input),
criterion::BatchSize::SmallInput,
);
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use protest::primitives::{IntGenerator, VecGenerator};
#[test]
fn test_generates_inputs() {
let generator = IntGenerator::new(0, 100);
let mut rng = StdRng::from_seed([42; 32]);
let config = GeneratorConfig::default();
let inputs: Vec<i32> = (0..10)
.map(|_| generator.generate(&mut rng, &config))
.collect();
assert_eq!(inputs.len(), 10);
for input in inputs {
assert!((0..=100).contains(&input));
}
}
#[test]
fn test_vec_generator() {
let generator = VecGenerator::new(IntGenerator::new(0, 10), 5, 5);
let mut rng = StdRng::from_seed([42; 32]);
let config = GeneratorConfig::default();
let input = generator.generate(&mut rng, &config);
assert_eq!(input.len(), 5);
for &val in &input {
assert!((0..=10).contains(&val));
}
}
}