Files
smartwatch/test/test.fswatcher-linger.node.ts

87 lines
2.9 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as chokidar from 'chokidar';
import * as fs from 'fs';
import * as path from 'path';
const TEST_DIR = './test/assets';
const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
/**
* Count active FSWatcher handles in the process
*/
function countFSWatcherHandles(): number {
const handles = (process as any)._getActiveHandles();
return handles.filter((h: any) => h?.constructor?.name === 'FSWatcher').length;
}
tap.test('should not leave lingering FSWatcher handles after chokidar close', async () => {
const handlesBefore = countFSWatcherHandles();
console.log(`FSWatcher handles before: ${handlesBefore}`);
// Start a chokidar watcher
const watcher = chokidar.watch(path.resolve(TEST_DIR), {
persistent: true,
ignoreInitial: false,
});
// Wait for ready
await new Promise<void>((resolve) => watcher.on('ready', resolve));
const handlesDuring = countFSWatcherHandles();
console.log(`FSWatcher handles during watch: ${handlesDuring}`);
expect(handlesDuring).toBeGreaterThan(handlesBefore);
// Close the watcher
await watcher.close();
console.log('chokidar.close() resolved');
// Check immediately after close
const handlesAfterClose = countFSWatcherHandles();
console.log(`FSWatcher handles immediately after close: ${handlesAfterClose}`);
// Wait a bit and check again to see if handles are cleaned up asynchronously
await delay(500);
const handlesAfterDelay500 = countFSWatcherHandles();
console.log(`FSWatcher handles after 500ms: ${handlesAfterDelay500}`);
await delay(1500);
const handlesAfterDelay2000 = countFSWatcherHandles();
console.log(`FSWatcher handles after 2000ms: ${handlesAfterDelay2000}`);
const lingeringHandles = handlesAfterDelay2000 - handlesBefore;
console.log(`Lingering FSWatcher handles: ${lingeringHandles}`);
if (lingeringHandles > 0) {
console.log('WARNING: chokidar left lingering FSWatcher handles after close()');
} else {
console.log('OK: all FSWatcher handles were cleaned up');
}
expect(lingeringHandles).toEqual(0);
});
tap.test('should not leave handles after multiple open/close cycles', async () => {
const handlesBefore = countFSWatcherHandles();
console.log(`\nMulti-cycle test - handles before: ${handlesBefore}`);
for (let i = 0; i < 3; i++) {
const watcher = chokidar.watch(path.resolve(TEST_DIR), {
persistent: true,
ignoreInitial: false,
});
await new Promise<void>((resolve) => watcher.on('ready', resolve));
const during = countFSWatcherHandles();
console.log(` Cycle ${i + 1} - handles during: ${during}`);
await watcher.close();
await delay(500);
}
const handlesAfter = countFSWatcherHandles();
const leaked = handlesAfter - handlesBefore;
console.log(`Handles after 3 cycles: ${handlesAfter} (leaked: ${leaked})`);
expect(leaked).toEqual(0);
});
export default tap.start();