




















@@ -73,6 +73,8 @@ type BoundedStringLog = string[] & {
7373truncated?: boolean;
7474};
757576+type OpenClawTestChildProcess = Pick<ChildProcessWithoutNullStreams, "kill" | "pid">;
77+7678function createBoundedStringLog(): string[] {
7779const log = [] as BoundedStringLog;
7880log.byteLength = 0;
@@ -152,6 +154,7 @@ async function prepareGatewayEntrypoint(cwd: string): Promise<string[]> {
152154 cwd,
153155env: { ...process.env, VITEST: "1" },
154156stdio: ["ignore", "pipe", "pipe"],
157+detached: shouldUseOpenClawTestProcessGroup(),
155158});
156159child.stdout?.setEncoding("utf8");
157160child.stderr?.setEncoding("utf8");
@@ -167,7 +170,7 @@ async function prepareGatewayEntrypoint(cwd: string): Promise<string[]> {
167170]);
168171169172if (completed === null) {
170-child.kill("SIGKILL");
173+signalOpenClawTestProcess(child, "SIGKILL");
171174throw new Error(`timeout preparing gateway entrypoint\n${formatLogs(stdout, stderr)}`);
172175}
173176if (completed.code !== 0) {
@@ -401,6 +404,7 @@ export async function createOpenClawTestInstance(
401404 cwd,
402405 env,
403406stdio: ["ignore", "pipe", "pipe"],
407+detached: shouldUseOpenClawTestProcessGroup(),
404408},
405409);
406410@@ -428,7 +432,7 @@ export async function createOpenClawTestInstance(
428432}
429433if (!hasChildExited(child) && !child.killed) {
430434try {
431-child.kill("SIGTERM");
435+signalOpenClawTestProcess(child, "SIGTERM");
432436} catch {
433437// ignore
434438}
@@ -439,7 +443,7 @@ export async function createOpenClawTestInstance(
439443);
440444if (!exited && !hasChildExited(child) && !child.killed) {
441445try {
442-child.kill("SIGKILL");
446+signalOpenClawTestProcess(child, "SIGKILL");
443447} catch {
444448// ignore
445449}
@@ -479,6 +483,7 @@ async function runCommand(params: {
479483cwd: params.cwd,
480484env: params.env,
481485stdio: ["ignore", "pipe", "pipe"],
486+detached: shouldUseOpenClawTestProcessGroup(),
482487});
483488child.stdout?.setEncoding("utf8");
484489child.stderr?.setEncoding("utf8");
@@ -493,7 +498,7 @@ async function runCommand(params: {
493498sleep(params.timeoutMs).then(() => null),
494499]);
495500if (completed === null) {
496-child.kill("SIGKILL");
501+signalOpenClawTestProcess(child, "SIGKILL");
497502await waitForGatewayExit(child, GATEWAY_STOP_TIMEOUT_MS);
498503throw new Error(
499504`command timed out after ${params.timeoutMs}ms: ${params.args.join(" ")}\n${formatLogs(stdout, stderr)}`,
@@ -506,10 +511,31 @@ async function runCommand(params: {
506511};
507512}
508513514+function shouldUseOpenClawTestProcessGroup(): boolean {
515+return process.platform !== "win32";
516+}
517+518+function signalOpenClawTestProcess(
519+child: OpenClawTestChildProcess,
520+signal: NodeJS.Signals,
521+killProcess: (pid: number, signal: NodeJS.Signals) => boolean = process.kill,
522+): void {
523+if (shouldUseOpenClawTestProcessGroup() && typeof child.pid === "number") {
524+try {
525+killProcess(-child.pid, signal);
526+return;
527+} catch {
528+// Fall back to the direct child if the process group already exited.
529+}
530+}
531+child.kill(signal);
532+}
533+509534export const testing = {
510535 appendLogChunk,
511536 createBoundedStringLog,
512537 formatLogs,
513538 hasChildExited,
539+ signalOpenClawTestProcess,
514540 waitForPortOpen,
515541};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。