use tear_types::{PaneState, SessionId, TearSession};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AllPanesExited {
session: SessionId,
}
impl AllPanesExited {
#[must_use]
pub fn witness(session: &TearSession) -> Option<Self> {
if session.panes.is_empty() {
return None;
}
session
.panes
.values()
.all(|p| matches!(p.state, PaneState::Exited { .. }))
.then_some(Self {
session: session.id,
})
}
#[must_use]
pub const fn session(self) -> SessionId {
self.session
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use tear_types::{
InputPolicy, LayoutNode, PaneId, SessionSource, SessionState, TearPane, TearWindow,
WindowId, WindowState,
};
use super::*;
fn pane(id: u64, state: PaneState) -> TearPane {
TearPane {
yurai: tear_types::Yurai::Unknown,
id: PaneId(id),
shell: "/bin/sh".into(),
args: vec![],
cwd: None,
env: vec![],
size_cells: (80, 24),
origin_cells: (0, 0),
state,
title: "/bin/sh".into(),
input_policy: InputPolicy::default(),
}
}
fn session(source: SessionSource, panes: Vec<TearPane>) -> TearSession {
let wid = WindowId(1);
let mut pane_map = BTreeMap::new();
for p in panes {
pane_map.insert(p.id, p);
}
let mut windows = BTreeMap::new();
windows.insert(
wid,
TearWindow {
id: wid,
name: "main".into(),
layout: LayoutNode::leaf(PaneId(1)),
active_pane: PaneId(1),
size_cells: (80, 24),
state: WindowState::Active,
},
);
TearSession {
id: SessionId(7),
name: "s".into(),
windows,
panes: pane_map,
active_window: wid,
state: SessionState::Active,
created_at_unix: 0,
description: String::new(),
source,
freio: tear_types::Freio::Released,
}
}
#[test]
fn every_pane_exited_witnesses_the_session() {
let s = session(
SessionSource::Human,
vec![
pane(1, PaneState::Exited { code: 0 }),
pane(2, PaneState::Exited { code: 130 }),
],
);
assert_eq!(AllPanesExited::witness(&s).map(AllPanesExited::session), Some(s.id));
}
#[test]
fn one_live_pane_refuses_the_witness_whatever_the_provenance() {
for source in [
SessionSource::Human,
SessionSource::Agent,
SessionSource::Named("banken-bancada".into()),
] {
for live in [PaneState::Running, PaneState::Spawning] {
let s = session(
source.clone(),
vec![pane(1, PaneState::Exited { code: 0 }), pane(2, live)],
);
assert!(
AllPanesExited::witness(&s).is_none(),
"a {source:?} session with a {live:?} pane must not be reapable"
);
}
}
}
#[test]
fn all_running_refuses_the_witness() {
let s = session(
SessionSource::Named("banken-bancada".into()),
vec![pane(1, PaneState::Running), pane(2, PaneState::Running)],
);
assert!(AllPanesExited::witness(&s).is_none());
}
#[test]
fn a_pane_less_session_is_not_witnessed() {
let s = session(SessionSource::Human, vec![]);
assert!(AllPanesExited::witness(&s).is_none());
}
}