Skip to content

Commit a1715e1

Browse files
committed
wip - improve gc tests more
1 parent 1f81c4a commit a1715e1

File tree

9 files changed

+84
-144
lines changed

9 files changed

+84
-144
lines changed

.github/workflows/CI.yml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,15 @@ jobs:
7979
ARCH: ${{ matrix.ARCH }}
8080
steps:
8181
- uses: actions/checkout@v4
82-
- uses: actions/checkout@v4
83-
84-
# - name: Cache
85-
# uses: actions/cache@v4
86-
# with:
87-
# path: |
88-
# ./node_modules/
89-
# ./build/
90-
# key: "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-${{ hashFiles('./package.json') }}"
91-
# restore-keys: |
92-
# "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-"
82+
- name: Cache
83+
uses: actions/cache@v4
84+
with:
85+
path: |
86+
./node_modules/
87+
./build/
88+
key: "cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-${{ hashFiles('./package.json') }}"
89+
restore-keys: |
90+
"cache-OS:${{ matrix.os }}-arch:${{ matrix.node_arch }}-ZMQ_DRAFT:${{ matrix.zmq_draft }}-Node:${{ matrix.node_version }}-"
9391
9492
- name: Setup Cpp
9593
if: ${{ !matrix.docker }}
@@ -108,7 +106,6 @@ jobs:
108106
- name: Install Node
109107
if: ${{ !matrix.docker }}
110108
uses: actions/setup-node@v4
111-
uses: actions/setup-node@v4
112109
with:
113110
node-version: ${{ matrix.node_version }}
114111
architecture: ${{ matrix.node_arch }}
@@ -155,7 +152,6 @@ jobs:
155152
- name: Test (Debug)
156153
if: ${{ !matrix.docker }}
157154
uses: nick-fields/retry@v3
158-
uses: nick-fields/retry@v3
159155
with:
160156
timeout_minutes: 5
161157
max_attempts: 1
@@ -180,7 +176,6 @@ jobs:
180176
- name: Tests + GC Tests (Release)
181177
if: ${{ !matrix.docker }}
182178
uses: nick-fields/retry@v3
183-
uses: nick-fields/retry@v3
184179
with:
185180
timeout_minutes: 5
186181
max_attempts: 1

test/.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@typescript-eslint/no-use-before-define": "off",
1010
"@typescript-eslint/no-explicit-any": "off",
1111
"@typescript-eslint/no-unused-vars": "off",
12+
"@typescript-eslint/strict-boolean-expressions": "off",
1213
"@typescript-eslint/explicit-function-return-type": "off",
1314
"no-await-in-loop": "off",
1415
"require-await": "off",

test/package.json

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

test/pnpm-lock.yaml

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

test/tsconfig.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
{
22
"extends": "../tsconfig.json",
3-
"include": ["**/*.ts"]
3+
"include": [
4+
"**/*.ts"
5+
],
6+
"ts-node": {
7+
"files": true
8+
},
9+
"compilerOptions": {
10+
"types": [
11+
"mocha"
12+
],
13+
"lib": [
14+
"ESNext"
15+
]
16+
}
417
}

test/unit/helpers.ts

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

163163
return events
164164
}
165+
166+
// REAL typings for global.gc per
167+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.cc
168+
interface GCFunction {
169+
(options: {
170+
execution?: "sync"
171+
flavor?: "regular" | "last-resort"
172+
type?: "major-snapshot" | "major" | "minor"
173+
filename?: string
174+
}): void
175+
(options: {
176+
execution?: "async"
177+
flavor?: "regular" | "last-resort"
178+
type?: "major-snapshot" | "major" | "minor"
179+
filename?: string
180+
}): Promise<void>
181+
(options: {
182+
execution?: "async" | "sync"
183+
flavor?: "regular" | "last-resort"
184+
type?: "major-snapshot" | "major" | "minor"
185+
filename?: string
186+
}): void | Promise<void>
187+
}
188+
189+
export function getGcOrSkipTest(test: Mocha.Context) {
190+
if (process.env.SKIP_GC_TESTS) {
191+
test.skip()
192+
}
193+
194+
const gc = globalThis.gc as undefined | GCFunction
195+
if (typeof gc !== "function") {
196+
throw new Error(
197+
"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`",
198+
)
199+
}
200+
// https://github.com/nodejs/node/blob/v20.0.0/deps/v8/src/extensions/gc-extension.h
201+
// per docs, we we're using use case 2 (Test that certain objects indeed are reclaimed)
202+
const asyncMajorGc = () => gc({type: "major", execution: "async"})
203+
return asyncMajorGc
204+
}

test/unit/socket-close-test.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as zmq from "../../src"
33

44
import {assert} from "chai"
5-
import {testProtos, uniqAddress} from "./helpers"
5+
import {testProtos, uniqAddress, getGcOrSkipTest} from "./helpers"
66
import {isFullError} from "../../src/errors"
77

88
for (const proto of testProtos("tcp", "ipc", "inproc")) {
@@ -107,9 +107,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
107107
})
108108

