first commit
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const {
|
||||
resolveTerminalStdoutOverlayDecision,
|
||||
resolveTerminalStdoutRenderDecision,
|
||||
shouldDeferTerminalStdoutRender
|
||||
} = require("./terminalStdoutRenderPolicy.js");
|
||||
|
||||
describe("terminalStdoutRenderPolicy", () => {
|
||||
it("小 backlog 不会延后视图提交", () => {
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 1024,
|
||||
nextSlicesSinceLastRender: 1,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("中等 backlog 会先累计到阈值再提交", () => {
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 24 * 1024,
|
||||
pendingReplayBytes: 3 * 1024,
|
||||
nextSlicesSinceLastRender: 3,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 24 * 1024,
|
||||
pendingReplayBytes: 8 * 1024,
|
||||
nextSlicesSinceLastRender: 3,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("大 backlog 会使用更高阈值,避免频繁整包 setData", () => {
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 512 * 1024,
|
||||
pendingReplayBytes: 16 * 1024,
|
||||
nextSlicesSinceLastRender: 12,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 512 * 1024,
|
||||
pendingReplayBytes: 16 * 1024,
|
||||
nextSlicesSinceLastRender: 32,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("用户输入或终端响应存在时必须立即提交", () => {
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 512 * 1024,
|
||||
pendingReplayBytes: 1024,
|
||||
nextSlicesSinceLastRender: 1,
|
||||
pendingResponseCount: 1,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldDeferTerminalStdoutRender({
|
||||
remainingBytes: 512 * 1024,
|
||||
pendingReplayBytes: 1024,
|
||||
nextSlicesSinceLastRender: 1,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: true
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("会给出本轮提交或延后的决策原因,便于诊断 render 频率", () => {
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 512 * 1024,
|
||||
pendingReplayBytes: 1024,
|
||||
nextSlicesSinceLastRender: 1,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: true
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "user_input",
|
||||
policy: "interactive"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 24 * 1024,
|
||||
pendingReplayBytes: 3 * 1024,
|
||||
nextSlicesSinceLastRender: 3,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: true,
|
||||
reason: "defer_backlog",
|
||||
policy: "medium_backlog"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 24 * 1024,
|
||||
pendingReplayBytes: 8 * 1024,
|
||||
nextSlicesSinceLastRender: 3,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "pending_bytes_threshold",
|
||||
policy: "medium_backlog"
|
||||
});
|
||||
});
|
||||
|
||||
it("render 冷却期间即使进入尾段,也会继续延后视图提交", () => {
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 2048,
|
||||
nextSlicesSinceLastRender: 2,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 80,
|
||||
taskDone: false
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: true,
|
||||
reason: "render_cooldown",
|
||||
policy: "medium_backlog"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 2048,
|
||||
nextSlicesSinceLastRender: 2,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 280,
|
||||
taskDone: false
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "remaining_below_threshold",
|
||||
policy: "medium_backlog"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 0,
|
||||
pendingReplayBytes: 1024,
|
||||
nextSlicesSinceLastRender: 2,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 20,
|
||||
taskDone: true
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "task_complete"
|
||||
});
|
||||
});
|
||||
|
||||
it("高 backlog 时会进入更激进的降级模式,优先丢弃中间帧", () => {
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 4096,
|
||||
nextSlicesSinceLastRender: 4,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 600,
|
||||
taskDone: false,
|
||||
totalRawBytes: 80 * 1024,
|
||||
pendingStdoutBytes: 48 * 1024,
|
||||
pendingStdoutSamples: 160,
|
||||
schedulerWaitMs: 2400,
|
||||
activeStdoutAgeMs: 1800
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: true,
|
||||
reason: "defer_critical_backlog",
|
||||
policy: "critical_backlog"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 28 * 1024,
|
||||
nextSlicesSinceLastRender: 4,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 600,
|
||||
taskDone: false,
|
||||
totalRawBytes: 80 * 1024,
|
||||
pendingStdoutBytes: 48 * 1024,
|
||||
pendingStdoutSamples: 160,
|
||||
schedulerWaitMs: 2400,
|
||||
activeStdoutAgeMs: 1800
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "pending_bytes_threshold",
|
||||
policy: "critical_backlog"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutRenderDecision({
|
||||
remainingBytes: 4096,
|
||||
pendingReplayBytes: 4096,
|
||||
nextSlicesSinceLastRender: 24,
|
||||
pendingResponseCount: 0,
|
||||
yieldedToUserInput: false,
|
||||
timeSinceLastRenderMs: 600,
|
||||
taskDone: false,
|
||||
totalRawBytes: 80 * 1024,
|
||||
pendingStdoutBytes: 48 * 1024,
|
||||
pendingStdoutSamples: 160,
|
||||
schedulerWaitMs: 2400,
|
||||
activeStdoutAgeMs: 1800
|
||||
})
|
||||
).toMatchObject({
|
||||
defer: false,
|
||||
reason: "slice_threshold",
|
||||
policy: "critical_backlog"
|
||||
});
|
||||
});
|
||||
|
||||
it("stdout 持续输出时会对 overlay 做节流,但最终一帧仍会同步", () => {
|
||||
expect(
|
||||
resolveTerminalStdoutOverlayDecision({
|
||||
isFinalRender: false,
|
||||
yieldedToUserInput: false,
|
||||
overlayPassCount: 0,
|
||||
timeSinceLastOverlayMs: 0
|
||||
})
|
||||
).toMatchObject({
|
||||
sync: true,
|
||||
reason: "first_render"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutOverlayDecision({
|
||||
isFinalRender: false,
|
||||
yieldedToUserInput: false,
|
||||
overlayPassCount: 2,
|
||||
timeSinceLastOverlayMs: 80
|
||||
})
|
||||
).toMatchObject({
|
||||
sync: false,
|
||||
reason: "overlay_throttled"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutOverlayDecision({
|
||||
isFinalRender: false,
|
||||
yieldedToUserInput: false,
|
||||
overlayPassCount: 2,
|
||||
timeSinceLastOverlayMs: 260
|
||||
})
|
||||
).toMatchObject({
|
||||
sync: true,
|
||||
reason: "overlay_cooldown_elapsed"
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveTerminalStdoutOverlayDecision({
|
||||
isFinalRender: true,
|
||||
yieldedToUserInput: false,
|
||||
overlayPassCount: 2,
|
||||
timeSinceLastOverlayMs: 80
|
||||
})
|
||||
).toMatchObject({
|
||||
sync: true,
|
||||
reason: "task_complete"
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user