heckcheck 2.0.1

A heckin small test case generator
Documentation
//! A heckin small test case generator.
//!
//! # What is test case generation?
//!
//! A test case generator is a program which writes programs to test other programs.
//! The idea is that we can find more bugs if we test more paths in a program,
//! and the best way to do that is to provide a wide range of inputs to our
//! programs. And the best way to do _that_ is to write a program designed to
//! generate inputs. You may have heard this being referred to as "fuzzing" or
//! "property testing" as well.
//!
//! # Examples
//!
//! This is a basic roundtrip test for an RGB serializer and parser, which ensures
//! that the output matches the original input.
//!
//! ```
//! use heckcheck::prelude::*;
//!
//! /// A color value encoded as Red-Green-Blue
//! #[derive(Clone, Debug, Arbitrary, PartialEq)]
//! pub struct Rgb {
//!     pub r: u8,
//!     pub g: u8,
//!     pub b: u8,
//! }
//!
//! impl Rgb {
//!     /// Convert from RGB to Hexadecimal.
//!     pub fn to_hex(&self) -> String {
//!         format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
//!     }
//!
//!     /// Convert from Hexadecimal to RGB.
//!     pub fn from_hex(s: String) -> Self {
//!         let s = s.strip_prefix('#').unwrap();
//!         Rgb {
//!             r: u8::from_str_radix(&s[0..2], 16).unwrap(),
//!             g: u8::from_str_radix(&s[2..4], 16).unwrap(),
//!             b: u8::from_str_radix(&s[4..6], 16).unwrap(),
//!         }
//!     }
//! }
//!
//! // Validate values can be converted from RGB to Hex and back.
//! heckcheck::check(|rgb: Rgb| {
//!     let hex = rgb.to_hex();
//!     let res = Rgb::from_hex(hex);
//!     assert_eq!(rgb, res);
//!     Ok(())
//! });
//! ```
//!
//! # Philosophy
//!
//! We believe that test case generation is an *essential* tool in modern
//! programmer's toolboxes, and both fuzzing and property testing play a role in
//! this.  Just like [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz),
//! `heckcheck` relies on the stable `Arbitrary` trait to modify structured
//! data. This means that all code in a crate can just implement `Arbitrary` in
//! order to be hooked up to a variety of different test case generation tools.
//!
//! Unlike some other tools, `heckcheck` is also *extensible*. The built-in
//! shrinker and permutation algorithms aren't necessarily the best in class.
//! But they can be modified in order to be improved. The end vision would be to
//! see something like `Arbitrary`, `cargo-fuzz`, and `heckcheck` provided out
//! of the box by the `::test` module.
//!
//! # Replaying tests
//!
//! When a test generated by `heckcheck` fails, we print a `base64` string of
//! the input data as part of the panic message. You can pass this string
//! together with the failing code to `heckcheck::replay` to create a
//! reproduction. This makes it quick to go from finding a failing test to
//! having a reproduction available.
//!
//! # Conditional derives
//!
//! If you only want to only derive `Default` when the code is being tested, you
//! can use `cfg_attr(test)` to only compile it when code is being tested.
//!
//! ```rust
//! #[cfg_attr(test, derive(Arbitrary))]
//! #[derive(Clone, Debug, PartialEq)]
//! pub struct Rgb {
//!     pub r: u8,
//!     pub g: u8,
//!     pub b: u8,
//! }
//! ```
//!
//! # Acknowledgements
//!
//! This crate was built thanks to the work of the [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) team on the
//! [`arbitrary`](https://docs.rs/arbitrary/1.0.1/arbitrary/) crate. It has been
//! heavily inspired by burntsushi's work on
//! [`quickcheck`](https://docs.rs/quickcheck/1.0.3/quickcheck/). And we would like to thank
//! [Boxy](https://twitter.com/EllenNyan0214/status/1418730276440707079) who
//! solved a critical bug that almost made us give up.
//!

#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, unreachable_pub)]

pub use arbitrary;

use arbitrary::Arbitrary;

mod checker;
mod shrink;
mod shrinker;

pub use checker::HeckCheck;
pub use shrink::{Shrink, ShrinkReport};
pub use shrinker::Shrinker;

/// The `heckcheck` prelude
pub mod prelude {
    pub use arbitrary::Arbitrary;
}

/// Check a target.
///
/// This is a shorthand for calling `HeckCheck::new` and `HeckCheck::check`.
pub fn check<A, F>(f: F)
where
    A: for<'b> Arbitrary<'b>,
    F: FnMut(A) -> arbitrary::Result<()>,
{
    let mut checker = HeckCheck::new();
    checker.check(f);
}

/// Replay a failing test from a base64 string.
///
/// When a call to `check` fails, a base64-encoded string is printed
/// representing the failing input data. You can pass this string together with
/// the failing test code to `heckcheck::replay` to create a permanent
/// reproduction of the error.
pub fn replay<A, F>(bytes: &str, mut f: F)
where
    A: for<'b> Arbitrary<'b>,
    F: FnMut(A) -> arbitrary::Result<()>,
{
    let bytes = base64::decode(bytes).unwrap();
    let mut u = arbitrary::Unstructured::new(&bytes);
    let instance = A::arbitrary(&mut u).unwrap();
    f(instance).unwrap();
}