rmux-server 0.10.0

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

fn respawn_refresh_target(
    control: AttachControl,
    expected_session: &SessionName,
) -> crate::pane_io::AttachTarget {
    let AttachControl::Switch(target) = control else {
        panic!("expected linked alias switch refresh, got {control:?}");
    };
    let target = target.into_target();
    assert_eq!(&target.session_name, expected_session);
    *target
}

async fn receive_latest_respawn_refresh(
    receiver: &mut mpsc::UnboundedReceiver<AttachControl>,
    expected_session: &SessionName,
) -> crate::pane_io::AttachTarget {
    let control = timeout(Duration::from_secs(2), receiver.recv())
        .await
        .expect("linked alias must receive a respawn refresh")
        .expect("linked alias control channel remains open");
    let mut latest = respawn_refresh_target(control, expected_session);
    while let Ok(control) = receiver.try_recv() {
        latest = respawn_refresh_target(control, expected_session);
    }
    latest
}

#[tokio::test]
async fn respawn_window_refreshes_attached_link_aliases_after_active_pane_is_removed() {
    let handler = RequestHandler::new();
    let owner = session_name("respawn-refresh-owner");
    let alias = session_name("respawn-refresh-alias");
    let alias_peer = session_name("respawn-refresh-alias-peer");
    create_session(&handler, owner.as_str()).await;
    create_session(&handler, alias.as_str()).await;
    create_grouped_session(&handler, alias_peer.as_str(), &alias).await;

    let split = handler
        .handle(Request::SplitWindow(SplitWindowRequest {
            target: SplitWindowTarget::Session(owner.clone()),
            direction: SplitDirection::Vertical,
            before: false,
            environment: None,
        }))
        .await;
    let split_target = match split {
        Response::SplitWindow(response) => response.pane,
        response => panic!("expected owner split success, got {response:?}"),
    };
    let (retained_pane_id, removed_active_pane_id) = {
        let mut state = handler.state.lock().await;
        let owner_session = state.sessions.session_mut(&owner).expect("owner exists");
        owner_session
            .select_pane_in_window(0, split_target.pane_index())
            .expect("split pane becomes active");
        let window = owner_session.window_at(0).expect("owner window exists");
        (
            window.pane(0).expect("retained pane exists").id(),
            window.active_pane().expect("active split pane exists").id(),
        )
    };
    assert_ne!(retained_pane_id, removed_active_pane_id);

    let linked = handler
        .handle(Request::LinkWindow(LinkWindowRequest {
            source: WindowTarget::with_window(owner.clone(), 0),
            target: WindowTarget::with_window(alias.clone(), 0),
            after: false,
            before: false,
            kill_destination: true,
            detached: true,
        }))
        .await;
    assert!(matches!(linked, Response::LinkWindow(_)), "{linked:?}");
    {
        let state = handler.state.lock().await;
        for session_name in [&owner, &alias, &alias_peer] {
            let window = state
                .sessions
                .session(session_name)
                .and_then(|session| session.window_at(0))
                .expect("linked family window exists");
            assert_eq!(window.panes().len(), 2);
            assert_eq!(
                window
                    .active_pane()
                    .expect("linked active pane exists")
                    .id(),
                removed_active_pane_id,
                "{session_name} must initially render the pane respawn-window removes"
            );
        }
    }

    let (alias_tx, mut alias_rx) = mpsc::unbounded_channel();
    let (peer_tx, mut peer_rx) = mpsc::unbounded_channel();
    handler.register_attach(51, alias.clone(), alias_tx).await;
    handler
        .register_attach(52, alias_peer.clone(), peer_tx)
        .await;
    tokio::join!(
        drain_attach_controls(&mut alias_rx),
        drain_attach_controls(&mut peer_rx),
    );

    let response = handler
        .handle(Request::RespawnWindow(Box::new(RespawnWindowRequest {
            target: WindowTarget::with_window(owner.clone(), 0),
            kill: true,
            start_directory: None,
            environment: None,
            command: Some(quiet_window_test_command()),
        })))
        .await;
    assert!(
        matches!(response, Response::RespawnWindow(_)),
        "{response:?}"
    );

    let mut alias_target = receive_latest_respawn_refresh(&mut alias_rx, &alias).await;
    let mut peer_target = receive_latest_respawn_refresh(&mut peer_rx, &alias_peer).await;

    let output = {
        let state = handler.state.lock().await;
        for session_name in [&owner, &alias, &alias_peer] {
            let window = state
                .sessions
                .session(session_name)
                .and_then(|session| session.window_at(0))
                .expect("respawned linked family window exists");
            assert_eq!(window.panes().len(), 1);
            assert_eq!(
                window.active_pane().expect("respawned pane exists").id(),
                retained_pane_id,
                "{session_name} must select the retained respawned pane"
            );
        }
        state
            .pane_output_for_target(&alias, 0, 0)
            .expect("respawned alias output exists")
            .clone()
    };
    let expected = b"respawn-linked-alias-live-output".to_vec();
    output.send(expected.clone());
    for target in [&mut alias_target, &mut peer_target] {
        let received = timeout(Duration::from_secs(2), target.pane_output.recv())
            .await
            .expect("refreshed alias receiver follows the respawned runtime");
        let rmux_core::events::OutputCursorItem::Event(event) = received else {
            panic!("expected respawned pane output, got {received:?}");
        };
        assert_eq!(event.bytes(), expected);
    }
}