From 01eac5af246e3d43cb3aee248ace3418b6d2b1f1 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Wed, 12 Aug 2026 18:04:36 +0800 Subject: [PATCH] fix: pipe binding test output on Windows --- src/bindings/getLlama.ts | 2 +- src/bindings/utils/testBindingBinary.ts | 2 +- .../bindings/testBindingBinary.test.ts | 75 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/standalone/bindings/testBindingBinary.test.ts diff --git a/src/bindings/getLlama.ts b/src/bindings/getLlama.ts index 3132f9f8..015b0f96 100644 --- a/src/bindings/getLlama.ts +++ b/src/bindings/getLlama.ts @@ -524,7 +524,7 @@ export async function getLlamaForOptions({ }: LlamaOptions, { updateLastBuildInfoOnCompile = false, skipLlamaInit = false, - pipeBinaryTestErrorLogs = false + pipeBinaryTestErrorLogs = process.platform === "win32" }: { updateLastBuildInfoOnCompile?: boolean, skipLlamaInit?: boolean, diff --git a/src/bindings/utils/testBindingBinary.ts b/src/bindings/utils/testBindingBinary.ts index c26f4636..0ba85cbf 100644 --- a/src/bindings/utils/testBindingBinary.ts +++ b/src/bindings/utils/testBindingBinary.ts @@ -20,7 +20,7 @@ export async function testBindingBinary( extBackendsPath: string | undefined, gpu: BuildGpu, testTimeout: number = 1000 * 60 * 5, - pipeOutputOnNode: boolean = false + pipeOutputOnNode: boolean = process.platform === "win32" ): Promise { if (!detectedFileName.startsWith(expectedFileName)) { console.warn( diff --git a/test/standalone/bindings/testBindingBinary.test.ts b/test/standalone/bindings/testBindingBinary.test.ts new file mode 100644 index 00000000..79ff0f09 --- /dev/null +++ b/test/standalone/bindings/testBindingBinary.test.ts @@ -0,0 +1,75 @@ +import {EventEmitter} from "node:events"; +import {afterEach, describe, expect, test, vi} from "vitest"; + +const {forkMock} = vi.hoisted(() => ({forkMock: vi.fn()})); +vi.mock("node:child_process", () => ({fork: forkMock})); + +import {testBindingBinary} from "../../../src/bindings/utils/testBindingBinary.js"; + +function createFakeChildProcess() { + const child = new EventEmitter() as EventEmitter & { + exitCode: number | null, + killed: boolean, + stderr: EventEmitter, + stdout: EventEmitter, + kill(): void, + send(message: {type: string}): void + }; + + child.exitCode = null; + child.killed = false; + child.stderr = new EventEmitter(); + child.stdout = new EventEmitter(); + child.kill = () => { + child.killed = true; + child.exitCode = -1; + }; + child.send = (message) => { + if (message.type === "start") + queueMicrotask(() => child.emit("message", {type: "loaded"})); + else if (message.type === "test") + queueMicrotask(() => child.emit("message", {type: "done"})); + else if (message.type === "exit") { + child.exitCode = 0; + queueMicrotask(() => child.emit("exit", 0)); + } + }; + + return child; +} + +describe("testBindingBinary", () => { + afterEach(() => forkMock.mockReset()); + + function setupFakeFork(forkOptions: {stdio?: unknown}[]) { + forkMock.mockImplementation((_file: string, _args: string[], options: {stdio: unknown}) => { + forkOptions.push(options); + const child = createFakeChildProcess(); + queueMicrotask(() => child.emit("message", {type: "ready"})); + return child; + }); + } + + test("pipes child output by default on Windows", async () => { + const forkOptions: {stdio?: unknown}[] = []; + setupFakeFork(forkOptions); + + await expect(testBindingBinary("fake-binding", undefined, false, 1000)).resolves.toBe(true); + + expect(forkOptions).toHaveLength(1); + expect(forkOptions[0]?.stdio).toEqual( + process.platform === "win32" + ? ["ignore", "pipe", "pipe", "ipc"] + : ["ignore", "ignore", "ignore", "ipc"] + ); + }); + + test("honors an explicit output pipe setting", async () => { + const forkOptions: {stdio?: unknown}[] = []; + setupFakeFork(forkOptions); + + await expect(testBindingBinary("fake-binding", undefined, false, 1000, false)).resolves.toBe(true); + + expect(forkOptions[0]?.stdio).toEqual(["ignore", "ignore", "ignore", "ipc"]); + }); +});