use crate::native::bignum::BigInt;
use std::collections::HashMap;
use crate::native::core::choices::{BooleanChoice, IntegerChoice};
use crate::native::core::{ChoiceKind, ChoiceNode, ChoiceValue, Spans};
use crate::native::shrinker::{ShrinkRun, Shrinker, find_integer};
fn int_node(value: i128) -> ChoiceNode {
ChoiceNode::new(
ChoiceKind::Integer(IntegerChoice {
min_value: BigInt::from(i128::MIN + 1),
max_value: BigInt::from(i128::MAX),
shrink_towards: BigInt::from(0),
}),
ChoiceValue::Integer(BigInt::from(value)),
false,
)
}
fn bool_node(value: bool) -> ChoiceNode {
ChoiceNode::new(
ChoiceKind::Boolean(BooleanChoice),
ChoiceValue::Boolean(value),
false,
)
}
#[test]
fn replace_rejects_index_past_end_of_current_nodes() {
let mut shrinker = Shrinker::with_probe(
Box::new(|run| match run {
ShrinkRun::Full(nodes) => (true, nodes.to_vec(), Spans::new()),
ShrinkRun::Probe { .. } => (false, Vec::new(), Spans::new()),
}),
vec![int_node(5), int_node(10)],
Spans::new(),
);
let mut values = HashMap::new();
values.insert(99, ChoiceValue::Integer(BigInt::from(0)));
assert!(!shrinker.replace(&values).unwrap());
}
#[test]
fn replace_rejects_value_that_fails_kind_validate() {
let mut shrinker = Shrinker::with_probe(
Box::new(|run| match run {
ShrinkRun::Full(nodes) => (true, nodes.to_vec(), Spans::new()),
ShrinkRun::Probe { .. } => (false, Vec::new(), Spans::new()),
}),
vec![bool_node(true)],
Spans::new(),
);
let mut values = HashMap::new();
values.insert(0, ChoiceValue::Integer(BigInt::from(42)));
assert!(!shrinker.replace(&values).unwrap());
}
#[test]
fn find_integer_bails_when_exponential_probe_overflows() {
let result = find_integer(|_| true);
assert!(
result >= 1 << 60,
"result {result} should be very large; expected >= 2^60"
);
}
#[test]
fn consider_cache_short_circuits_repeat_lookups() {
use std::cell::RefCell;
use std::rc::Rc;
let seen = Rc::new(RefCell::new(Vec::<i128>::new()));
let seen_clone = seen.clone();
let mut shrinker = Shrinker::with_probe(
Box::new(move |run| match run {
ShrinkRun::Full(nodes) => {
let v = match &nodes[0].value {
ChoiceValue::Integer(v) => i128::try_from(v.clone()).unwrap(),
_ => unreachable!(),
};
seen_clone.borrow_mut().push(v);
(false, nodes.to_vec(), Spans::new())
}
ShrinkRun::Probe { .. } => (false, Vec::new(), Spans::new()),
}),
vec![int_node(0)],
Spans::new(),
);
shrinker.max_stall = usize::MAX;
for v in 1..=200_i128 {
shrinker.consider(&[int_node(v)]).unwrap();
}
assert_eq!(seen.borrow().len(), 200);
for v in 1..=200_i128 {
shrinker.consider(&[int_node(v)]).unwrap();
}
assert_eq!(seen.borrow().len(), 200, "cache short-circuit failed");
}
#[test]
fn consider_cache_distinguishes_kind_punned_candidates() {
use std::cell::RefCell;
use std::rc::Rc;
let seen = Rc::new(RefCell::new(Vec::<&'static str>::new()));
let seen_clone = seen.clone();
let mut shrinker = Shrinker::with_probe(
Box::new(move |run| match run {
ShrinkRun::Full(nodes) => {
let tag = match &nodes[0].value {
ChoiceValue::Boolean(_) => "bool",
ChoiceValue::Integer(_) => "int",
_ => "other",
};
seen_clone.borrow_mut().push(tag);
(false, nodes.to_vec(), Spans::new())
}
ShrinkRun::Probe { .. } => (false, Vec::new(), Spans::new()),
}),
vec![bool_node(true)],
Spans::new(),
);
shrinker.max_stall = usize::MAX;
shrinker.consider(&[bool_node(false)]).unwrap();
shrinker.consider(&[int_node(0)]).unwrap();
assert_eq!(seen.borrow().as_slice(), &["bool", "int"]);
}