use super::*;
use crate::boundary::deposit;
use crate::test_support::{spawn_guard, wire::ephemeral, world_under};
use crate::ui_state::Clock;
use crate::watch::NoRepaint;
use serde_json::json;
use std::time::{Duration, Instant};
use tempfile::tempdir;
struct AtClock(i64);
impl Clock for AtClock {
fn now(&self) -> Instant {
Instant::now()
}
fn stamp(&self) -> String {
self.0.to_string()
}
}
#[test]
fn a_windowless_engine_answers_a_deposit_and_stops_on_drop() {
let _guard = spawn_guard();
let root = tempdir().unwrap();
let world = world_under(root.path());
ephemeral(&world);
let stale = world.yog_stage_root().join("nonce");
std::fs::create_dir_all(&stale).unwrap();
let far_future = 1_000_000_000_000;
let state_root = world.yog_state_root();
deposit::deposit(&state_root, "e-1", &json!({"op": "attention"})).unwrap();
let engine = Engine::boot(
&world,
&[],
None,
Arc::new(AtClock(far_future)),
std::sync::Arc::new(NoRepaint),
);
assert!(!stale.exists(), "the §5.2 startup sweep is the engine's");
assert_eq!(
engine.model.ui_json_path(),
state_root.join("ui.json"),
"the model is rooted in the world it was booted into"
);
let deadline = Instant::now() + Duration::from_secs(10);
let reply = loop {
if let Some(reply) = deposit::read_reply(&state_root, "e-1") {
break reply;
}
assert!(Instant::now() < deadline, "no face answered the deposit");
std::thread::sleep(Duration::from_millis(10));
};
assert_eq!(reply["ok"], true);
assert_eq!(reply["kind"], "attention", "the §6 queue, headless");
assert_eq!(
reply["rows"].as_array().map(Vec::len),
Some(0),
"an empty world asks nothing of anyone"
);
drop(engine); }
#[test]
fn a_booted_engine_hands_its_window_a_seat_on_its_own_wire() {
let _guard = spawn_guard();
let root = tempdir().unwrap();
let world = world_under(root.path());
ephemeral(&world);
let mut engine = Engine::boot(
&world,
&[],
None,
Arc::new(AtClock(0)),
std::sync::Arc::new(NoRepaint),
);
let mut asker = engine
.asker(&world)
.expect("a seat on the engine's own wire");
assert!(
engine.asker(&world).is_none(),
"one asker per engine: the link end is taken, not shared"
);
let question = crate::boundary::codec::encode(&crate::boundary::Gesture::Ask(
crate::boundary::Query::Workspaces,
));
engine.model.refresh();
assert!(engine.model.wire_ask(&question).is_none());
engine.model.refresh();
engine.model.wire_ask(&question);
assert_eq!(asker.pass(), 1, "one standing question, asked");
engine.model.refresh();
let Some(Ok(crate::boundary::reply::Reply::Workspaces(view))) =
engine.model.wire_ask(&question)
else {
panic!("the frame paints a decoded reply that crossed the wire");
};
assert!(view.rows.is_empty(), "an empty world enumerates nothing");
drop(engine);
}
#[test]
fn a_window_takes_both_halves_of_the_wire_once() {
let _guard = spawn_guard();
let root = tempdir().unwrap();
let world = world_under(root.path());
ephemeral(&world);
let mut engine = Engine::boot(
&world,
&[],
None,
Arc::new(AtClock(0)),
std::sync::Arc::new(NoRepaint),
);
let wire = engine.window_wire(&world).expect("both halves");
assert!(engine.window_wire(&world).is_none(), "taken, never shared");
drop(wire);
drop(engine);
}