use std::time::Duration;
use visual_cortex::{
Brightness, EventKind, FakeSource, Frame, Rate, Region, Session, StabilityMask,
};
const W: usize = 24;
fn scene(bg: u8, tooltip: Option<u8>) -> Frame {
Frame::from_fn(W as u32, W as u32, |x, y| {
let lum = match tooltip {
Some(t) if (8..16).contains(&x) && (8..16).contains(&y) => t,
_ => bg,
};
[lum, lum, lum, 255]
})
}
fn mask() -> StabilityMask {
StabilityMask::new()
.block_size(8)
.stable_ticks(3)
.baseline_ticks(20)
.min_component(1)
.dilate(0)
}
async fn drain_brightness(stream: &mut visual_cortex::EventStream, secs: u64) -> Vec<f64> {
let mut vals = Vec::new();
loop {
match tokio::time::timeout(Duration::from_secs(secs), stream.next()).await {
Ok(Some(ev)) => {
if let EventKind::Changed { new, .. } = &ev.kind {
vals.push(new.as_number().unwrap());
}
}
_ => return vals,
}
}
}
#[tokio::test(start_paused = true)]
async fn browsing_hand_off_keeps_the_popup_live() {
let mut frames = Vec::new();
frames.extend((0..10).map(|_| scene(60, None)));
frames.extend((0..15).map(|_| scene(60, Some(200)))); frames.extend((0..15).map(|_| scene(60, Some(240)))); frames.extend((0..20).map(|_| scene(60, None)));
let session = Session::builder()
.source(FakeSource::new(frames))
.build()
.await
.unwrap();
let mut stream = session
.watch("browse")
.region(Region::Full)
.rate(Rate::hz(20.0))
.preprocess(mask())
.detector(Brightness)
.subscribe()
.unwrap();
let vals = drain_brightness(&mut stream, 5).await;
assert!(vals.len() >= 3, "expected a rich event sequence: {vals:?}");
assert!(vals[0] < 1.0, "cold start is black: {vals:?}");
let a_idx = vals.iter().position(|v| *v > 15.0).expect("A revealed");
let b_idx = vals.iter().position(|v| *v > 24.0).expect("B revealed");
assert!(b_idx >= a_idx, "A before B");
assert!(
vals[a_idx..=b_idx].iter().all(|v| *v > 15.0),
"hand-off must never go dark between A and B (v1 patchwork): {vals:?}"
);
assert!(
vals.last().unwrap() < &1.0,
"dismissal ends black: {vals:?}"
);
}
#[tokio::test(start_paused = true)]
async fn scene_change_reseeds_and_nothing_is_kept() {
let mut frames = Vec::new();
frames.extend((0..8).map(|_| scene(60, None)));
frames.extend((0..14).map(|i| scene(if i % 2 == 0 { 90 } else { 180 }, None)));
frames.extend((0..40).map(|_| scene(140, None)));
let session = Session::builder()
.source(FakeSource::new(frames))
.build()
.await
.unwrap();
let mut stream = session
.watch("walk")
.region(Region::Full)
.rate(Rate::hz(20.0))
.preprocess(mask())
.detector(Brightness)
.subscribe()
.unwrap();
let vals = drain_brightness(&mut stream, 4).await;
assert!(!vals.is_empty(), "initial event expected");
assert!(vals[0] < 1.0, "cold start black");
assert!(
vals.iter().all(|v| *v < 1.0),
"new scene must re-seed, never be kept: {vals:?}"
);
}