③ 驱逐用例抗并发负载:一次灌满替代 48 轮逐张等待

- 现象:驱逐用例逐张 waitFor 48 张(3 个读取槽 + 48 轮 act/轮询)在同树多个写者并发跑测试时,会把用例 5s 超时耗光(Test timed out in 5000ms),红的是超时不是断言;
- 改法:48 张一次性入队,只等"读取都已发出"(read 次数 ≥ 49),不再逐张断言 48 个卡的状态;
- 原因说明:驱逐本来就会连锁(目标卡被驱逐 → 补扫描重读 → 再挤掉一张未被观察的填充卡),逐张断言会在链条行进中读到 undefined,红的是时序不是契约;
- 核心断言一条不动:目标卡必须被重新请求(读次数 1 → 2)且最终回到 loaded;本用例超时放宽到 60s、内部 waitFor 20s;
- 验证:改动前后各连跑 6 轮 + 4 路并发同时跑两个用例文件,全绿。
This commit is contained in:
2026-09-11 20:23:10 +08:00
parent a0d1fca30a
commit cfd89ba570
@@ -1920,7 +1920,7 @@ describe('冷启动首屏的放行范围与放行顺序', () => {
} }
// 首屏要保证有图可读:可见的 12 张一张都不能少。 // 首屏要保证有图可读:可见的 12 张一张都不能少。
expect(readPathsSet.size).toBe(resources.length - offscreen.length); expect(readPathsSet.size).toBe(resources.length - offscreen.length);
}); }, 30000);
it('releases intersecting cards in two bands: viewport first, margin second', async () => { it('releases intersecting cards in two bands: viewport first, margin second', async () => {
// B:一次 observer 回调可能同时报来 ~21 张相交卡,物理读取只有 3 个槽。 // B:一次 observer 回调可能同时报来 ~21 张相交卡,物理读取只有 3 个槽。
@@ -2037,16 +2037,31 @@ describe('预览缓存驱逐后的可见性复核', () => {
expect(targetReads()).toHaveLength(1); expect(targetReads()).toHaveLength(1);
// 灌满缓存:第 49 张落地时把最早的、仍然可见的这张挤出去。 // 灌满缓存:第 49 张落地时把最早的、仍然可见的这张挤出去。
for (const filler of fillers) { //
const identity = result.current.identityByResourceId.get(filler.id)!; // 一次性把 48 张都排进队列、只等"读取都发出去了",**不逐张断言 48 个卡的状态**:
act(() => result.current.requestPreview(filler, identity, 'visible')); // 驱逐本来就会往后连锁(目标卡被驱逐 → 补扫描重读 → 再挤掉一张别人),
await waitFor(() => // 逐张断言会在链条行进中读到 `undefined`,红的是时序不是契约。
expect(result.current.previews.get(identity)?.status).toBe('loaded'), act(() => {
); for (const filler of fillers) {
} result.current.requestPreview(
filler,
result.current.identityByResourceId.get(filler.id)!,
'visible',
);
}
});
await waitFor(
() =>
expect(previewReadCalls(invoke).length).toBeGreaterThanOrEqual(
fillers.length + 1,
),
{ timeout: 20000 },
);
// 驱逐后必须补一次可见性复核:仍可见的卡要重新入队并重新读到图。 // 驱逐后必须补一次可见性复核:仍可见的卡要重新入队并重新读到图。
await waitFor(() => expect(targetReads()).toHaveLength(2)); await waitFor(() => expect(targetReads()).toHaveLength(2), {
timeout: 20000,
});
expect(result.current.previews.get(targetIdentity)?.status).toBe('loaded'); expect(result.current.previews.get(targetIdentity)?.status).toBe('loaded');
}); }, 60000);
}); });