Skip to content

Commit 5b45ef1

Browse files
rotuaminya
authored andcommitted
test: use WeakRef instead of weak-napi for GC tests
1 parent 46d129d commit 5b45ef1

File tree

9 files changed

+101
-205
lines changed

9 files changed

+101
-205
lines changed

CONTRIBUTING.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,3 @@ tool similar to Cmake. GYP was originally created to generate native IDE project
3838
files (Visual Studio, Xcode) for building Chromium.
3939

4040
The `.gyp` file is structured as a Python dictionary.
41-
42-
## Weak-napi
43-
44-
https://www.npmjs.com/package/weak-napi On certain rarer occasions, you run into
45-
the need to be notified when a JavaScript object is going to be garbage
46-
collected. This feature is exposed to V8's C++ API, but not to JavaScript.
47-
48-
That's where weak-napi comes in! This module exports the JS engine's GC tracking
49-
functionality to JavaScript. This allows you to create weak references, and
50-
optionally attach a callback function to any arbitrary JS object.

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,9 @@
9595
"build.native.debug": "node-gyp configure --debug && node-gyp configure --debug -- -f compile_commands_json && cross-env CMAKE_BUILD_TYPE=Debug node-gyp build --debug",
9696
"build": "run-s build.js build.native",
9797
"build.debug": "run-s build.js build.native.debug",
98-
"test.deps": "cd test && pnpm install && cd ..",
99-
"test": "run-s clean.temp test.deps build && mocha",
100-
"test.skip_gc_tests": "run-s clean.temp test.deps build.debug && cross-env SKIP_GC_TESTS=true mocha",
101-
"test.electron.main": "run-s clean.temp test.deps build && electron-mocha",
98+
"test": "run-s clean.temp build && mocha",
99+
"test.skip_gc_tests": "run-s clean.temp build.debug && cross-env SKIP_GC_TESTS=true mocha",
100+
"test.electron.main": "run-s clean.temp build && electron-mocha",
102101
"format": "prettier --write .",
103102
"test.electron.renderer": "run-s build && electron-mocha --renderer",
104103
"lint.clang-format": "clang-format -i -style=file ./src/*.cc ./src/*.h ./src/util/*.h",

test/package.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/pnpm-lock.yaml

Lines changed: 0 additions & 81 deletions
This file was deleted.

test/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
22
"extends": "../tsconfig.json",
3-
"include": ["**/*.ts"]
3+
"include": [
4+
"**/*.ts"
5+
]
46
}

test/unit/helpers.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,43 @@ export async function captureEventsUntil(
235235

236236
return events
237237
}
238+
239+
// REAL typings for global.gc per
240+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.cc
241+
interface GCFunction {
242+
(options: {
243+
execution?: "sync"
244+
flavor?: "regular" | "last-resort"
245+
type?: "major-snapshot" | "major" | "minor"
246+
filename?: string
247+
}): void
248+
(options: {
249+
execution?: "async"
250+
flavor?: "regular" | "last-resort"
251+
type?: "major-snapshot" | "major" | "minor"
252+
filename?: string
253+
}): Promise<void>
254+
(options: {
255+
execution?: "async" | "sync"
256+
flavor?: "regular" | "last-resort"
257+
type?: "major-snapshot" | "major" | "minor"
258+
filename?: string
259+
}): void | Promise<void>
260+
}
261+
262+
export function getGcOrSkipTest(test: Mocha.Context) {
263+
if (process.env.SKIP_GC_TESTS === "true") {
264+
test.skip()
265+
}
266+
267+
const gc = globalThis.gc as undefined | GCFunction
268+
if (typeof gc !== "function") {
269+
throw new Error(
270+
"Garbage collection is not exposed. It may be enabled by the node --expose-gc flag. To skip GC tests, set the environment variable `SKIP_GC_TESTS`",
271+
)
272+
}
273+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.h
274+
// per docs, we we're using use case 2 (Test that certain objects indeed are reclaimed)
275+
const asyncMajorGc = () => gc({type: "major", execution: "async"})
276+
return asyncMajorGc
277+
}

test/unit/socket-close-test.ts

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
/* eslint-disable @typescript-eslint/no-var-requires */
1+
/// <reference lib="ESNext" />
2+
23
import * as zmq from "../../src"
34

45
import {assert} from "chai"
5-
import {testProtos, uniqAddress} from "./helpers"
6+
import {testProtos, uniqAddress, getGcOrSkipTest} from "./helpers"
67
import {isFullError} from "../../src/errors"
78

89
for (const proto of testProtos("tcp", "ipc", "inproc")) {
@@ -107,75 +108,49 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
107108
})
108109

109110
it("should release reference to context", async function () {
110-
if (process.env.SKIP_GC_TESTS === "true") {
111-
this.skip()
112-
}
113-
if (global.gc === undefined) {
114-
console.warn("gc is not exposed by the runtime")
115-
this.skip()
116-
}
117-
111+
const gc = getGcOrSkipTest(this)
118112
this.slow(200)
119113

120-
const weak = require("weak-napi") as typeof import("weak-napi")
114+
let weakRef: undefined | WeakRef<any>
121115

122-
let released = false
123116
const task = async () => {
124-
let context: zmq.Context | undefined = new zmq.Context()
117+
const context: zmq.Context | undefined = new zmq.Context()
125118
const socket = new zmq.Dealer({context, linger: 0})
119+
weakRef = new WeakRef(context)
126120

127-
weak(context, () => {
128-
released = true
129-
})
130-
context = undefined
131-
132-
global.gc!()
133121
socket.connect(await uniqAddress(proto))
134122
await socket.send(Buffer.from("foo"))
135123
socket.close()
136124
}
137125

138126
await task()
139-
global.gc()
140-
await new Promise(resolve => {
141-
setTimeout(resolve, 5)
142-
})
143-
assert.equal(released, true)
127+
await gc()
128+
129+
assert.isDefined(weakRef)
130+
assert.isUndefined(weakRef!.deref())
144131
})
145132
})
146133

147134
describe("in gc finalizer", function () {
148135
it("should release reference to context", async function () {
149-
if (process.env.SKIP_GC_TESTS === "true") {
150-
this.skip()
151-
}
152-
if (global.gc === undefined) {
153-
console.warn("gc is not exposed by the runtime")
136+
const gc = getGcOrSkipTest(this)
137+
if (process.env.SKIP_GC_FINALIZER_TESTS) {
154138
this.skip()
155139
}
156140
this.slow(200)
157141

158-
const weak = require("weak-napi") as typeof import("weak-napi")
159-
160-
let released = false
142+
let weakRef: undefined | WeakRef<any>
161143
const task = async () => {
162-
let context: zmq.Context | undefined = new zmq.Context()
163-
144+
const context: zmq.Context | undefined = new zmq.Context()
164145
const _dealer = new zmq.Dealer({context, linger: 0})
165-
166-
weak(context, () => {
167-
released = true
168-
})
169-
context = undefined
170-
global.gc!()
146+
weakRef = new WeakRef(context)
171147
}
172148

173149
await task()
174-
global.gc()
175-
await new Promise(resolve => {
176-
setTimeout(resolve, 5)
177-
})
178-
assert.equal(released, true)
150+
await gc()
151+
152+
assert.isDefined(weakRef)
153+
assert.isUndefined(weakRef!.deref())
179154
})
180155
})
181156
})

0 commit comments

Comments
 (0)