use renew_frame::{
FrameLoop, FramePlan, FrameStats, StateHash, Step, StepBudget, Timestamp, Timestep,
};
const DT: u64 = 16_666_667;
const ORIGIN: u64 = 1_000_000_000;
const FROZEN_SCHEDULE_DIGEST: u64 = 0x5d29_0b68_1d14_462c;
fn hostile_trace(origin: u64) -> Vec<Timestamp> {
let mut stamps = Vec::new();
let mut now = origin;
for delta in [0, 1, DT - 1, DT, DT + 1, 3_000_000_000, 8_000_000, DT] {
now = now.saturating_add(delta);
stamps.push(Timestamp::from_nanos(now));
}
stamps.push(Timestamp::from_nanos(now - 1_000_000_000));
stamps.push(Timestamp::from_nanos(now));
stamps.push(Timestamp::from_nanos(u64::MAX));
stamps.push(Timestamp::from_nanos(u64::MAX));
stamps
}
fn run(start: u64) -> (Vec<FramePlan>, FrameStats) {
let mut frame = FrameLoop::new(
Timestep::HZ_60,
StepBudget::DEFAULT,
Timestamp::from_nanos(start),
);
let mut stats = FrameStats::new();
let plans = hostile_trace(ORIGIN)
.into_iter()
.map(|now| {
let plan = frame.begin_frame(now);
stats.absorb(&plan);
plan
})
.collect();
(plans, stats)
}
fn assert_not_vacuous(plans: &[FramePlan], stats: &FrameStats) {
assert!(
plans.iter().any(|plan| plan.step_count() > 0),
"the trace executed no simulation step"
);
assert!(
plans.iter().any(|plan| plan.dropped() > 0),
"the step budget never engaged"
);
assert!(
plans
.iter()
.any(|plan| plan.step_count() == StepBudget::DEFAULT.get().get()),
"no frame was clamped to the budget"
);
assert!(
plans.iter().any(|plan| plan.remainder().get() > 0),
"the bank was empty on every frame"
);
assert_ne!(
stats.schedule_hash(),
StateHash::new().finish(),
"nothing was absorbed"
);
}
#[test]
fn eight_runs_of_one_trace_produce_one_schedule() {
let runs: Vec<_> = (0..8).map(|_| run(ORIGIN)).collect();
let (plans, stats) = runs.first().expect("eight runs").clone();
assert_not_vacuous(&plans, &stats);
for (index, (other_plans, other_stats)) in runs.iter().enumerate() {
assert_eq!(other_plans, &plans, "run {index} planned differently");
assert_eq!(
other_stats.schedule_hash(),
stats.schedule_hash(),
"run {index} digested differently"
);
}
assert_eq!(stats.frames(), 12);
}
#[test]
fn the_canonical_trace_matches_its_frozen_digest() {
let (plans, stats) = run(ORIGIN);
assert_not_vacuous(&plans, &stats);
assert_eq!(
stats.schedule_hash(),
FROZEN_SCHEDULE_DIGEST,
"the canonical schedule digest changed; if that was deliberate, \
update FROZEN_SCHEDULE_DIGEST in the same commit and say why"
);
}
#[test]
fn a_one_nanosecond_perturbation_of_the_start_changes_the_digest() {
let (plans, stats) = run(ORIGIN);
let (perturbed_plans, perturbed_stats) = run(ORIGIN - 1);
assert_not_vacuous(&plans, &stats);
assert_not_vacuous(&perturbed_plans, &perturbed_stats);
assert_ne!(
perturbed_stats.schedule_hash(),
stats.schedule_hash(),
"the digest ignored a one-nanosecond change in its input"
);
assert_ne!(perturbed_plans, plans);
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct World {
position: u64,
velocity: u64,
ticks: u64,
}
impl World {
fn step(&mut self, step: Step) {
self.velocity = self.velocity.wrapping_add(step.dt.get() >> 12);
self.position = self
.position
.wrapping_add(self.velocity >> 6)
.wrapping_add(step.sim_time.get() >> 20);
self.ticks = self.ticks.wrapping_add(step.tick).wrapping_add(1);
}
fn absorb(self, hash: StateHash) -> StateHash {
hash.absorb_u64(self.position)
.absorb_u64(self.velocity)
.absorb_u64(self.ticks)
}
}
#[test]
fn eight_runs_of_one_trace_produce_one_world_state() {
let simulate = || {
let mut frame = FrameLoop::new(
Timestep::HZ_60,
StepBudget::DEFAULT,
Timestamp::from_nanos(ORIGIN),
);
let mut world = World::default();
let mut stats = FrameStats::new();
for now in hostile_trace(ORIGIN) {
let plan = frame.begin_frame(now);
for step in plan.steps() {
world.step(step);
}
stats.absorb(&plan);
}
(
world,
stats.schedule_hash(),
world.absorb(StateHash::new()).finish(),
)
};
let (world, schedule, state) = simulate();
assert_ne!(world, World::default(), "the world never advanced");
assert_ne!(
state,
StateHash::new().finish(),
"no world state was absorbed"
);
for index in 1..8 {
assert_eq!(simulate(), (world, schedule, state), "run {index} diverged");
}
}
#[test]
fn nothing_a_renderer_reads_off_a_plan_escapes_the_digest() {
fn digested(plan: &FramePlan) -> (u64, u32, u64, u64, u64) {
(
plan.first_tick(),
plan.step_count(),
plan.dropped(),
plan.remainder().get(),
plan.dt().nanos().get(),
)
}
fn presented(plan: &FramePlan) -> (u64, u64) {
(plan.remainder().get(), plan.dt().nanos().get())
}
let (left, _) = run(ORIGIN);
let (right, _) = run(ORIGIN);
assert!(!left.is_empty(), "the trace produced no plans");
let mut compared = 0;
for (a, b) in left.iter().zip(right.iter()) {
if digested(a) == digested(b) {
assert_eq!(
presented(a),
presented(b),
"two plans the digest cannot distinguish present differently, \
so a renderer can see state the digest does not cover"
);
compared += 1;
}
}
assert_eq!(
compared,
left.len(),
"the two walks diverged in their digested fields, which is a \
determinism failure before it is an alpha question"
);
let distinct = left.iter().any(|plan| digested(plan) != digested(&left[0]));
assert!(
distinct,
"every plan in the trace digests identically; the comparison above \
is vacuous"
);
}