cfg_if::cfg_if! {
if #[cfg(fuzzing_libfuzzer)] {
pub use bolero_libfuzzer::LibFuzzerEngine as DefaultEngine;
} else if #[cfg(fuzzing_afl)] {
pub use bolero_afl::AflEngine as DefaultEngine;
} else if #[cfg(fuzzing_honggfuzz)] {
pub use bolero_honggfuzz::HonggfuzzEngine as DefaultEngine;
} else if #[cfg(test)] {
mod test;
pub use bolero_engine::rng::RngEngine as DefaultEngine;
} else {
mod test;
pub use crate::test::TestEngine as DefaultEngine;
}
}
pub mod generator {
pub use bolero_generator::{self, prelude::*};
}
#[doc(hidden)]
pub use bolero_engine::TargetLocation;
pub use bolero_engine::{rng::RngEngine, Driver, DriverMode, Engine, Test};
use bolero_generator::{
combinator::{AndThenGenerator, FilterGenerator, FilterMapGenerator, MapGenerator},
TypeValueGenerator,
};
#[macro_export]
macro_rules! fuzz {
($($tt:tt)*) => {
$crate::__bolero_parse_input!(fuzz; $($tt)*)
};
}
#[macro_export]
macro_rules! check {
($($tt:tt)*) => {
$crate::__bolero_parse_input!(check; $($tt)*)
};
}
pub struct TestTarget<Generator, Engine> {
generator: Generator,
driver_mode: Option<DriverMode>,
engine: Engine,
}
#[doc(hidden)]
pub fn fuzz(location: TargetLocation) -> TestTarget<SliceGenerator, DefaultEngine> {
if std::env::var("CARGO_BOLERO_PATH").is_ok() {
print!("{}", std::env::args().nth(0).unwrap());
std::process::exit(0);
}
TestTarget::new(DefaultEngine::new(location))
}
#[doc(hidden)]
pub fn check(location: TargetLocation) -> TestTarget<SliceGenerator, RngEngine> {
TestTarget::new(RngEngine::new(location))
}
#[derive(Copy, Clone, Default, PartialEq)]
pub struct SliceGenerator;
impl<Engine> TestTarget<SliceGenerator, Engine> {
pub fn new(engine: Engine) -> TestTarget<SliceGenerator, Engine> {
Self {
driver_mode: None,
generator: SliceGenerator,
engine,
}
}
}
impl<G, Engine> TestTarget<G, Engine> {
pub fn with_generator<NewG: generator::ValueGenerator>(
self,
generator: NewG,
) -> TestTarget<NewG, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator,
engine: self.engine,
}
}
pub fn with_type<T: generator::TypeGenerator>(
self,
) -> TestTarget<TypeValueGenerator<T>, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator: generator::gen(),
engine: self.engine,
}
}
}
impl<G: generator::ValueGenerator, Engine> TestTarget<G, Engine> {
pub fn map<F: Fn(G::Output) -> T, T>(self, map: F) -> TestTarget<MapGenerator<G, F>, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.map(map),
engine: self.engine,
}
}
pub fn and_then<F: Fn(G::Output) -> T, T: generator::ValueGenerator>(
self,
map: F,
) -> TestTarget<AndThenGenerator<G, F>, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.and_then(map),
engine: self.engine,
}
}
pub fn filter<F: Fn(&G::Output) -> bool>(
self,
filter: F,
) -> TestTarget<FilterGenerator<G, F>, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.filter(filter),
engine: self.engine,
}
}
pub fn filter_map<F: Fn(G::Output) -> Option<T>, T>(
self,
filter_map: F,
) -> TestTarget<FilterMapGenerator<G, F>, Engine> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.filter_map(filter_map),
engine: self.engine,
}
}
pub fn with_driver_mode(self, mode: DriverMode) -> Self {
TestTarget {
driver_mode: Some(mode),
generator: self.generator,
engine: self.engine,
}
}
}
impl<G> TestTarget<G, RngEngine> {
pub fn with_iterations(mut self, iterations: usize) -> Self {
self.engine.iterations = iterations;
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator,
engine: self.engine,
}
}
pub fn with_max_len(mut self, max_len: usize) -> Self {
self.engine.max_len = max_len;
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator,
engine: self.engine,
}
}
}
impl<G, E> TestTarget<G, E>
where
G: generator::ValueGenerator,
{
pub fn for_each<F>(mut self, test: F) -> E::Output
where
E: Engine<bolero_engine::GeneratorTest<F, G>>,
bolero_engine::GeneratorTest<F, G>: Test,
{
let test = bolero_engine::GeneratorTest::new(test, self.generator);
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
impl<E> TestTarget<SliceGenerator, E> {
pub fn for_each<T, Ret>(mut self, test: T) -> E::Output
where
E: Engine<T>,
T: Test + FnMut(&[u8]) -> Ret,
{
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
#[test]
#[should_panic]
fn slice_generator_test() {
check!().for_each(|input| {
assert!(input.len() > 1000);
});
}
#[test]
#[should_panic]
fn type_generator_test() {
check!().with_type().for_each(|input: u8| {
assert!(input < 128);
});
}
#[test]
fn range_generator_test() {
check!().with_generator(0..=5).for_each(|_input: u8| {
});
}
#[doc(hidden)]
#[macro_export]
macro_rules! __bolero_parse_input {
($target:ident;) => {
$crate::$target($crate::TargetLocation {
manifest_dir: env!("CARGO_MANIFEST_DIR"),
module_path: module_path!(),
file: file!(),
line: line!(),
})
};
($target:ident; for $value:pat in gen() { $($tt:tt)* }) => {
$crate::$target!(for $value in ($crate::generator::gen()) { $($tt)* })
};
($target:ident; for $value:pat in all() { $($tt:tt)* }) => {
$crate::$target!(for $value in ($crate::generator::gen()) { $($tt)* })
};
($target:ident; for $value:pat in all($gen:expr) { $($tt:tt)* }) => {
$crate::$target!(for $value in ($gen) { $($tt)* })
};
($target:ident; for $value:pat in each($gen:expr) { $($tt:tt)* }) => {
$crate::$target!(for $value in ($gen) { $($tt)* })
};
($target:ident; for $value:pat in $gen:path { $($tt:tt)* }) => {
$crate::$target!(for $value in ($gen) { $($tt)* })
};
($target:ident; for $value:pat in ($gen:expr) { $($tt:tt)* }) => {
$crate::$target!()
.with_generator({
use $crate::generator::*;
$gen
})
.for_each(|$value| {
$($tt)*
})
};
($target:ident; $fun:path) => {
$crate::$target!(|input| { $fun(input); })
};
($target:ident; |$input:ident $(: &[u8])?| $impl:expr) => {
$crate::$target!().for_each(|$input: &[u8]| {
$impl;
})
};
($target:ident; |$input:ident: $ty:ty| $impl:expr) => {
$crate::$target!().with_type().for_each(|$input: $ty| {
$impl;
})
};
}
#[cfg(test)]
mod derive_tests {
use bolero_generator::*;
fn gen_foo() -> impl ValueGenerator<Output = u32> {
4..5
}
#[derive(Debug, PartialEq, TypeGenerator)]
pub struct Bar {
#[generator(gen_foo())]
foo: u32,
bar: u64,
baz: u8,
}
#[derive(Debug, PartialEq, TypeGenerator)]
pub enum Operation {
Insert {
#[generator(1..3)]
index: usize,
value: u32,
},
Remove {
#[generator(4..6)]
index: usize,
},
Bar(Bar),
Other(#[generator(42..53)] usize),
Clear,
}
#[derive(TypeGenerator)]
pub union Foo {
foo: u32,
bar: u64,
baz: u8,
}
#[test]
fn operation_test() {
check!().with_type().for_each(|_input: Vec<Operation>| {
});
}
}