use super::{FINISHED_HISTORY, RingContract, Violation};
fn full_cycle(contract: &mut RingContract, user_data: usize) {
contract.observe_push(user_data);
contract.observe_completion(user_data);
}
#[test]
fn a_complete_lifecycle_is_quiescent() {
let mut contract = RingContract::new();
full_cycle(&mut contract, 1);
full_cycle(&mut contract, 2);
full_cycle(&mut contract, 3);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn nothing_at_all_is_quiescent() {
let contract = RingContract::new();
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn a_push_that_never_completes_is_reported() {
let mut contract = RingContract::new();
full_cycle(&mut contract, 1);
contract.observe_push(2);
assert_eq!(
contract.check_quiescent(),
vec![Violation::Outstanding { user_data: 2 }]
);
}
#[test]
fn a_completion_settles_its_operation() {
let mut contract = RingContract::new();
contract.observe_push(7);
contract.observe_completion(7);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn a_push_without_a_completion_is_still_outstanding() {
let mut contract = RingContract::new();
contract.observe_push(11);
assert_eq!(
contract.check_quiescent(),
vec![Violation::Outstanding { user_data: 11 }]
);
}
#[test]
fn a_second_completion_for_the_same_operation_is_reported() {
let mut contract = RingContract::new();
contract.observe_push(4);
contract.observe_completion(4);
contract.observe_completion(4);
assert!(
contract
.violations()
.contains(&Violation::DuplicateCompletion { user_data: 4 }),
"a duplicate completion must be reported: {:?}",
contract.violations()
);
}
#[test]
fn a_completion_for_an_operation_never_pushed_is_reported() {
let mut contract = RingContract::new();
contract.observe_completion(0xDEAD);
assert!(
contract
.violations()
.contains(&Violation::UnexpectedCompletion { user_data: 0xDEAD }),
"an unrecognised completion must be reported: {:?}",
contract.violations()
);
}
#[test]
fn a_completion_after_a_claim_is_a_duplicate_not_a_fresh_operation() {
let mut contract = RingContract::new();
full_cycle(&mut contract, 5);
contract.observe_completion(5);
assert!(
contract
.violations()
.contains(&Violation::DuplicateCompletion { user_data: 5 }),
"a completion after a full cycle is a duplicate: {:?}",
contract.violations()
);
}
#[test]
fn a_busy_registered_buffer_is_reported_at_quiescence() {
let mut contract = RingContract::new();
contract.observe_buffer(0, 0);
contract.observe_buffer(1, 2);
assert_eq!(
contract.check_quiescent(),
vec![Violation::BufferStillInUse {
index: 1,
outstanding: 2
}]
);
}
#[test]
fn a_quiet_registered_buffer_is_not_reported() {
let mut contract = RingContract::new();
for index in 0..8 {
contract.observe_buffer(index, 0);
}
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn a_buffer_reported_busy_then_quiet_is_not_reported() {
let mut contract = RingContract::new();
contract.observe_buffer(3, 1);
contract.observe_buffer(3, 0);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn violations_are_reported_in_a_stable_order() {
let mut contract = RingContract::new();
for user_data in [50_usize, 10, 30, 20, 40] {
contract.observe_push(user_data);
}
let first = contract.check_quiescent();
let second = contract.check_quiescent();
assert_eq!(first, second, "repeated checks must agree");
assert_eq!(
first,
vec![
Violation::Outstanding { user_data: 10 },
Violation::Outstanding { user_data: 20 },
Violation::Outstanding { user_data: 30 },
Violation::Outstanding { user_data: 40 },
Violation::Outstanding { user_data: 50 },
]
);
}
#[test]
fn every_violation_is_reported_rather_than_only_the_first() {
let mut contract = RingContract::new();
contract.observe_push(1); contract.observe_push(2); contract.observe_buffer(0, 3);
let violations = contract.check_quiescent();
assert_eq!(violations.len(), 3, "got {violations:?}");
}
#[test]
fn assert_quiescent_names_every_violation() {
let mut contract = RingContract::new();
contract.observe_push(1);
contract.observe_push(2);
let panicked = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
contract.assert_quiescent();
}));
let payload = panicked.expect_err("assert_quiescent must panic when the contract is broken");
let message = payload
.downcast_ref::<String>()
.expect("a string panic payload");
assert!(
message.contains("0x1"),
"should name operation 1: {message}"
);
assert!(
message.contains("0x2"),
"should name operation 2: {message}"
);
}
#[test]
fn assert_quiescent_is_silent_when_the_contract_holds() {
let mut contract = RingContract::new();
full_cycle(&mut contract, 1);
contract.assert_quiescent();
}
#[test]
fn a_tokenless_operation_is_complete_once_it_completes() {
let mut contract = RingContract::new();
contract.observe_push(1);
contract.observe_completion(1);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn a_tokenless_operation_that_never_completes_is_still_reported() {
let mut contract = RingContract::new();
contract.observe_push(2);
assert_eq!(
contract.check_quiescent(),
vec![Violation::Outstanding { user_data: 2 }]
);
}
#[test]
fn a_tokenless_operation_completing_twice_is_still_a_duplicate() {
let mut contract = RingContract::new();
contract.observe_push(3);
contract.observe_completion(3);
contract.observe_completion(3);
assert!(
contract
.violations()
.contains(&Violation::DuplicateCompletion { user_data: 3 }),
"got {:?}",
contract.violations()
);
}
#[test]
fn a_push_that_was_rejected_is_simply_never_observed() {
let mut contract = RingContract::new();
contract.observe_push(1);
contract.observe_completion(1);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn busy_registered_buffers_are_reported_in_index_order() {
const BUSY: u32 = 8;
let mut contract = RingContract::new();
contract.observe_buffer(99, 0); for index in (0..BUSY).rev() {
contract.observe_buffer(index, (index + 1) as usize);
}
let reported = contract.check_quiescent();
let expected: Vec<_> = (0..BUSY)
.map(|index| Violation::BufferStillInUse {
index,
outstanding: (index + 1) as usize,
})
.collect();
assert_eq!(
reported, expected,
"busy buffers must be reported by ascending index, and the quiet one omitted"
);
}
#[test]
fn a_finished_operation_stops_being_tracked() {
let mut contract = RingContract::new();
full_cycle(&mut contract, 1);
full_cycle(&mut contract, 2);
full_cycle(&mut contract, 3);
assert!(
contract.operations.is_empty(),
"a claimed operation must leave the tracking map, found: {:?}",
contract.operations
);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn the_tracking_map_follows_concurrency_not_uptime() {
let mut contract = RingContract::new();
let cycles = FINISHED_HISTORY * 4;
for user_data in 0..cycles {
full_cycle(&mut contract, user_data);
}
assert!(
contract.operations.is_empty(),
"{cycles} sequential operations left {} tracked",
contract.operations.len()
);
assert!(
contract.finished.len() <= FINISHED_HISTORY,
"the history must stay capped at {FINISHED_HISTORY}, found {}",
contract.finished.len()
);
assert_eq!(
contract.finished.len(),
contract.finished_set.len(),
"the queue and its membership index must stay in step, or eviction \
leaks entries out of one and not the other"
);
assert_eq!(contract.check_quiescent(), Vec::new());
}
#[test]
fn a_duplicate_is_named_precisely_within_the_window_and_still_reported_beyond_it() {
let mut contract = RingContract::new();
let oldest = 0_usize;
full_cycle(&mut contract, oldest);
for user_data in 1..=FINISHED_HISTORY {
full_cycle(&mut contract, user_data);
}
let recent = FINISHED_HISTORY;
contract.observe_completion(recent);
assert!(
contract
.violations()
.contains(&Violation::DuplicateCompletion { user_data: recent }),
"a duplicate still inside the window is a duplicate: {:?}",
contract.violations()
);
contract.observe_completion(oldest);
assert!(
contract
.violations()
.contains(&Violation::UnexpectedCompletion { user_data: oldest }),
"a duplicate that has fallen out of the window is still reported, \
under the weaker name: {:?}",
contract.violations()
);
}