use std::time::Duration;
use visual_cortex::{
Brightness, EventKind, FakeSource, Frame, Rate, Region, Session, StabilityMask,
};
fn scene(noise: u8, tooltip: bool) -> Frame {
const W: usize = 24;
let mut data = vec![0u8; W * W * 4];
for y in 0..W {
for x in 0..W {
let lum = if x < 8 && y < 8 {
noise
} else if x >= 16 && y >= 16 {
220
} else if (8..16).contains(&x) && (8..16).contains(&y) && tooltip {
230
} else {
60
};
let px = (y * W + x) * 4;
data[px] = lum;
data[px + 1] = lum;
data[px + 2] = lum;
data[px + 3] = 255;
}
}
Frame::new(24, 24, data).unwrap()
}
#[tokio::test(start_paused = true)]
async fn mask_isolates_tooltip_and_holds_the_diff_gate() {
let frames: Vec<Frame> = (0..80)
.map(|i| scene(if i % 2 == 0 { 40 } else { 200 }, i >= 10))
.collect();
let session = Session::builder()
.source(FakeSource::new(frames))
.build()
.await
.unwrap();
let mask = StabilityMask::new()
.block_size(8)
.stable_ticks(3)
.baseline_ticks(20)
.dilate(0);
let mut stream = session
.watch("tooltip")
.region(Region::Full)
.rate(Rate::hz(20.0))
.preprocess(mask)
.detector(Brightness)
.subscribe()
.unwrap();
let ev = tokio::time::timeout(Duration::from_secs(10), stream.next())
.await
.expect("initial event")
.expect("stream open");
let EventKind::Changed { new, .. } = &ev.kind else {
panic!("expected Changed, got {:?}", ev.kind);
};
assert!(
new.as_number().unwrap() < 1.0,
"cold-start mask must be fully black, got {new:?}"
);
let ev = tokio::time::timeout(Duration::from_secs(10), stream.next())
.await
.expect("tooltip event")
.expect("stream open");
let EventKind::Changed { new, .. } = &ev.kind else {
panic!("expected Changed, got {:?}", ev.kind);
};
let brightness = new.as_number().unwrap();
assert!(
brightness > 10.0,
"tooltip block must pass through once stable, got {brightness}"
);
assert!(
brightness < 60.0,
"noise/HUD must remain suppressed, got {brightness}"
);
let quiet = tokio::time::timeout(Duration::from_secs(2), stream.next()).await;
assert!(
quiet.is_err(),
"no further events while the tooltip is held: {quiet:?}"
);
}