use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{GW_HWNDNEXT, GetWindow};
use windows::core::w;
use super::api_owner_chain_probe_tests::{
arrange_z, create_probe_window, destroy_all, ensure_ime_anchor, link_chain, only, place_after,
unlink_all, z_shape,
};
const CHAIN_SIZE: usize = 4;
const DECLARED: [usize; CHAIN_SIZE] = [0, 1, 2, 3];
fn raw_next(hwnd: HWND) -> Option<HWND> {
unsafe { GetWindow(hwnd, GW_HWNDNEXT) }.ok()
}
fn head_is_raw_next_of_second(set: &[HWND]) -> bool {
raw_next(set[1]) == Some(set[0])
}
fn root_is_raw_next_of_anchor(set: &[HWND]) -> bool {
raw_next(set[CHAIN_SIZE - 2]) == Some(set[CHAIN_SIZE - 1])
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Shape {
HeadAfterSecond,
SecondAfterHead,
RootAfterAnchor,
RootAfterHead,
Chosen,
NoNudge,
}
struct SweepOnce {
landed: Vec<usize>,
head_adjacent: bool,
root_adjacent: bool,
}
fn sweep_once(set: &[HWND], order: &[usize], shape: Shape) -> SweepOnce {
unlink_all(set);
arrange_z(set, order);
link_chain(set);
let head_adjacent = head_is_raw_next_of_second(set);
let root_adjacent = root_is_raw_next_of_anchor(set);
let root = CHAIN_SIZE - 1;
let anchor = CHAIN_SIZE - 2;
match shape {
Shape::HeadAfterSecond => place_after(set[0], set[1]),
Shape::SecondAfterHead => place_after(set[1], set[0]),
Shape::RootAfterAnchor => place_after(set[root], set[anchor]),
Shape::RootAfterHead => place_after(set[root], set[0]),
Shape::Chosen => {
if root_adjacent {
place_after(set[root], set[0]);
} else {
place_after(set[root], set[anchor]);
}
}
Shape::NoNudge => {}
}
let landed = z_shape(set);
unlink_all(set);
SweepOnce {
landed,
head_adjacent,
root_adjacent,
}
}
fn all_orders() -> Vec<Vec<usize>> {
let mut out = Vec::new();
for a in 0..CHAIN_SIZE {
for b in 0..CHAIN_SIZE {
for c in 0..CHAIN_SIZE {
for d in 0..CHAIN_SIZE {
let order = vec![a, b, c, d];
let mut seen = order.clone();
seen.sort_unstable();
seen.dedup();
if seen.len() == CHAIN_SIZE {
out.push(order);
}
}
}
}
}
out
}
fn sweep_shape(set: &[HWND], shape: Shape) -> (usize, usize, usize) {
let mut landed_ok = 0;
let mut head_adjacent = 0;
let mut root_adjacent = 0;
for order in all_orders() {
let once = sweep_once(set, &order, shape);
if once.landed == DECLARED.to_vec() {
landed_ok += 1;
}
if once.head_adjacent {
head_adjacent += 1;
}
if once.root_adjacent {
root_adjacent += 1;
}
}
(landed_ok, head_adjacent, root_adjacent)
}
#[test]
fn owner_chain_probe_only_moving_an_owning_window_rearranges_the_chain() {
ensure_ime_anchor();
let set: Vec<HWND> = (0..CHAIN_SIZE)
.map(|_| create_probe_window(w!("owner-chain-nudge/sweep")))
.collect();
let head_after_second = sweep_shape(&set, Shape::HeadAfterSecond);
let second_after_head = sweep_shape(&set, Shape::SecondAfterHead);
let root_after_anchor = sweep_shape(&set, Shape::RootAfterAnchor);
let root_after_head = sweep_shape(&set, Shape::RootAfterHead);
let chosen = sweep_shape(&set, Shape::Chosen);
let no_nudge = sweep_shape(&set, Shape::NoNudge);
unlink_all(&set);
destroy_all(&set);
eprintln!("[probe-10] (landed_ok, head_adjacent, root_adjacent) / 24 通り");
eprintln!("[probe-10] head->after second (初版) = {head_after_second:?}");
eprintln!("[probe-10] second->after head = {second_after_head:?}");
eprintln!("[probe-10] root->after anchor = {root_after_anchor:?}");
eprintln!("[probe-10] root->after head = {root_after_head:?}");
eprintln!("[probe-10] chosen (2 択) = {chosen:?}");
eprintln!("[probe-10] no nudge (対照) = {no_nudge:?}");
assert_eq!(
no_nudge.0, 1,
"【対照】後押し無しで並びが動いている(以下の比較が空虚になる)"
);
assert_eq!(
head_after_second.0, 0,
"初版の後押し(先頭を 2 番目の直後へ)で鎖が収まっている=受け皿が効かず易しい配置を測っている"
);
assert!(
head_after_second.1 > 0,
"先頭が 2 番目の生の直後に居る巡が 1 つも無い(掃き出しが引き金配置を作れていない)"
);
assert_eq!(
second_after_head.0, 24,
"2 番目(先頭の所有者)を動かす形で収まらない巡がある"
);
assert_eq!(
root_after_anchor.0, 24,
"根を錨の直後へ差し直す形で収まらない巡がある"
);
assert_eq!(
root_after_head.0, 24,
"根を先頭の直後へ差し直す形で収まらない巡がある"
);
assert_eq!(chosen.0, 24, "本 spec が採る 2 択の形で収まらない巡がある");
assert!(
chosen.2 > 0,
"根が錨の生の直後に居る巡が 1 つも無い(切り替えの枝を 1 度も踏んでいない)"
);
}
#[test]
fn owner_chain_probe_the_chosen_nudge_keeps_outsiders_in_relative_order() {
ensure_ime_anchor();
let chain: Vec<HWND> = (0..3)
.map(|_| create_probe_window(w!("owner-chain-nudge/out-chain")))
.collect();
let front = create_probe_window(w!("owner-chain-nudge/out-front"));
let back = create_probe_window(w!("owner-chain-nudge/out-back"));
let mut all = chain.clone();
all.push(front);
all.push(back);
arrange_z(&all, &[3, 2, 1, 0, 4]);
let start = z_shape(&all);
link_chain(&chain);
place_after(chain[2], chain[1]);
let after = z_shape(&all);
eprintln!("[probe-11] start = {start:?}");
eprintln!("[probe-11] after = {after:?}");
unlink_all(&chain);
destroy_all(&all);
assert_eq!(
start,
vec![3, 2, 1, 0, 4],
"助走: 部外者で前後を挟み鎖は逆順"
);
assert_eq!(
only(&after, &[0, 1, 2]),
vec![0, 1, 2],
"【実測】採る形の後押しで鎖は宣言順へ収まる"
);
assert_eq!(
only(&after, &[3, 4]),
vec![3, 4],
"【実測・要件 6.1/6.2】鎖の外どうしの相対順は変わらない"
);
}