use super::set_titles_support::{
delivered_titles, new_detached_session, remembered_title, set_global, title_capable_context,
};
use super::*;
async fn attach_sized_client(
handler: &RequestHandler,
session: &rmux_proto::SessionName,
attach_pid: u32,
client_size: TerminalSize,
) -> mpsc::UnboundedReceiver<AttachControl> {
let (control_tx, control_rx) = mpsc::unbounded_channel();
let uid = current_owner_uid();
handler
.register_attach_with_access(
attach_pid,
session.clone(),
None,
AttachRegistration {
control_tx,
control_backlog: Arc::new(AtomicUsize::new(0)),
closing: Arc::new(AtomicBool::new(false)),
persistent_overlay_epoch: Arc::new(AtomicU64::new(0)),
terminal_context: title_capable_context(),
client_title: None,
flags: crate::handler::attach_support::ClientFlags::default(),
render_stream: false,
uid,
user: rmux_os::identity::UserIdentity::Uid(uid),
can_write: true,
client_size: Some(client_size),
},
)
.await
.expect("attach registration succeeds");
control_rx
}
async fn list_clients_bindings(handler: &RequestHandler) -> Vec<(String, String)> {
let response = handler
.handle(Request::ListClients(Box::new(
rmux_proto::ListClientsRequest {
target_session: None,
format: Some(
"#{client_pid}\tW=#{client_width}-H=#{client_height}-N=#{client_name}"
.to_owned(),
),
filter: None,
sort_order: None,
reversed: false,
},
)))
.await;
let Response::ListClients(list) = response else {
panic!("list-clients must answer, got {response:?}");
};
String::from_utf8_lossy(list.output.stdout())
.lines()
.filter_map(|line| line.split_once('\t'))
.map(|(pid, rest)| (pid.to_owned(), rest.to_owned()))
.collect()
}
#[tokio::test]
async fn two_clients_expand_their_own_size_and_name_into_the_title() {
let handler = RequestHandler::new();
let alpha = session_name("alpha");
new_detached_session(&handler, &alpha).await;
let wide_pid = 41_821;
let narrow_pid = 41_822;
let mut wide_rx = attach_sized_client(
&handler,
&alpha,
wide_pid,
TerminalSize {
cols: 120,
rows: 30,
},
)
.await;
let mut narrow_rx = attach_sized_client(
&handler,
&alpha,
narrow_pid,
TerminalSize { cols: 80, rows: 24 },
)
.await;
let expected = list_clients_bindings(&handler).await;
let expected_wide = expected
.iter()
.find(|(pid, _)| pid == &wide_pid.to_string())
.map(|(_, value)| value.clone())
.expect("the wide client is listed");
let expected_narrow = expected
.iter()
.find(|(pid, _)| pid == &narrow_pid.to_string())
.map(|(_, value)| value.clone())
.expect("the narrow client is listed");
assert!(
expected_wide.contains("W=120-H=30-N=") && !expected_wide.ends_with("N="),
"the list-clients oracle must resolve the wide client, got {expected_wide:?}"
);
assert_ne!(
expected_wide, expected_narrow,
"the two clients must differ in the oracle"
);
set_global(
&handler,
OptionName::SetTitlesString,
"W=#{client_width}-H=#{client_height}-N=#{client_name}",
)
.await;
set_global(&handler, OptionName::SetTitles, "on").await;
assert_eq!(
delivered_titles(&mut wide_rx),
vec![expected_wide.clone()],
"the wide client's title must carry its own client bindings"
);
assert_eq!(
delivered_titles(&mut narrow_rx),
vec![expected_narrow.clone()],
"the narrow client's title must carry its own client bindings"
);
assert_eq!(
remembered_title(&handler, wide_pid).await,
Some(expected_wide)
);
assert_eq!(
remembered_title(&handler, narrow_pid).await,
Some(expected_narrow)
);
set_global(&handler, OptionName::StatusInterval, "19").await;
assert!(
delivered_titles(&mut wide_rx).is_empty(),
"the wide client's unchanged title must not be rewritten"
);
assert!(
delivered_titles(&mut narrow_rx).is_empty(),
"the narrow client's unchanged title must not be rewritten"
);
}
#[tokio::test]
async fn the_title_resolves_the_client_key_table_and_terminal() {
let handler = RequestHandler::new();
let alpha = session_name("alpha");
new_detached_session(&handler, &alpha).await;
let attach_pid = 41_823;
let mut control_rx = attach_sized_client(
&handler,
&alpha,
attach_pid,
TerminalSize { cols: 90, rows: 25 },
)
.await;
set_global(
&handler,
OptionName::SetTitlesString,
"K=#{client_key_table}/P=#{client_prefix}/T=#{client_termname}/C=#{client_control_mode}",
)
.await;
set_global(&handler, OptionName::SetTitles, "on").await;
assert_eq!(
delivered_titles(&mut control_rx),
vec!["K=root/P=0/T=xterm-256color/C=0".to_owned()],
"an attached client's own terminal and key table must reach the title"
);
}