use std::{
cell::Cell,
rc::Rc,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
use crate::{Composition, MemoryApplier, frame_clock::FrameClock, snapshot_id_set::SnapshotId};
#[test]
fn a_frame_callback_fires_once_for_the_frame_it_registered_for() {
let composition = Composition::new(MemoryApplier::new());
let clock = FrameClock::new(composition.runtime_handle());
let millis = Arc::new(AtomicUsize::new(0));
let nanos = Arc::new(AtomicUsize::new(0));
let millis_sink = Arc::clone(&millis);
let _millis = clock.with_frame_millis(move |_| {
millis_sink.fetch_add(1, Ordering::SeqCst);
});
let nanos_sink = Arc::clone(&nanos);
let _nanos = clock.with_frame_nanos(move |_| {
nanos_sink.fetch_add(1, Ordering::SeqCst);
});
clock.runtime_handle().drain_frame_callbacks(16_000_000);
assert_eq!(millis.load(Ordering::SeqCst), 1);
assert_eq!(nanos.load(Ordering::SeqCst), 1);
clock.runtime_handle().drain_frame_callbacks(32_000_000);
assert_eq!(millis.load(Ordering::SeqCst), 1);
assert_eq!(nanos.load(Ordering::SeqCst), 1);
}
#[test]
fn a_perpetual_frame_keeps_asking_after_every_dispatch() {
let composition = Composition::new(MemoryApplier::new());
let clock = FrameClock::new(composition.runtime_handle());
let seen = Rc::new(Cell::new(0usize));
for frame in 1..=2u64 {
let mut next = Box::pin(clock.next_perpetual_frame());
let waker = std::task::Waker::noop().clone();
let mut context = std::task::Context::from_waker(&waker);
assert!(
Future::poll(next.as_mut(), &mut context).is_pending(),
"a frame was answered before it was dispatched"
);
clock
.runtime_handle()
.drain_frame_callbacks(frame * 16_000_000);
if Future::poll(next.as_mut(), &mut context).is_ready() {
seen.set(seen.get() + 1);
}
}
assert_eq!(seen.get(), 2, "the perpetual frame stopped answering");
}
#[test]
fn a_snapshot_id_range_adds_every_id_in_it_and_nothing_outside() {
use crate::snapshot_id_set::SnapshotIdSet;
let set = SnapshotIdSet::EMPTY.add_range(SnapshotId(3), SnapshotId(7));
assert_eq!(
set.to_list(),
vec![SnapshotId(3), SnapshotId(4), SnapshotId(5), SnapshotId(6)],
"the range is half-open"
);
assert_eq!(
set.add_range(SnapshotId(5), SnapshotId(5)).to_list(),
set.to_list()
);
assert_eq!(
set.add_range(SnapshotId(9), SnapshotId(2)).to_list(),
set.to_list()
);
let widened = set.add_range(SnapshotId(5), SnapshotId(9));
assert_eq!(
widened.to_list(),
vec![
SnapshotId(3),
SnapshotId(4),
SnapshotId(5),
SnapshotId(6),
SnapshotId(7),
SnapshotId(8)
]
);
}
#[test]
fn a_callback_holder_forwards_to_whichever_closure_was_last_stored() {
use std::{cell::Cell, rc::Rc};
use crate::callbacks::CallbackHolder;
let holder = CallbackHolder::new();
let calls = Rc::new(Cell::new(0u32));
let forward = holder.clone_rc();
forward();
assert_eq!(calls.get(), 0);
let first = Rc::clone(&calls);
holder.update_boxed(Box::new(move || first.set(first.get() + 1)));
forward();
assert_eq!(calls.get(), 1);
let second = Rc::clone(&calls);
holder.update_boxed(Box::new(move || second.set(second.get() + 10)));
forward();
assert_eq!(calls.get(), 11);
}
#[test]
fn a_ui_task_label_is_taken_by_the_next_task_and_not_the_one_after() {
use std::sync::{
Arc,
atomic::{AtomicU32, Ordering},
};
use crate::runtime::label_next_ui_task;
let composition = Composition::new(MemoryApplier::new());
let runtime = composition.runtime_handle();
let order = Arc::new(AtomicU32::new(0));
label_next_ui_task("labelled");
let first = Arc::clone(&order);
runtime.post_ui(move || {
first.fetch_add(1, Ordering::SeqCst);
});
let second = Arc::clone(&order);
runtime.post_ui(move || {
second.fetch_add(10, Ordering::SeqCst);
});
runtime.drain_ui();
assert_eq!(
order.load(Ordering::SeqCst),
11,
"a labelled task changed what ran"
);
}
#[test]
fn a_subcompose_state_takes_a_policy_and_pool_limits_without_disturbing_its_slots() {
use crate::subcompose::{ContentTypeReusePolicy, SubcomposeState};
let mut state = SubcomposeState::new(Box::new(ContentTypeReusePolicy));
assert_eq!(
state.was_last_slot_reused(),
None,
"a state that has composed nothing cannot have reused anything"
);
assert_eq!(state.active_slots_count(), 0);
state.set_policy(Box::new(ContentTypeReusePolicy));
state.set_reusable_pool_limits(2, 3);
assert_eq!(
state.active_slots_count(),
0,
"changing the policy disturbed the slots it was only meant to govern"
);
assert_eq!(state.was_last_slot_reused(), None);
}