use super::{assert_lock_order, LockRank};
use proptest::prelude::*;
const MAX_CORE_ORDINAL: u8 = 30;
fn rank(value: u8) -> LockRank {
LockRank(value)
}
#[test]
fn test_premium_accepts_inclusive_lower_bound() {
assert!(LockRank::premium(40).is_some());
}
#[test]
fn test_premium_accepts_inclusive_upper_bound() {
assert!(LockRank::premium(59).is_some());
}
#[test]
fn test_premium_rejects_just_below_range() {
assert!(LockRank::premium(39).is_none());
}
#[test]
fn test_premium_rejects_just_above_range() {
assert!(LockRank::premium(60).is_none());
}
#[test]
fn test_premium_rejects_zero() {
assert!(LockRank::premium(0).is_none());
}
#[test]
fn test_core_ranks_are_strictly_ascending() {
let order = [
LockRank::GPU_VECTORS_SNAPSHOT,
LockRank::VECTORS,
LockRank::COLUMNAR,
LockRank::LAYERS,
LockRank::NEIGHBORS,
];
for pair in order.windows(2) {
assert_lock_order(pair[0], pair[1]);
assert!(pair[0] < pair[1]);
}
}
#[test]
fn test_max_core_ordinal_matches_neighbors() {
assert_eq!(LockRank::NEIGHBORS.ordinal(), MAX_CORE_ORDINAL);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_premium_range_iff_and_above_core(v in any::<u8>()) {
let in_range = (LockRank::PREMIUM_MIN..=LockRank::PREMIUM_MAX).contains(&v);
match LockRank::premium(v) {
Some(r) => {
prop_assert!(in_range);
prop_assert_eq!(r.ordinal(), v);
prop_assert!(r.ordinal() > MAX_CORE_ORDINAL);
prop_assert!(r > LockRank::NEIGHBORS);
}
None => prop_assert!(!in_range),
}
}
#[test]
fn prop_ascending_order_holds(x in any::<u8>(), y in any::<u8>()) {
prop_assume!(x != y);
let (lo, hi) = (x.min(y), x.max(y));
let (low, high) = (rank(lo), rank(hi));
prop_assert!(low < high);
prop_assert!(high > low);
assert_lock_order(low, high);
}
}
#[cfg(debug_assertions)]
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_descending_order_fails_in_debug(x in any::<u8>(), y in any::<u8>()) {
prop_assume!(x != y);
let (lo, hi) = (x.min(y), x.max(y));
let (low, high) = (rank(lo), rank(hi));
let outcome = with_panic_output_silenced(|| {
std::panic::catch_unwind(|| assert_lock_order(high, low))
});
prop_assert!(outcome.is_err());
}
}
#[cfg(debug_assertions)]
fn with_panic_output_silenced<T>(f: impl FnOnce() -> T) -> T {
use std::cell::Cell;
use std::sync::Once;
thread_local! {
static SILENCED: Cell<bool> = const { Cell::new(false) };
}
static INSTALL_HOOK: Once = Once::new();
struct Unsilence;
impl Drop for Unsilence {
fn drop(&mut self) {
SILENCED.with(|silenced| silenced.set(false));
}
}
INSTALL_HOOK.call_once(|| {
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if !SILENCED.with(Cell::get) {
previous(info);
}
}));
});
SILENCED.with(|silenced| silenced.set(true));
let _unsilence = Unsilence;
f()
}