use bolero_generator::{
combinator::{AndThenGenerator, FilterGenerator, FilterMapGenerator, MapGenerator},
TypeValueGenerator,
};
use core::{fmt::Debug, marker::PhantomData, time::Duration};
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(kani)] {
pub use bolero_kani::KaniEngine as DefaultEngine;
} else {
mod test;
pub use crate::test::TestEngine as DefaultEngine;
}
}
pub mod generator {
pub use bolero_generator::{self, prelude::*};
}
pub use bolero_generator::prelude::*;
#[doc(hidden)]
pub use bolero_engine::{self, TargetLocation, __item_path__};
pub use bolero_engine::{Driver, Engine, Test};
#[cfg(test)]
mod tests;
#[macro_export]
macro_rules! check {
() => {{
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: $crate::__item_path__!(),
test_name: None,
};
if !location.should_run() {
return;
}
$crate::test(location)
}};
($fun:path) => {
$crate::check!(|input| { $fun(input) })
};
(| $input:ident $(: &[u8])? | $impl:expr) => {
$crate::check!().for_each(|$input: &[u8]| $impl)
};
(| $input:ident : $ty:ty | $impl:expr) => {
$crate::check!().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: $crate::__item_path__!(),
test_name: Some(format!("{}", $target_name)),
};
if !location.should_run() {
return;
}
$crate::test(location)
}};
}
#[macro_export]
#[deprecated = "`fuzz!` has been deprecated in favor of `check!`."]
macro_rules! fuzz {
($($arg:tt)*) => {
$crate::check!($($arg)*)
}
}
pub struct TestTarget<Generator, Engine, InputOwnership> {
generator: Generator,
driver_options: bolero_generator::driver::Options,
engine: Engine,
input_ownership: PhantomData<InputOwnership>,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BorrowedInput;
#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ClonedInput;
#[doc(hidden)]
pub fn test(
location: TargetLocation,
) -> TestTarget<ByteSliceGenerator, DefaultEngine, BorrowedInput> {
TestTarget::new(DefaultEngine::new(location))
}
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct ByteSliceGenerator;
impl<Engine> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
pub fn new(engine: Engine) -> TestTarget<ByteSliceGenerator, Engine, BorrowedInput> {
Self {
generator: ByteSliceGenerator,
engine,
driver_options: Default::default(),
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 {
generator,
engine: self.engine,
driver_options: self.driver_options,
input_ownership: self.input_ownership,
}
}
pub fn with_type<T: Debug + generator::TypeGenerator>(
self,
) -> TestTarget<TypeValueGenerator<T>, Engine, InputOwnership> {
TestTarget {
generator: generator::produce(),
engine: self.engine,
driver_options: self.driver_options,
input_ownership: self.input_ownership,
}
}
#[cfg(feature = "arbitrary")]
pub fn with_arbitrary<T>(
self,
) -> TestTarget<bolero_generator::arbitrary::ArbitraryGenerator<T>, Engine, InputOwnership>
where
T: 'static + Debug,
T: for<'a> bolero_generator::arbitrary::Arbitrary<'a>,
{
TestTarget {
generator: generator::gen_arbitrary(),
engine: self.engine,
driver_options: self.driver_options,
input_ownership: self.input_ownership,
}
}
pub fn with_shrink_time(mut self, shrink_time: Duration) -> Self {
self.driver_options.set_shrink_time(shrink_time);
self
}
pub fn exhaustive(mut self) -> Self {
self.driver_options.set_exhaustive(true);
self
}
}
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 {
generator: self.generator.map_gen(map),
engine: self.engine,
driver_options: self.driver_options,
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 {
generator: self.generator.and_then_gen(map),
engine: self.engine,
driver_options: self.driver_options,
input_ownership: self.input_ownership,
}
}
pub fn filter<F: Fn(&G::Output) -> bool>(
self,
filter: F,
) -> TestTarget<FilterGenerator<G, F>, Engine, InputOwnership> {
TestTarget {
generator: self.generator.filter_gen(filter),
engine: self.engine,
driver_options: self.driver_options,
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 {
generator: self.generator.filter_map_gen(filter_map),
engine: self.engine,
driver_options: self.driver_options,
input_ownership: self.input_ownership,
}
}
#[deprecated = "Driver mode is no longer being used by generator implementations"]
#[allow(deprecated)]
pub fn with_driver_mode(self, mode: DriverMode) -> Self {
let _ = mode;
self
}
pub fn with_max_depth(mut self, max_depth: usize) -> Self {
self.driver_options.set_max_depth(max_depth);
self
}
}
cfg_if::cfg_if! {
if #[cfg(any(fuzzing, kani))] {
impl<G, Engine, InputOwnership> TestTarget<G, Engine, InputOwnership> {
pub fn with_test_time(self, test_time: Duration) -> Self {
let _ = test_time;
self
}
pub fn with_iterations(self, iterations: usize) -> Self {
let _ = iterations;
self
}
pub fn with_max_len(mut self, max_len: usize) -> Self {
self.driver_options.set_max_len(max_len);
self
}
}
} else {
impl<G, InputOwnership> TestTarget<G, crate::test::TestEngine, InputOwnership> {
pub fn with_test_time(mut self, test_time: Duration) -> Self {
self.engine.with_test_time(test_time);
self
}
pub fn with_iterations(mut self, iterations: usize) -> Self {
self.engine.with_iterations(iterations);
self
}
pub fn with_max_len(mut self, max_len: usize) -> Self {
self.driver_options.set_max_len(max_len);
self.engine.with_max_len(max_len);
self
}
}
}
}
impl<G, Engine> TestTarget<G, Engine, BorrowedInput>
where
G: generator::ValueGenerator,
<G as generator::ValueGenerator>::Output: Clone,
{
pub fn cloned(self) -> TestTarget<G, Engine, ClonedInput> {
TestTarget {
generator: self.generator,
engine: self.engine,
driver_options: self.driver_options,
input_ownership: PhantomData,
}
}
}
impl<G, E> TestTarget<G, E, BorrowedInput>
where
G: generator::ValueGenerator,
{
pub fn for_each<F>(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);
self.engine.run(test, self.driver_options)
}
}
impl<G, E> TestTarget<G, E, ClonedInput>
where
G: generator::ValueGenerator,
{
pub fn for_each<F>(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);
self.engine.run(test, self.driver_options)
}
}
impl<E> TestTarget<ByteSliceGenerator, E, BorrowedInput> {
pub fn for_each<T>(self, test: T) -> E::Output
where
E: Engine<bolero_engine::BorrowedSliceTest<T>>,
bolero_engine::BorrowedSliceTest<T>: Test,
{
let test = bolero_engine::BorrowedSliceTest::new(test);
self.engine.run(test, self.driver_options)
}
#[cfg(feature = "std")]
pub fn run<T, R>(self, mut test: T) -> E::Output
where
T: FnMut() -> R + core::panic::RefUnwindSafe,
R: bolero_engine::IntoResult,
E: bolero_engine::ScopedEngine,
{
self.engine.run(move |_| test(), self.driver_options)
}
#[cfg(feature = "std")]
pub fn run_with_replay<T, R>(self, test: T) -> E::Output
where
T: FnMut(bool) -> R + core::panic::RefUnwindSafe,
R: bolero_engine::IntoResult,
E: bolero_engine::ScopedEngine,
{
self.engine.run(test, self.driver_options.with_replay_on_fail(true))
}
}
impl<E> TestTarget<ByteSliceGenerator, E, ClonedInput> {
pub fn for_each<T>(self, test: T) -> E::Output
where
E: Engine<bolero_engine::ClonedSliceTest<T>>,
bolero_engine::ClonedSliceTest<T>: Test,
{
let test = bolero_engine::ClonedSliceTest::new(test);
self.engine.run(test, self.driver_options)
}
}