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 {
mod test;
pub use crate::test::TestEngine as DefaultEngine;
}
}
pub mod generator {
pub use bolero_generator::{self, prelude::*};
}
#[doc(hidden)]
pub use bolero_engine::{self, TargetLocation, __item_path__};
pub use bolero_engine::{rng::RngEngine, Driver, DriverMode, Engine, Test};
use bolero_generator::{
combinator::{AndThenGenerator, FilterGenerator, FilterMapGenerator, MapGenerator},
TypeValueGenerator,
};
use core::{fmt::Debug, marker::PhantomData};
#[macro_export]
macro_rules! fuzz {
() => {{
let item_path = $crate::__item_path__!();
$crate::fuzz!(name = item_path)
}};
($fun:path) => {
$crate::fuzz!(|input| { $fun(input) })
};
(| $input:ident $(: &[u8])? | $impl:expr) => {
$crate::fuzz!().for_each(|$input: &[u8]| $impl)
};
(| $input:ident : $ty:ty | $impl:expr) => {
$crate::fuzz!().with_type().for_each(|$input: $ty| $impl)
};
(name = $target_name:expr) => {{
let location = $crate::TargetLocation {
package_name: env!("CARGO_PKG_NAME"),
manifest_dir: env!("CARGO_MANIFEST_DIR"),
module_path: module_path!(),
file: file!(),
line: line!(),
item_path: format!("{}", $target_name),
};
if !location.should_run() {
return;
}
$crate::fuzz(location)
}};
}
pub struct TestTarget<Generator, Engine, InputOwnership> {
generator: Generator,
driver_mode: Option<DriverMode>,
engine: Engine,
input_ownership: PhantomData<InputOwnership>,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BorrowedInput;
#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ClonedInput;
#[doc(hidden)]
pub fn fuzz(
location: TargetLocation,
) -> TestTarget<ByteSliceGenerator, DefaultEngine, BorrowedInput> {
TestTarget::new(DefaultEngine::new(location))
}
#[derive(Copy, Clone, Default, PartialEq)]
pub struct ByteSliceGenerator;
impl<Engine> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
pub fn new(engine: Engine) -> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
Self {
driver_mode: None,
generator: ByteSliceGenerator,
engine,
input_ownership: PhantomData,
}
}
}
impl<G, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
pub fn with_generator<Generator: generator::ValueGenerator>(
self,
generator: Generator,
) -> TestTarget<Generator, Engine, InputOwnership>
where
Generator::Output: Debug,
{
TestTarget {
driver_mode: self.driver_mode,
generator,
engine: self.engine,
input_ownership: self.input_ownership,
}
}
pub fn with_type<T: Debug + generator::TypeGenerator>(
self,
) -> TestTarget<TypeValueGenerator<T>, Engine, InputOwnership> {
TestTarget {
driver_mode: self.driver_mode,
generator: generator::gen(),
engine: self.engine,
input_ownership: self.input_ownership,
}
}
}
impl<G: generator::ValueGenerator, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
pub fn map<F: Fn(G::Output) -> T, T: Debug>(
self,
map: F,
) -> TestTarget<MapGenerator<G, F>, Engine, InputOwnership> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.map(map),
engine: self.engine,
input_ownership: self.input_ownership,
}
}
pub fn and_then<F: Fn(G::Output) -> T, T: generator::ValueGenerator>(
self,
map: F,
) -> TestTarget<AndThenGenerator<G, F>, Engine, InputOwnership>
where
T::Output: Debug,
{
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.and_then(map),
engine: self.engine,
input_ownership: self.input_ownership,
}
}
pub fn filter<F: Fn(&G::Output) -> bool>(
self,
filter: F,
) -> TestTarget<FilterGenerator<G, F>, Engine, InputOwnership> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.filter(filter),
engine: self.engine,
input_ownership: self.input_ownership,
}
}
pub fn filter_map<F: Fn(G::Output) -> Option<T>, T>(
self,
filter_map: F,
) -> TestTarget<FilterMapGenerator<G, F>, Engine, InputOwnership> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator.filter_map(filter_map),
engine: self.engine,
input_ownership: self.input_ownership,
}
}
pub fn with_driver_mode(self, mode: DriverMode) -> Self {
TestTarget {
driver_mode: Some(mode),
generator: self.generator,
engine: self.engine,
input_ownership: self.input_ownership,
}
}
}
impl<G, InputOwnership> TestTarget<G, RngEngine, InputOwnership> {
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,
input_ownership: self.input_ownership,
}
}
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,
input_ownership: self.input_ownership,
}
}
}
impl<G, Engine> TestTarget<G, Engine, BorrowedInput> {
pub fn cloned(self) -> TestTarget<G, Engine, ClonedInput> {
TestTarget {
driver_mode: self.driver_mode,
generator: self.generator,
engine: self.engine,
input_ownership: PhantomData,
}
}
}
impl<G, E> TestTarget<G, E, BorrowedInput>
where
G: generator::ValueGenerator,
{
pub fn for_each<F>(mut self, test: F) -> E::Output
where
E: Engine<bolero_engine::BorrowedGeneratorTest<F, G, G::Output>>,
bolero_engine::BorrowedGeneratorTest<F, G, G::Output>: Test,
{
let test = bolero_engine::BorrowedGeneratorTest::new(test, self.generator);
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
impl<G, E> TestTarget<G, E, ClonedInput>
where
G: generator::ValueGenerator,
{
pub fn for_each<F>(mut self, test: F) -> E::Output
where
E: Engine<bolero_engine::ClonedGeneratorTest<F, G, G::Output>>,
bolero_engine::ClonedGeneratorTest<F, G, G::Output>: Test,
{
let test = bolero_engine::ClonedGeneratorTest::new(test, self.generator);
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
impl<E> TestTarget<ByteSliceGenerator, E, BorrowedInput> {
pub fn for_each<T>(mut self, test: T) -> E::Output
where
E: Engine<bolero_engine::BorrowedSliceTest<T>>,
bolero_engine::BorrowedSliceTest<T>: Test,
{
let test = bolero_engine::BorrowedSliceTest::new(test);
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
impl<E> TestTarget<ByteSliceGenerator, E, ClonedInput> {
pub fn for_each<T>(mut self, test: T) -> E::Output
where
E: Engine<bolero_engine::ClonedSliceTest<T>>,
bolero_engine::ClonedSliceTest<T>: Test,
{
let test = bolero_engine::ClonedSliceTest::new(test);
if let Some(mode) = self.driver_mode {
self.engine.set_driver_mode(mode);
}
self.engine.run(test)
}
}
#[test]
#[should_panic]
fn slice_generator_test() {
fuzz!().for_each(|input| {
assert!(input.len() > 1000);
});
}
#[test]
#[should_panic]
fn type_generator_test() {
fuzz!().with_type().for_each(|input: &u8| {
assert!(input < &128);
});
}
#[test]
#[should_panic]
fn type_generator_cloned_test() {
fuzz!().with_type().cloned().for_each(|input: u8| {
assert!(input < 128);
});
}
#[test]
fn range_generator_test() {
fuzz!().with_generator(0..=5).for_each(|_input: &u8| {
});
}
#[test]
fn range_generator_cloned_test() {
fuzz!()
.with_generator(0..=5)
.cloned()
.for_each(|_input: u8| {
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nested_test() {
fuzz!().with_generator(0..=5).for_each(|_input: &u8| {
});
}
}