fix(watcher): cap startup wait
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
// Watcher Unit Tests — exercises WorkspaceWatcher against a temp dir with
|
||||
// real chokidar. We use real FS because mocking it would test the mock,
|
||||
// not the actual behavior under inotify.
|
||||
import { describe, it, before, after, beforeEach } from "node:test";
|
||||
import * as assert from "node:assert/strict";
|
||||
import * as fs from "node:fs";
|
||||
@@ -9,9 +6,6 @@ import * as path from "node:path";
|
||||
import { WorkspaceWatcher, type FileEvent } from "../../src/watcher.ts";
|
||||
import { pathToUri } from "../../src/root.ts";
|
||||
|
||||
// Wait For - Polls a predicate up to timeoutMs. Returns true if it became
|
||||
// true, false if it timed out. Used to wait on async chokidar events
|
||||
// without arbitrary sleeps.
|
||||
async function waitFor(
|
||||
predicate: () => boolean,
|
||||
timeoutMs = 2000,
|
||||
@@ -24,8 +18,6 @@ async function waitFor(
|
||||
return predicate();
|
||||
}
|
||||
|
||||
// Wait Quiet - Waits for the debounce window to elapse so any pending
|
||||
// events flush. Slightly longer than DEBOUNCE_MAX_WAIT_MS.
|
||||
const FLUSH_WAIT_MS = 700;
|
||||
|
||||
describe("WorkspaceWatcher", () => {
|
||||
@@ -47,7 +39,6 @@ describe("WorkspaceWatcher", () => {
|
||||
watcher = null;
|
||||
}
|
||||
received = [];
|
||||
// Reset Dir - Wipe contents between tests so file lists are predictable.
|
||||
for (const entry of fs.readdirSync(tmpDir)) {
|
||||
fs.rmSync(path.join(tmpDir, entry), { recursive: true, force: true });
|
||||
}
|
||||
@@ -140,7 +131,6 @@ describe("WorkspaceWatcher", () => {
|
||||
|
||||
it("respects WatchKind to filter event types", async () => {
|
||||
watcher = new WorkspaceWatcher(tmpDir, (evs) => received.push(evs));
|
||||
// Kind 1 = Create only - delete events should be suppressed.
|
||||
watcher.setPatterns([{ globPattern: "**/*.ts", kind: 1 }]);
|
||||
await watcher.ready();
|
||||
|
||||
@@ -166,8 +156,6 @@ describe("WorkspaceWatcher", () => {
|
||||
watcher.setPatterns([{ globPattern: "**/*.ts" }]);
|
||||
await watcher.ready();
|
||||
|
||||
// Write Several Files Quickly - within the 50ms debounce window they
|
||||
// should all land in one batch, capped at 500ms max wait.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
fs.writeFileSync(path.join(tmpDir, `f${i}.ts`), "x");
|
||||
}
|
||||
@@ -176,8 +164,6 @@ describe("WorkspaceWatcher", () => {
|
||||
|
||||
const all = received.flat();
|
||||
assert.strictEqual(all.length, 5, `Expected 5 events, got ${all.length}`);
|
||||
// The batches should be << 5 (debounce coalesces). Allow up to 2 in
|
||||
// case the loop straddled a flush boundary.
|
||||
assert.ok(
|
||||
received.length <= 2,
|
||||
`Expected <=2 batches, got ${received.length}: ${JSON.stringify(received)}`,
|
||||
@@ -217,8 +203,6 @@ describe("WorkspaceWatcher", () => {
|
||||
});
|
||||
|
||||
it("matches absolute-path glob patterns (gopls-style)", async () => {
|
||||
// Regression - gopls registers e.g. `/tmp/.../**/*.{go,mod}` rather
|
||||
// than a bare relative `**/*.go`. The matcher must accept both.
|
||||
watcher = new WorkspaceWatcher(tmpDir, (evs) => received.push(evs));
|
||||
watcher.setPatterns([
|
||||
{ globPattern: `${tmpDir}/**/*.{go,mod}` },
|
||||
@@ -240,7 +224,6 @@ describe("WorkspaceWatcher", () => {
|
||||
watcher.setPatterns([{ globPattern: "**/*.ts" }]);
|
||||
await watcher.ready();
|
||||
|
||||
// Create Then Delete - both within the debounce quiet window.
|
||||
const file = path.join(tmpDir, "transient.ts");
|
||||
fs.writeFileSync(file, "x");
|
||||
fs.unlinkSync(file);
|
||||
@@ -255,15 +238,12 @@ describe("WorkspaceWatcher", () => {
|
||||
});
|
||||
|
||||
it("coalesces Deleted+Created (replacement) to Changed", async () => {
|
||||
// Setup - file exists before the watcher starts, so chokidar's initial
|
||||
// scan registers it (with ignoreInitial: true, no event fires).
|
||||
const file = path.join(tmpDir, "replace.ts");
|
||||
fs.writeFileSync(file, "v1");
|
||||
watcher = new WorkspaceWatcher(tmpDir, (evs) => received.push(evs));
|
||||
watcher.setPatterns([{ globPattern: "**/*.ts" }]);
|
||||
await watcher.ready();
|
||||
|
||||
// Delete Then Recreate - within one debounce window.
|
||||
fs.unlinkSync(file);
|
||||
fs.writeFileSync(file, "v2");
|
||||
|
||||
@@ -272,11 +252,6 @@ describe("WorkspaceWatcher", () => {
|
||||
const all = received.flat();
|
||||
const events = all.filter((e) => e.uri === pathToUri(file));
|
||||
assert.ok(events.length > 0, `Expected an event for replaced file`);
|
||||
// Expected: the coalesced single event is Changed (type=2). Some
|
||||
// platforms may split the delete+create across debounce windows, in
|
||||
// which case the server still sees correct state. Accept either:
|
||||
// - one Changed event (coalesced), or
|
||||
// - Deleted then Created in separate batches (not coalesced, fine)
|
||||
const types = events.map((e) => e.type).sort();
|
||||
const acceptable =
|
||||
JSON.stringify(types) === JSON.stringify([2]) ||
|
||||
@@ -293,8 +268,6 @@ describe("WorkspaceWatcher", () => {
|
||||
await watcher.ready();
|
||||
|
||||
fs.writeFileSync(path.join(tmpDir, "pending.ts"), "x");
|
||||
// Don't wait for the debounce - clear patterns immediately so the
|
||||
// pending batch should be dropped, not flushed.
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
watcher.setPatterns([]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user