#![allow(clippy::zombie_processes)]
use std::time::Duration;
use predicates::prelude::*;
use super::common::{flow, unique_pipe_name};
use super::test_desktop::{
DaemonGuard, TestDesktop, TestWindow, query_layout_virtual, send_ipc_ignore, start_test_daemon,
unique_title,
};
const HOOK_SETTLE: Duration = Duration::from_millis(1500);
const SWAP_SETTLE: Duration = Duration::from_millis(500);
fn column_window_ids(json: &serde_json::Value) -> Vec<Vec<i64>> {
json["columns"]
.as_array()
.map(|cols| {
cols.iter()
.map(|col| {
col["rows"]
.as_array()
.map(|rows| rows.iter().filter_map(|r| r.as_i64()).collect::<Vec<_>>())
.unwrap_or_default()
})
.collect()
})
.unwrap_or_default()
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn swap_column_right_swaps_two_columns() {
let td = TestDesktop::create().expect("test desktop");
let pipe = unique_pipe_name();
let mut _child = start_test_daemon(&pipe, &td.name).expect("start daemon");
let _guard = DaemonGuard::new(&pipe);
std::thread::sleep(Duration::from_millis(500));
let title_a = unique_title("SwapColR-A");
let title_b = unique_title("SwapColR-B");
let _wa = TestWindow::create(&title_a).expect("create window A");
let _wb = TestWindow::create(&title_b).expect("create window B");
std::thread::sleep(HOOK_SETTLE);
use flow_wm::ipc::message::SocketMessage;
send_ipc_ignore(&pipe, &SocketMessage::FocusLeft);
std::thread::sleep(SWAP_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
let cols_before = column_window_ids(&before);
assert_eq!(
cols_before.len(),
2,
"expected 2 columns, got {cols_before:?}"
);
flow(&pipe)
.args(["dispatch", "swap-column", "right"])
.assert()
.stdout(predicate::str::contains("column swapped"))
.success();
std::thread::sleep(SWAP_SETTLE);
let after = query_layout_virtual(&pipe).expect("query layout after");
let cols_after = column_window_ids(&after);
assert_eq!(
cols_after.len(),
2,
"expected 2 columns after swap, got {cols_after:?}"
);
assert_eq!(
cols_after[0], cols_before[1],
"column 0 should now hold the old column 1 windows"
);
assert_eq!(
cols_after[1], cols_before[0],
"column 1 should now hold the old column 0 windows"
);
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn swap_column_left_swaps_two_columns() {
let td = TestDesktop::create().expect("test desktop");
let pipe = unique_pipe_name();
let mut _child = start_test_daemon(&pipe, &td.name).expect("start daemon");
let _guard = DaemonGuard::new(&pipe);
std::thread::sleep(Duration::from_millis(500));
let title_a = unique_title("SwapColL-A");
let title_b = unique_title("SwapColL-B");
let _wa = TestWindow::create(&title_a).expect("create window A");
let _wb = TestWindow::create(&title_b).expect("create window B");
std::thread::sleep(HOOK_SETTLE);
use flow_wm::ipc::message::SocketMessage;
send_ipc_ignore(&pipe, &SocketMessage::FocusRight);
std::thread::sleep(SWAP_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
let cols_before = column_window_ids(&before);
assert_eq!(
cols_before.len(),
2,
"expected 2 columns, got {cols_before:?}"
);
flow(&pipe)
.args(["dispatch", "swap-column", "left"])
.assert()
.stdout(predicate::str::contains("column swapped"))
.success();
std::thread::sleep(SWAP_SETTLE);
let after = query_layout_virtual(&pipe).expect("query layout after");
let cols_after = column_window_ids(&after);
assert_eq!(cols_after.len(), 2);
assert_eq!(
cols_after[0], cols_before[1],
"column 0 should now hold the old column 1 windows"
);
assert_eq!(
cols_after[1], cols_before[0],
"column 1 should now hold the old column 0 windows"
);
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn move_window_right_swaps_two_columns() {
let td = TestDesktop::create().expect("test desktop");
let pipe = unique_pipe_name();
let mut _child = start_test_daemon(&pipe, &td.name).expect("start daemon");
let _guard = DaemonGuard::new(&pipe);
std::thread::sleep(Duration::from_millis(500));
let title_a = unique_title("MoveWinR-A");
let title_b = unique_title("MoveWinR-B");
let _wa = TestWindow::create(&title_a).expect("create window A");
let _wb = TestWindow::create(&title_b).expect("create window B");
std::thread::sleep(HOOK_SETTLE);
use flow_wm::ipc::message::SocketMessage;
send_ipc_ignore(&pipe, &SocketMessage::FocusLeft);
std::thread::sleep(SWAP_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
let cols_before = column_window_ids(&before);
assert_eq!(
cols_before.len(),
2,
"expected 2 columns, got {cols_before:?}"
);
flow(&pipe)
.args(["dispatch", "move-window", "right"])
.assert()
.stdout(predicate::str::contains("window moved"))
.success();
std::thread::sleep(SWAP_SETTLE);
let after = query_layout_virtual(&pipe).expect("query layout after");
let cols_after = column_window_ids(&after);
assert_eq!(cols_after.len(), 2);
assert_eq!(
cols_after[0], cols_before[1],
"column 0 should now hold the old column 1 windows"
);
assert_eq!(
cols_after[1], cols_before[0],
"column 1 should now hold the old column 0 windows"
);
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn swap_column_right_at_edge_returns_error() {
let td = TestDesktop::create().expect("test desktop");
let pipe = unique_pipe_name();
let mut _child = start_test_daemon(&pipe, &td.name).expect("start daemon");
let _guard = DaemonGuard::new(&pipe);
std::thread::sleep(Duration::from_millis(500));
let title = unique_title("EdgeOnly");
let _w = TestWindow::create(&title).expect("create single window");
std::thread::sleep(HOOK_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
let cols_before = column_window_ids(&before);
assert_eq!(
cols_before.len(),
1,
"expected exactly 1 column, got {cols_before:?}"
);
flow(&pipe)
.args(["dispatch", "swap-column", "right"])
.assert()
.failure();
}