Initial import from garrytan/gstack@026751e (main snapshot via local relay)
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Source: https://github.com/garrytan/gstack/commit/026751e
This commit is contained in:
158
browse/test/xvfb.test.ts
Normal file
158
browse/test/xvfb.test.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
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' });
|
||||
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.
|
||||
const myStart = readPidStartTime(process.pid);
|
||||
expect(isOurXvfb(process.pid, myStart)).toBe(false);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user