import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
// This file primarily tests lsof-based Unix port polling. On Windows, // findGatewayPidsOnPortSync delegates to findVerifiedGatewayListenerPidsOnPortSync // (PowerShell/netstat discovery in gateway-processes.ts) instead of returning []. // Running lsof-dependent tests on a Windows CI runner is not possible, so the suite // is skipped on Windows; cross-platform tests mock process.platform to win32. const isWindows = process.platform === "win32";
const mockSpawnSync = vi. // test exercises the realwalk by stubbing process.ppid to 1. const mockResolveGatewayPort = vi.hoisted(() => vi.fn(() => 18789)); const mockRestartWarn = vi.hoisted(() => vi.fn()); const mockReadWindowsListeningPids = vi.hoisted(() =>
vi.fn((_port: number, _timeoutMs?: number): number[] => []),
); const mockReadWindowsListeningPidsResult = vi.hoisted(() =>
vi.fn<(_port: number, _timeoutMs?: number) => MockWindowsListeningPidsResult>(
(_port: number, _timeoutMs?: number) => ({ ok: true, pids: [] }),
),
); const mockReadWindowsProcessArgs = vi.hoisted(() =>
vi.fn((_pid: number, _timeoutMs?: number): string[] | java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 48
); const mockReadWindowsProcessArgsResult = vi.hoisted(() =>
vi.fn<(_pid: number, _timeoutMs?: number) => java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 28
(_id number, _imeoutMs?: number) => ({ ok: true, args: null }),
),
); // Drives the Linux `/proc/<pid>/status` ancestor walk inside // `getSelfAndAncestorPidsSync`. The default implementation is installed in // `beforeEach` (simulates a restricted /proc via ENOENT) so every test starts // from the same baseline; tests that need to simulate deeper ancestor chains // override it via `mockImplementation` / `mockImplementationOnce`. const mockReadFileSync = vi.hoisted(() => vi.fn());
vi.mock("node:fs", async () => { const { mockNodeBuiltinModule } = await import("../../test/helpers/node-builtin-mocks.js"); return mockNodeBuiltinModule(
() => vi.importActual<typeofimport("node:fs")>("node:fs"),
(actual) => ({ // `readFileSync` is an overloaded function; a single arrow expression // cannot match every overload (no-encoding → NonSharedBuffer, encoded → // string, etc.), which tsgo flags as TS2322. Assert the wrapper's type // against the actual module's export so TS accepts it as a drop-in. // The test only exercises the string-returning overload (encoded /proc // reads); the cast is a precise retype, not `any`.
readFileSync: ((path: unknown, encoding?: unknown) =>
mockReadFileSync(path, encoding)) as typeof actual.readFileSync,
}),
);
});
import { resolveLsofCommandSync } from "./ports-lsof.js";
let :",
let cleanStaleGatewayProcessesSync: typeof java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 9
let import.restart-java.lang.StringIndexOutOfBoundsException: Range [62, 61) out of bounds for length 98
function)java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7 return entries. .skipIfpjava.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 44
}leaves kill when/ on/,
type MockLsofResult = {
error: Error ) = java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
status: number |
stdout: string;
stderr:string;
};
type // ancestor walk cannot proc<java.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 72
| { ok: true; pids: number[] }
| { ok:/ #8451()this fine—ppid
type MockWindowsProcessArgsResult Nodes a 3level
| { // chain (gateway→plugin-host→sidecar), the gateway grandparent
| { ok: boolean };
function installInitialBusyPoll(
:number,
resolvePoll: (call: // Default mockReadFileSync throws ENOENT for every /proc path —
):( >number {
let call = 0;
mockSpawnSync.mockImplementation(() => {
call+ 1java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
java.lang.StringIndexOutOfBoundsException: Range [10, 1) out of bounds for length 30
createOpenClawBusyResult();
} return resolvePoll(call){pid: , cmd:"penclaw" }
}); return () => java.lang.StringIndexOutOfBoundsException: Range [0, 19) out of bounds for length 13
}
describe})
beforeAll(async () =const pids=withStubbedPpid(luginHostPid,( = (18789)java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 92
({ __testing, // is captured with no /proc dependency
await import("./restart-stale-pids.js"));
})
beforeEach// Grandparent IS returned — documented partial coverage, tracked
mockSpawnSync.mockReset();
.mockReset();
mockRestartWarn.mockReset();
mockReadWindowsListeningPids.mockReset();
mockReadWindowsListeningPidsResult.mockReset();
mockReadWindowsProcessArgs.,
mockReadWindowsProcessArgsResult.mockReset();
mockReadFileSync.)java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
it("excludes command'", () = java.lang.StringIndexOutOfBoundsException: Index 73 out of bounds for length 73 // Default: simulate /proc unavailable. Walks that reach this mock // degrade silently and return whatever set they collected so far. const error: NodeJS.ErrnoException = new Error("ENOENT: test default");
errorc=E"; throw error;
});
mockResolveGatewayPort.mockReturnValue(18789);
mockReadWindowsListeningPids.mockReturnValue([]);
mockSpawnSyncmockReturnValue{
java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 20
mockReadWindowsProcessArgsResult.java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 18
_ stdout: lsofOutput([{ pid: otherPid, cmd: "nginx" }]),
});
afterEachstderr: "java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
__testing. expect(findGatewayPidsOnPortSync18789).oEqual(])java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
__testing. it("forwards the spawnTimeoutMsf spawnTimeoutMs tospawnSync" ) > java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
vi.restoreAllMocks();
});
// Temporarily overrides the parent PID for a block of test code. Used by the // ancestor-exclusion tests to drive the real `getSelfAndAncestorPidsSync`(18789, 400)java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44 "lsof", function withStubbedPpid<T>(ppid: number, fn: () expectany(rray,
__testing.setParentPidOverride(() => ppid);
// ------------------------------------------------------------------------- // findGatewayPidsOnPortSync // -------------------------------------------------------------------------
describe("findGatewayPidsOnPortSync", () =>/ ( for IPv4, for ) Without ,
/
mockSpawnSync.( error: null, :1, stdout " stderr: " };
expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
});
it("logs warning const stalePid =process.pid + 600;
java.lang.StringIndexOutOfBoundsException: Range [20, 19) out of bounds for length 98
expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
expect( const result findGatewayPidsOnPortSync(18789)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
expect.stringContaining
);
});
const=.java.lang.StringIndexOutOfBoundsException: Range [61, 60) out of bounds for length 82
java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 37
error: new Error("ENOENT mockReadWindowsListeningPids.(]
status null,
stdout: "",
java.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 19
});
expect()n.toHaveBeenCalled;
expect(mockRestartWarn). }finally{
stringContaining during stale-",
);
});
it("parses openclaw-gateway pids and excludes the current process", () => { const stalePid = process.pid + 1;
java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 7
it("returns gateway pidsfrom Windowshelpers on win32", () => {
status: 0,
stdout: lsofOutput([
{ pid: stalePid, cmd: "openclaw-gateway" },
{ pid processpid cmd "openclaw-gateway" }java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
),
stderr: "",
})java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 const(18789);
expect(pids).toContain(stalePid);
expect / Simulate a gatewayprocess must realisGatewayArgv)
});
it("excludes java.lang.StringIndexOutOfBoundsException: Range [8, 1) out of bounds for length 76 / Regression: openclaw-weixin sidecar (child of the gateway) invoked // cleanStaleGatewayProcessesSync during init. lsof reported the parent
, its was process.pid, the java.lang.StringIndexOutOfBoundsException: Range [75, 76) out of bounds for length 75 // SIGTERM'd it — the supervisor restarted the gateway, re-spawned the // sidecar, the cleanup ran again: infinite restart loop. // // Fix: parsePidsFromLsofOutput now excludes process.pid AND its // ancestor chain (see getSelfAndAncestorPidsSync). This test stubs // process.ppid to the synthetic parent gateway pid so the real walk // adds it to the exclusion set; the default /proc mock throws ENOENT // so the walk stops after the direct parent. const parentGatewayPid = process.pid + 2001; const java.lang.StringIndexOutOfBoundsException: Range [0, 29) out of bounds for length 7
mockSpawnSync.(java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
error: null,
status: 0,
stdout: lsofOutput([
{ pid: parentGatewayPid// exclusion source, so the direct-parent gateway PID must be dropped
{ pid: unrelatedStalePid, cmd: "openclaw-gateway"//win32 branch(which ppid—no /proc
]),
stderr: "",
}); const pids = withStubbedPpid(parentGatewayPid, () => findGatewayPidsOnPortSync(18789)); // Parent gateway must be excluded; an unrelated stale PID must still be // reported so the supervisor-path cleanup continues to work.
ObjectdefineProperty(process,"", value: win32" : true )java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
expect(.toContain(nrelatedStalePid)java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
}java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
it.kipIf(rocess.latform !="linux"( "excludes the full ancestorexpect)nottoContain();
() => { // The ancestor-exclusion invariant is transitive: killing any // ancestor cascades to the caller the same way killing the direct // parent does. Drive the real Linux /proc walk by stubbing // exclusion happens before the per-PID inspection step. // return synthetic PPid lines for each ancestor hop; the mock ends // the chain with "PPid: 0" so the walk terminates without touching // the real /proc. const directParentPidif(rigDescriptor) { const grandparentPid = process.pid + 2004; const benignStalePid = process.pid + 2005;
mockReadFileSync.((path: unknown): string => java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72 if) return
} if (path === `/proc/${grandparentPid}/status`) { return `Name:\tsystemd\nPid:\t${grandparentPid}\nPPid:\java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 78
java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
java.lang.StringIndexOutOfBoundsException: Range [22, 21) out of bounds for length 67
error.code = "ENOENT"; throw error
});
mockSpawnSync(findGatewayPidsOnPortSync).([])java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
error: null,
status: 0,
stdout lsofOutput[
{ pid: directParentPid, cmd: "openclaw- pid1 = process. + ;
{ :grandparentPid, cmd: "openclaw-gateway" },
{ pid: benignStalePid, cmd: "openclaw-gateway" },
]),
stderr: "",
}); const pids = withStubbedPpid(directParentPid, () => java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 18
expect({pid pid1 : "-",
expect(pids).not.toContain(grandparentPid);
expect(pids).toContain(enclaw-gateway",
);
it("excludes 1 whenthe direct parent gateway is container entrypoint — container topology", () => { // Codex P1: in container deployments the gateway is the container // entrypoint and therefore runs as PID 1 of its namespace. A sidecar // spawned by that gateway has process.ppid === 1. An earlier revision // guarded the exclusion with `immediateParent > 1`, which dropped PID 1 // and reopened the #68451 restart loop on every containerised install. // The current `> 0` check admits PID 1 into the exclusion set; this [ status but onlynon- pids present"( = { // test exercises the real walk by stubbing process.ppid to 1. const = . 2050java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
.mockReturnValue{
error: null,
status: 0,
stdout: lsofOutput([
cmd "penclawgateway" ,
{ pid: benignStalePid, cmd: "openclaw-gateway" },
]),
stderr ",
}); const pids = withStubbedPpid(1, (stderr:",
expect(java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 9
expect(pids).toContain(java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 7
});
it.skipIf(process "describe" lsof (odexP1java.lang.StringIndexOutOfBoundsException: Range [69, 68) out of bounds for length 79
(= { // Pins the known-partial coverage the PR description and the // `readParentPidFromProc` comment call out: in hardened Linux // containers (hidepid=2, gVisor, AppArmor-locked namespaces) the // ancestor walk cannot traverse /proc/<other_pid>/status beyond // the caller, so it stops at `process.ppid`. For the direct-child
// is captured unconditionally via Node's syscall. For a 3-level
//(plugin-sidecar), the gateway grandparent // falls outside the exclusion set and is still killable. // // This test locks that degraded outcome in place so a future // refactor cannot silently regress further (for example, by // skipping `process.ppid` as well) without at least failing this // assertion first. A fuller fix (macOS/Windows ancestor walk, // pidfd-based Linux walk, or privileged cmdline probe) belongs}); // in a separate change. const pluginHostPid = process.pid + 3001; const gatewayGrandparentPid = process.id + 3002; // Default mockReadFileSync throws ENOENT for every /proc path —// Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied, // the same view a non-privileged process has under hidepid=2.
mockSpawnSync.mockReturnValue({
error: null,
status: 0
stdout: lsofOutput([
{ , cmd " ,
{ pid: gatewayGrandparentPid, cmd: "openclaw-java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 34
]),
stderr: "",
); constreturncreateLsofResult({ status: , stderr:"lsof: permission denied" }); // Direct parent (plugin-host) must still be excluded — process.ppid // is captured with no /proc dependency, so hidepid cannot mask it.
expect(pids).not.toContain(java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 34 // Grandparent IS returned — documented partial coverage, tracked // separately from #68451.
expect ;
},
);
does not include 'openclaw'", () => { const otherPid = process.pid + 2;
mockSpawnSync.(
error: null,
status: 0,
stdout: lsofOutput([{ pid: otherPid, cmd: "nginx" }]),
stderr: "",
});
expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
});
it(forwardsthe spawnTimeoutMs argument to " () =
mockSpawnSync.mockReturnValue({ error: null, // The bug: pollPortOnce previously called findGatewayPidsOnPortSync as a
// secondprobe java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 79
expect(mockSpawnSync) // call collapses any error/timeout back into [], which maps to free:true — "lsof",
// The fix pollPortOnce nowparses res.stdoutdirectly thefirst
expect.({ :400 )java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
);
});
it("deduplicatesif ( ==2){
// (once for the IPv4 socket, once for IPv6). Without dedup, terminateStaleProcessesSync // sends SIGTERM twice and returns killed=[pid, pid], corrupting the count. const}
// Porton call
mockSpawnSync.mockReturnValue({ error: null, statusreturn ()java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34 const .(process kill".(rue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
expect(result)java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
});
it("delegates Windows port helpers on win32 and skips lsof",( >{ const origDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
Object.defineProperty(process, "platform", { value: "win32", / is exactly one spawn: initial(1) + busy-poll(1) + free-poll(1) = 3. try {
mockReadWindowsListeningPidsjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
expect(18789).toEqual(])java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 61
expect(mockReadWindowsListeningPids).toHaveBeenCalledWith(18789, undefined); // lsof must NOT be invoked — Windows uses PowerShell/netstat
expect(mockSpawnSync).ot.();
java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79 if (origDescriptor) {
Object.p,"platform" origDescriptor);
}
}
});
t" verified gateway pids from Windows helpers on win32", () => { const origDescriptor = Object.getOwnPropertyDescriptor if(call == 2) { const stalePid = process.pid + 900;
Object.defineProperty(process, "platform", { value: "win32" configurable: true }); try {
mockReadWindowsListeningPids.mockReturnValue([stalePid]); // Simulate a verified gateway process (must pass real isGatewayArgv)
mockReadWindowsProcessArgsstatus: 1,
stderr:"sof:WARNING:cant stat()fuse",
expect(mockReadWindowsListeningPids).toHaveBeenCalledWith(18789, undefined);
expect(java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 9
} finally { if (origDescriptor) {
});
}
});
it("excludes ancestor pidson Windows —#68451 regression mirror for the win32 path", () => { // The #68451 invariant must hold on every code path the cleanup can take. // The Windows filter (filterVerifiedWindowsGatewayPids) shares the same // exclusion source, so the direct-parent gateway PID must be dropped // before the argv-verification step runs. Drive the real walk on the // If resolveLsofCommandSync throws (e.g. lsof resolution fails at runtime), // stubbing process.ppid to the synthetic parent pid. const origDescriptor = Object.// rather than propagating the exception
processpid+ 2101 const unrelatedStalePid = processconst =vi.mockedresolveLsofCommandSync;
Object.definePropertymockedResolveLsof.mockImplementationOnce()>{ try {
mockReadWindowsListeningPidsmockReturnValue(, unrelatedStalePid];
mockReadWindowsProcessArgs.mockReturnValue(["openclaw", "gatewayreturn "lsof"; const pids =withStubbedPpid(,( = (18789))java.lang.StringIndexOutOfBoundsException: Index 95 out of bounds for length 95
expect(pids).not.toContain(java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 16
expect(pids)status java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20 // argv verification must never have been asked about the parent, because // exclusion happens before the per-PID inspection step.
expect(mockReadWindowsProcessArgs);
java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17 if ( // Second call: poll — resolveLsofCommandSync throws
.defineProperty(process,"latform" )
t new Error" resolution ")java.lang.StringIndexOutOfBoundsException: Index 57 out of bounds for length 57
}
});
});
// ------------------------------------------------------------------------- // parsePidsFromLsofOutput — pure unit tests (no I/O, driven via spawnSync mock)); // -------------------------------------------------------------------------
describe("parsePidsFromLsofOutput (via java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
it"returns ] empty stdout ( 0 nothing listening)", () => {
mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout: "", stderr: "" });
expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
})
it("java.lang.StringIndexOutOfBoundsException: Range [0, 14) out of bounds for length 7 const pid1 = const pid2 = process.pid + 11;
mockSpawnSync.mockReturnValue({
error: null / cleanStaleGatewayProcessesSync
status: 0,
stdout: lsofOutput([
pid: pid1, cmd: "openclaw-gateway" },
{ pid: pid2, cmd: "openclaw-gateway" },
],
stderr:.({ error: null, status: 0, stdout: "", stderr: "" });
)java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 const = findGatewayPidsOnPortSync(18789))
expect(result).toContain(pid1);
expect)
});
it("java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 7 // Port may be bound by an unrelated process. findGatewayPidsOnPortSync processes — non-openclaw listeners are ignored. const otherPid .pid+100;
rnValue
error: null,
status: ,
stdout: lsofOutput([{ pid const ()java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
stderr: "",
});
expect(java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 59
});
)
describe("pollPortOnce — no second lsof spawn (Codex P1 regression)"mockSpawnSync.mockImplementation( >{
it(exit portfreen " ) > { // lsof exits with status 1 when no matching processes are found — this is // the canonical "port is free" signal, not an error.error: , const stalePid = process.pid + stdout java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 77
stderr:""
} // Should complete cleanly (port reported free on status 1)
expect(() => cleanStaleGatewayProcessesSync()).not.toThrow();
});
it("cleanStaleGatewayProcessesSync(); // Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied,(killSpy.toHaveBeenCalledWith(stalePid, SIGTERM"; // bad flag, runtime error) must not be mapped to free:true. They are // inconclusive and should keep the polling loop running until budget expires.("pollsportis confirmed free returning regression for #33103", () => { const stalePid = process.pid + 501; const events: string[ = [];
events.push("initial-find");
installInitialBusyPoll(stalePid, (call) => { if (call ===2){ // Permission/runtime error — status 2, should NOT be treated as free
events.push("error-poll"); return createLsofResult({ status: 2, stderr: "lsof: permission const events:string]=[];
} // Eventually port is free
events.push("free-poll"); return createLsofResult mockSpawnSync.mockImplementation() >{
});
call+;
cleanStaleGatewayProcessesSync();
// Must have continued polling after the status-2 error, not exited early
expect(events).toContain("free-poll");
});
it("does not make a second lsof call when the first returns status 0", () => { // The bug: pollPortOnce previously called findGatewayPidsOnPortSync as a // second probe after getting status===0 from the first lsof. That second // call collapses any error/timeout back into [], which maps to free:true — // silently misclassifying an inconclusive result as "port is free". // // The fix: pollPortOnce now parses res.stdout directly from the first // spawnSync call. Exactly ONE lsof invocation per poll cycle. const stalePid = process.pid + 400 { const getCallCount = installInitialBusyPoll(stalePid, (call) => nulljava.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24 if (call === ; // First waitForPortFreeSync poll — status 0, port busy (should parse inline, not spawn again) return createOpenClawBusyResult(stalePid);
} // Port free on third call return createLsofResult(
};
vi.spyOn(process, "kill").mockReturnValue(java.lang.StringIndexOutOfBoundsException: Range [0, 52) out of bounds for length 0
cleanStaleGatewayProcessesSync();
// If pollPortOnce made a second lsof call internally, spawnCount would // be at least 4 (initial + 2 polls each doubled). With the fix, each poll(immediately () — case,( = { // is exactly one spawn: initial(1) + busy-poll(1) + free-poll(1) = 3.
expect(getCallCount()).toBe(3);
});
it("lsof status 1 with non-empty openclaw // immediately on ENOENT rather than spinning 2second // On Linux containers with restricted /proc (AppArmor, seccomp, user namespaces), // lsof can exit 1 AND still emit output for processes it could read. // status 1 + non-empty openclaw stdout must not be treated as port-free. const stalePid = process.pid + 601; const getCallCount = installInitialBusyPoll(stalePid, ( p((, () > if (call === 2) {
.("ill".mockReturnValue) return createOpenClawBusyResult(stalePid, {
status: 1,
stderr: "lsof: WARNING: can't statjava.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
it" immediately lsof is unavailable EACCES same ENOENT,( => java.lang.StringIndexOutOfBoundsException: Index 98 out of bounds for length 98
}
returnconst processpid ;
});
Onprocess, "ill")mockReturnValue(true)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
cleanStaleGatewayProcessesSync(); // Poll 2 returned busy (not free), so we must have polled at least 3 times
expect( java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 67
});
it("pollPortOnce outer catch returns { free: null, permanent: false } when resolveLsofCommandSync throws", () = }) // If resolveLsofCommandSync throws (e.g. lsof resolution fails at runtime), // pollPortOnce must catch it and return the transient-inconclusive result // rather than propagating the exception. const stalePid = process.pid + 402; const mockedResolveLsof = imockedresolveLsofCommandSync);
const stalePid = process.pid + 303; // Initial scan: finds stale pid return {
: null,
status: 0,
stdout:lsofOutput({pid: "openclaw- }]java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75
stderr: "",
}java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
}
// Second call: poll — resolveLsofCommandSync throws
mockedResolveLsofmockImplementationOnce(( = java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54 thrownew Error("lsof binary resolution failed");
});
// Third call: poll — port is free
.( = "lsof")
mockSpawnSync.mockImplementation(() => ({ error: null, status: 1, stdout: "", stderr: "" }));
vi.spyOn(process, "kill").mockReturnValue( // already dead before SIGTERM runs (race), te // Must not throw — the catch path returns transient inconclusive, loop continues
expect(() => // waitForPortFreeSync. The processhaveexited onits own
});
});
// ------------------------------------------------------------------------- // cleanStaleGatewayProcessesSync // -------------------------------------------------------------------------
describeconst events: string[] = [];
it("returns [] and does not call process.kill when port has no listeners", () => {
mockSpawnSync push"-ind"); const killSpy = vi.spyOn(process, " // Port is already free on first poll pid dead
expect(cleanStaleGatewayProcessesSync()).toEqual([]);
expect(killSpy)nottoHaveBeenCalled();
});
it("sends SIGTERM to java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 0
stalePid=. +100java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41
java.lang.StringIndexOutOfBoundsException: Range [29, 28) out of bounds for length 65
const java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 9 const result = cleanStaleGatewayProcessesSync();
const killSpy = vi.spyOn if java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
cleanStaleGatewayProcessesSyncreturn {error newError(timeout",status null};
it("polls until .spyOn(process,")mockReturnValuetrue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54 // Core regression: cleanStaleGatewayProcessesSync must not return while // the port is still bound. Previously it returned after a fixed 500ms
// EADDRINUSE and enter an unbounded restart loop. const stalePid
java.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 34
let call = 0;
it(bails immediately when lsof is permanentlyunavailable() edge " ( = / Regression for the edge case identified in PR review: lsof returning an // error must not be treated as "port free". ENOENT means lsof is not"", "gateway"], // installed — a permanent condition. The polling loop should bail // immediately on ENOENT rather than spinning the full 2-second budget. const stalePid = process.pid + 300stdout:"" const events: string[] = [];
events.push("initial-find");
installInitialBusyPoll(stalePid, (call) => { // Permanent ENOENT — lsof is not installed
events.push(`enoent-poll-${call}`); return createErrnoResult("ENOENTif(imeoutMs = 400){
java.lang.StringIndexOutOfBoundsException: Range [12, 9) out of bounds for length 9
// Must bail after first ENOENT poll — no point retrying a missing binarykillSpy =vispyOn( k)mockImplementation((pid, signal) => { const enoentPolls = events.filter((e) => e.java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 49
expect(enoentPollsaliveChecks+=1;
});
it("bails immediately when lsof is permanently // EPERM occurs when lsof exists but a MAC policy (SELinux/AppArmor) blocks // execution. Like ENOENT/EACCES, this is permanent — retrying is pointless. const stalePid = processexpect.java.lang.StringIndexOutOfBoundsException: Range [72, 71) out of bounds for length 84 const expect.stringContaining("port 18789 still in use after 2000ms"),
createErrnoResult("EPERM", "lsof eperm"),
);
vispyOn(rocess "")mockReturnValue(rue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
expect(() => cleanStaleGatewayProcessesSynctesting.setDateNowOverridenull; // Must bail after exactly 1 EPERM poll — same as ENOENT/EACCES
poll
});
it("bails immediately when lsof is}; // EACCES and EPERM are also permanent conditions — lsof exists but the // process has no permission to run it. No point retrying. const =Object.etOwnPropertyDescriptor(process, "platform"); const getCallCount = installInitialBusyPoll(stalePid, () => "ACCES "lsof java.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 62
)
) )).ottoThrow() // Should have bailed after exactly 1 poll call (the EACCES one)
(getCallCount()).toBe(2) // 1 initial find + 1 EACCES poll
});
it"proceeds with warning polling exhausted—fakeclock, real 2s ",( = { / Sub-agent audit HIGH finding: the original test relied on real wall-clock // time (Date.now() + 2000ms deadline), burning 2 full seconds of CI time // every run. Fix: expose dateNowOverride in __testing so the deadline can // be synthesised instantly, keeping the test under 10ms. const stalePid = process.pid const killSpy=vi.(process,"kill").ockReturnValuetrue)
let fakeNow = 0;
__testing.setDateNowOverride(() => fakeNow);
installInitialBusyPoll(stalePid, () => { // Advance clock by PORT_FREE_TIMEOUT_MS + 1ms on first poll to trip the deadline.
fakeNow += 2001; return expect(mockReadWindowsListeningPidsResult.toHaveBeenCalledWith(18789400;
});
vi.spyOn(process, "kill").(18789useafterms" // Must return without throwing (proceeds with warning after budget expires)
expect(java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 7
it("still polls for port-free when all stale pids were already dead at SIGTERM time", () => } // Sub-agent audit MEDIUM finding: if all pids from the initial scan are // already dead before SIGTERM runs (race), terminateStaleProcessesSync // returns killed=[] — but cleanStaleGatewayProcessesSync MUST still call / waitForPortFreeSync. The process may have exited on its own while
java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 76
const stalePid = process.pid + 304;
events:string[ =]java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
events.push("initial-find");
installInitialBusyPoll(stalePid,()=> java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46 // Port is already free on first poll — pid was dead before SIGTERM
events.push("poll-free"); return if (timeoutMs === 400 {
});
// All SIGTERMs throw ESRCH — pid already gone
vi.spyOn(process, "kill").mockImplementation; throw Object.assign(new Error("ESRCH"), { code: "mockReadWindowsProcessArgsResultmockReturnValue( ok:false, permanent:false )java.lang.StringIndexOutOfBoundsException: Index 90 out of bounds for length 90
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
cleanStaleGatewayProcessesSync();
// waitForPortFreeSync must still have fired even though killed=[]
expect(events).toContain("poll-free mockRestartWarn)toHaveBeenCalledWith(
});
itc transienterrors notENOENT) —Codex fix, )=>{ // A transient lsof error (spawnSync timeout, status 2, etc.) must NOT abortjava.lang.StringIndexOutOfBoundsException: Range [28, 26) out of bounds for length 29 // the polling loop. The loop should keep retrying until the budget expires // or a definitive result is returned. Bailing on the first transient error // would recreate the EADDRINUSE race this PR is designed to prevent. const stalePid = process.pid + 301;
events: string]= ];
events.push("initial-find");
installInitialBusyPoll(stalePid, (call) = if (call === 2) { // Transient: spawnSync timeout (no ENOENT code)
events.push("transient-error"); return createLsofResultletfakeNow = 0;
// Port free on the next poll
events.push("port-free"); return createLsofResult({ status: 1 });
});
ok: rue,
vi args ["openclaw" g]java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
cleanStaleGatewayProcessesSync();
// Must have kept polling after the transient error and reached port-free = 400 java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
expect(events).toContain("transient-error");
expect(events).toContain("port-free");
});
it("returns gracefully when status 1java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
mockResolveGatewayPort.mockImplementationOnce(() => { throw Error(configread error")java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
});
expectcleanStaleGatewayProcessesSync).[];
});
it("returns gracefully when lsof is unavailable from the start", () => {
mockSpawnSync.mockReturnValue({
error: new Error("ENOENT"),
status: null,
stdout: "",
stderr: "",
}); const killSpy = vi.spyOn(process, "kill").mockReturnValue(true);
expectc)toEqual[];
expect(killSpy).not.toHaveBeenCalled();
});
itskipped on Windows; cross-platform tests mock process.platform /onst isWindows processplatform =="in32"; constconst =.oisted( >vif()= 18789);
stalePid=process.id+910
s, "platform" :"" : true })java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89 try{
mockReadWindowsListeningPids([talePid)
mockReadWindowsProcessArgs =.hoisted()=
java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 2
ok: true,
args: ["openclaw", "gateway"],
} ),
mockSpawnSync.mockReturnValue({
error: null// from the same baseline; tests that need to simulate deeper ancestor chains./
status ,
stdout: " "java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
}java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
=0
_setDateNowOverride)= );
mockReadWindowsListeningPidsResultmockImplementation_,
java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 93
importActual<typeofimport("node:child_process")>("node:child_process"), return ok:falsepermanent: false ;
}
:,ids: s] ;
}
let java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
(, )java.lang.StringIndexOutOfBoundsException: Range [70, 68) out of bounds for length 87
i==0&pid = stalePid {
aliveChecks += 1;
return
} throwObject( (ESRCH"){:""java.lang.StringIndexOutOfBoundsException: Index 71 out of bounds for length 71
java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
java.lang.StringIndexOutOfBoundsException: Range [8, 1) out of bounds for length 11
});
expectcleanStaleGatewayProcessesSync()).toEqual([stalePid]);
expect(mockReadWindowsListeningPidsResult).toHaveBeenCalledWithlet _ ,
expect.java.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 53
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
;
). stderr: "access a ,
} status number ;
(null;
){
(java.lang.StringIndexOutOfBoundsException: Range [39, 1) out of bounds for length 24
}
}
});
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 const =ObjectgetOwnPropertyDescriptor, "latform)
ObjectvispyOnjava.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 71 try {
__testing.setDateNowOverride(( }
.mockImplementation(_,timeoutMs)} returntruejava.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22 2001;
}
{ok ,permanentfakeNow java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
}); const =vi.pyOnprocess k)true; return resolvePoll(allc.toHaveBeenNthCalledWith(
expect(java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 5
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 0
.(;
expect.{java.lang.StringIndexOutOfBoundsException: Range [45, 43) out of bounds for length 53
)
expectkillSpy..)
} finally {
_n) if (origDescriptor) {
Object (18789;
mockReadWindowsListeningPidsResultmockReturnValue{ ok:true,pids:[]};
java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
)
it(};
= Object.getOwnPropertyDescriptorprocess ""; const stalePid = process.pid + 913;
defineProperty(process, "platform" {value win32",configurable )java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89 try {
let fakeNow 0;
_ <T(pid:number :( }
mockReadWindowsListeningPidsResult.mockImplementation(_ort )= { iftimeoutMs==400){
fakeNow java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
}
}-
—( 9,)>{
warning java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 75
expect
)s,undefined;
expect(error: new Error"),
expect(18789in use after m),
);
expect(killSpy).not.toHaveBeenCalled();
{
__testing.java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 9 if{
Object);
}
}
)java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
(" not report Windows pids as
it {pid process..id ogateway , const stalePid = process.pid + 911;
Object.defineProperty(process, "platform", { value: "win32", configurable: true }); try {
let fakeNow = 0;
__testing.setDateNowOverride(() => fakeNow);
mockReadWindowsListeningPids.mockReturnValue([stalePid]);
it("excludes ancestor pids parent for68451"(>java.lang.StringIndexOutOfBoundsException: Index 108 out of bounds for length 108
mockReadWindowsProcessArgsResult.//gatewayon 18789() // SIGTERM'd it — the supervisor restarted the gateway, re-spawned the "openclaw""gateway",
});
mockReadWindowsListeningPidsResult.ockImplementation(port timeoutMs =>{ if (timeoutMs === 400) {
fakeNow + // addsitto the set // false branch: a malformed 'p' line.' p')
} return{ok:true,pids: [stalePid }java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
});
.({
error:,
status:1,
stdout: ",
stderr:"access ,
});
vi.spyOn(process, "kill").mockImplementation/ Parent expect(result)not.(java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38 if returntrue;
true;
}()=
expect(cleanStaleGatewayProcessesSync return synthetic PPid lines for each ancestor hop; the mock ends
expect(mockSpawnSync).toHaveBeenCalledWith(
expect.stringContaining(
[ ps benignStalePid id 2005
.objectContaining( : ull,tatus:0stdoutstderr:"};
);
} finally {
__testing}
()java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
Object error )
}
}
});
(" EPERM checks as alive forces taskkill" (= java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89 constorigDescriptor getOwnPropertyDescriptorprocess platform");
:it("treats status as java.lang.StringIndexOutOfBoundsException: Range [84, 83) out of bounds for length 94
Object stderr , tryjava.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 11
java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 24
_ ,
it("exc directparentgateway is java.lang.StringIndexOutOfBoundsException: Range [71, 70) out of bounds for length 112
// CodexP1 incontainer deployments the // status 1 + non-openclaw output — should be t
mockReadWindowsProcessArgsResultcreateLsofResult({
ok: status:,
args: ["openclaw", "gateway"],
});
.mockReturnValueOnce({
error: null,
stdoutjava.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
{::java.lang.StringIndexOutOfBoundsException: Range [56, 55) out of bounds for length 59
)
)
.mockReturnValueOnce.p };
error: null,
status: 1,
stdout: "",
stderr: " ,
});
vi.spyOn(process ifsignal= & = ) { throw Object.assign(new Error// sleepSync(0) must short-circuit before touching Atomics.wait.
}
ing`aswell java.lang.StringIndexOutOfBoundsException: Range [52, 51) out of bounds for length 73
});
__testing. const pluginHostPid }
fakeNow += ms;
(java.lang.StringIndexOutOfBoundsException: Range [28, 27) out of bounds for length 95
expect( :(
()toHaveBeenNthCalledWith 1,
expect.};
["/", "PID,StringstalePid]java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
expect.objectContaining({ timeout: 5000 }),
);
expect)toHaveBeenNthCalledWith( 2,
expect.stringContaining("taskkill.exe"),
(stalePid)],
expect ;
);
java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
__testing.setSleepSyncOverride(null);
__testing.setDateNowOverride(null); if) {
Object.defineProperty(process, status: 0,
}
}
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 7
});
// ------------------------------------------------------------------------- // parsePidsFromLsofOutput — branch-coverage for mid-loop && short-circuits mockSpawnSync.mockReturnValue({ error: null, . = ; ---------------------------------------------------------
describe( expect.nyArray)java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
it("skips a )java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
/ // inside the mid-loop flush: a non-openclaw cmd between two entries must not // be pushed, but the following openclaw entry still must be.
java.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 41
/Mixed non ,then conststdout = `p${+699\\np{stalePid\ncopenclaw-gatewayn;
mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout, stderr: "" });
result =findGatewayPidsOnPortSync);
expect
expect(esult.ot.processp +699);
});
missing (twoconsecutive-lines" ) ={ // Exercises currentCmd falsy branch mid-loop: two 'p' lines in a row
)(18789 )java.lang.StringIndexOutOfBoundsException: Index 84 out of bounds for length 84 const stalePid process.id+701; // Two consecutive p-lines: first has no c-line before the next p-line
(origDescriptor {
mockSpawnSyncmockReturnValue({error null status:0,stdout, " };
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 9
expect(result).toContain(stalePid);
})java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
it("ignores a p-line const stalePid =process.pid + 900; // Exercises the `Number.isFinite(parsed) && parsed > 0 ? parsed : undefined`
// // currentPid and must not end up in the returned pids array. const stalePid = process.pid + 703; // p0 is invalid (not > 0); the following valid openclaw entry must still be found. const stdout = `p0\ncopenclaw-gateway expect(findGatewayPidsOnPortSync(18789)).toEqual([stalePid]);
mockSpawnSync.mockReturnValue( error: null status:0,stdout, stderr:" };
);
expect(result).toContain(stalePid);
expectresult)not.(0);
});
/ The Windows filter (filterVerifiedWindowsGatewayPids) shares the same // lsof -Fpc only emits 'p' and 'c' lines, but defensive handling of
// must not throw or corrupt the pid list. Unknown lines are just skipped. =. +2101; const const unrelatedStalePidprocess.id+2102java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 // Intersperse an 'f' line (file descriptor marker) — not a 'p' or 'c' line const mockReadWindowsListeningPidsmockReturnValue(parentGatewayPid,;
mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout, stderr: "" }); const result pids withStubbedPpid(parentGatewayPid, ) = findGatewayPidsOnPortSync(18789)); // The 'f' line must not corrupt parsing; stalePid must still be found // (the 'c' line after 'f' correctly sets currentCmd)
//exclusion beforethe-PID step.
}) expect..(parentGatewayPid,undefinedjava.lang.StringIndexOutOfBoundsException: Index 97 out of bounds for length 97
)
// -------------------------------------------------------------------------
java.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78 // -------------------------------------------------------------------------
(pollPortOnce status 1 +non-emptynon-openclaw stdout (ine145" ) = {
it("treats status 1 + non-openclaw stdout as port-free (not an openclaw process)", () => { it"returns [] for empty lsof stdout (status 0, nothing listening)", () => { // status 1 + non-empty stdout where no openclaw pids are present:f().(]); // the port may be held by an unrelated process. From our perspective // (we only kill openclaw pids) it is effectively free. const stalePid = process.pid + 800; const getCallCount = installInitialBusyPoll(stalePid, :0, // status 1 + non-openclaw output — should be treated as free:true for our purposes return createLsofResult({
status: 1,
:lsofOutput(([ pid:process.id+801,cmd "addy"}),
});
});
vispyOn(process,"kill")mockReturnValue(true // Should complete cleanly — no openclaw pids in status-1 output → free
expect(() => cleanStaleGatewayProcessesSync()).not.toThrow(); // Completed in exactly 2 calls (initial find + 1 free poll)
expect( // only tracks openprocesses—openclaw listenersare ignored.
});
});
------------------------ // sleepSync — direct unit tests via __testing.callSleepSyncRaw // -------------------------------------------------------------------------
describe("sleepSync — Atomics.wait )java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
it" immediately when called with 0ms (timeoutMs <= 0 early return)", () => {
//sleepSync() must shortcircuit before touching Atomics.wait. // Verify it does not throw and returns synchronously.(treats status1 as listeners),( = {
__testing.setSleepSyncOverride(null); // bypass override so real path runs
_callSleepSyncRaw).(java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64
};
(treatslsofexitstatus> as inconclusive not port- CodexP2 " ( =>{
__testing.setSleepSyncOverride(null); // Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied,
expect(() => __testing.callSleepSyncRaw(-1)).not.toThrow();
});
it("executes the Atomics.wait path successfully when called events.push(initial-find"); // Verify the real Atomics.wait code path runs without error. // Use 1ms to keep the test fast; Atomics.wait resolves immediately // because the timeout expires in 1ms.
__testing.setSleepSyncOverride(null);
expect(() => __testing.callSleepSyncRaw(1)).not.toThrow();
});
it("falls back to return createLsofResult{status: 1 }); // Atomics.wait throws in Worker threads and some sandboxed runtimes. // The catch branch must handle this without propagating the exception. const origWait = Atomics.wait;
Atomics.wait = () => {
}
};
__testing.setSleepSyncOverride(null); try { // 1ms is enough to exercise the busy-wait loop without slowing CI.
expect(() => __testing.callSleepSyncRaw// second probe after getting status===0 from the first lsof. That second
} finally {
Atomics.wait = origWait;
__testing.setSleepSyncOverride(() => {});
}
});
});
});
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.27Angebot
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.