From d25f16e8ea9593fdefef87dd259ab9d01a24d736 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 19:15:18 +0000 Subject: [PATCH] test: stabilize macOS runs by lengthening tick defaults macOS backs fs.watch with FSEvents, which has a ~100-500ms dispatch cycle and coalesces rapid changes. The current 110ms default tick and 300-400ms beforeEach/afterEach waits race those timings, which is the main source of macOS-only test flakes (`should aggregate changes while paused`, the `fast` DirectoryWatcher variant, residual events leaking between tests, etc.). - bump the default tick to 260ms on darwin (unchanged elsewhere) - bump beforeEach quiesce to 700ms and afterEach to 500ms on darwin - bump the DirectoryWatcher "fast" interval from 50 to 150ms on darwin so consecutive writes are not coalesced by FSEvents Test-only change; no library behavior is affected. --- test/DirectoryWatcher.test.js | 6 +++++- test/helpers/TestHelper.js | 13 +++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/test/DirectoryWatcher.test.js b/test/DirectoryWatcher.test.js index 62323f7..21e270e 100644 --- a/test/DirectoryWatcher.test.js +++ b/test/DirectoryWatcher.test.js @@ -138,10 +138,14 @@ describe("DirectoryWatcher", () => { }); }); + const IS_OSX = require("os").platform() === "darwin"; + /** @type {Record<"slow" | "fast", number>} */ + // FSEvents coalesces rapid changes, so on macOS the "fast" case needs a + // larger interval or consecutive writes will be dropped. const timings = { slow: 300, - fast: 50, + fast: IS_OSX ? 150 : 50, }; for (const name of Object.keys(timings)) { const time = timings[/** @type {keyof typeof timings} */ (name)]; diff --git a/test/helpers/TestHelper.js b/test/helpers/TestHelper.js index 57ea6cb..d8f8461 100644 --- a/test/helpers/TestHelper.js +++ b/test/helpers/TestHelper.js @@ -12,6 +12,8 @@ const writeFileAtomic = require("write-file-atomic"); const watchEventSource = require("../../lib/watchEventSource"); +const IS_OSX = require("os").platform() === "darwin"; + require("../../lib/getWatcherManager"); let watcherManagerModule = require.cache[require.resolve("../../lib/getWatcherManager")]; @@ -48,7 +50,10 @@ class TestHelper { */ tick(arg, fn) { // if polling is set, ensure the tick is longer than the polling interval. - const defaultTick = (Number(process.env.WATCHPACK_POLLING) || 100) + 10; + // On macOS the FSEvents dispatch cycle is ~100-500ms, so use a larger + // default tick there to keep fs.watch-based tests stable. + const defaultTick = + (Number(process.env.WATCHPACK_POLLING) || (IS_OSX ? 250 : 100)) + 10; if (typeof arg === "function") { fn = arg; @@ -66,7 +71,7 @@ class TestHelper { */ before(done) { checkAllWatcherClosed(); - this.tick(400, () => { + this.tick(IS_OSX ? 700 : 400, () => { this.rm(this.testdir); fs.mkdirSync(this.testdir); done(); @@ -88,10 +93,10 @@ class TestHelper { return; } checkAllWatcherClosed(); - this.tick(300, done); + this.tick(IS_OSX ? 500 : 300, done); }; - this.tick(300, () => { + this.tick(IS_OSX ? 500 : 300, () => { del(); }); }