





















@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
33import fs from "node:fs";
44import os from "node:os";
55import path from "node:path";
6+import { pathToFileURL } from "node:url";
67import { describe, expect, it } from "vitest";
78import {
89buildGroupedTestComparison,
@@ -26,6 +27,15 @@ function makeTempDir() {
2627return fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-group-report-"));
2728}
282930+function isProcessAlive(pid: number): boolean {
31+try {
32+process.kill(pid, 0);
33+return true;
34+} catch {
35+return false;
36+}
37+}
38+2939function writeGroupedReport(filePath: string) {
3040fs.writeFileSync(
3141filePath,
@@ -634,6 +644,57 @@ describe("scripts/test-group-report child process guard", () => {
634644}
635645});
636646647+it("keeps the wrapper alive while timed process-group descendants await SIGKILL", () => {
648+if (process.platform === "win32" || !fs.existsSync("/usr/bin/time")) {
649+return;
650+}
651+652+const tempDir = makeTempDir();
653+const childPidPath = path.join(tempDir, "child.pid");
654+const reportModuleUrl = pathToFileURL(path.resolve("scripts/test-group-report.mjs")).href;
655+let childPid: number | undefined;
656+try {
657+const childScript = [
658+"const fs = require('node:fs');",
659+"process.on('SIGTERM', () => {});",
660+"process.on('SIGHUP', () => {});",
661+`fs.writeFileSync(${JSON.stringify(childPidPath)}, String(process.pid));`,
662+"setInterval(() => {}, 1000);",
663+].join("\n");
664+const runnerScript = [
665+`import { spawnText } from ${JSON.stringify(reportModuleUrl)};`,
666+"const result = await spawnText(",
667+' "/usr/bin/time",',
668+` [process.execPath, "--eval", ${JSON.stringify(childScript)}],`,
669+" { cwd: process.cwd(), env: process.env, killGraceMs: 50, timeoutMs: 500 },",
670+");",
671+"process.stdout.write(JSON.stringify(result));",
672+].join("\n");
673+const result = spawnSync(process.execPath, ["--input-type=module", "--eval", runnerScript], {
674+cwd: process.cwd(),
675+encoding: "utf8",
676+timeout: 5_000,
677+});
678+if (fs.existsSync(childPidPath)) {
679+childPid = Number.parseInt(fs.readFileSync(childPidPath, "utf8"), 10);
680+}
681+682+expect(result.status).toBe(0);
683+expect(result.stdout).not.toBe("");
684+const parsed = JSON.parse(result.stdout) as Awaited<ReturnType<typeof spawnText>>;
685+expect(parsed).toMatchObject({
686+status: 1,
687+timedOut: true,
688+});
689+expect(parsed.output).toContain("sending SIGKILL");
690+} finally {
691+if (childPid !== undefined && isProcessAlive(childPid)) {
692+process.kill(childPid, "SIGKILL");
693+}
694+fs.rmSync(tempDir, { recursive: true, force: true });
695+}
696+});
697+637698it("streams large child output to a log path without retaining it", async () => {
638699const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-group-report-log-"));
639700const logPath = path.join(tempDir, "child.log");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。