use super::*;
pub(crate) const SOME_STARTED_AT_MS: u64 = 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Torn {
Job,
Console,
}
pub(crate) fn sample_spec() -> SessionSpec {
SessionSpec {
launch_directory: PathBuf::from("/work"),
command: AppCommand::for_test(&["app.exe"]),
started_at_unix_ms: SOME_STARTED_AT_MS,
}
}
pub(crate) const SAMPLE_APP_EXIT: i32 = 7;
pub(crate) const ORDINARY_ATTACH: Message = Message::Attach {
size: WindowSize::new(80, 24).expect("a fixture size is not empty"),
};
pub(crate) struct StartedSession {
pub(crate) startup_conn: ConnId,
pub(crate) session_id: SessionId,
pub(crate) launcher_tie: LauncherTie,
pub(crate) pipe_name: String,
}
pub(crate) fn commit_startup(
transport: &MemoryTransport,
listener: ListenerId,
phase_reporter: &WatchdogPhaseReporter,
) -> StartedSession {
phase_reporter.report("waiting for the supervisor startup connection");
let conn = transport.accept(listener).unwrap();
phase_reporter.report("waiting for the supervisor startup response");
let Message::StartupOk {
session_id,
launcher_tie,
pipe_name,
} = transport.recv(conn).unwrap()
else {
panic!("expected startup ok");
};
transport.send(conn, &Message::StartupCommit).unwrap();
StartedSession {
startup_conn: conn,
session_id,
launcher_tie,
pipe_name,
}
}
pub(crate) fn mock_processes(exit: Arc<(Mutex<bool>, Condvar)>) -> MockProcesses {
mock_processes_with(exit, LauncherTie::NoneDetected, AppWait::Reports)
}
#[derive(Clone, Copy)]
pub(crate) enum AppWait {
Reports,
Fails,
}
pub(crate) fn mock_processes_with(
exit: Arc<(Mutex<bool>, Condvar)>,
launcher_tie: LauncherTie,
wait: AppWait,
) -> MockProcesses {
mock_processes_with_close_job(exit, launcher_tie, wait, |_| {})
}
pub(crate) fn mock_processes_with_close_job(
exit: Arc<(Mutex<bool>, Condvar)>,
launcher_tie: LauncherTie,
wait: AppWait,
close_job: impl Fn(JobId) + Send + Sync + 'static,
) -> MockProcesses {
let mut processes = MockProcesses::new();
processes
.expect_launcher_tie()
.returning(move || launcher_tie);
processes
.expect_create_lifetime_job()
.returning(|| Ok(JobId::for_test(1)));
processes
.expect_spawn_app()
.returning(|_| Ok(AppId::for_test(1)));
processes.expect_current_identity().returning(|| {
Ok(ProcessIdentity {
pid: 10_u32.try_into().unwrap(),
creation_time: 100,
})
});
processes
.expect_random_nonce()
.returning(|| "nonce".to_string());
processes.expect_close_job().returning(close_job);
processes.expect_wait_app().returning(move |_| {
let (lock, cvar) = &*exit;
let mut done = lock
.lock()
.expect("the exit flag is read or set without callbacks or assertions");
while !*done {
done = cvar
.wait(done)
.expect("the exit flag is read or set without callbacks or assertions");
}
match wait {
AppWait::Reports => Ok(SAMPLE_APP_EXIT),
AppWait::Fails => Err(PalError::new(PalErrorKind::Other)),
}
});
processes
}
pub(crate) fn shared_session<T: Transport + Clone, C: Pseudoconsole + Clone>(
transport: &T,
pty_host: &C,
) -> Shared<T, C> {
Shared {
transport: transport.clone(),
pty_host: pty_host.clone(),
pty: pty_host.create(DEFAULT_PTY_SIZE).unwrap(),
session_id: SessionId::from_u32(1).unwrap(),
client: Mutex::new(None),
attach: Mutex::new(()),
attached_generation: Arc::new(AtomicU64::default()),
preamble: Mutex::new(Some(Vec::new())),
first_attach: Mutex::new(FirstAttach::default()),
first_attach_changed: Condvar::new(),
stopping: AtomicBool::new(false),
}
}
pub(crate) fn client_conn<T: Transport, C>(shared: &Shared<T, C>) -> Option<ConnId> {
shared.client().as_ref().map(|client| client.conn)
}
pub(crate) fn connected_pair(transport: &MemoryTransport, name: &str) -> (ConnId, ConnId) {
let listener = transport.listen(name).unwrap();
let client = transport.connect(name, CONNECT_TIMEOUT).unwrap();
let supervisor = transport.accept(listener).unwrap();
(supervisor, client)
}
pub(crate) fn attach_recorder() -> (Arc<Mutex<Vec<bool>>>, impl Fn(u64, bool)) {
let flags = Arc::new(Mutex::new(Vec::new()));
let recorder = {
let flags = Arc::clone(&flags);
move |_generation: u64, attached: bool| {
flags
.lock()
.expect("the recorder only appends values under its lock")
.push(attached);
}
};
(flags, recorder)
}