import { afterEach, beforeAll, describe, expect, test } from "bun:test"; import { bunEnv, bunExe, isWindows, makeTree, tempDirWithFiles } from "harness"; import path from "node:path"; import { symbols, test_skipped } from "../../src/bun.js/bindings/libuv/generate_uv_posix_stubs_constants"; import source from "./uv-stub-stuff/uv_impl.c"; const symbols_to_test = symbols.filter(s => !test_skipped.includes(s)); // We use libuv on Windows describe.if(!isWindows)("uv stubs", () => { const cwd = process.cwd(); let tempdir: string = ""; let outdir: string = ""; let nativeModule: any; beforeAll(async () => { const files = { "uv_impl.c": await Bun.file(source).text(), "package.json": JSON.stringify({ "name": "fake-plugin", "module": "index.ts", "type": "module", "devDependencies": { "@types/bun": "latest", }, "peerDependencies": { "typescript": "^5.0.0", }, "scripts": { "build:napi": "node-gyp configure && node-gyp build", }, "dependencies": { "node-gyp": "10.2.0", }, }), "binding.gyp": `{ "targets": [ { "target_name": "uv_test", "sources": [ "uv_impl.c" ], "include_dirs": [ ".", "./libuv" ], "cflags": ["-fPIC"], "ldflags": ["-Wl,--export-dynamic"] }, ] }`, }; tempdir = tempDirWithFiles("uv-tests", files); await makeTree(tempdir, files); outdir = path.join(tempdir, "dist"); process.chdir(tempdir); const libuvDir = path.join(__dirname, "../../src/bun.js/bindings/libuv"); await Bun.$`cp -R ${libuvDir} ${path.join(tempdir, "libuv")}`; await Bun.$`${bunExe()} i && ${bunExe()} build:napi`.env(bunEnv).cwd(tempdir); nativeModule = require(path.join(tempdir, "./build/Release/uv_test.node")); }); afterEach(() => { process.chdir(cwd); }); test("mutex init and destroy", () => { expect(() => nativeModule.testMutexInitDestroy()).not.toThrow(); }); test("recursive mutex", () => { expect(() => nativeModule.testMutexRecursive()).not.toThrow(); }); test("mutex trylock", () => { expect(() => nativeModule.testMutexTrylock()).not.toThrow(); }); test("process IDs", () => { const result = nativeModule.testProcessIds(); expect(result).toHaveProperty("pid"); expect(result).toHaveProperty("ppid"); expect(result.pid).toBeGreaterThan(0); expect(result.ppid).toBeGreaterThan(0); // The process ID should match Node's process.pid expect(result.pid).toBe(process.pid); }); test("uv_once", () => { expect(nativeModule.testUvOnce()).toBe(1); expect(nativeModule.testUvOnce()).toBe(1); expect(nativeModule.testUvOnce()).toBe(1); }); test("hrtime", () => { const result = nativeModule.testHrtime(); // Reconstruct the 64-bit values const time1 = (BigInt(result.time1High) << 32n) | BigInt(result.time1Low >>> 0); const time2 = (BigInt(result.time2High) << 32n) | BigInt(result.time2Low >>> 0); // Verify that: // 1. time2 is greater than time1 (time passed) expect(time2 > time1).toBe(true); // 2. The difference should be at least 1ms (we slept for 1ms) // hrtime is in nanoseconds, so 1ms = 1,000,000 ns const diff = time2 - time1; expect(diff >= 1_000_000n).toBe(true); // 3. The difference shouldn't be unreasonably large // Let's say not more than 100ms (100,000,000 ns) expect(diff <= 100_000_000n).toBe(true); }); });