Expand description
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
,
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.
#[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
team on the
arbitrary
crate. It has been
heavily inspired by burntsushi’s work on
quickcheck
. And we would like to thank
Boxy who
solved a critical bug that almost made us give up.
Re-exports
pub use arbitrary;
Modules
- The
heckcheck
prelude
Structs
- The main test checker.
- The default test case shrinker.
Enums
- The result of a shrinking pass.
Traits
- Test case shrinking.
Functions
- Check a target.
- Replay a failing test from a base64 string.