use std::sync::mpsc;
use std::thread;
use super::{SetWindowPosGuard, is_self_initiated};
#[test]
fn a_guard_held_on_another_thread_is_invisible_from_this_thread() {
let (raised_tx, raised_rx) = mpsc::channel::<bool>();
let (release_tx, release_rx) = mpsc::channel::<()>();
let worker = thread::spawn(move || {
let guard = SetWindowPosGuard::new();
raised_tx
.send(is_self_initiated())
.expect("主スレッドが受け取る前に落ちてはならない");
release_rx
.recv()
.expect("主スレッドが解放を指示する前に channel が閉じてはならない");
drop(guard);
is_self_initiated()
});
let seen_by_holder = raised_rx
.recv()
.expect("持ち上げ側が送る前に channel が閉じてはならない");
assert!(
seen_by_holder,
"ガードを持ち上げた当のスレッドでは is_self_initiated() が真であること"
);
assert!(
!is_self_initiated(),
"別スレッドが持ち上げたガードがこちらのスレッドの判定を汚してはならない"
);
release_tx.send(()).expect("持ち上げ側が生きていること");
let seen_after_drop = worker.join().expect("持ち上げ側がパニックしていないこと");
assert!(
!seen_after_drop,
"ガードを落とした後は当のスレッドでも偽へ戻ること"
);
assert!(
!is_self_initiated(),
"他スレッドの解放がこちらの判定を動かしてはならない"
);
}
#[test]
fn nested_guards_on_one_thread_stay_true_until_the_last_is_dropped() {
let observed = thread::spawn(|| {
let at_rest = is_self_initiated();
let outer = SetWindowPosGuard::new();
let with_one = is_self_initiated();
let inner = SetWindowPosGuard::new();
let with_two = is_self_initiated();
drop(inner);
let after_inner = is_self_initiated();
drop(outer);
let after_both = is_self_initiated();
(at_rest, with_one, with_two, after_inner, after_both)
})
.join()
.expect("観測スレッドがパニックしていないこと");
let (at_rest, with_one, with_two, after_inner, after_both) = observed;
assert!(!at_rest, "新しいスレッドのカウンタは 0 から始まること");
assert!(with_one, "1 枚目で真になること");
assert!(with_two, "入れ子でも真のままであること");
assert!(
after_inner,
"内側 1 枚を落としても外側が残るので真のままであること"
);
assert!(!after_both, "最後の 1 枚を落としたら偽へ戻ること");
}
#[test]
fn two_threads_hold_their_own_depth_without_mixing() {
let (report_tx, report_rx) = mpsc::channel::<(u8, bool)>();
let (release_a_tx, release_a_rx) = mpsc::channel::<()>();
let (release_b_tx, release_b_rx) = mpsc::channel::<()>();
let tx_a = report_tx.clone();
let a = thread::spawn(move || {
let guard = SetWindowPosGuard::new();
tx_a.send((0, is_self_initiated()))
.expect("受け手が生きていること");
release_a_rx.recv().expect("解放指示が来ること");
drop(guard);
is_self_initiated()
});
let b = thread::spawn(move || {
let guard = SetWindowPosGuard::new();
report_tx
.send((1, is_self_initiated()))
.expect("受け手が生きていること");
release_b_rx.recv().expect("解放指示が来ること");
drop(guard);
is_self_initiated()
});
let first = report_rx.recv().expect("1 本目の報告が来ること");
let second = report_rx.recv().expect("2 本目の報告が来ること");
assert!(
first.1 && second.1,
"各スレッドが自分の持ち上げを真と見ること"
);
assert_ne!(
first.0, second.0,
"2 本の別々のスレッドから 1 回ずつ届くこと"
);
assert!(
!is_self_initiated(),
"他スレッド 2 本ぶんの深度が主スレッドへ漏れてはならない"
);
release_a_tx.send(()).expect("A が生きていること");
let a_after = a.join().expect("A がパニックしていないこと");
assert!(!a_after, "A は落とした後 偽へ戻ること");
assert!(!is_self_initiated(), "主スレッドは終始 偽であること");
release_b_tx.send(()).expect("B が生きていること");
let b_after = b.join().expect("B がパニックしていないこと");
assert!(!b_after, "B は落とした後 偽へ戻ること");
assert!(!is_self_initiated(), "主スレッドは終始 偽であること");
}