pub use super::boundary::BoundaryLengths;
pub use super::edge_cases::EdgeCases;
pub use super::exhaustive::ExhaustiveByteRange;
pub use super::pathological::Pathological;
pub use super::random::RandomUniform;
pub use super::u32_pathological::U32Pathological;
use crate::spec::types::OpSignature;
#[cfg(test)]
mod tests {
use super::*;
use crate::spec::types::DataType;
use std::collections::HashSet;
fn unary_sig() -> OpSignature {
OpSignature {
inputs: vec![DataType::U32],
output: DataType::U32,
}
}
fn binary_sig() -> OpSignature {
OpSignature {
inputs: vec![DataType::U32, DataType::U32],
output: DataType::U32,
}
}
fn byte_sig() -> OpSignature {
OpSignature {
inputs: vec![DataType::Bytes],
output: DataType::Bytes,
}
}
#[test]
fn u32_bytes_encoding() {
assert_eq!(u32_bytes(0), vec![0, 0, 0, 0]);
assert_eq!(u32_bytes(1), vec![1, 0, 0, 0]);
assert_eq!(u32_bytes(0xDEAD_BEEF), vec![0xEF, 0xBE, 0xAD, 0xDE]);
}
#[test]
fn pair_bytes_encoding() {
let bytes = pair_bytes(1, 2);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[..4], &[1, 0, 0, 0]);
assert_eq!(&bytes[4..], &[2, 0, 0, 0]);
}
#[test]
fn triple_bytes_encoding() {
let bytes = triple_bytes(1, 2, 3);
assert_eq!(bytes.len(), 12);
}
#[test]
fn sig_matchers_unary() {
assert!(sig::is_unary_u32(&unary_sig()));
assert!(!sig::is_binary_u32(&unary_sig()));
assert!(!sig::is_byte_input(&unary_sig()));
}
#[test]
fn sig_matchers_binary() {
assert!(!sig::is_unary_u32(&binary_sig()));
assert!(sig::is_binary_u32(&binary_sig()));
}
#[test]
fn sig_matchers_bytes() {
assert!(!sig::is_unary_u32(&byte_sig()));
assert!(sig::is_byte_input(&byte_sig()));
}
#[test]
fn default_generators_non_empty() {
let gens = default_generators();
assert!(gens.len() >= 5, "expected at least 5 generators");
}
#[test]
fn all_generators_have_unique_names() {
let gens = default_generators();
let names: HashSet<_> = gens.iter().map(|g| g.name()).collect();
assert_eq!(
names.len(),
gens.len(),
"duplicate generator names detected"
);
}
#[test]
fn every_generator_handles_unary_or_binary_or_bytes() {
let gens = default_generators();
let sigs = [unary_sig(), binary_sig(), byte_sig()];
for gen in &gens {
let handles_any = sigs.iter().any(|s| gen.handles(s));
assert!(
handles_any,
"generator '{}' handles no known signature shape",
gen.name()
);
}
}
#[test]
fn generators_produce_deterministic_output() -> Result<(), String> {
let gens = default_generators();
let sig = binary_sig();
let seed = 42;
for gen in &gens {
if !gen.handles(&sig) {
continue;
}
let run1 = gen.generate(&sig, seed);
let Some(fresh) = default_generators()
.into_iter()
.find(|g| g.name() == gen.name())
else {
return Err(format!(
"Fix: default_generators must preserve generator '{}' across calls",
gen.name()
));
};
let run2 = fresh.generate(&sig, seed);
assert_eq!(
run1.len(),
run2.len(),
"generator '{}' produced different counts on same seed",
gen.name()
);
for (a, b) in run1.iter().zip(run2.iter()) {
assert_eq!(
a,
b,
"generator '{}' produced different output on same seed",
gen.name()
);
}
}
Ok(())
}
#[test]
fn generators_produce_deterministic_output_for_op() -> Result<(), String> {
let gens = default_generators();
let sig = binary_sig();
let seed = 42;
let op_id = "test_op";
for gen in &gens {
if !gen.handles(&sig) {
continue;
}
let run1 = gen.generate_for_op(op_id, &sig, seed);
let Some(fresh) = default_generators()
.into_iter()
.find(|g| g.name() == gen.name())
else {
return Err(format!(
"Fix: default_generators must preserve generator '{}' across calls",
gen.name()
));
};
let run2 = fresh.generate_for_op(op_id, &sig, seed);
assert_eq!(
run1.len(),
run2.len(),
"generator '{}' produced different counts on same seed for generate_for_op",
gen.name()
);
for (a, b) in run1.iter().zip(run2.iter()) {
assert_eq!(
a,
b,
"generator '{}' produced different output on same seed for generate_for_op",
gen.name()
);
}
}
Ok(())
}
#[test]
fn generators_produce_correct_input_sizes_for_binary() {
let gens = default_generators();
let sig = binary_sig();
for gen in &gens {
if !gen.handles(&sig) {
continue;
}
for (label, bytes) in gen.generate(&sig, 0) {
assert!(
bytes.len() >= 8,
"generator '{}' produced {}-byte input for binary op (need >=8): {}",
gen.name(),
bytes.len(),
label
);
}
}
}
#[test]
fn generators_produce_correct_input_sizes_for_unary() {
let gens = default_generators();
let sig = unary_sig();
for gen in &gens {
if !gen.handles(&sig) {
continue;
}
for (label, bytes) in gen.generate(&sig, 0) {
assert!(
bytes.len() >= 4,
"generator '{}' produced {}-byte input for unary op (need >=4): {}",
gen.name(),
bytes.len(),
label
);
}
}
}
#[test]
fn edge_case_generator_produces_nonzero_count() {
let gen = EdgeCases;
let inputs = gen.generate(&binary_sig(), 0);
assert!(inputs.len() > 100, "edge cases too few: {}", inputs.len());
}
#[test]
fn exhaustive_byte_range_covers_0_to_255_for_unary() {
let gen = ExhaustiveByteRange;
let inputs = gen.generate(&unary_sig(), 0);
assert_eq!(inputs.len(), 256, "expected exactly 256 exhaustive inputs");
}
#[test]
fn random_uniform_seed_sensitivity() {
let gen = RandomUniform;
let a = gen.generate(&unary_sig(), 12345);
let b = gen.generate(&unary_sig(), 98765);
assert_ne!(a[0].1, b[0].1, "different seeds produced same first input");
}
#[test]
fn u32_pathological_covers_shift_boundaries() {
let gen = U32Pathological;
let inputs = gen.generate(&binary_sig(), 0);
let labels: HashSet<_> = inputs.iter().map(|i| i.0.as_str()).collect();
assert!(labels.contains("patho:shift:0"), "missing shift=0");
assert!(labels.contains("patho:shift:31"), "missing shift=31");
assert!(labels.contains("patho:shift:32"), "missing shift=32");
assert!(labels.contains("patho:rot_0"), "missing rot=0");
}
}