use proptest::{
strategy::{Strategy, ValueTree},
test_runner::{Config, RngAlgorithm, TestRng, TestRunner},
};
use quick_junit::Report;
use std::{
hash::{Hash, Hasher},
path::Path,
};
use xxhash_rust::xxh3::Xxh3;
static CORPUS_DIR: &str = "fuzz/corpus/fuzz_deserialize";
fn main() {
let mut generator = ValueGenerator::from_seed("fuzz_junit_corpus");
std::fs::create_dir_all(CORPUS_DIR).expect("should create corpus dir");
for n in 0..1024 {
let report: Report = generator.generate(proptest::arbitrary::any::<Report>());
let xml = report.to_string().expect("should serialize report to XML");
let path = Path::new(CORPUS_DIR).join(format!("seed-{n}"));
std::fs::write(path, &xml).expect("should write to corpus");
}
println!("Generated 1024 corpus files in {CORPUS_DIR}");
}
#[derive(Debug, Default)]
pub struct ValueGenerator {
runner: TestRunner,
}
impl ValueGenerator {
pub fn new() -> Self {
Self {
runner: TestRunner::new(Config::default()),
}
}
pub fn deterministic() -> Self {
Self {
runner: TestRunner::deterministic(),
}
}
pub fn from_seed(seed: impl Hash) -> Self {
let mut rng_seed = [0u8; 32];
for hash_seed in 0usize..=3 {
let mut hasher = Xxh3::with_seed(hash_seed as u64);
seed.hash(&mut hasher);
rng_seed[hash_seed..(hash_seed + 8)].copy_from_slice(&hasher.finish().to_be_bytes());
}
Self {
runner: TestRunner::new_with_rng(
Config::default(),
TestRng::from_seed(RngAlgorithm::default(), &rng_seed),
),
}
}
pub fn partial_clone(&mut self) -> Self {
Self {
runner: TestRunner::new_with_rng(Config::default(), self.runner.new_rng()),
}
}
pub fn generate<S: Strategy>(&mut self, strategy: S) -> S::Value {
strategy
.new_tree(&mut self.runner)
.expect("creating a new value should succeed")
.current()
}
}