rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use super::*;

use crate::handler::scripting_support::QueueExecutionContext;

async fn arm_display_panes(
    handler: &RequestHandler,
    requester_pid: u32,
    session: SessionName,
    target_client: Option<String>,
    template: Option<String>,
) {
    let response = handler
        .dispatch(
            requester_pid,
            Request::DisplayPanes(Box::new(rmux_proto::DisplayPanesRequest {
                target: session,
                duration_ms: Some(60_000),
                non_blocking: true,
                no_command: false,
                template,
                target_client,
            })),
        )
        .await
        .response;
    assert!(
        matches!(response, Response::DisplayPanes(_)),
        "{response:?}"
    );
}

async fn displayed_label_for_pane(
    handler: &RequestHandler,
    attach_pid: u32,
    pane_index: u32,
) -> String {
    handler
        .active_attach
        .lock()
        .await
        .by_pid
        .get(&attach_pid)
        .and_then(|active| active.display_panes.as_ref())
        .and_then(|display_panes| {
            display_panes
                .labels
                .iter()
                .find(|label| label.target.pane_index() == pane_index)
        })
        .map(|label| label.label.clone())
        .expect("requested pane has a displayed label")
}

async fn assert_probe_absent(handler: &RequestHandler, name: &str) {
    assert!(
        handler.state.lock().await.buffers.show(Some(name)).is_err(),
        "stale display-panes selection must not execute its command"
    );
}

#[tokio::test]
async fn display_panes_default_selection_rejects_a_relinked_window_occurrence() {
    let handler = RequestHandler::new();
    let requester_pid = std::process::id();
    let session = session_name("display-panes-relinked-occurrence");
    let _control_rx = create_attached_session(&handler, requester_pid, &session).await;
    assert!(matches!(
        handler
            .handle(Request::SplitWindow(SplitWindowRequest {
                target: SplitWindowTarget::Session(session.clone()),
                direction: rmux_proto::SplitDirection::Vertical,
                before: false,
                environment: None,
            }))
            .await,
        Response::SplitWindow(_)
    ));
    assert!(matches!(
        handler
            .handle(Request::SelectPane(Box::new(SelectPaneRequest {
                target: PaneTarget::with_window(session.clone(), 0, 0),
                title: None,
                style: None,
                input_disabled: None,
                preserve_zoom: false,
            })))
            .await,
        Response::SelectPane(_)
    ));
    assert!(matches!(
        handler
            .handle(Request::LinkWindow(LinkWindowRequest {
                source: WindowTarget::with_window(session.clone(), 0),
                target: WindowTarget::with_window(session.clone(), 2),
                after: false,
                before: false,
                kill_destination: false,
                detached: true,
            }))
            .await,
        Response::LinkWindow(_)
    ));
    assert!(matches!(
        handler
            .handle(Request::SelectWindow(SelectWindowRequest {
                target: WindowTarget::with_window(session.clone(), 2),
            }))
            .await,
        Response::SelectWindow(_)
    ));

    arm_display_panes(&handler, requester_pid, session.clone(), None, None).await;
    let label = displayed_label_for_pane(&handler, requester_pid, 1).await;
    {
        let mut state = handler.state.lock().await;
        state
            .unlink_window(WindowTarget::with_window(session.clone(), 2), false)
            .expect("displayed occurrence unlinks");
        state
            .link_window(LinkWindowRequest {
                source: WindowTarget::with_window(session.clone(), 0),
                target: WindowTarget::with_window(session.clone(), 2),
                after: false,
                before: false,
                kill_destination: false,
                detached: true,
            })
            .expect("same window relinks at the displayed slot");
    }

    let result = handler
        .handle_attached_live_input_for_test(requester_pid, label.as_bytes())
        .await;
    assert!(result.is_err(), "stale label must fail closed");
    let state = handler.state.lock().await;
    assert_eq!(
        state
            .sessions
            .session(&session)
            .and_then(|session| session.window_at(0))
            .expect("shared window survives")
            .active_pane_index(),
        0,
        "the replacement occurrence must not receive the stale selection"
    );
}

