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); }); });