feat(tree): close rust family shell cutover

This commit is contained in:
lix-2026
2026-04-28 16:30:51 +08:00
parent 4ab36a9386
commit 7965c6c107
75 changed files with 9721 additions and 1174 deletions
+110
View File
@@ -0,0 +1,110 @@
const assert = require("node:assert");
const { spawn } = require("node:child_process");
const net = require("node:net");
const { test } = require("node:test");
const { resolveBackendExecutable, ensurePortFree, isPortFree } = require("./desktop-hot.js");
function findFreePort() {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(0, "127.0.0.1", () => {
const address = server.address();
if (!address || typeof address === "string") {
server.close(() => reject(new Error("无法分配测试端口")));
return;
}
const port = address.port;
server.close(() => resolve(port));
});
server.on("error", reject);
});
}
function startPythonListener(port) {
const pythonBin = resolveBackendExecutable("PYTHON_BIN", "python");
const child = spawn(
pythonBin,
[
"-c",
`
import socket
import time
import threading
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", ${port}))
server.listen(16)
def accept_loop():
while True:
conn, _addr = server.accept()
conn.close()
threading.Thread(target=accept_loop, daemon=True).start()
print("ready", flush=True)
while True:
time.sleep(1)
`,
],
{
stdio: ["ignore", "pipe", "pipe"],
},
);
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
child.kill("SIGKILL");
reject(new Error("测试监听进程启动超时"));
}, 3000);
child.stdout.on("data", (chunk) => {
if (chunk.toString("utf8").includes("ready")) {
clearTimeout(timeout);
resolve(child);
}
});
child.on("error", (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
function waitForExit(child, timeoutMs = 3000) {
if (child.exitCode !== null || child.signalCode !== null) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
child.off("exit", onExit);
reject(new Error(`进程 ${child.pid} 未在 ${timeoutMs}ms 内退出`));
}, timeoutMs);
const onExit = () => {
clearTimeout(timeout);
resolve();
};
child.once("exit", onExit);
});
}
test("ensurePortFree 在非 Windows 平台能释放后端监听进程", async (t) => {
if (process.platform === "win32") {
t.skip("该用例只覆盖 Linux/macOS 分支");
return;
}
const port = await findFreePort();
const child = await startPythonListener(port);
t.after(() => {
if (!child.killed) {
child.kill("SIGKILL");
}
});
assert.equal(await isPortFree("127.0.0.1", port), false);
const ok = await ensurePortFree(port, "backend");
assert.equal(ok, true);
assert.equal(await isPortFree("127.0.0.1", port), true);
await waitForExit(child);
});