293 lines
9.2 KiB
Rust
293 lines
9.2 KiB
Rust
//! Tests for the event queue.
|
|
//!
|
|
//! 事件队列的测试。
|
|
|
|
use apsis_event::{Event, EventId, EventQueue};
|
|
|
|
#[test]
|
|
fn an_empty_queue_reports_itself_idle() {
|
|
let queue: EventQueue<u32> = EventQueue::new();
|
|
assert!(queue.is_empty());
|
|
assert_eq!(queue.len(), 0);
|
|
assert_eq!(queue.next_time(), None);
|
|
assert_eq!(queue.peek(), None);
|
|
assert_eq!(queue.delivered(), 0);
|
|
assert!(queue.state().is_idle());
|
|
assert_eq!(queue.state().next_time, None);
|
|
}
|
|
|
|
#[test]
|
|
fn events_are_delivered_in_time_order() {
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(30.0, "third");
|
|
queue.schedule(10.0, "first");
|
|
queue.schedule(20.0, "second");
|
|
|
|
assert_eq!(queue.len(), 3);
|
|
assert_eq!(queue.next_time(), Some(10.0));
|
|
assert_eq!(queue.peek(), Some(&"first"));
|
|
|
|
let mut order = Vec::new();
|
|
while let Some(event) = queue.pop() {
|
|
order.push(event.payload);
|
|
}
|
|
assert_eq!(order, vec!["first", "second", "third"]);
|
|
assert!(queue.is_empty());
|
|
assert_eq!(queue.delivered(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn events_at_the_same_time_are_delivered_in_schedule_order() {
|
|
// The tie-break is what makes a run reproducible.
|
|
// 并列打破规则正是使运行可复现的东西。
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(10.0, 'a');
|
|
queue.schedule(10.0, 'b');
|
|
queue.schedule(10.0, 'c');
|
|
|
|
let order: Vec<char> = queue
|
|
.drain()
|
|
.into_iter()
|
|
.map(|event| event.payload)
|
|
.collect();
|
|
assert_eq!(order, vec!['a', 'b', 'c']);
|
|
}
|
|
|
|
#[test]
|
|
fn the_tie_break_holds_across_a_heap_rebuild() {
|
|
// Scheduling in a different order must still deliver in schedule order,
|
|
// because the sequence number, not the insertion position, decides.
|
|
// 以不同次序调度仍必须按调度次序投递,因为决定的是序号而非插入位置。
|
|
let mut queue = EventQueue::new();
|
|
let mut ids = Vec::new();
|
|
for index in 0..20 {
|
|
ids.push(queue.schedule(5.0, index).expect("the time is usable"));
|
|
}
|
|
let delivered: Vec<u64> = queue
|
|
.drain()
|
|
.into_iter()
|
|
.map(|event| event.payload)
|
|
.collect();
|
|
assert_eq!(delivered, (0..20).collect::<Vec<u64>>());
|
|
|
|
// The identifiers are also in schedule order.
|
|
// 标识符也按调度次序排列。
|
|
for (position, id) in ids.iter().enumerate() {
|
|
assert_eq!(id.sequence(), position as u64);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn an_event_carries_its_identifier_and_time() {
|
|
let mut queue = EventQueue::new();
|
|
let id = queue.schedule(12.5, "payload").expect("the time is usable");
|
|
assert_eq!(id.time(), 12.5);
|
|
assert_eq!(id.sequence(), 0);
|
|
|
|
let event = queue.pop().expect("the queue is not empty");
|
|
assert_eq!(event.id, id);
|
|
assert_eq!(event.time, 12.5);
|
|
assert_eq!(event.payload, "payload");
|
|
}
|
|
|
|
#[test]
|
|
fn scheduling_a_negative_or_non_finite_time_is_refused() {
|
|
let mut queue = EventQueue::new();
|
|
for time in [-1.0, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
|
|
assert_eq!(queue.schedule(time, "x"), None, "time {time}");
|
|
}
|
|
// The queue is unchanged and the sequence number was not consumed.
|
|
// 队列不变,且序号未被消耗。
|
|
assert!(queue.is_empty());
|
|
let id = queue.schedule(0.0, "zero").expect("zero is usable");
|
|
assert_eq!(id.sequence(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn a_follow_up_event_is_scheduled_relative_to_now() {
|
|
let mut queue = EventQueue::new();
|
|
queue
|
|
.schedule_after(100.0, 30.0, "follow up")
|
|
.expect("the delay is usable");
|
|
let event = queue.pop().expect("the queue is not empty");
|
|
assert_eq!(event.time, 130.0);
|
|
}
|
|
|
|
#[test]
|
|
fn a_follow_up_with_a_bad_delay_is_refused() {
|
|
let mut queue = EventQueue::new();
|
|
assert_eq!(queue.schedule_after(0.0, -1.0, "x"), None);
|
|
assert_eq!(queue.schedule_after(0.0, f64::NAN, "x"), None);
|
|
assert_eq!(queue.schedule_after(0.0, f64::INFINITY, "x"), None);
|
|
assert!(queue.is_empty());
|
|
// A zero delay schedules at the current instant.
|
|
// 零延时在当前瞬时调度。
|
|
let id = queue.schedule_after(5.0, 0.0, "now").expect("usable");
|
|
assert_eq!(id.time(), 5.0);
|
|
}
|
|
|
|
#[test]
|
|
fn events_can_be_scheduled_from_a_handler() {
|
|
// A handler that schedules its successor, which is the pattern a
|
|
// discrete-event simulation uses for a periodic process.
|
|
// 一个调度其后继的处理器,这是离散事件仿真用于周期过程的模式。
|
|
let mut queue: EventQueue<u32> = EventQueue::new();
|
|
queue.schedule(0.0, 0).expect("usable");
|
|
let mut ticks = 0;
|
|
while let Some(event) = queue.pop() {
|
|
ticks += 1;
|
|
if event.payload < 3 {
|
|
queue
|
|
.schedule_after(event.time, 10.0, event.payload + 1)
|
|
.expect("the delay is usable");
|
|
}
|
|
}
|
|
assert_eq!(ticks, 4);
|
|
assert_eq!(queue.delivered(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn clearing_discards_the_pending_events() {
|
|
let mut queue = EventQueue::new();
|
|
for index in 0..5 {
|
|
queue.schedule(f64::from(index), index).expect("usable");
|
|
}
|
|
queue.pop().expect("the queue is not empty");
|
|
assert_eq!(queue.len(), 4);
|
|
assert_eq!(queue.delivered(), 1);
|
|
|
|
assert_eq!(queue.clear(), 4);
|
|
assert!(queue.is_empty());
|
|
// The delivered count records history and is not reset.
|
|
// 已投递计数记录历史且不重置。
|
|
assert_eq!(queue.delivered(), 1);
|
|
assert_eq!(queue.pop(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn draining_returns_the_events_in_delivery_order() {
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(3.0, 'c');
|
|
queue.schedule(1.0, 'a');
|
|
queue.schedule(2.0, 'b');
|
|
|
|
let events = queue.drain();
|
|
let payloads: Vec<char> = events.iter().map(|event| event.payload).collect();
|
|
assert_eq!(payloads, vec!['a', 'b', 'c']);
|
|
assert!(queue.is_empty());
|
|
assert_eq!(queue.delivered(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn the_queue_state_reports_what_is_pending() {
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(10.0, "a").expect("usable");
|
|
queue.schedule(20.0, "b").expect("usable");
|
|
let state = queue.state();
|
|
assert_eq!(state.pending, 2);
|
|
assert_eq!(state.next_time, Some(10.0));
|
|
assert_eq!(state.delivered, 0);
|
|
assert!(!state.is_idle());
|
|
|
|
queue.pop();
|
|
let state = queue.state();
|
|
assert_eq!(state.pending, 1);
|
|
assert_eq!(state.next_time, Some(20.0));
|
|
assert_eq!(state.delivered, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn a_payload_slot_is_reused_after_a_pop() {
|
|
// The queue indexes payloads by sequence, so a popped slot must be
|
|
// reusable without disturbing a later event.
|
|
// 队列按序号索引载荷,故被弹出的槽位必须可复用而不扰动更晚的事件。
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(1.0, "first").expect("usable");
|
|
queue.schedule(2.0, "second").expect("usable");
|
|
let first = queue.pop().expect("non-empty");
|
|
assert_eq!(first.payload, "first");
|
|
let second = queue.pop().expect("non-empty");
|
|
assert_eq!(second.payload, "second");
|
|
assert_eq!(queue.pop(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn the_identifier_orders_by_time_then_sequence() {
|
|
// The derived ordering on the identifier is the delivery order, which is
|
|
// what lets a caller sort identifiers to reproduce a run.
|
|
// 标识符上派生的次序即投递次序,这正是使调用方可对标识符排序以复现一次运行的
|
|
// 原因。
|
|
let earlier = EventId::new(1.0, 5).expect("usable");
|
|
let later_time = EventId::new(2.0, 0).expect("usable");
|
|
let later_sequence = EventId::new(1.0, 6).expect("usable");
|
|
assert!(earlier < later_time);
|
|
assert!(earlier < later_sequence);
|
|
assert!(EventId::new(1.0, 0).expect("usable") < earlier);
|
|
}
|
|
|
|
#[test]
|
|
fn an_identifier_reports_itself() {
|
|
let id = EventId::new(3.5, 7).expect("usable");
|
|
assert_eq!(id.time(), 3.5);
|
|
assert_eq!(id.sequence(), 7);
|
|
assert!(id.to_string().contains("7"));
|
|
assert!(id.to_string().contains("3.5"));
|
|
}
|
|
|
|
#[test]
|
|
fn an_event_reports_whether_it_is_due() {
|
|
let event = Event {
|
|
id: EventId::new(5.0, 0).expect("usable"),
|
|
time: 5.0,
|
|
payload: "x",
|
|
};
|
|
assert!(!event.is_due(4.999));
|
|
assert!(event.is_due(5.0));
|
|
assert!(event.is_due(6.0));
|
|
}
|
|
|
|
#[test]
|
|
fn an_event_can_be_rescheduled() {
|
|
let event = Event {
|
|
id: EventId::new(5.0, 0).expect("usable"),
|
|
time: 5.0,
|
|
payload: "x",
|
|
};
|
|
let moved = event.reschedule(9.0, 1).expect("the new time is usable");
|
|
assert_eq!(moved.time, 9.0);
|
|
assert_eq!(moved.id.sequence(), 1);
|
|
assert_eq!(moved.payload, "x");
|
|
// The original is unchanged.
|
|
// 原件不变。
|
|
assert_eq!(event.time, 5.0);
|
|
|
|
assert!(event.reschedule(-1.0, 2).is_none());
|
|
assert!(event.reschedule(f64::NAN, 3).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn an_event_can_change_its_payload() {
|
|
let event = Event {
|
|
id: EventId::new(5.0, 0).expect("usable"),
|
|
time: 5.0,
|
|
payload: 1_u32,
|
|
};
|
|
let changed = event.with_payload("text");
|
|
assert_eq!(changed.payload, "text");
|
|
assert_eq!(changed.time, 5.0);
|
|
}
|
|
|
|
#[test]
|
|
fn a_zero_time_event_is_the_earliest_possible() {
|
|
let mut queue = EventQueue::new();
|
|
queue.schedule(1.0, "later").expect("usable");
|
|
queue.schedule(0.0, "first").expect("usable");
|
|
assert_eq!(queue.pop().expect("non-empty").payload, "first");
|
|
}
|
|
|
|
#[test]
|
|
fn the_queue_default_is_empty() {
|
|
let queue: EventQueue<u8> = EventQueue::default();
|
|
assert!(queue.is_empty());
|
|
}
|