Files
orca/config/scripts/performance/idle-cpu/idle-cpu-process-sampling.test.mjs
T
Neil 9bd6272fdf refactor(tooling): organize build, CI, and test owners (#38130)
* refactor(tooling): organize test runtime and maintenance owners

* test(tooling): preserve relocated runtime fixture dependency edges

* refactor(tooling): preserve xterm routing owner checks

* ci(tooling): include routing owner in sparse checkout

* test(tooling): preserve copied workflow dependency closures

* fix(tooling): normalize hashed JSON assets in release parity

* fix(tooling): distinguish asset aliases from literal references

* fix(tooling): frame normalized asset reference payloads

* fix(tooling): normalize decoded JSON asset references

* refactor(tooling): organize build, CI and release owners

* fix(tooling): align moved generator and workflow path contracts

* fix(tooling): update generated plugin bundle owner banners

* test(tooling): create relocated mobile verifier fixture directories

* test(tooling): copy headless detector dependency closure

* test(tooling): align native batch policy fixtures with owner path

* fix(tooling): link moved reliability command to historical evidence

* fix(tooling): preserve nested sandbox owners and canonical guide source

* fix(tooling): target release version owners in checkout fixture
2026-11-04 21:08:19 -08:00

123 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { spawn } from 'node:child_process'
import { describe, expect, it } from 'vitest'
import {
cimCpuSeconds,
readProcessRows,
sampleProcessTreeUntilWorkloadsComplete
} from './idle-cpu-process-sampling.mjs'
function createClock(workloadCompletesAt) {
let currentMs = 0
let resolveWorkload
const workloadPromise = new Promise((resolve) => {
resolveWorkload = resolve
})
const wait = async (durationMs) => {
currentMs += durationMs
if (currentMs >= workloadCompletesAt) {
resolveWorkload('complete')
}
await Promise.resolve()
}
const readRows = () => [
{
pid: 10,
ppid: 0,
percentCpu: 0,
rssBytes: 1_024,
cpuTimeSeconds: currentMs / 2_000,
command: 'electron'
}
]
return { now: () => currentMs, readRows, wait, workloadPromise }
}
describe('Windows CIM CPU time mapping', () => {
it('converts the 100ns user and kernel times to a summed second count', () => {
// 10_000_000 and 25_000_000 units of 100ns are 1s and 2.5s.
expect(cimCpuSeconds({ UserModeTime: 10_000_000, KernelModeTime: 25_000_000 })).toBe(3.5)
})
it('maps a missing or non-numeric time to null instead of a kernel-only reading', () => {
expect(cimCpuSeconds({ UserModeTime: null, KernelModeTime: 10_000_000 })).toBeNull()
expect(cimCpuSeconds({ KernelModeTime: 10_000_000 })).toBeNull()
expect(cimCpuSeconds({ UserModeTime: '10000000', KernelModeTime: 10_000_000 })).toBeNull()
expect(cimCpuSeconds({ UserModeTime: Number.NaN, KernelModeTime: 10_000_000 })).toBeNull()
expect(
cimCpuSeconds({ UserModeTime: Number.POSITIVE_INFINITY, KernelModeTime: 10_000_000 })
).toBeNull()
})
})
describe.skipIf(process.platform !== 'win32')('Windows process table read', () => {
// Timeout exceeds reader’s 60s bound; cold PowerShell 5.1 has reached 48s on CI, so this is not a latency budget.
it('parses a command line whose characters the OEM code page maps to control bytes', () => {
const marker = 'idle-cpu-oem • → ♪'
const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 60_000)', marker], {
stdio: 'ignore'
})
try {
const row = readProcessRows().find((candidate) => candidate.pid === child.pid)
expect(row?.command).toContain(marker)
expect(row?.cpuTimeSeconds).toEqual(expect.any(Number))
} finally {
child.kill()
}
}, 75_000)
})
describe('idle CPU process sampling window', () => {
it('extends through a slow workload and captures a final CPU delta', async () => {
const clock = createClock(40)
const result = await sampleProcessTreeUntilWorkloadsComplete({
rootPid: 10,
requestedDurationMs: 20,
intervalMs: 10,
maxWorkloadOverrunMs: 100,
...clock
})
expect(result.workloadResult).toBe('complete')
expect(result.samples.map((sample) => sample.at)).toEqual([10, 20, 30, 40])
expect(result.samples.at(-1)).toEqual({
at: 40,
elapsedMs: 10,
totalCpuPercent: expect.closeTo(50, 8),
totalRssBytes: 1_024,
processes: [
{
pid: 10,
ppid: 0,
percentCpu: 0,
rssBytes: 1_024,
cpuTimeSeconds: 0.02,
command: 'electron',
kind: 'main',
cpu: expect.closeTo(50, 8)
}
]
})
expect(result.samplingWindow).toEqual({
requestedDurationMs: 20,
measuredDurationMs: 40,
maxWorkloadOverrunMs: 100,
extendedForWorkload: true,
workloadSettledElapsedMs: 40,
workloadSettledBeforeStop: true
})
})
it('invalidates a run that reaches the workload overrun guard', async () => {
const clock = createClock(Infinity)
await expect(
sampleProcessTreeUntilWorkloadsComplete({
rootPid: 10,
requestedDurationMs: 20,
intervalMs: 10,
maxWorkloadOverrunMs: 20,
...clock
})
).rejects.toThrow('exceeded the 20ms sampling overrun limit')
})
})