rmux-server 0.10.0

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

async fn selection_hook_fixture(label: &str) -> (RequestHandler, SessionName) {
    let handler = RequestHandler::new();
    let session = session_name(label);
    create_session(&handler, session.as_str()).await;
    let split = handler
        .handle(Request::SplitWindow(SplitWindowRequest {
            target: SplitWindowTarget::Session(session.clone()),
            direction: SplitDirection::Vertical,
            before: false,
            environment: None,
        }))
        .await;
    assert!(matches!(split, Response::SplitWindow(_)), "{split:?}");
    handler.wait_for_initial_panes_for_test().await;
    handler
        .state
        .lock()
        .await
        .sessions
        .session_mut(&session)
        .expect("fixture session exists")
        .select_pane_in_window(0, 0)
        .expect("fixture selects pane zero");
    (handler, session)
}

async fn install_after_select_probe(handler: &RequestHandler, buffer_name: &str) {
    let response = handler
        .handle(Request::SetHookMutation(SetHookMutationRequest {
            scope: ScopeSelector::Global,
            hook: HookName::AfterSelectPane,
            command: Some(format!("set-buffer -b {buffer_name} fired")),
            lifecycle: HookLifecycle::Persistent,
            append: false,
            unset: false,
            run_immediately: false,
            index: None,
        }))
        .await;
    assert!(matches!(response, Response::SetHook(_)), "{response:?}");
    let state = handler.state.lock().await;
    assert!(
        state.buffers.show(Some(buffer_name)).is_err(),
        "probe {buffer_name} fired before the selection under test"
    );
}

async fn assert_probe(handler: &RequestHandler, buffer_name: &str, expected: bool) {
    let state = handler.state.lock().await;
    let probe = state.buffers.show(Some(buffer_name));
    assert_eq!(probe.is_ok(), expected, "buffer {buffer_name}: {probe:?}");
}

async fn run_queued_select(handler: &RequestHandler, command: &str) {
    let parsed = rmux_core::command_parser::CommandParser::new()
        .parse(command)
        .expect("queued select-pane parses");
    handler
        .execute_parsed_commands(
            std::process::id(),
            parsed,
            crate::handler::scripting_support::QueueExecutionContext::without_caller_cwd(),
        )
        .await
        .expect("queued select-pane executes");
}

#[tokio::test]
async fn direct_select_hook_requires_an_active_pane_change() {
    let (handler, session) = selection_hook_fixture("hook-direct-change").await;
    install_after_select_probe(&handler, "hook-direct-change").await;

    let response = handler
        .handle(Request::SelectPane(Box::new(SelectPaneRequest {
            target: PaneTarget::with_window(session, 0, 1),
            title: None,
            style: None,
            input_disabled: None,
            preserve_zoom: false,
        })))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_probe(&handler, "hook-direct-change", true).await;
}

#[tokio::test]
async fn direct_select_noop_title_and_input_mutations_do_not_run_hook() {
    let cases = [
        ("noop", None, None),
        ("title", Some("renamed".to_owned()), None),
        ("input", None, Some(true)),
    ];
    for (label, title, input_disabled) in cases {
        let buffer_name = format!("hook-direct-{label}");
        let (handler, session) = selection_hook_fixture(&buffer_name).await;
        install_after_select_probe(&handler, &buffer_name).await;
        let target_pane = u32::from(title.is_some() || input_disabled.is_some());
        assert_eq!(
            handler
                .state
                .lock()
                .await
                .sessions
                .session(&session)
                .expect("fixture session exists")
                .window()
                .active_pane_index(),
            0,
            "fixture active pane changed before {label}"
        );

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

#[tokio::test]
async fn adjacent_select_hook_runs_only_when_the_active_pane_changes() {
    let (handler, session) = selection_hook_fixture("hook-adjacent").await;
    install_after_select_probe(&handler, "hook-adjacent-change").await;

    let response = handler
        .handle(Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
            target: PaneTarget::with_window(session.clone(), 0, 0),
            direction: SelectPaneDirection::Down,
            preserve_zoom: false,
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_probe(&handler, "hook-adjacent-change", true).await;

    let (noop_handler, noop_session) = selection_hook_fixture("hook-adjacent-noop").await;
    install_after_select_probe(&noop_handler, "hook-adjacent-noop").await;
    let response = noop_handler
        .handle(Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
            target: PaneTarget::with_window(noop_session, 0, 0),
            direction: SelectPaneDirection::Right,
            preserve_zoom: false,
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_probe(&noop_handler, "hook-adjacent-noop", false).await;
}

#[tokio::test]
async fn last_pane_does_not_run_after_select_pane_hook() {
    let (handler, session) = selection_hook_fixture("hook-last-pane").await;
    install_after_select_probe(&handler, "hook-last-pane").await;

    let response = handler
        .handle(Request::LastPane(LastPaneRequest {
            target: WindowTarget::with_window(session, 0),
            preserve_zoom: false,
            input_disabled: None,
        }))
        .await;
    assert!(matches!(response, Response::LastPane(_)), "{response:?}");
    assert_probe(&handler, "hook-last-pane", false).await;
}

#[tokio::test]
async fn sdk_select_hook_requires_an_active_pane_change() {
    let (handler, session) = selection_hook_fixture("hook-sdk").await;
    let pane_one_id = {
        let state = handler.state.lock().await;
        state
            .sessions
            .session(&session)
            .and_then(|session| session.window_at(0))
            .and_then(|window| window.pane(1))
            .expect("pane one exists")
            .id()
    };
    install_after_select_probe(&handler, "hook-sdk-change").await;
    let response = handler
        .handle(Request::PaneSelect(PaneSelectRequest {
            target: PaneTargetRef::by_id(session.clone(), pane_one_id),
            title: None,
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_probe(&handler, "hook-sdk-change", true).await;

    let (title_handler, title_session) = selection_hook_fixture("hook-sdk-title").await;
    let title_pane_id = {
        let state = title_handler.state.lock().await;
        state
            .sessions
            .session(&title_session)
            .and_then(|session| session.window_at(0))
            .and_then(|window| window.pane(1))
            .expect("pane one exists")
            .id()
    };
    install_after_select_probe(&title_handler, "hook-sdk-title").await;
    let response = title_handler
        .handle(Request::PaneSelect(PaneSelectRequest {
            target: PaneTargetRef::by_id(title_session, title_pane_id),
            title: Some("renamed".to_owned()),
        }))
        .await;
    assert!(matches!(response, Response::SelectPane(_)), "{response:?}");
    assert_probe(&title_handler, "hook-sdk-title", false).await;
}

#[tokio::test]
async fn queued_select_hook_uses_the_same_change_gate() {
    let (change_handler, change_session) = selection_hook_fixture("hook-queue-change").await;
    install_after_select_probe(&change_handler, "hook-queue-change").await;
    run_queued_select(
        &change_handler,
        &format!("select-pane -D -t {change_session}:0.0"),
    )
    .await;
    assert_probe(&change_handler, "hook-queue-change", true).await;

    let (noop_handler, noop_session) = selection_hook_fixture("hook-queue-noop").await;
    install_after_select_probe(&noop_handler, "hook-queue-noop").await;
    run_queued_select(
        &noop_handler,
        &format!("select-pane -R -t {noop_session}:0.0"),
    )
    .await;
    assert_probe(&noop_handler, "hook-queue-noop", false).await;
}