fix(app-iced): 动效全局态按「测试隔离」修 —— 串行门 + 探针旁路(复审 P2-5 返工)

tip 8b6f090 上 7 条用例在 -j 2 下红,根因两层:
1) P2-5 引入的 configure_for_test 只挡调用点,而 App::update(Boot) 会走**产品** configure
   再按真探针定档 —— 本机(RDP 会话)SPI_GETCLIENTAREAANIMATION 报「动画关」⇒ 档位被压成
   NONE ⇒ 位移恒 0 ⇒ 门槛 B/C 的守卫全红(日志里 [motion] scale=0.00 就是它)。
2) 动效全局态(档位 + 进程级时间轴)在并行用例间共享,谁先写谁赢。

修法(判据一条没动):
- components :: motion :: serialize():**跨模块共用的唯一一把**串行门,subscription.rs 与
  update/motion_sync.rs 的用例都取它(idle_app 把 guard 一起返回,活到用例结束)。
- configure_for_test 置 thread-local BYPASS_PROBE,configure 读它 ⇒ 产品启动路径也不会再按
  探针改档;reset() 清旁路,供「故意走真探针」那条用例使用。
- NONE 仍要求严格 0、STANDARD 仍要求 12~20 帧与端点归位。
This commit is contained in:
AskaEth committed 2026-11-12 10:50:03 +08:00
1 parent 1edf645c2f
commit c21c6df51b
3 files changed
+55 -29

No files matched your search

+26 -2
View File
@@ -84,13 +84,22 @@ thread_local! {
/// 上次收到帧的时刻(`advance` 用它把「帧到达」换算成流逝毫秒)。
#[allow(clippy::missing_const_for_thread_local)]
static LAST_FRAME: Cell<Option<Instant>> = const { Cell::new(None) };
/// **测试专用**:置真后 [`configure`] 不再读系统偏好(见 [`configure_for_test`])。
#[allow(clippy::missing_const_for_thread_local)]
static BYPASS_PROBE: Cell<bool> = const { Cell::new(false) };
}
/// 定档:`on_boot` 调一次(截图档给 `NONE`,CPU 档给 `HALF`,见 `app/update/boot_env.rs`)。
///
/// **系统「减少动效」在这里合成**(见模块文档的表):`Reduced` ⇒ `NONE`;`Unknown` ⇒ 不动。
pub fn configure(scale: MotionScale, pin: Option<f64>) {
let scale = effective_scale(scale, ReduceMotion::probe(), pin.is_some());
// 用例走 `configure_for_test` 置位旁路:**产品启动路径**(`on_boot` → 这里)也会重新定档,
// 只挡调用点是不够的。
let scale = if BYPASS_PROBE.with(Cell::get) {
scale
} else {
effective_scale(scale, ReduceMotion::probe(), pin.is_some())
};
PUMP.with(|pump| pump.borrow_mut().configure(scale, pin));
}
@@ -102,7 +111,8 @@ pub fn configure(scale: MotionScale, pin: Option<f64>) {
/// 这条判据悄悄删掉。
#[cfg(test)]
pub fn configure_for_test(scale: MotionScale, pin: Option<f64>) {
PUMP.with(|pump| pump.borrow_mut().configure(scale, pin));
BYPASS_PROBE.with(|flag| flag.set(true));
configure(scale, pin);
}
/// 档位与系统偏好的**合成本身**(纯函数,可断言)。
@@ -121,6 +131,18 @@ pub fn scale() -> MotionScale {
PUMP.with(|pump| pump.borrow().scale())
}
/// **测试专用**:动效全局态的串行门(复审 P2-5 的第二半)。
///
/// `Pump` 与「旁路探针」都是 thread-local,正常并行用例本就互不干扰;但用例会读**产品启动路径**
/// 写下的档位,而 libtest 允许 `--test-threads=1`(届时所有用例同线程)。凡是会改写动效全局态的
/// 用例都先取这把锁,把时序固定下来——**跨模块共用的只有这一把**(`subscription.rs` 与
/// `motion_sync.rs` 都从这里取)。
#[cfg(test)]
pub fn serialize() -> std::sync::MutexGuard<'static, ()> {
static GATE: std::sync::Mutex<()> = std::sync::Mutex::new(());
GATE.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}
/// 已推进的帧数。
pub fn samples() -> u64 {
PUMP.with(|pump| pump.borrow().samples())
@@ -236,6 +258,8 @@ pub fn shifted<'a, M: 'a>(
pub fn reset() {
PUMP.with(|pump| *pump.borrow_mut() = pump::Pump::new());
LAST_FRAME.with(|last| last.set(None));
// 旁路也要清:「故意走真探针」的那条用例靠 `reset()` 回到未旁路状态。
BYPASS_PROBE.with(|flag| flag.set(false));
}
/// 测试专用:按给定毫秒推进(`advance` 读真实时钟,单元测试不能等它)。