Files
bun.sh/test/js/node/watch/fs.watchFile.test.ts
dave caruso acfd028e8f feat(runtime): Implement fs.watchFile (#4467)
* really lame prototype

* uses threads but badly

* it works i guess

* unwatchFile but lame

* it works

* test

* a

* aomitcs

* fix unwatching race condition

* use hasPendingActivity and GC stuff better

* test

* revert this
2023-09-05 17:41:39 -07:00

122 lines
4.1 KiB
TypeScript

import fs, { FSWatcher } from "node:fs";
import path from "path";
import { tempDirWithFiles, bunRun, bunRunAsScript } from "harness";
import { pathToFileURL } from "bun";
import { describe, expect, test } from "bun:test";
// Because macOS (and possibly other operating systems) can return a watcher
// before it is actually watching, we need to repeat the operation to avoid
// a race condition.
function repeat(fn: any) {
const interval = setInterval(fn, 20);
return interval;
}
const encodingFileName = `新建文夹件.txt`;
const testDir = tempDirWithFiles("watch", {
"watch.txt": "hello",
[encodingFileName]: "hello",
});
describe("fs.watchFile", () => {
test("zeroed stats if does not exist", async () => {
let entries: any = [];
fs.watchFile(path.join(testDir, "does-not-exist"), (curr, prev) => {
entries.push([curr, prev]);
});
await Bun.sleep(35);
fs.unwatchFile(path.join(testDir, "does-not-exist"));
expect(entries.length).toBe(1);
expect(entries[0][0].size).toBe(0);
expect(entries[0][0].mtimeMs).toBe(0);
expect(entries[0][1].size).toBe(0);
expect(entries[0][1].mtimeMs).toBe(0);
});
test("it watches a file", async () => {
let entries: any = [];
fs.watchFile(path.join(testDir, "watch.txt"), { interval: 50 }, (curr, prev) => {
entries.push([curr, prev]);
});
await Bun.sleep(100);
fs.writeFileSync(path.join(testDir, "watch.txt"), "hello2");
await Bun.sleep(100);
fs.unwatchFile(path.join(testDir, "watch.txt"));
expect(entries.length).toBeGreaterThan(0);
expect(entries[0][0].size).toBe(6);
expect(entries[0][1].size).toBe(5);
expect(entries[0][0].mtimeMs).toBeGreaterThan(entries[0][1].mtimeMs);
});
test("unicode file name", async () => {
let entries: any = [];
fs.watchFile(path.join(testDir, encodingFileName), { interval: 50 }, (curr, prev) => {
entries.push([curr, prev]);
});
await Bun.sleep(100);
fs.writeFileSync(path.join(testDir, encodingFileName), "hello2");
await Bun.sleep(100);
fs.unwatchFile(path.join(testDir, encodingFileName));
expect(entries.length).toBeGreaterThan(0);
expect(entries[0][0].size).toBe(6);
expect(entries[0][1].size).toBe(5);
expect(entries[0][0].mtimeMs).toBeGreaterThan(entries[0][1].mtimeMs);
});
test("bigint stats", async () => {
let entries: any = [];
fs.watchFile(path.join(testDir, encodingFileName), { interval: 50, bigint: true }, (curr, prev) => {
entries.push([curr, prev]);
});
await Bun.sleep(100);
fs.writeFileSync(path.join(testDir, encodingFileName), "hello2");
await Bun.sleep(100);
fs.unwatchFile(path.join(testDir, encodingFileName));
expect(entries.length).toBeGreaterThan(0);
expect(typeof entries[0][0].mtimeMs === "bigint").toBe(true);
});
test("StatWatcherScheduler stress test (1000 watchers with random times)", async () => {
const EventEmitter = require("events");
let defaultMaxListeners = EventEmitter.defaultMaxListeners;
try {
EventEmitter.defaultMaxListeners = 1000;
// This tests StatWatcher's scheduler for add/remove race conditions,
// as the actual stat()ing is done on another thread using a specialized linked list implementation
// so we're testing that here, less so that stats will properly notify js, since that code is already known to be very threadsafe.
const set = new Set<string>();
const { promise, resolve } = Promise.withResolvers();
for (let i = 0; i < 1000; i++) {
const file = path.join(testDir, i + ".txt");
setTimeout(() => {
let first = true;
fs.watchFile(file, { interval: 500 }, (curr, prev) => {
set.add(file);
if (first) {
first = false;
setTimeout(() => {
fs.unwatchFile(file);
if (set.size === 1000) resolve();
}, Math.random() * 2000);
}
});
}, Math.random() * 2000);
}
await promise;
expect(set.size).toBe(1000);
} finally {
EventEmitter.defaultMaxListeners = defaultMaxListeners;
}
}, 20000);
});