#![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, wait_until_windows_tiled,
};
const HOOK_SETTLE: Duration = Duration::from_millis(1500);
const DISPATCH_SETTLE: Duration = Duration::from_millis(500);
fn tiled_window_count(json: &serde_json::Value) -> usize {
json["columns"]
.as_array()
.map(|cols| {
cols.iter()
.filter_map(|col| col["rows"].as_array().map(|rows| rows.len()))
.sum()
})
.unwrap_or(0)
}
fn column_count(json: &serde_json::Value) -> usize {
json["columns"]
.as_array()
.map(|cols| cols.len())
.unwrap_or(0)
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn set_window_float_then_tile_roundtrips_layout() {
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("SetWin-A");
let title_b = unique_title("SetWin-B");
let _wa = TestWindow::create(&title_a).expect("create window A");
let _wb = TestWindow::create(&title_b).expect("create window B");
wait_until_windows_tiled(&pipe, 2).expect("2 windows tiled at baseline");
use flow_wm::ipc::message::SocketMessage;
send_ipc_ignore(&pipe, &SocketMessage::FocusLeft);
std::thread::sleep(DISPATCH_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
let cols_before = column_count(&before);
let tiled_before = tiled_window_count(&before);
assert_eq!(
cols_before, 2,
"expected 2 tiled columns at baseline, got {cols_before}"
);
assert_eq!(
tiled_before, 2,
"expected 2 tiled windows at baseline, got {tiled_before}"
);
flow(&pipe)
.args(["dispatch", "set-window", "float"])
.assert()
.stdout(predicate::str::contains("window set to float"))
.success();
std::thread::sleep(DISPATCH_SETTLE);
let after_float = query_layout_virtual(&pipe).expect("query layout after float");
let cols_after_float = column_count(&after_float);
let tiled_after_float = tiled_window_count(&after_float);
assert_eq!(
cols_after_float, 1,
"floating one of two windows must collapse to 1 column, got {cols_after_float}"
);
assert_eq!(
tiled_after_float, 1,
"floating one of two windows must leave 1 tiled window, got {tiled_after_float}"
);
flow(&pipe)
.args(["dispatch", "set-window", "tile"])
.assert()
.stdout(predicate::str::contains("window set to tile"))
.success();
std::thread::sleep(DISPATCH_SETTLE);
let after_tile = query_layout_virtual(&pipe).expect("query layout after tile");
let cols_after_tile = column_count(&after_tile);
let tiled_after_tile = tiled_window_count(&after_tile);
assert_eq!(
cols_after_tile, 2,
"re-tiling the floated window must restore 2 columns, got {cols_after_tile}"
);
assert_eq!(
tiled_after_tile, 2,
"re-tiling the floated window must restore 2 tiled windows, got {tiled_after_tile}"
);
}
#[ignore = "non-deterministic on isolated test desktop: daemon hooks may not register/tile windows before the assertion (startup hook race)"]
#[test]
fn set_window_cycle_toggles_tiled_to_floating() {
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("SetWinCyc-A");
let title_b = unique_title("SetWinCyc-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(DISPATCH_SETTLE);
let before = query_layout_virtual(&pipe).expect("query layout before");
assert_eq!(column_count(&before), 2);
flow(&pipe)
.args(["dispatch", "set-window", "cycle"])
.assert()
.stdout(predicate::str::contains("window set to cycle"))
.success();
std::thread::sleep(DISPATCH_SETTLE);
let after = query_layout_virtual(&pipe).expect("query layout after cycle");
assert_eq!(
column_count(&after),
1,
"cycle on a tiled window must float it → 1 column"
);
}