fix(app-iced): 复审 p2 五条(方向取轨序 / 退场重开 / 遮罩口径 / 恒真断言 / 探针隔离)

- P2-1 方向改由**导航轨序**决定:新增 nav_rank(route)(NavItem::ALL 下标),
  Pump::observe 收 (screen, rank, detail),forward 比较 (rank, key) 而非只比屏键 ——
  修掉 Logs↔Settings 的左右镜像;Download↔Content 同 rank,仍判同级水平(已登记偏离)。
- P2-2 退场途中开新框:observe_modal 先当场收尾(Hidden + 待关)再起 Opening;
  sync_motion 收尾时若队列里还有框就不把 dialog_model 置空。
- P2-3 只改文字:遮罩起手是**整块画布色的不透明矩形**(把页面内容遮住),不是透明看不见。
- P2-4 删掉恒真断言(单个 Option 的 <=1),写明「不累积」是字段形状 + mem::replace 给的;
  tests/timeline.rs 与 tests/modal.rs 的每条用例都补了「抓不住」。
- P2-5 新增 motion::configure_for_test(绕过 OS 探针),用例一律走它;
  另留一条故意走真探针的用例证明 Reduced ⇒ NONE 的合成仍在产品入口上。
- 顺带:pump 的模态状态机拆到 pump/modal.rs(守 400 行硬线)。
This commit is contained in:
AskaEth committed 2026-11-12 10:50:03 +08:00
1 parent 4a77f1b421
commit 1edf645c2f
9 files changed
+446 -274

No files matched your search

