rmux-core 0.10.0

Core session, pane, layout, format, hook, and buffer model for the RMUX terminal multiplexer.
Documentation
use std::collections::BTreeMap;

use super::{session_name, Session};
use crate::{PaneId, SessionId, WindowId, WINLINK_ACTIVITY, WINLINK_BELL};
use rmux_proto::TerminalSize;

fn grouped_sessions() -> (Session, Session) {
    let size = TerminalSize { cols: 80, rows: 24 };
    let mut source = Session::new(session_name("owner"), size);
    source.create_window(size).expect("second window");
    let peer = source.clone_as_group_member(
        session_name("peer"),
        session_name("owner"),
        SessionId::new(2),
    );
    (source, peer)
}

#[test]
fn grouped_clone_removal_preserves_owner_and_peer_local_fallbacks() {
    let size = TerminalSize { cols: 80, rows: 24 };
    let mut owner = Session::new_with_initial_window(
        session_name("owner-local-selection"),
        size,
        2,
        PaneId::new(0),
        WindowId::new(0),
    );
    owner
        .insert_window_with_initial_pane(0, size)
        .expect("window 0 insert succeeds");
    owner
        .insert_window_with_initial_pane(1, size)
        .expect("window 1 insert succeeds");
    let mut peer = owner.clone_as_group_member(
        session_name("peer-local-selection"),
        session_name("selection-group"),
        SessionId::new(1),
    );
    let owner_expected_id = owner.window_at(1).expect("owner predecessor exists").id();
    let peer_expected_id = peer.window_at(0).expect("peer initial winlink exists").id();

    owner
        .remove_window(2)
        .expect("owner target removal succeeds");
    peer.remove_window(2)
        .expect("group peer target removal succeeds");

    assert_eq!(owner.window().id(), owner_expected_id);
    assert_eq!(owner.active_window_index(), 1);
    assert_eq!(owner.last_window_index(), None);
    assert_eq!(peer.window().id(), peer_expected_id);
    assert_eq!(peer.active_window_index(), 0);
    assert_eq!(peer.last_window_index(), None);
}

#[test]
fn group_sync_preserves_peer_local_winlink_alert_flags() {
    let (mut source, mut peer) = grouped_sessions();
    assert!(source.add_winlink_alert_flags(0, WINLINK_BELL));
    assert!(peer.add_winlink_alert_flags(1, WINLINK_ACTIVITY));

    peer.synchronize_group_from(&source);

    assert_eq!(peer.winlink_alert_flags(0), crate::AlertFlags::empty());
    assert_eq!(peer.winlink_alert_flags(1), WINLINK_ACTIVITY);
}

#[test]
fn group_sync_rekeys_peer_alert_flags_with_explicit_window_map() {
    let (mut source, mut peer) = grouped_sessions();
    assert!(peer.add_winlink_alert_flags(1, WINLINK_ACTIVITY));
    let index_map = source
        .reindex_windows_from(1)
        .expect("source window reindex succeeds");

    peer.synchronize_group_from_with_window_selection_map(&source, &index_map);

    assert_eq!(peer.winlink_alert_flags(1), crate::AlertFlags::empty());
    assert_eq!(peer.winlink_alert_flags(2), WINLINK_ACTIVITY);
}

#[test]
fn group_sync_duplicate_alias_removal_preserves_surviving_slot_flags() {
    let size = TerminalSize { cols: 80, rows: 24 };
    let mut source = Session::new(session_name("owner-duplicate"), size);
    let duplicate = source.window_at(0).expect("source window").clone();
    source
        .link_window(1, duplicate, false, false)
        .expect("duplicate alias insert succeeds");
    let mut peer = source.clone_as_group_member(
        session_name("peer-duplicate"),
        session_name("owner-duplicate"),
        SessionId::new(3),
    );
    assert!(peer.add_winlink_alert_flags(0, WINLINK_BELL));
    assert!(peer.add_winlink_alert_flags(1, WINLINK_ACTIVITY));
    source
        .remove_window(0)
        .expect("source duplicate alias removal succeeds");

    peer.synchronize_group_from(&source);

    assert_eq!(peer.winlink_alert_flags(1), WINLINK_ACTIVITY);
}

#[test]
fn group_sync_uses_tmux_cyclic_previous_identity_after_active_removal() {
    let size = TerminalSize { cols: 80, rows: 24 };
    let mut source = Session::new(session_name("owner-fallback"), size);
    source
        .insert_window_with_initial_pane(1, size)
        .expect("window 1 insert succeeds");
    source
        .insert_window_with_initial_pane(2, size)
        .expect("window 2 insert succeeds");
    let expected_window_id = source
        .window_at(2)
        .expect("oracle fallback exists before removal")
        .id();
    let mut peer = source.clone_as_group_member(
        session_name("peer-fallback"),
        session_name("owner-fallback"),
        SessionId::new(5),
    );

    source
        .remove_window(0)
        .expect("active source window removal succeeds");
    peer.synchronize_group_from(&source);

    assert_eq!(source.window().id(), expected_window_id);
    assert_eq!(peer.window().id(), expected_window_id);
    assert_eq!(peer.active_window_index(), 2);
}

#[test]
fn group_sync_separates_peer_selection_from_duplicate_alias_alert_permutation() {
    let size = TerminalSize { cols: 80, rows: 24 };
    let mut source = Session::new(session_name("owner-swap-duplicate"), size);
    let duplicate = source.window_at(0).expect("source window").clone();
    source
        .link_window(1, duplicate, false, false)
        .expect("duplicate alias insert succeeds");
    let mut peer = source.clone_as_group_member(
        session_name("peer-swap-duplicate"),
        session_name("owner-swap-duplicate"),
        SessionId::new(4),
    );
    peer.select_window(1)
        .expect("peer selects the second winlink");
    assert!(peer.add_winlink_alert_flags(0, WINLINK_ACTIVITY));

    source.swap_windows(0, 1).expect("source aliases swap");
    peer.synchronize_group_from_with_window_selection_and_winlink_alert_maps(
        &source,
        &BTreeMap::new(),
        &BTreeMap::from([(0, 1), (1, 0)]),
    );

    assert_eq!(peer.active_window_index(), 1);
    assert_eq!(peer.last_window_index(), Some(0));
    assert_eq!(peer.winlink_alert_flags(0), crate::AlertFlags::empty());
    assert_eq!(peer.winlink_alert_flags(1), WINLINK_ACTIVITY);
}