1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115import { 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);
});
});