@@ -119,7 +119,7 @@ fn every_transition_is_built_from_registered_tokens() {
#[test]
fn a_page_enters_over_twelve_to_twenty_frames_and_lands_on_the_endpoint() {
let mut pump = standard();
assert!(pump.observe(Some(2), None), "首次观察要起跑 Boot");
assert!(pump.observe(Some(2), Some(1), None), "首次观察要起跑 Boot");
let frames = settle(&mut pump);
assert!(
(12..=20).contains(&frames),
@@ -127,7 +127,7 @@ fn a_page_enters_over_twelve_to_twenty_frames_and_lands_on_the_endpoint() {
);
// 重跑一次,逐帧看端点与单调性
pump.observe(Some(3), None); // 同级换页:medium2 = 300ms
pump.observe(Some(3), Some(1), None); // 同级换页:medium2 = 300ms
let trace = samples(&mut pump);
assert!(trace.len() >= 12, "中间态太少:{}", trace.len());
assert_eq!(
@@ -163,10 +163,10 @@ fn a_page_enters_over_twelve_to_twenty_frames_and_lands_on_the_endpoint() {
#[test]
fn the_old_page_moves_the_other_way_and_leaves_first() {
let mut pump = standard();
pump.observe(Some(2), None);
pump.observe(Some(2), Some(1), None);
settle(&mut pump);
assert!(pump.observe(Some(3), None));
assert!(pump.observe(Some(3), Some(1), None));
let first = pump.page_frame();
assert_eq!(first.enter, (12.0, 0.0));
assert_eq!(first.exit, (0.0, 0.0), "退场起点是 0");
@@ -201,23 +201,23 @@ fn the_old_page_moves_the_other_way_and_leaves_first() {
#[test]
fn direction_follows_navigation_depth() {
let mut pump = standard();
pump.observe(Some(1), None);
pump.observe(Some(1), Some(0), None);
settle(&mut pump);
// 向右推进(屏键 1 → 3)
pump.observe(Some(3), None);
pump.observe(Some(3), Some(1), None);
assert_eq!(pump.kind(), Some(Kind::Peer { forward: true }));
assert_eq!(pump.page_frame().enter, (12.0, 0.0), "同级往右 = 水平位移");
settle(&mut pump);
// 向左退回(3 → 1)
pump.observe(Some(1), None);
pump.observe(Some(1), Some(0), None);
assert_eq!(pump.kind(), Some(Kind::Peer { forward: false }));
assert_eq!(pump.page_frame().enter, (-12.0, 0.0), "同级往左 = 镜像");
settle(&mut pump);
// 进深层(同屏:列表 → 详情)
pump.observe(Some(1), Some("instance-7"));
pump.observe(Some(1), Some(0), Some("instance-7"));
assert_eq!(pump.kind(), Some(Kind::Push));
let push = pump.page_frame();
assert_eq!(push.enter, (0.0, 16.0), "进深层 = 纵向(从下方上浮)");
@@ -225,11 +225,42 @@ fn direction_follows_navigation_depth() {
settle(&mut pump);
// 出深层(详情 → 列表)
pump.observe(Some(1), None);
pump.observe(Some(1), Some(0), None);
assert_eq!(pump.kind(), Some(Kind::Pop));
assert_eq!(pump.page_frame().enter, (0.0, -16.0), "出深层必须镜像");
}
/// **左右方向看导航轨序,不看屏键**(复审 P2-1 的守卫)。
///
/// 两处顺序不同:`Screen::ALL` 是 `… Content / Settings / Logs`,导航轨是 `… Logs / Settings / Ai`。
/// 用屏键判方向会让 **Logs↔Settings 镜像**。
///
/// 抓得住:把 `Pump::observe` 的方向改回「比较屏键」(`new > old`)——本用例里
/// Settings(键 4 / **序 3**) → Logs(键 5 / **序 2**) 会被判成向右推进,第一条断言立即红。
/// 也抓得住「谁调了 `Screen::ALL` 的顺序」:这里**只引用轨序**,与那张表的声明顺序解耦。
/// 抓不住:导航轨本身的视觉位置(那是 `shell/nav.rs` 的几何,归它的守卫)。
#[test]
fn direction_comes_from_the_nav_rail_not_the_screen_table() {
let mut pump = standard();
pump.observe(Some(4), Some(3), None); // Settings:屏键 4、轨序 3
settle(&mut pump);
// 轨序 3 → 2(向左)。若按屏键 4 → 5,会被判成向右 —— 那正是被修的 bug。
pump.observe(Some(5), Some(2), None);
assert_eq!(
pump.kind(),
Some(Kind::Peer { forward: false }),
"Settings → Logs 在导航轨上是向左"
);
assert_eq!(pump.page_frame().enter, (-12.0, 0.0));
settle(&mut pump);
// 反向:轨序 2 → 3 = 向右
pump.observe(Some(4), Some(3), None);
assert_eq!(pump.kind(), Some(Kind::Peer { forward: true }));
assert_eq!(pump.page_frame().enter, (12.0, 0.0));
}
/// **`MotionScale::NONE` 下位移与缩放严格为 0**(不是"很小")。
///
/// 抓得住:把 `play_page` 的 `is_off()` 分支删掉(变成跑一遍 0 时长动画)——那时 `frame()`
@@ -241,13 +272,13 @@ fn none_scale_is_strictly_zero_not_merely_small() {
assert!(pump.scale().is_off());
// 首屏 / 换页 / 进退层:三次状态变化都不许留下轨道,位移必须 `== 0.0`(不是 `abs() < eps`)
for (screen, detail) in [
(Some(1), None),
(Some(2), None),
(Some(2), Some("instance-7")),
(Some(2), None),
for (screen, rank, detail) in [
(Some(1), Some(0), None),
(Some(2), Some(1), None),
(Some(2), Some(1), Some("instance-7")),
(Some(2), Some(1), None),
] {
assert!(!pump.observe(screen, detail), "关掉动效时不许起跑");
assert!(!pump.observe(screen, rank, detail), "关掉动效时不许起跑");
let frame = pump.page_frame();
assert_eq!(frame.enter.0, 0.0);
assert_eq!(frame.enter.1, 0.0);
@@ -282,11 +313,11 @@ fn none_scale_is_strictly_zero_not_merely_small() {
#[test]
fn repeated_paints_never_restart_the_animation() {
let mut pump = standard();
pump.observe(Some(3), Some("instance-9"));
pump.observe(Some(3), Some(1), Some("instance-9"));
settle(&mut pump);
for _ in 0..30 {
assert!(!pump.observe(Some(3), Some("instance-9")));
assert!(!pump.observe(Some(3), Some(1), Some("instance-9")));
assert_eq!(pump.page_frame(), PageFrame::STILL);
}
assert!(!pump.is_animating());
@@ -296,13 +327,19 @@ fn repeated_paints_never_restart_the_animation() {
#[test]
fn screens_rendered_elsewhere_never_start_a_track() {
let mut pump = standard();
pump.observe(Some(1), None);
pump.observe(Some(1), Some(0), None);
settle(&mut pump);
assert!(!pump.observe(None, None), "没有 pages 树可动 ⇒ 不许白开帧");
assert!(
!pump.observe(None, None, None),
"没有 pages 树可动 ⇒ 不许白开帧"
);
assert!(!pump.is_animating());
assert!(pump.observe(Some(1), None), "从账户页回来仍然是换页");
assert!(
pump.observe(Some(1), Some(0), None),
"从账户页回来仍然是换页"
);
assert_eq!(pump.kind(), Some(Kind::Boot), "没有旧页可退 ⇒ 按首屏方向");
}
@@ -314,10 +351,10 @@ fn screens_rendered_elsewhere_never_start_a_track() {
#[test]
fn a_second_navigation_mid_flight_takes_over_cleanly() {
let mut pump = standard();
pump.observe(Some(1), None);
pump.observe(Some(1), Some(0), None);
settle(&mut pump);
pump.observe(Some(2), None); // 往右推进
pump.observe(Some(2), Some(1), None); // 往右推进
for _ in 0..4 {
pump.advance(FRAME_MS); // 走 3~5 帧就再点一次
}
@@ -327,7 +364,7 @@ fn a_second_navigation_mid_flight_takes_over_cleanly() {
"第一次过渡还在半路"
);
pump.observe(Some(1), None); // 立刻退回左边
pump.observe(Some(1), Some(0), None); // 立刻退回左边
let restarted = pump.page_frame();
assert_eq!(restarted.enter, (-12.0, 0.0), "新方向立刻生效(满幅起点)");
assert_eq!(