#[tokio::test]
async fn display_panes_command_rejects_a_reused_pane_slot() {
    let handler = RequestHandler::new();
    let requester_pid = std::process::id();
    let session = session_name("display-panes-pane-slot-aba");
    let _control_rx = create_attached_session(&handler, requester_pid, &session).await;
    assert!(matches!(
        handler
            .handle(Request::SplitWindow(SplitWindowRequest {
                target: SplitWindowTarget::Session(session.clone()),
                direction: rmux_proto::SplitDirection::Vertical,
                before: false,
                environment: None,
            }))
            .await,
        Response::SplitWindow(_)
    ));
    arm_display_panes(
        &handler,
        requester_pid,
        session.clone(),
        None,
        Some("set-buffer -b display-panes-slot-aba fired".to_owned()),
    )
    .await;
    let label = displayed_label_for_pane(&handler, requester_pid, 1).await;

    let response = tokio::time::timeout(
        std::time::Duration::from_secs(5),
        handler.handle(Request::KillPane(rmux_proto::KillPaneRequest {
            target: PaneTarget::with_window(session.clone(), 0, 1),
            kill_all_except: false,
        })),
    )
    .await
    .expect("kill-pane must not wait behind its prepared selection notifications");
    assert!(matches!(response, Response::KillPane(_)));
    assert!(matches!(
        handler
            .handle(Request::SplitWindow(SplitWindowRequest {
                target: SplitWindowTarget::Session(session.clone()),
                direction: rmux_proto::SplitDirection::Vertical,
                before: false,
                environment: None,
            }))
            .await,
        Response::SplitWindow(_)
    ));

    let result = handler
        .handle_attached_live_input_for_test(requester_pid, label.as_bytes())
        .await;
    assert!(result.is_err(), "stale label must fail closed");
    assert_probe_absent(&handler, "display-panes-slot-aba").await;
}

#[tokio::test]
async fn display_panes_command_rejects_a_respawned_pane_generation() {
    let handler = RequestHandler::new();
    let requester_pid = std::process::id();
    let session = session_name("display-panes-respawn-generation");
    let _control_rx = create_attached_session(&handler, requester_pid, &session).await;
    arm_display_panes(
        &handler,
        requester_pid,
        session.clone(),
        None,
        Some("set-buffer -b display-panes-respawn fired".to_owned()),
    )
    .await;
    let label = displayed_label_for_pane(&handler, requester_pid, 0).await;

    assert!(matches!(
        handler
            .handle(Request::RespawnPane(Box::new(
                rmux_proto::RespawnPaneRequest {
                    target: PaneTarget::with_window(session, 0, 0),
                    kill: true,
                    start_directory: None,
                    environment: None,
                    command: None,
                    process_command: None,
                }
            )))
            .await,
        Response::RespawnPane(_)
    ));

    let result = handler
        .handle_attached_live_input_for_test(requester_pid, label.as_bytes())
        .await;
    assert!(result.is_err(), "stale label must fail closed");
    assert_probe_absent(&handler, "display-panes-respawn").await;
}

#[tokio::test]
async fn display_panes_target_client_does_not_bypass_the_queued_lifecycle_lease() {
    let handler = RequestHandler::new();
    let requester_pid = std::process::id();
    let target_pid = requester_pid.saturating_add(1);
    let lifecycle_session = session_name("display-panes-lease-origin");
    let displayed_session = session_name("display-panes-lease-client");
    let _origin_rx = create_attached_session(&handler, requester_pid, &lifecycle_session).await;
    let _target_rx = create_attached_session(&handler, target_pid, &displayed_session).await;
    let lifecycle_target = PaneTarget::with_window(lifecycle_session.clone(), 0, 0);
    let lease = handler
        .state
        .lock()
        .await
        .capture_retained_pane_lifecycle_target(&lifecycle_target)
        .expect("lifecycle target is retainable");
    let commands = handler
        .parse_control_commands(&format!(
            "display-panes -b -t {target_pid} 'set-buffer -b display-panes-client-lease fired'"
        ))
        .await
        .expect("targeted display-panes parses");
    handler
        .execute_parsed_commands(
            requester_pid,
            commands,
            QueueExecutionContext::without_caller_cwd()
                .with_current_target(Some(Target::Pane(lifecycle_target.clone())))
                .with_retained_lifecycle_target(Some(lease)),
        )
        .await
        .expect("targeted display-panes arms while the lease is live");
    let label = displayed_label_for_pane(&handler, target_pid, 0).await;

    assert!(matches!(
        handler
            .handle(Request::RespawnPane(Box::new(
                rmux_proto::RespawnPaneRequest {
                    target: lifecycle_target,
                    kill: true,
                    start_directory: None,
                    environment: None,
                    command: None,
                    process_command: None,
                }
            )))
            .await,
        Response::RespawnPane(_)
    ));

    let result = handler
        .handle_attached_live_input_for_test(target_pid, label.as_bytes())
        .await;
    assert!(result.is_err(), "retired lease must fail closed");
    assert_probe_absent(&handler, "display-panes-client-lease").await;
}