#![allow(clippy::type_complexity)]
mod common;
#[path = "../examples/toggle.rs"]
mod toggle;
use common::Rng;
use toggle::{PointerEvent, PointerKind, Toggle, ToggleReference};
fn pointer(kind: PointerKind, x: f32, y: f32) -> Vec<PointerEvent> {
vec![PointerEvent { kind, x, y }]
}
#[test]
fn scripted_trace_matches_reference_and_each_step_takes_effect() {
use PointerKind::*;
let trace: Vec<(Vec<PointerEvent>, f32, f32)> = vec![
(vec![], 0.0, 0.0),
(pointer(Down, 640.0, 400.0), 0.0, 1.0), (pointer(Up, 640.0, 400.0), 1.0, 0.0), (pointer(Down, 630.25, 399.5), 1.0, 1.0),
(pointer(Up, 630.25, 399.5), 0.0, 0.0), (pointer(Down, 613.984_4, 400.0), 0.0, 0.0), (pointer(Down, 614.0, 384.0), 0.0, 1.0), (pointer(Up, 666.0, 400.0), 0.0, 0.0), (pointer(Down, 640.0, 400.0), 0.0, 1.0),
(pointer(Cancel, 0.0, 0.0), 0.0, 0.0), (pointer(Up, 640.0, 400.0), 0.0, 0.0), (pointer(Down, 665.984_4, 415.984_38), 0.0, 1.0),
(pointer(Up, 665.984_4, 415.984_38), 1.0, 0.0), ];
let mut program = Toggle::new();
let mut reference = ToggleReference::default();
for (events, on, armed) in trace {
let output = program.step(1280.0, 800.0, &events).unwrap();
assert_eq!(output, reference.step(1280.0, 800.0, &events));
assert_eq!((output.on, output.armed), (on, armed), "after {events:?}");
}
}
#[test]
fn events_in_one_evaluation_apply_down_then_up_then_cancel() {
use PointerKind::*;
let at = |kind| PointerEvent { kind, x: 640.0, y: 400.0 };
let cases: Vec<(Vec<Vec<PointerEvent>>, (f32, f32))> = vec![
(vec![vec![at(Down)], vec![at(Up), at(Down)]], (1.0, 0.0)),
(vec![vec![at(Cancel), at(Down)]], (0.0, 0.0)),
];
for (frames, expected) in cases {
let mut program = Toggle::new();
let mut reference = ToggleReference::default();
let mut decisions = (0.0, 0.0);
for events in &frames {
let output = program.step(1280.0, 800.0, events).unwrap();
assert_eq!(output, reference.step(1280.0, 800.0, events));
decisions = (output.on, output.armed);
}
assert_eq!(decisions, expected);
}
}
#[test]
fn random_traces_near_the_edges_match_reference() {
let mut rng = Rng::new(2026);
let kinds = [PointerKind::Down, PointerKind::Up, PointerKind::Cancel];
for _ in 0..50 {
let mut program = Toggle::new();
let mut reference = ToggleReference::default();
for _ in 0..200 {
let width = rng.pick(&[1280.0, 1281.0, 333.0, 52.0, 0.0]);
let height = rng.pick(&[800.0, 801.0, 32.0, 17.0]);
let near = |rng: &mut Rng, edge: f64| ((edge + rng.uniform(-2.0, 2.0)) * 64.0).round() / 64.0;
let left = width / 2.0 - 26.0;
let top = height / 2.0 - 16.0;
let events: Vec<PointerEvent> = (0..rng.below(3))
.map(|_| {
let kind = rng.pick(&kinds);
let (edge_x, edge_y) =
(rng.pick(&[left, left + 52.0, left + 26.0]), rng.pick(&[top, top + 32.0, top + 16.0]));
PointerEvent { kind, x: near(&mut rng, edge_x) as f32, y: near(&mut rng, edge_y) as f32 }
})
.collect();
let output = program.step(width as f32, height as f32, &events).unwrap();
assert_eq!(output, reference.step(width, height, &events), "{width}×{height} {events:?}");
}
}
}
#[test]
fn the_table_check_rejects_a_coordinate_off_the_grid() {
let mut program = Toggle::new();
let error = program.step(1280.0, 800.0, &pointer(PointerKind::Down, 640.001, 400.0)).unwrap_err();
assert!(error.contains("down.x must be a multiple of 0.015625"), "{error}");
}