mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-13 16:38:56 +02:00
spawnSync/execSync/Bun.spawnSync BLOCK the main thread, so bun's in-process per-test timeout can never fire while one waits — a hung child (stdin read, network probe, dead daemon) wedges the whole shard until the runner's external wall-clock SIGKILL. This exact class reached main: free-tests run 33262077256, test/gstack-memory-ingest.test.ts (normally 2.3s) held shard 2 at the 360s wall while its five siblings finished in ~65s. Mechanical sweep in two waves (12 + 4 fan-out agents, every edit verified against its call site): default timeout: 30_000 (matches the free runner's per-test budget), 120_000 for genuinely slow ops (installs, builds, playwright, provider CLIs), helper wrappers fixed ONCE where call sites route through them. Sites that only LOOK like calls (string fixtures, grep needles, comments) were skipped with reasons — the enforcement commit that follows marks them exempt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
177 lines
6.1 KiB
TypeScript
177 lines
6.1 KiB
TypeScript
import { describe, test, expect } from 'bun:test';
|
|
import {
|
|
shouldSpawnXvfb,
|
|
isOurXvfb,
|
|
readPidStartTime,
|
|
readPidCmdline,
|
|
cleanupXvfb,
|
|
pickFreeDisplay,
|
|
isDisplayFree,
|
|
} from '../src/xvfb';
|
|
|
|
const HAS_XVFB = (() => {
|
|
if (process.platform !== 'linux') return false;
|
|
const result = Bun.spawnSync(['which', 'Xvfb'], { stdout: 'pipe', stderr: 'pipe', timeout: 30_000 });
|
|
return result.exitCode === 0;
|
|
})();
|
|
|
|
describe('shouldSpawnXvfb', () => {
|
|
test('skips when not headed', () => {
|
|
const d = shouldSpawnXvfb({}, 'linux');
|
|
expect(d.spawn).toBe(false);
|
|
expect(d.reason).toContain('not headed');
|
|
});
|
|
|
|
test('skips on macOS even when headed', () => {
|
|
const d = shouldSpawnXvfb({ BROWSE_HEADED: '1' }, 'darwin');
|
|
expect(d.spawn).toBe(false);
|
|
expect(d.reason).toContain('darwin');
|
|
});
|
|
|
|
test('skips on Windows even when headed', () => {
|
|
const d = shouldSpawnXvfb({ BROWSE_HEADED: '1' }, 'win32');
|
|
expect(d.spawn).toBe(false);
|
|
expect(d.reason).toContain('win32');
|
|
});
|
|
|
|
test('skips on Linux when DISPLAY already set', () => {
|
|
const d = shouldSpawnXvfb({ BROWSE_HEADED: '1', DISPLAY: ':0' }, 'linux');
|
|
expect(d.spawn).toBe(false);
|
|
expect(d.reason).toContain('DISPLAY=:0');
|
|
});
|
|
|
|
test('skips on Linux when WAYLAND_DISPLAY set (codex F2)', () => {
|
|
const d = shouldSpawnXvfb({ BROWSE_HEADED: '1', WAYLAND_DISPLAY: 'wayland-0' }, 'linux');
|
|
expect(d.spawn).toBe(false);
|
|
expect(d.reason).toContain('Wayland');
|
|
});
|
|
|
|
test('spawns on Linux + headed + no DISPLAY/WAYLAND_DISPLAY', () => {
|
|
const d = shouldSpawnXvfb({ BROWSE_HEADED: '1' }, 'linux');
|
|
expect(d.spawn).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('isOurXvfb (PID validation)', () => {
|
|
test('returns false when pid is 0', () => {
|
|
expect(isOurXvfb(0, 'whatever')).toBe(false);
|
|
});
|
|
|
|
test('returns false when startTime is empty', () => {
|
|
expect(isOurXvfb(process.pid, '')).toBe(false);
|
|
});
|
|
|
|
test('returns false when cmdline does not contain Xvfb', () => {
|
|
// Current bun process is not Xvfb. PID-correct, cmdline-wrong → reject.
|
|
// NOTE: this very suite's argv CONTAINS "xvfb.test.ts" — a substring
|
|
// match over the whole cmdline identified the test runner as our Xvfb
|
|
// on the first Linux CI run. Identity rests on argv[0]'s basename.
|
|
const myStart = readPidStartTime(process.pid);
|
|
expect(isOurXvfb(process.pid, myStart)).toBe(false);
|
|
});
|
|
|
|
test('a process whose ARGUMENTS mention xvfb is not ours (argv0 identity)', async () => {
|
|
// sh's $0 trick plants "xvfb" in the child's args while argv[0] stays sh.
|
|
// Killing this process because its arguments mention xvfb is the exact
|
|
// sibling-kill class the identity check exists to prevent.
|
|
const child = Bun.spawn(['/bin/sh', '-c', 'sleep 2', 'xvfb-lookalike-arg']);
|
|
try {
|
|
const start = readPidStartTime(child.pid);
|
|
// On non-Linux, /proc is absent and both reads return '' → false either way.
|
|
expect(isOurXvfb(child.pid, start || 'recorded')).toBe(false);
|
|
} finally {
|
|
child.kill();
|
|
await child.exited;
|
|
}
|
|
});
|
|
|
|
test('returns false when start-time differs (PID reuse defense)', () => {
|
|
// Even if we somehow had the right PID, a stale start-time means it's a
|
|
// different process. We never fake the cmdline test, so this assertion
|
|
// is structural: the function must not pass on stale start-time alone.
|
|
expect(isOurXvfb(process.pid, 'Mon Jan 1 00:00:00 1970')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('readPidStartTime', () => {
|
|
test('returns non-empty for current process', () => {
|
|
if (process.platform === 'win32') return; // ps not available
|
|
const t = readPidStartTime(process.pid);
|
|
expect(t.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('returns empty string for nonexistent PID', () => {
|
|
expect(readPidStartTime(99999999)).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('readPidCmdline', () => {
|
|
test('returns non-empty for current process on Linux', () => {
|
|
if (process.platform !== 'linux') return; // /proc unavailable
|
|
const c = readPidCmdline(process.pid);
|
|
expect(c.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('returns empty for nonexistent PID', () => {
|
|
expect(readPidCmdline(99999999)).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('cleanupXvfb', () => {
|
|
test('no-op when pid is 0', () => {
|
|
expect(() => cleanupXvfb({ pid: 0, startTime: '', display: ':99' })).not.toThrow();
|
|
});
|
|
|
|
test('no-op when not our Xvfb (won\'t kill unrelated process)', () => {
|
|
// Pass the current bun process's PID + a stale start-time. cleanupXvfb
|
|
// should refuse to send signals because cmdline doesn't match Xvfb.
|
|
expect(() => cleanupXvfb({
|
|
pid: process.pid,
|
|
startTime: 'Mon Jan 1 00:00:00 1970',
|
|
display: ':99',
|
|
})).not.toThrow();
|
|
// The current process is still alive after the no-op cleanup attempt.
|
|
expect(process.kill(process.pid, 0)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('pickFreeDisplay (Xvfb installed)', () => {
|
|
test.skipIf(!HAS_XVFB)('returns a number in the requested range', () => {
|
|
const n = pickFreeDisplay(99, 105);
|
|
if (n != null) {
|
|
expect(n).toBeGreaterThanOrEqual(99);
|
|
expect(n).toBeLessThanOrEqual(105);
|
|
}
|
|
// null means all displays in range are busy — also valid.
|
|
});
|
|
|
|
test.skipIf(!HAS_XVFB)('isDisplayFree returns boolean', () => {
|
|
const result = isDisplayFree(99);
|
|
expect(typeof result).toBe('boolean');
|
|
});
|
|
});
|
|
|
|
describe('xvfb spawn → cleanup round trip (Linux + Xvfb only)', () => {
|
|
test.skipIf(!HAS_XVFB)('spawn, validate ownership, cleanup', async () => {
|
|
const { spawnXvfb } = await import('../src/xvfb');
|
|
const display = pickFreeDisplay(99, 110);
|
|
if (display == null) {
|
|
// No free display in range — skip.
|
|
return;
|
|
}
|
|
const handle = await spawnXvfb(display);
|
|
try {
|
|
expect(handle.pid).toBeGreaterThan(0);
|
|
expect(handle.display).toBe(`:${display}`);
|
|
expect(handle.startTime.length).toBeGreaterThan(0);
|
|
// Validation should pass.
|
|
expect(isOurXvfb(handle.pid, handle.startTime)).toBe(true);
|
|
} finally {
|
|
handle.close();
|
|
// After cleanup, our Xvfb should be gone.
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
expect(isOurXvfb(handle.pid, handle.startTime)).toBe(false);
|
|
}
|
|
});
|
|
});
|