rmux-server 0.9.1

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

struct LinkedPaneFixture {
    owner: SessionName,
    grouped_peer: SessionName,
    linked_peer: SessionName,
    pane_one_id: rmux_proto::PaneId,
}

async fn linked_two_pane_fixture(handler: &RequestHandler, label: &str) -> LinkedPaneFixture {
    let owner = session_name(&format!("{label}-owner"));
    let grouped_peer = session_name(&format!("{label}-grouped"));
    let linked_peer = session_name(&format!("{label}-linked"));
    create_session(handler, owner.as_str()).await;
    create_grouped_session(handler, grouped_peer.as_str(), &owner).await;

    let split = handler
        .handle(Request::SplitWindow(SplitWindowRequest {
            target: SplitWindowTarget::Session(owner.clone()),
            direction: SplitDirection::Vertical,
            before: false,
            environment: None,
        }))
        .await;
    assert!(matches!(split, Response::SplitWindow(_)), "{split:?}");

    create_session(handler, linked_peer.as_str()).await;
    let linked = handler
        .handle(Request::LinkWindow(LinkWindowRequest {
            source: WindowTarget::with_window(owner.clone(), 0),
            target: WindowTarget::with_window(linked_peer.clone(), 1),
            after: false,
            before: false,
            kill_destination: false,
            detached: true,
        }))
        .await;
    assert!(matches!(linked, Response::LinkWindow(_)), "{linked:?}");

    let pane_one_id = {
        let mut state = handler.state.lock().await;
        for target in [
            WindowTarget::with_window(owner.clone(), 0),
            WindowTarget::with_window(grouped_peer.clone(), 0),
            WindowTarget::with_window(linked_peer.clone(), 1),
        ] {
            state
                .sessions
                .session_mut(target.session_name())
                .expect("fixture session exists")
                .select_pane_in_window(target.window_index(), 0)
                .expect("fixture pane zero selection succeeds");
        }
        state
            .sessions
            .session(&owner)
            .and_then(|session| session.window_at(0))
            .and_then(|window| window.pane(1))
            .expect("fixture pane one exists")
            .id()
    };

    LinkedPaneFixture {
        owner,
        grouped_peer,
        linked_peer,
        pane_one_id,
    }
}

async fn assert_linked_active_pane(
    handler: &RequestHandler,
    fixture: &LinkedPaneFixture,
    expected: u32,
) {
    let state = handler.state.lock().await;
    for target in [
        WindowTarget::with_window(fixture.owner.clone(), 0),
        WindowTarget::with_window(fixture.grouped_peer.clone(), 0),
        WindowTarget::with_window(fixture.linked_peer.clone(), 1),
    ] {
        assert_eq!(
            state
                .sessions
                .session(target.session_name())
                .and_then(|session| session.window_at(target.window_index()))
                .expect("linked window alias exists")
                .active_pane_index(),
            expected,
            "active pane diverged for {target}"
        );
    }
}

#[tokio::test]
async fn select_pane_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "select-linked").await;

    let (control_tx, mut control_rx) = mpsc::unbounded_channel();
    handler
        .register_attach(7_091, fixture.grouped_peer.clone(), control_tx)
        .await;
    drain_attach_controls(&mut control_rx).await;

    let response = handler
        .handle(Request::SelectPane(Box::new(SelectPaneRequest {
            target: PaneTarget::with_window(fixture.owner.clone(), 0, 1),
            title: None,
            style: None,
            input_disabled: None,
            preserve_zoom: false,
        })))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_linked_active_pane(&handler, &fixture, 1).await;

    let refreshed = timeout(Duration::from_secs(2), control_rx.recv())
        .await
        .expect("attached grouped alias refresh is bounded")
        .expect("attached grouped alias remains registered");
    assert!(
        matches!(refreshed, AttachControl::Switch(_)),
        "{refreshed:?}"
    );
}

#[tokio::test]
async fn sdk_select_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "sdk-select-linked").await;

    let response = handler
        .handle(Request::PaneSelect(PaneSelectRequest {
            target: PaneTargetRef::by_id(fixture.linked_peer.clone(), fixture.pane_one_id),
            title: None,
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_linked_active_pane(&handler, &fixture, 1).await;
}

#[tokio::test]
async fn adjacent_select_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "adjacent-linked").await;

    let response = handler
        .handle(Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
            target: PaneTarget::with_window(fixture.owner.clone(), 0, 0),
            direction: SelectPaneDirection::Down,
            preserve_zoom: false,
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_linked_active_pane(&handler, &fixture, 1).await;
}

#[tokio::test]
async fn last_pane_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "last-linked").await;

    let response = handler
        .handle(Request::LastPane(LastPaneRequest {
            target: WindowTarget::with_window(fixture.owner.clone(), 0),
            preserve_zoom: false,
            input_disabled: None,
        }))
        .await;
    assert!(matches!(response, Response::LastPane(_)), "{response:?}");
    assert_linked_active_pane(&handler, &fixture, 1).await;
}

