#![cfg(feature = "wgpu")]
use egui::{pos2, Rect};
use facett_core::engine::pick::PickId;
use facett_core::render::gpu::pick_host::{install_pick_host, pick_at, pick_host_installed, pick_passes_run};
use facett_core::render::gpu::picking::PickBatch;
fn render_state() -> egui_wgpu::RenderState {
let setup = egui_kittest::wgpu::default_wgpu_setup();
egui_kittest::wgpu::create_render_state(setup, Default::default())
}
const W: u32 = 160;
const H: u32 = 100;
fn two_halves() -> (PickBatch, PickId, PickId) {
let left = PickId::new(7, 1001);
let right = PickId::new(7, 2002);
let mut b = PickBatch::new();
b.push_quad(Rect::from_min_max(pos2(0.0, 0.0), pos2(80.0, H as f32)), left);
b.push_quad(Rect::from_min_max(pos2(80.0, 0.0), pos2(W as f32, H as f32)), right);
(b, left, right)
}
#[test]
fn a_host_with_only_a_render_state_resolves_clicks_through_pick_at() {
let rs = render_state();
let (batch, left, right) = two_halves();
assert_ne!(left, right, "the two objects must be DISTINGUISHABLE or this proves nothing");
let before = pick_at(&rs, &batch, (W, H), pos2(20.0, 50.0), 1.0, false);
assert!(
before.is_none() || pick_host_installed(),
"with no host installed, pick_at must return None — a fabricated NOTHING would \
look exactly like a real click on the background and silently deselect"
);
assert!(install_pick_host(&rs), "a fresh RenderState must accept the host");
assert!(pick_host_installed(), "installing must arm the latch that unlocks the lane");
assert!(!install_pick_host(&rs), "installing twice must be a no-op, not a second host");
let passes_before = pick_passes_run();
let a = pick_at(&rs, &batch, (W, H), pos2(20.0, 50.0), 1.0, false).expect("the lane must answer");
assert_eq!(a, left, "clicking the left half read {:#010x}, expected {:#010x}", a.0, left.0);
let b = pick_at(&rs, &batch, (W, H), pos2(120.0, 50.0), 1.0, false).expect("the lane must answer");
assert_eq!(b, right, "clicking the right half read {:#010x}, expected {:#010x}", b.0, right.0);
assert_ne!(a, b, "two clicks on two objects must not return one id");
assert!(
pick_passes_run() >= passes_before + 2,
"two clicks must have submitted two id passes — got {} more",
pick_passes_run() - passes_before
);
eprintln!("[gpu_pick_host] resolved {:#010x} and {:#010x} through a real RenderState", a.0, b.0);
}
#[test]
fn empty_space_is_some_nothing_not_none() {
let rs = render_state();
install_pick_host(&rs);
let id = PickId::new(3, 42);
let mut batch = PickBatch::new();
batch.push_quad(Rect::from_min_max(pos2(10.0, 10.0), pos2(30.0, 30.0)), id);
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(20.0, 20.0), 1.0, false), Some(id), "the object");
let miss = pick_at(&rs, &batch, (W, H), pos2(120.0, 80.0), 1.0, false);
assert_eq!(
miss,
Some(PickId::NOTHING),
"a click on background must be Some(NOTHING) — a real 'you hit nothing', \
distinct from None which means the lane declined"
);
assert!(miss.is_some(), "the lane DID answer; it just answered 'nothing'");
}
#[test]
fn each_fail_safe_gate_declines_rather_than_guessing() {
let rs = render_state();
install_pick_host(&rs);
let (batch, left, _) = two_halves();
assert_eq!(
pick_at(&rs, &batch, (W, H), pos2(20.0, 50.0), 1.0, false),
Some(left),
"control: valid arguments must resolve, or the None assertions below are vacuous"
);
assert_eq!(pick_at(&rs, &PickBatch::new(), (W, H), pos2(20.0, 50.0), 1.0, false), None, "empty batch");
assert_eq!(pick_at(&rs, &batch, (0, H), pos2(20.0, 50.0), 1.0, false), None, "zero-width pane");
assert_eq!(pick_at(&rs, &batch, (W, 0), pos2(20.0, 50.0), 1.0, false), None, "zero-height pane");
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(-1.0, 50.0), 1.0, false), None, "left of the pane");
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(20.0, -1.0), 1.0, false), None, "above the pane");
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(500.0, 50.0), 1.0, false), None, "right of the pane");
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(20.0, 500.0), 1.0, false), None, "below the pane");
}
#[test]
fn the_host_persists_across_many_clicks() {
let rs = render_state();
install_pick_host(&rs);
let (batch, left, right) = two_halves();
for i in 0..25 {
let x = if i % 2 == 0 { 20.0 } else { 120.0 };
let want = if i % 2 == 0 { left } else { right };
assert_eq!(pick_at(&rs, &batch, (W, H), pos2(x, 50.0), 1.0, false), Some(want), "click {i}");
}
let guard = rs.renderer.read();
let host = guard
.callback_resources
.get::<facett_core::render::gpu::pick_host::PickHost>()
.expect("the host must still be in callback_resources after 25 clicks");
assert_eq!(host.flat_size(), (W, H), "the id target must be allocated once at the pane size");
}