109109
it("should release reference to context", async function () {
110-
if (process.env.SKIP_GC_TESTS) {
111-
this.skip()
112-
}
110+
const gc = getGcOrSkipTest(this)
113111
this.slow(200)
114112

115113
let weakRef: undefined | WeakRef<any>
@@ -125,10 +123,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
125123
}
126124

127125
await task()
128-
global.gc?.()
129-
await new Promise(resolve => {
130-
setTimeout(resolve, 5)
131-
})
126+
await gc()
132127

133128
assert.isDefined(weakRef)
134129
assert.isUndefined(weakRef!.deref())
@@ -137,9 +132,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
137132

138133
describe("in gc finalizer", function () {
139134
it("should release reference to context", async function () {
140-
if (process.env.SKIP_GC_TESTS) {
141-
this.skip()
142-
}
135+
const gc = getGcOrSkipTest(this)
143136
if (process.env.SKIP_GC_FINALIZER_TESTS) {
144137
this.skip()
145138
}
@@ -152,14 +145,12 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
152145
new zmq.Dealer({context, linger: 0})
153146
weakRef = new WeakRef(context)
154147
context = undefined
155-
global.gc?.()
148+
await gc()
156149
}
157150

158151
await task()
159-
global.gc?.()
160-
await new Promise(resolve => {
161-
setTimeout(resolve, 5)
162-
})
152+
await gc()
153+
163154
assert.isDefined(weakRef)
164155
assert.isUndefined(weakRef!.deref())
165156
})

test/unit/socket-send-receive-test.ts

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as zmq from "../../src"
33

44
import {assert} from "chai"
5-
import {testProtos, uniqAddress} from "./helpers"
5+
import {testProtos, uniqAddress, getGcOrSkipTest} from "./helpers"
66
import {isFullError} from "../../src/errors"
77

88
for (const proto of testProtos("tcp", "ipc", "inproc")) {
@@ -91,9 +91,7 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
9191
})
9292

9393
it("should copy and release small buffers", async function () {
94-
if (process.env.SKIP_GC_TESTS) {
95-
this.skip()
96-
}
94+
const gc = getGcOrSkipTest(this)
9795
let weakRef: undefined | WeakRef<any>
9896
sockA.connect(uniqAddress(proto))
9997
const send = async (size: number) => {
@@ -103,18 +101,13 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
103101
}
104102

105103
await send(16)
106-
global.gc?.()
107-
await new Promise(resolve => {
108-
setTimeout(resolve, 5)
109-
})
104+
await gc()
110105
assert.isDefined(weakRef)
111106
assert.isUndefined(weakRef!.deref())
112107
})
113108

114109
it("should retain large buffers", async function () {
115-
if (process.env.SKIP_GC_TESTS) {
116-
this.skip()
117-
}
110+
const gc = getGcOrSkipTest(this)
118111
let weakRef: undefined | WeakRef<any>
119112

120113
sockA.connect(uniqAddress(proto))
@@ -125,12 +118,9 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
125118
}
126119

127120
await send(1025)
128-
global.gc?.()
129-
await new Promise(resolve => {
130-
setTimeout(resolve, 5)
131-
})
121+
await gc()
132122
assert.isDefined(weakRef)
133-
assert.isUndefined(weakRef!.deref())
123+
assert.isDefined(weakRef.deref())
134124
})
135125
})
136126

@@ -338,13 +328,11 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
338328
})
339329

340330
it("should release buffers", async function () {
341-
if (process.env.SKIP_GC_TESTS) {
342-
this.skip()
343-
}
331+
const gc = await getGcOrSkipTest(this)
332+
344333
const weakRefs: WeakRef<any>[] = []
345334

346335
const n = 10
347-
let released = 0
348336

349337
const send = async (size: number) => {
350338
for (let i = 0; i < n; i++) {
@@ -365,7 +353,8 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
365353

366354
/* Repeated GC to allow inproc messages from being collected. */
367355
for (let i = 0; i < 5; i++) {
368-
global.gc?.()
356+
await gc()
357+
369358
await new Promise(resolve => {
370359
setTimeout(resolve, 2)
371360
})
@@ -377,14 +366,11 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
377366
})
378367

379368
it("should release buffers after echo", async function () {
380-
if (process.env.SKIP_GC_TESTS) {
381-
this.skip()
382-
}
369+
const gc = getGcOrSkipTest(this)
383370

384371
const weakRefs: WeakRef<any>[] = []
385372

386373
const n = 10
387-
let released = 0
388374

389375
const echo = async () => {
390376
for (let i = 0; i < n; i++) {
@@ -412,10 +398,10 @@ for (const proto of testProtos("tcp", "ipc", "inproc")) {
412398

413399
/* Repeated GC to allow inproc messages from being collected. */
414400
for (let i = 0; i < 5; i++) {
415-
global.gc?.()
416401
await new Promise(resolve => {
417402
setTimeout(resolve, 2)
418403
})
404+
await gc()
419405
}
420406

421407
assert.lengthOf(weakRefs, n * 3)

tsconfig.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"module": "commonjs",
77
"types": [
88
"node",
9-
"mocha"
109
],
1110
"strictPropertyInitialization": false, // TODO
1211
"strict": true,
@@ -21,7 +20,7 @@
2120
"sourceMap": true,
2221
"esModuleInterop": true,
2322
"lib": [
24-
"ES2020",
23+
"ES2022",
2524
"dom"
2625
]
2726
}

0 commit comments

Comments
 (0)