#[tokio::test]
async fn attached_mouse_focus_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "mouse-select-linked").await;
    let requester_pid = std::process::id();
    let (control_tx, _control_rx) = mpsc::unbounded_channel();
    handler
        .register_attach(requester_pid, fixture.owner.clone(), control_tx)
        .await;
    let identity = handler.active_attach_identity_for_test(requester_pid).await;
    let (session_id, window_id) = {
        let state = handler.state.lock().await;
        let session = state
            .sessions
            .session(&fixture.owner)
            .expect("fixture owner exists");
        (session.id(), session.window().id().as_u32())
    };

    handler
        .select_attached_mouse_focus(
            identity,
            &fixture.owner,
            session_id,
            window_id,
            fixture.pane_one_id,
        )
        .await
        .expect("attached mouse focus succeeds");
    assert_linked_active_pane(&handler, &fixture, 1).await;
}

#[tokio::test]
async fn switch_client_pane_target_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "switch-select-linked").await;
    let requester_pid = std::process::id();
    let (control_tx, mut control_rx) = mpsc::unbounded_channel();
    handler
        .register_attach(requester_pid, fixture.owner.clone(), control_tx)
        .await;
    let (peer_control_tx, mut peer_control_rx) = mpsc::unbounded_channel();
    handler
        .register_attach(
            requester_pid.saturating_add(1),
            fixture.grouped_peer.clone(),
            peer_control_tx,
        )
        .await;
    drain_attach_control_pair(&mut control_rx, &mut peer_control_rx).await;

    let response = handler
        .handle_switch_client_ext3(
            requester_pid,
            rmux_proto::request::SwitchClientExt3Request {
                target_client: None,
                target: Some(format!("{}:0.1", fixture.owner)),
                key_table: None,
                last_session: false,
                next_session: false,
                previous_session: false,
                toggle_read_only: false,
                sort_order: None,
                skip_environment_update: false,
                zoom: false,
            },
        )
        .await;
    assert!(
        matches!(response, Response::SwitchClient(_)),
        "{response:?}"
    );
    assert_linked_active_pane(&handler, &fixture, 1).await;

    let requester_switch = timeout(Duration::from_secs(2), control_rx.recv())
        .await
        .expect("switched client update is bounded")
        .expect("switched client remains registered");
    assert!(
        matches!(requester_switch, AttachControl::Switch(_)),
        "{requester_switch:?}"
    );
    let peer_refresh = timeout(Duration::from_secs(2), peer_control_rx.recv())
        .await
        .expect("linked peer refresh is bounded")
        .expect("linked peer remains registered");
    assert!(
        matches!(peer_refresh, AttachControl::Switch(_)),
        "{peer_refresh:?}"
    );
    let redundant = control_rx.try_recv();
    assert!(
        redundant.is_err(),
        "the switched client must not receive a redundant refresh: {redundant:?}"
    );
}

#[tokio::test]
async fn control_switch_client_pane_target_synchronizes_linked_and_grouped_window_aliases() {
    let handler = RequestHandler::new();
    let fixture = linked_two_pane_fixture(&handler, "control-switch-select-linked").await;
    let requester_pid = std::process::id().saturating_add(200);
    let (event_tx, mut event_rx) =
        tokio::sync::mpsc::channel(crate::control::CONTROL_SERVER_EVENT_CAPACITY);
    handler
        .register_control_with_closing(
            requester_pid,
            crate::control::ControlModeUpgrade {
                initial_command_count: 0,
                mode: rmux_proto::ControlMode::Plain,
                terminal_context: crate::outer_terminal::OuterTerminalContext::default(),
            },
            event_tx,
            std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
        )
        .await;
    handler
        .set_control_session(requester_pid, Some(fixture.owner.clone()))
        .await
        .expect("control session registration succeeds");
    while event_rx.try_recv().is_ok() {}

    let (peer_control_tx, mut peer_control_rx) = mpsc::unbounded_channel();
    handler
        .register_attach(
            requester_pid.saturating_add(1),
            fixture.grouped_peer.clone(),
            peer_control_tx,
        )
        .await;
    drain_attach_controls(&mut peer_control_rx).await;

    let response = handler
        .handle_switch_client_ext3(
            requester_pid,
            rmux_proto::request::SwitchClientExt3Request {
                target_client: None,
                target: Some(format!("{}:0.1", fixture.owner)),
                key_table: None,
                last_session: false,
                next_session: false,
                previous_session: false,
                toggle_read_only: false,
                sort_order: None,
                skip_environment_update: false,
                zoom: false,
            },
        )
        .await;
    assert!(
        matches!(response, Response::SwitchClient(_)),
        "{response:?}"
    );
    assert_linked_active_pane(&handler, &fixture, 1).await;

    let peer_refresh = timeout(Duration::from_secs(2), peer_control_rx.recv())
        .await
        .expect("control switch linked peer refresh is bounded")
        .expect("linked peer remains registered");
    assert!(
        matches!(peer_refresh, AttachControl::Switch(_)),
        "{peer_refresh:?}"
    );
}