use protest::{
ArrayGenerator, BTreeMapGenerator, BTreeSetGenerator, Generator, GeneratorConfig,
HashSetGenerator, IntGenerator, Property, PropertyError, ResultGenerator, StringGenerator,
UnitGenerator, check,
};
use rand::thread_rng;
use std::collections::{BTreeMap, BTreeSet, HashSet};
struct HashSetUniquenessProperty;
impl Property<HashSet<i32>> for HashSetUniquenessProperty {
type Output = ();
fn test(&self, set: HashSet<i32>) -> Result<Self::Output, PropertyError> {
let values: Vec<_> = set.iter().collect();
let unique_count = values.len();
if unique_count == set.len() {
Ok(())
} else {
Err(PropertyError::property_failed(format!(
"HashSet invariant violated: {} unique values but length is {}",
unique_count,
set.len()
)))
}
}
}
struct BTreeMapOrderProperty;
impl Property<BTreeMap<i32, String>> for BTreeMapOrderProperty {
type Output = ();
fn test(&self, map: BTreeMap<i32, String>) -> Result<Self::Output, PropertyError> {
let keys: Vec<_> = map.keys().copied().collect();
for window in keys.windows(2) {
if window[0] >= window[1] {
return Err(PropertyError::property_failed(format!(
"BTreeMap keys not in order: {} >= {}",
window[0], window[1]
)));
}
}
Ok(())
}
}
struct BTreeSetOrderProperty;
impl Property<BTreeSet<i32>> for BTreeSetOrderProperty {
type Output = ();
fn test(&self, set: BTreeSet<i32>) -> Result<Self::Output, PropertyError> {
let values: Vec<_> = set.iter().copied().collect();
for window in values.windows(2) {
if window[0] >= window[1] {
return Err(PropertyError::property_failed(format!(
"BTreeSet values not in order: {} >= {}",
window[0], window[1]
)));
}
}
Ok(())
}
}
struct ResultVariantProperty;
impl Property<Result<i32, String>> for ResultVariantProperty {
type Output = ();
fn test(&self, result: Result<i32, String>) -> Result<Self::Output, PropertyError> {
match result {
Ok(n) => {
if (0..=100).contains(&n) {
Ok(())
} else {
Err(PropertyError::property_failed(format!(
"Ok value {} out of expected range",
n
)))
}
}
Err(s) => {
if !s.is_empty() {
Ok(())
} else {
Err(PropertyError::property_failed(
"Err string should not be empty",
))
}
}
}
}
}
struct ArraySizeProperty;
impl Property<[i32; 5]> for ArraySizeProperty {
type Output = ();
fn test(&self, arr: [i32; 5]) -> Result<Self::Output, PropertyError> {
if arr.len() == 5 {
for elem in &arr {
if !(*elem >= 1 && *elem <= 100) {
return Err(PropertyError::property_failed(format!(
"Array element {} out of range",
elem
)));
}
}
Ok(())
} else {
Err(PropertyError::property_failed(format!(
"Array size should be 5 but was {}",
arr.len()
)))
}
}
}
fn main() {
println!("=== Collection Generators Examples ===\n");
println!("1. HashSet Generator");
println!(" Generating HashSet<i32> with 0-10 elements in range 1-100");
let hashset_gen = HashSetGenerator::new(IntGenerator::new(1, 100), 0, 10);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let sample_set = hashset_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", sample_set);
println!(" Size: {}", sample_set.len());
let result = check(hashset_gen.clone(), HashSetUniquenessProperty);
println!(" Property check: {:?}\n", result);
println!("2. BTreeMap Generator");
println!(" Generating BTreeMap<i32, String> with 0-5 entries");
let btreemap_gen = BTreeMapGenerator::new(
IntGenerator::new(1, 50),
StringGenerator::ascii_printable(3, 8),
0,
5,
);
let sample_map = btreemap_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", sample_map);
println!(" Size: {}", sample_map.len());
let result = check(btreemap_gen, BTreeMapOrderProperty);
println!(" Property check (sorted order): {:?}\n", result);
println!("3. BTreeSet Generator");
println!(" Generating BTreeSet<i32> with 0-8 elements in range 1-50");
let btreeset_gen = BTreeSetGenerator::new(IntGenerator::new(1, 50), 0, 8);
let sample_btreeset = btreeset_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", sample_btreeset);
println!(" Size: {}", sample_btreeset.len());
let result = check(btreeset_gen, BTreeSetOrderProperty);
println!(" Property check (sorted order): {:?}\n", result);
println!("4. Result Generator");
println!(" Generating Result<i32, String> with 50% probability each");
let result_gen = ResultGenerator::new(
IntGenerator::new(0, 100),
StringGenerator::ascii_printable(5, 20),
);
println!(" Samples:");
for i in 0..5 {
let sample_result = result_gen.generate(&mut rng, &config);
println!(" [{}] {:?}", i, sample_result);
}
let result = check(result_gen, ResultVariantProperty);
println!(" Property check: {:?}\n", result);
println!("5. Result Generator with Custom Probability");
println!(" Generating Result<i32, String> with 80% Ok probability");
let biased_result_gen = ResultGenerator::with_ok_probability(
IntGenerator::new(0, 100),
StringGenerator::ascii_printable(5, 20),
0.8, );
let mut ok_count = 0;
let mut err_count = 0;
for _ in 0..20 {
match biased_result_gen.generate(&mut rng, &config) {
Ok(_) => ok_count += 1,
Err(_) => err_count += 1,
}
}
println!(
" Out of 20 samples: {} Ok, {} Err (expected ~16 Ok, ~4 Err)\n",
ok_count, err_count
);
println!("6. Unit Generator");
println!(" Generating unit type ()");
let unit_gen = UnitGenerator;
unit_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", ());
println!(" Unit type has only one value - always ()\n");
println!("7. Array Generator");
println!(" Generating [i32; 5] arrays with elements in range 1-100");
let array_gen = ArrayGenerator::<i32, _, 5>::new(IntGenerator::new(1, 100));
let sample_array = array_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", sample_array);
println!(" Length: {} (always 5 by type)", sample_array.len());
let result = check(array_gen, ArraySizeProperty);
println!(" Property check: {:?}\n", result);
println!("8. Nested Collections");
println!(" Generating HashSet<BTreeSet<i32>>");
let nested_gen =
HashSetGenerator::new(BTreeSetGenerator::new(IntGenerator::new(1, 20), 1, 3), 0, 5);
let nested = nested_gen.generate(&mut rng, &config);
println!(" Sample: {:?}", nested);
println!(" Outer HashSet size: {}", nested.len());
for (i, inner) in nested.iter().enumerate() {
println!(" Inner BTreeSet {}: {:?}", i, inner);
}
println!("\n=== All examples completed successfully! ===");
}