- Memory checker: reader 与 ctx.signal race,abort 返回 memory/timeout,reject 保持 memory/snapshot - CPU checker: 第二次快照异常返回 cpu/snapshot,计算前校验空数组/核心数不一致/非有限值/负 delta - CPU 计算: 零 delta 安全处理,observation 不含 NaN/Infinity - 文档: CPU 互补描述修正,Memory timeout 约束说明 - 测试: +18 覆盖 timeout、异常和边界输入
178 lines
6.9 KiB
TypeScript
178 lines
6.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
||
|
||
import type { CpuCoreSnapshot } from "../../../../../src/server/checker/runner/cpu/types";
|
||
|
||
import { calculateCpuStats, validateCpuSnapshots } from "../../../../../src/server/checker/runner/cpu/calculate";
|
||
|
||
function makeCore(user: number, nice: number, sys: number, idle: number, irq: number): CpuCoreSnapshot {
|
||
return { times: { idle, irq, nice, sys, user } };
|
||
}
|
||
|
||
describe("calculateCpuStats", () => {
|
||
test("单核心完全空闲", () => {
|
||
const before = [makeCore(0, 0, 0, 100, 0)];
|
||
const after = [makeCore(0, 0, 0, 200, 0)];
|
||
const stats = calculateCpuStats(before, after);
|
||
expect(stats.usagePercent).toBe(0);
|
||
expect(stats.idlePercent).toBe(100);
|
||
expect(stats.maxCoreUsagePercent).toBe(0);
|
||
expect(stats.minCoreUsagePercent).toBe(0);
|
||
expect(stats.logicalCoreCount).toBe(1);
|
||
});
|
||
|
||
test("单核心完全忙碌(idle 不变)", () => {
|
||
const before = [makeCore(100, 0, 0, 100, 0)];
|
||
const after = [makeCore(200, 0, 0, 100, 0)];
|
||
// idle delta = 0, total delta = 100
|
||
// idlePercent = 0, usagePercent = 100
|
||
const stats = calculateCpuStats(before, after);
|
||
expect(stats.usagePercent).toBe(100);
|
||
expect(stats.idlePercent).toBe(0);
|
||
});
|
||
|
||
test("单核心部分使用", () => {
|
||
const before = [makeCore(100, 0, 0, 900, 0)];
|
||
const after = [makeCore(150, 0, 0, 950, 0)];
|
||
// idle delta = 50, total delta = 100
|
||
// idlePercent = 50, usagePercent = 50
|
||
const stats = calculateCpuStats(before, after);
|
||
expect(stats.usagePercent).toBe(50);
|
||
expect(stats.idlePercent).toBe(50);
|
||
});
|
||
|
||
test("多核心加权平均", () => {
|
||
// 核心 0: idle delta = 200, total delta = 1000 -> 80% usage
|
||
// 核心 1: idle delta = 800, total delta = 1000 -> 20% usage
|
||
const before = [makeCore(0, 0, 0, 1000, 0), makeCore(0, 0, 0, 1000, 0)];
|
||
const after = [makeCore(800, 0, 0, 1200, 0), makeCore(200, 0, 0, 1800, 0)];
|
||
const stats = calculateCpuStats(before, after);
|
||
// 总 idle = 200+800=1000, 总 delta = 1000+1000=2000
|
||
// idlePercent = 1000/2000*100 = 50
|
||
// usagePercent = 100 - 50 = 50
|
||
expect(stats.idlePercent).toBe(50);
|
||
expect(stats.usagePercent).toBe(50);
|
||
expect(stats.maxCoreUsagePercent).toBe(80);
|
||
expect(stats.minCoreUsagePercent).toBe(20);
|
||
expect(stats.logicalCoreCount).toBe(2);
|
||
expect(stats.perCoreUsagePercent).toEqual([80, 20]);
|
||
});
|
||
|
||
test("四核心各不相同", () => {
|
||
const bf = [
|
||
makeCore(1000, 0, 0, 9000, 0), // core 0 baseline
|
||
makeCore(1000, 0, 0, 9000, 0), // core 1
|
||
makeCore(1000, 0, 0, 9000, 0), // core 2
|
||
makeCore(1000, 0, 0, 9000, 0), // core 3
|
||
];
|
||
const af = [
|
||
makeCore(1900, 0, 0, 9100, 0), // delta: user=900, idle=100, total=1000 -> 90% usage, 10% idle
|
||
makeCore(1500, 0, 0, 9500, 0), // delta: user=500, idle=500, total=1000 -> 50% usage
|
||
makeCore(1200, 0, 0, 9800, 0), // delta: user=200, idle=800, total=1000 -> 20% usage
|
||
makeCore(1010, 0, 0, 9990, 0), // delta: user=10, idle=990, total=1000 -> 1% usage
|
||
];
|
||
const stats = calculateCpuStats(bf, af);
|
||
// 总 idle = 100+500+800+990 = 2390, 总 delta = 4000
|
||
// idlePercent = 2390/4000*100 = 59.75 -> 59.8
|
||
expect(stats.idlePercent).toBe(59.8);
|
||
expect(stats.usagePercent).toBe(40.2);
|
||
expect(stats.maxCoreUsagePercent).toBe(90);
|
||
expect(stats.minCoreUsagePercent).toBe(1);
|
||
expect(stats.perCoreUsagePercent).toEqual([90, 50, 20, 1]);
|
||
expect(stats.logicalCoreCount).toBe(4);
|
||
});
|
||
|
||
test("delta 为 0 时返回 0", () => {
|
||
const before = [makeCore(100, 0, 0, 100, 0)];
|
||
const after = [makeCore(100, 0, 0, 100, 0)];
|
||
const stats = calculateCpuStats(before, after);
|
||
expect(stats.usagePercent).toBe(0);
|
||
expect(stats.idlePercent).toBe(0);
|
||
});
|
||
|
||
test("保留 1 位小数", () => {
|
||
// 总 idle = 333, 总 delta = 1000 -> idlePercent = 33.3
|
||
const before = [makeCore(0, 0, 0, 1000, 0)];
|
||
const after = [makeCore(667, 0, 0, 1333, 0)];
|
||
const stats = calculateCpuStats(before, after);
|
||
// idle delta = 333, total delta = 1000
|
||
expect(stats.idlePercent).toBe(33.3);
|
||
expect(stats.usagePercent).toBe(66.7);
|
||
});
|
||
|
||
test("nice 和 irq 计入 total 但不影响 idle", () => {
|
||
const bf = [makeCore(0, 0, 0, 0, 0)];
|
||
const af = [makeCore(300, 100, 100, 400, 100)];
|
||
// total delta = 300+100+100+400+100 = 1000
|
||
// idle delta = 400
|
||
// idlePercent = 400/1000*100 = 40
|
||
const stats = calculateCpuStats(bf, af);
|
||
expect(stats.idlePercent).toBe(40);
|
||
expect(stats.usagePercent).toBe(60);
|
||
});
|
||
});
|
||
|
||
describe("validateCpuSnapshots", () => {
|
||
test("合法 snapshot 返回 null", () => {
|
||
const before = [makeCore(100, 0, 0, 900, 0)];
|
||
const after = [makeCore(200, 0, 0, 800, 0)];
|
||
expect(validateCpuSnapshots(before, after)).toBeNull();
|
||
});
|
||
|
||
test("空 before snapshot", () => {
|
||
const after = [makeCore(0, 0, 0, 0, 0)];
|
||
expect(validateCpuSnapshots([], after)).toBe("CPU 快照为空");
|
||
});
|
||
|
||
test("空 after snapshot", () => {
|
||
const before = [makeCore(0, 0, 0, 0, 0)];
|
||
expect(validateCpuSnapshots(before, [])).toBe("CPU 快照为空");
|
||
});
|
||
|
||
test("核心数不一致", () => {
|
||
const before = [makeCore(0, 0, 0, 0, 0)];
|
||
const after = [makeCore(0, 0, 0, 0, 0), makeCore(0, 0, 0, 0, 0)];
|
||
expect(validateCpuSnapshots(before, after)).toBe("CPU 快照核心数不一致: before=1, after=2");
|
||
});
|
||
|
||
test("before 包含 NaN time 值", () => {
|
||
const before = [{ times: { idle: NaN, irq: 0, nice: 0, sys: 0, user: 0 } }];
|
||
const after = [makeCore(0, 0, 0, 0, 0)];
|
||
const error = validateCpuSnapshots(before, after);
|
||
expect(error).toContain("非有限值");
|
||
expect(error).toContain("before[0]");
|
||
});
|
||
|
||
test("after 包含 Infinity time 值", () => {
|
||
const before = [makeCore(0, 0, 0, 0, 0)];
|
||
const after = [{ times: { idle: Infinity, irq: 0, nice: 0, sys: 0, user: 0 } }];
|
||
const error = validateCpuSnapshots(before, after);
|
||
expect(error).toContain("非有限值");
|
||
expect(error).toContain("after[0]");
|
||
});
|
||
|
||
test("负数 total delta", () => {
|
||
const before = [makeCore(1000, 0, 0, 0, 0)];
|
||
const after = [makeCore(100, 0, 0, 0, 0)];
|
||
const error = validateCpuSnapshots(before, after);
|
||
expect(error).toContain("负数 delta");
|
||
});
|
||
|
||
test("零 delta 合法", () => {
|
||
const before = [makeCore(100, 0, 0, 100, 0)];
|
||
const after = [makeCore(100, 0, 0, 100, 0)];
|
||
expect(validateCpuSnapshots(before, after)).toBeNull();
|
||
});
|
||
|
||
test("零 delta 不产生除零错误", () => {
|
||
const before = [makeCore(100, 0, 0, 100, 0)];
|
||
const after = [makeCore(100, 0, 0, 100, 0)];
|
||
const stats = calculateCpuStats(before, after);
|
||
expect(Number.isFinite(stats.usagePercent)).toBe(true);
|
||
expect(Number.isFinite(stats.idlePercent)).toBe(true);
|
||
expect(Number.isFinite(stats.maxCoreUsagePercent)).toBe(true);
|
||
expect(Number.isFinite(stats.minCoreUsagePercent)).toBe(true);
|
||
expect(stats.usagePercent).toBe(0);
|
||
expect(stats.idlePercent).toBe(0);
|
||
});
|
||
});
|