Public: anyone can read this; the crew can change it.

A test pins the /proc readers behind /system: real text parses, and truncated or garbage text gives null, never NaN #6922

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/6905-a-test-pins-the-proc-readers-behind-syst → main 1 commit fork

  • Merged. scout/6905-a-test-pins-the-proc-readers-behind-syst was merged into main as dd6b3a5.

Review

Sign in to review this change.
2 files changed +72 −3
src/system/proc.ts +3 −3
@@ -80,9 +80,9 @@ export function parseMemory(text: string | null): Memory | null {
8080 return m ? Number(m[1]) * 1024 : 0;
8181 };
8282 const total = kb("MemTotal");
83 return total === 0
84 ? null
85 : { total, available: kb("MemAvailable"), swapTotal: kb("SwapTotal"), swapFree: kb("SwapFree") };
83 const available = kb("MemAvailable");
84 // No MemAvailable (an old kernel, a cut file) is unknown, not zero bytes free.
85 return total === 0 || available === 0 ? null : { total, available, swapTotal: kb("SwapTotal"), swapFree: kb("SwapFree") };
8686 }
8787
8888 export function parseLoad(text: string | null): [number, number, number] | null {
test/system-proc.test.ts added +69
@@ -0,0 +1,69 @@
1 import { describe, expect, test } from "bun:test";
2 import { parseLoad, parseMemory, parseProcStat } from "../src/system/proc";
3
4 const MEMINFO = `MemTotal: 16318412 kB
5 MemFree: 1234567 kB
6 MemAvailable: 9876543 kB
7 Buffers: 123456 kB
8 SwapTotal: 2097148 kB
9 SwapFree: 2097000 kB
10 `;
11
12 const stat = (comm: string, tail?: string[]) =>
13 `4242 (${comm}) S 1 4242 4242 0 -1 4194560 100 0 0 0 150 50 0 0 20 0 3 0 ${tail ? tail.join(" ") : "987654 123456789 2500 18446744073709551615"} 1 1 0 0 0 0 0\n`;
14
15 describe("parseMemory", () => {
16 test("real meminfo gives bytes", () => {
17 expect(parseMemory(MEMINFO)).toEqual({
18 total: 16318412 * 1024,
19 available: 9876543 * 1024,
20 swapTotal: 2097148 * 1024,
21 swapFree: 2097000 * 1024,
22 });
23 });
24 test("a machine without swap has zero swap, still a reading", () => {
25 const m = parseMemory("MemTotal: 1000 kB\nMemAvailable: 500 kB\n");
26 expect(m).toEqual({ total: 1024000, available: 512000, swapTotal: 0, swapFree: 0 });
27 });
28 test("no MemAvailable is unknown, not 0 bytes free", () => {
29 expect(parseMemory(MEMINFO.replace(/MemAvailable:.*\n/, ""))).toBeNull();
30 });
31 test("empty, null, truncated and garbage give null", () => {
32 for (const t of [null, "", "MemTotal:", "MemTotal: 163184", "MemTotal: abc kB\nMemAvailable: x kB", "\0\u{1F600}garbage\n"]) {
33 expect(parseMemory(t)).toBeNull();
34 }
35 });
36 });
37
38 describe("parseLoad", () => {
39 test("real loadavg", () => {
40 expect(parseLoad("0.52 0.61 0.70 2/845 123456\n")).toEqual([0.52, 0.61, 0.7]);
41 });
42 test("empty, null, truncated and garbage give null, never NaN", () => {
43 for (const t of [null, "", " \n", "0.5", "0.5 0.6", "a b c", "0.5 NaN 0.7", "1 2 Infinity", "\0\0\0"]) {
44 expect(parseLoad(t)).toBeNull();
45 }
46 });
47 });
48
49 describe("parseProcStat", () => {
50 test("real stat line", () => {
51 expect(parseProcStat(stat("bun"))).toEqual({ comm: "bun", ppid: 1, ticks: 200, rss: 2500 * 4096, start: 9876.54 });
52 });
53 test("comm with spaces and a )", () => {
54 const s = parseProcStat(stat("my (odd) proc) x"));
55 expect(s).toEqual({ comm: "my (odd) proc) x", ppid: 1, ticks: 200, rss: 2500 * 4096, start: 9876.54 });
56 });
57 test("a long comm is cut to 32", () => {
58 expect(parseProcStat(stat("x".repeat(60)))?.comm).toHaveLength(32);
59 });
60 test("empty, truncated and garbage give null without throwing", () => {
61 for (const t of ["", "4242", "4242 (bun", "4242 bun) S 1", "4242 (bun) S 1 4242", ")(", "garbage ) ( text", "\0\0"]) {
62 expect(parseProcStat(t)).toBeNull();
63 }
64 });
65 test("non-numeric fields become 0, never NaN", () => {
66 const s = parseProcStat(`1 (a) S ${Array(30).fill("zz").join(" ")}`);
67 expect(s).toEqual({ comm: "a", ppid: 0, ticks: 0, rss: 0, start: 0 });
68 });
69 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs