fix(plugins): await plugin unload on quit with deactivate timeout

- Move plugin unload into the will-quit cleanup handler so it starts first and quit waits for it
- Give each plugin's deactivate 3 s (configurable via deactivateTimeoutMs) before disposing it anyway
- Document the quit timeout in docs-site/plugins.md
This commit is contained in:
sudacode committed 2026-10-26 23:22:25 -07:00
1 parent 04c91add22
commit 7a526531d7
8 files changed
+84 -5

No files matched your search

+29 -1
View File
@@ -42,7 +42,10 @@ function createFakeWindow() {
return fake;
}
function createHarness(config: Record<string, PluginConfigEntry>) {
function createHarness(
config: Record<string, PluginConfigEntry>,
overrides: Partial<PluginHostDeps<{ marker: string }>> = {},
) {
const root = mkdtempSync(path.join(os.tmpdir(), 'subminer-plugins-'));
const errors: string[] = [];
const notifications: string[] = [];
@@ -82,6 +85,7 @@ function createHarness(config: Record<string, PluginConfigEntry>) {
return { window: window.asWindow(), created: true };
},
unstable: { marker: 'internals' },
...overrides,
};
return {
root: deps.pluginsDir,
@@ -227,6 +231,30 @@ test('mpv listener errors are contained and unload detaches listeners and calls
}
});
test('unload gives up on a deactivate that never settles and still disposes the plugin', async () => {
const h = createHarness({ stuck: enabled() }, { deactivateTimeoutMs: 20 });
const client = createFakeMpvClient();
try {
writePlugin(h.root, 'stuck', {
'plugin.json': JSON.stringify({ main: 'main.cjs' }),
'main.cjs': `exports.activate = (ctx) => {
ctx.mpv.on('pause-change', () => ctx.notify('still listening'));
return () => new Promise(() => {});
};`,
});
h.host.attachMpvClient(client);
await h.host.loadAll();
await h.host.unloadAll();
client.emit('pause-change', { paused: true });
assert.deepEqual(h.notifications, []);
assert.deepEqual(h.errors, ['deactivate failed: timed out after 20 ms']);
} finally {
h.cleanup();
}
});
test('events follow a replacement mpv client', async () => {
const h = createHarness({ watcher: enabled() });
const first = createFakeMpvClient();