Files
tutti/tools/scripts/prewarm-electron.test.mjs
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

203 lines
6.0 KiB
JavaScript

import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import {
mkdirSync,
mkdtempSync,
readdirSync,
rmSync,
writeFileSync
} from "node:fs";
import { hostname, tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
getElectronCacheLockPath,
getElectronCacheRoot,
withElectronCacheLock
} from "./prewarm-electron.mjs";
test("resolves Electron's platform cache locations", () => {
assert.equal(
getElectronCacheRoot({
env: {},
homeDirectory: "/Users/developer",
platform: "darwin"
}),
"/Users/developer/Library/Caches/electron"
);
assert.equal(
getElectronCacheRoot({
env: { XDG_CACHE_HOME: "/cache" },
homeDirectory: "/home/developer",
platform: "linux"
}),
"/cache/electron"
);
assert.equal(
getElectronCacheRoot({
env: { electron_config_cache: "/custom/electron-cache" },
platform: "darwin"
}),
"/custom/electron-cache"
);
});
test("serializes prewarm calls for the same Electron artifact", async () => {
const cacheRoot = mkdtempSync(join(tmpdir(), "tutti-electron-cache-"));
const lockInput = {
arch: "arm64",
cacheRoot,
electronVersion: "43.2.0",
platform: "darwin",
pollIntervalMilliseconds: 1
};
let releaseFirstOperation;
let secondOperationStarted = false;
let signalFirstOperation;
const firstOperationStarted = new Promise((resolvePromise) => {
signalFirstOperation = resolvePromise;
});
try {
const first = withElectronCacheLock(lockInput, async () => {
signalFirstOperation();
await new Promise((resolvePromise) => {
releaseFirstOperation = resolvePromise;
});
});
await firstOperationStarted;
const second = withElectronCacheLock(lockInput, async () => {
secondOperationStarted = true;
});
await new Promise((resolvePromise) => setTimeout(resolvePromise, 20));
assert.equal(secondOperationStarted, false);
releaseFirstOperation();
await Promise.all([first, second]);
assert.equal(secondOperationStarted, true);
} finally {
rmSync(cacheRoot, { force: true, recursive: true });
}
});
test("reclaims a prewarm lock left by a dead local process", async () => {
const cacheRoot = mkdtempSync(join(tmpdir(), "tutti-electron-cache-"));
const lockInput = {
arch: "arm64",
cacheRoot,
electronVersion: "43.2.0",
platform: "darwin",
pollIntervalMilliseconds: 1
};
const lockPath = getElectronCacheLockPath(lockInput);
mkdirSync(cacheRoot, { recursive: true });
writeFileSync(
lockPath,
`${JSON.stringify({ hostName: hostname(), processId: 99_999_999 })}\n`
);
try {
const result = await withElectronCacheLock(lockInput, () => "ready");
assert.equal(result, "ready");
} finally {
rmSync(cacheRoot, { force: true, recursive: true });
}
});
test("serializes concurrent recovery of a stale prewarm lock", async () => {
const cacheRoot = mkdtempSync(join(tmpdir(), "tutti-electron-cache-"));
const readyDirectory = join(cacheRoot, "ready");
const startPath = join(cacheRoot, "start");
const activePath = join(cacheRoot, "active");
const lockInput = {
arch: "arm64",
cacheRoot,
electronVersion: "43.2.0",
platform: "darwin",
pollIntervalMilliseconds: 1
};
const lockPath = getElectronCacheLockPath(lockInput);
mkdirSync(cacheRoot, { recursive: true });
mkdirSync(readyDirectory, { recursive: true });
writeFileSync(
lockPath,
`${JSON.stringify({ hostName: hostname(), processId: 99_999_999 })}\n`
);
try {
const children = Array.from({ length: 4 }, () =>
spawn(process.execPath, ["--input-type=module", "--eval", childScript], {
env: {
...process.env,
TUTTI_ELECTRON_LOCK_ACTIVE_PATH: activePath,
TUTTI_ELECTRON_LOCK_INPUT: JSON.stringify(lockInput),
TUTTI_ELECTRON_LOCK_MODULE_URL: new URL(
"./prewarm-electron.mjs",
import.meta.url
).href,
TUTTI_ELECTRON_LOCK_READY_DIRECTORY: readyDirectory,
TUTTI_ELECTRON_LOCK_START_PATH: startPath
}
})
);
await waitFor(() => readdirSync(readyDirectory).length === children.length);
writeFileSync(startPath, "start\n");
const outcomes = await Promise.all(children.map(waitForChild));
for (const outcome of outcomes) {
assert.equal(outcome.code, 0, outcome.stderr);
}
} finally {
rmSync(cacheRoot, { force: true, recursive: true });
}
});
const childScript = `
import { closeSync, existsSync, openSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
const { withElectronCacheLock } = await import(process.env.TUTTI_ELECTRON_LOCK_MODULE_URL);
const input = JSON.parse(process.env.TUTTI_ELECTRON_LOCK_INPUT);
const readyDirectory = process.env.TUTTI_ELECTRON_LOCK_READY_DIRECTORY;
const startPath = process.env.TUTTI_ELECTRON_LOCK_START_PATH;
const activePath = process.env.TUTTI_ELECTRON_LOCK_ACTIVE_PATH;
writeFileSync(join(readyDirectory, String(process.pid)), "ready\\n");
while (!existsSync(startPath)) {
await new Promise((resolvePromise) => setTimeout(resolvePromise, 1));
}
await withElectronCacheLock(input, async () => {
const descriptor = openSync(activePath, "wx");
try {
await new Promise((resolvePromise) => setTimeout(resolvePromise, 30));
} finally {
closeSync(descriptor);
rmSync(activePath, { force: true });
}
});
`;
async function waitFor(predicate) {
const deadline = Date.now() + 5_000;
while (!predicate()) {
if (Date.now() >= deadline) {
throw new Error("timed out waiting for concurrent lock test children");
}
await new Promise((resolvePromise) => setTimeout(resolvePromise, 5));
}
}
function waitForChild(child) {
return new Promise((resolvePromise, rejectPromise) => {
let stderr = "";
child.stderr.on("data", (data) => {
stderr += data;
});
child.on("error", rejectPromise);
child.on("close", (code) => {
resolvePromise({ code, stderr });
});
});
}