mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-16 13:55:51 -07:00
refactor(main): eliminate local pass-through wrappers in main.ts (#168)
This commit is contained in:
@@ -52,7 +52,13 @@ function runKeepaliveScript(
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
'/bin/sh',
|
||||
['-c', APPIMAGE_MOUNT_KEEPALIVE_SCRIPT, APPIMAGE_MOUNT_KEEPALIVE_LABEL, appImagePath, ...extraArgs],
|
||||
[
|
||||
'-c',
|
||||
APPIMAGE_MOUNT_KEEPALIVE_SCRIPT,
|
||||
APPIMAGE_MOUNT_KEEPALIVE_LABEL,
|
||||
appImagePath,
|
||||
...extraArgs,
|
||||
],
|
||||
{ timeout: 30_000 },
|
||||
(error) => {
|
||||
if (error && typeof error.code !== 'number') {
|
||||
@@ -69,113 +75,107 @@ function writeExecutable(filePath: string, content: string): void {
|
||||
fs.writeFileSync(filePath, content, { mode: 0o755 });
|
||||
}
|
||||
|
||||
test(
|
||||
'keepalive script releases the mount only after straggler processes exit',
|
||||
{ skip: process.platform !== 'linux' },
|
||||
async () => {
|
||||
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-keepalive-test-'));
|
||||
const resultsDir = path.join(workDir, 'results');
|
||||
fs.mkdirSync(resultsDir);
|
||||
const mountDir = path.join(workDir, 'fake-mount');
|
||||
fs.mkdirSync(mountDir);
|
||||
const linuxTest = process.platform === 'linux' ? test : test.skip;
|
||||
|
||||
// AppRun leaves behind a straggler that keeps executing *from the mount*
|
||||
// after AppRun itself exits — mimicking Chromium utility children.
|
||||
fs.copyFileSync('/usr/bin/sleep', path.join(mountDir, 'straggler'));
|
||||
fs.chmodSync(path.join(mountDir, 'straggler'), 0o755);
|
||||
writeExecutable(
|
||||
path.join(mountDir, 'AppRun'),
|
||||
[
|
||||
'#!/bin/sh',
|
||||
`"${mountDir}/straggler" 1 &`,
|
||||
`date +%s%N > "${resultsDir}/apprun-exited"`,
|
||||
'exit 42',
|
||||
].join('\n'),
|
||||
linuxTest('keepalive script releases the mount only after straggler processes exit', async () => {
|
||||
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-keepalive-test-'));
|
||||
const resultsDir = path.join(workDir, 'results');
|
||||
fs.mkdirSync(resultsDir);
|
||||
const mountDir = path.join(workDir, 'fake-mount');
|
||||
fs.mkdirSync(mountDir);
|
||||
|
||||
// AppRun leaves behind a straggler that keeps executing *from the mount*
|
||||
// after AppRun itself exits — mimicking Chromium utility children.
|
||||
fs.copyFileSync('/usr/bin/sleep', path.join(mountDir, 'straggler'));
|
||||
fs.chmodSync(path.join(mountDir, 'straggler'), 0o755);
|
||||
writeExecutable(
|
||||
path.join(mountDir, 'AppRun'),
|
||||
[
|
||||
'#!/bin/sh',
|
||||
`"${mountDir}/straggler" 1 &`,
|
||||
`date +%s%N > "${resultsDir}/apprun-exited"`,
|
||||
'exit 42',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const fakeAppImage = path.join(workDir, 'Fake.AppImage');
|
||||
writeExecutable(
|
||||
fakeAppImage,
|
||||
[
|
||||
'#!/bin/sh',
|
||||
'if [ "${1:-}" = "--appimage-mount" ]; then',
|
||||
` echo "${mountDir}"`,
|
||||
` trap ': > "${resultsDir}/holder-released"; sleep 0.1; date +%s%N > "${resultsDir}/holder-released"; exit 0' TERM INT`,
|
||||
' while :; do sleep 0.05; done',
|
||||
'fi',
|
||||
`date +%s%N > "${resultsDir}/direct-run"`,
|
||||
'exit 0',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
try {
|
||||
const { status } = await runKeepaliveScript(fakeAppImage);
|
||||
|
||||
assert.equal(status, 42, 'exit code of AppRun must be propagated');
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(resultsDir, 'direct-run')),
|
||||
'must not fall back to direct AppImage run when mount succeeds',
|
||||
);
|
||||
|
||||
const fakeAppImage = path.join(workDir, 'Fake.AppImage');
|
||||
writeExecutable(
|
||||
fakeAppImage,
|
||||
[
|
||||
'#!/bin/sh',
|
||||
'if [ "${1:-}" = "--appimage-mount" ]; then',
|
||||
` echo "${mountDir}"`,
|
||||
` trap ': > "${resultsDir}/holder-released"; sleep 0.1; date +%s%N > "${resultsDir}/holder-released"; exit 0' TERM INT`,
|
||||
' while :; do sleep 0.05; done',
|
||||
'fi',
|
||||
`date +%s%N > "${resultsDir}/direct-run"`,
|
||||
'exit 0',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
try {
|
||||
const { status } = await runKeepaliveScript(fakeAppImage);
|
||||
|
||||
assert.equal(status, 42, 'exit code of AppRun must be propagated');
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(resultsDir, 'direct-run')),
|
||||
'must not fall back to direct AppImage run when mount succeeds',
|
||||
);
|
||||
// The script does not wait for the holder to finish handling SIGTERM
|
||||
// (the real runtime unmounts on its own after the signal), so poll.
|
||||
const releasedMarker = path.join(resultsDir, 'holder-released');
|
||||
const pollDeadline = Date.now() + 2000;
|
||||
let holderReleased: number | null = null;
|
||||
while (holderReleased === null && Date.now() < pollDeadline) {
|
||||
if (fs.existsSync(releasedMarker)) {
|
||||
const timestamp = fs.readFileSync(releasedMarker, 'utf8').trim();
|
||||
if (/^\d+$/.test(timestamp)) holderReleased = Number(timestamp);
|
||||
}
|
||||
if (holderReleased !== null) break;
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
// The script does not wait for the holder to finish handling SIGTERM
|
||||
// (the real runtime unmounts on its own after the signal), so poll.
|
||||
const releasedMarker = path.join(resultsDir, 'holder-released');
|
||||
const pollDeadline = Date.now() + 2000;
|
||||
let holderReleased: number | null = null;
|
||||
while (holderReleased === null && Date.now() < pollDeadline) {
|
||||
if (fs.existsSync(releasedMarker)) {
|
||||
const timestamp = fs.readFileSync(releasedMarker, 'utf8').trim();
|
||||
if (/^\d+$/.test(timestamp)) holderReleased = Number(timestamp);
|
||||
}
|
||||
assert.ok(holderReleased !== null, 'holder release timestamp must be recorded');
|
||||
|
||||
const appRunExited = Number(
|
||||
fs.readFileSync(path.join(resultsDir, 'apprun-exited'), 'utf8').trim(),
|
||||
);
|
||||
const drainNs = holderReleased - appRunExited;
|
||||
assert.ok(
|
||||
drainNs >= 0.8e9,
|
||||
`holder must outlive the 1s straggler (drained after ${drainNs / 1e9}s)`,
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
if (holderReleased !== null) break;
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
},
|
||||
);
|
||||
assert.ok(holderReleased !== null, 'holder release timestamp must be recorded');
|
||||
|
||||
test(
|
||||
'keepalive script falls back to direct run when --appimage-mount fails',
|
||||
{ skip: process.platform !== 'linux' },
|
||||
async () => {
|
||||
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-keepalive-test-'));
|
||||
const resultsDir = path.join(workDir, 'results');
|
||||
fs.mkdirSync(resultsDir);
|
||||
|
||||
const fakeAppImage = path.join(workDir, 'Fake.AppImage');
|
||||
writeExecutable(
|
||||
fakeAppImage,
|
||||
[
|
||||
'#!/bin/sh',
|
||||
'if [ "${1:-}" = "--appimage-mount" ]; then',
|
||||
' exit 1',
|
||||
'fi',
|
||||
`printf '%s\\n' "$@" > "${resultsDir}/direct-run"`,
|
||||
'exit 7',
|
||||
].join('\n'),
|
||||
const appRunExited = Number(
|
||||
fs.readFileSync(path.join(resultsDir, 'apprun-exited'), 'utf8').trim(),
|
||||
);
|
||||
const drainNs = holderReleased - appRunExited;
|
||||
assert.ok(
|
||||
drainNs >= 0.8e9,
|
||||
`holder must outlive the 1s straggler (drained after ${drainNs / 1e9}s)`,
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const { status } = await runKeepaliveScript(fakeAppImage, ['--start', '--background']);
|
||||
assert.equal(status, 7, 'direct-run exit code must be propagated');
|
||||
assert.equal(
|
||||
fs.readFileSync(path.join(resultsDir, 'direct-run'), 'utf8'),
|
||||
'--start\n--background\n',
|
||||
'launch args must be forwarded to the direct run',
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
linuxTest('keepalive script falls back to direct run when --appimage-mount fails', async () => {
|
||||
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-keepalive-test-'));
|
||||
const resultsDir = path.join(workDir, 'results');
|
||||
fs.mkdirSync(resultsDir);
|
||||
|
||||
const fakeAppImage = path.join(workDir, 'Fake.AppImage');
|
||||
writeExecutable(
|
||||
fakeAppImage,
|
||||
[
|
||||
'#!/bin/sh',
|
||||
'if [ "${1:-}" = "--appimage-mount" ]; then',
|
||||
' exit 1',
|
||||
'fi',
|
||||
`printf '%s\\n' "$@" > "${resultsDir}/direct-run"`,
|
||||
'exit 7',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
try {
|
||||
const { status } = await runKeepaliveScript(fakeAppImage, ['--start', '--background']);
|
||||
assert.equal(status, 7, 'direct-run exit code must be propagated');
|
||||
assert.equal(
|
||||
fs.readFileSync(path.join(resultsDir, 'direct-run'), 'utf8'),
|
||||
'--start\n--background\n',
|
||||
'launch args must be forwarded to the direct run',
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user