Skip to content

Commit 0b6b2a4

Browse files
tannallegendecas
authored andcommitted
test: merge ongc and gcutil into gc.js
PR-URL: #54355 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 7616855 commit 0b6b2a4

32 files changed

+122
-101
lines changed

test/addons/cppgc-object/test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Flags: --expose-gc
44

55
const common = require('../../common');
6-
6+
const { gcUntil } = require('../../common/gc');
77
// Verify that addons can create GarbageCollected objects and
88
// have them traced properly.
99

@@ -35,7 +35,7 @@ setTimeout(async function() {
3535
for (let i = 0; i < count; ++i) {
3636
array[i] = new CppGCed();
3737
}
38-
await common.gcUntil(
38+
await gcUntil(
3939
'All old CppGCed are destroyed',
4040
() => states[kDestructCount] === count,
4141
);
@@ -44,7 +44,7 @@ setTimeout(async function() {
4444
array = null;
4545
globalThis.gc();
4646

47-
await common.gcUntil(
47+
await gcUntil(
4848
'All old CppGCed are destroyed',
4949
() => states[kDestructCount] === count * 2,
5050
);

test/async-hooks/test-async-local-storage-gcable.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
const common = require('../common');
88
const { AsyncLocalStorage } = require('async_hooks');
9-
const onGC = require('../common/ongc');
9+
const { onGC } = require('../common/gc');
1010

1111
let asyncLocalStorage = new AsyncLocalStorage();
1212

test/common/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ module exports a single `onGC()` function.
982982

983983
```js
984984
require('../common');
985-
const onGC = require('../common/ongc');
985+
const { onGC } = require('../common/gc');
986986

987987
onGC({}, { ongc() { console.log('collected'); } });
988988
```

test/common/gc.js

+67-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,72 @@
11
'use strict';
22

33
const wait = require('timers/promises').setTimeout;
4+
const assert = require('assert');
5+
const common = require('../common');
6+
const gcTrackerMap = new WeakMap();
7+
const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER';
48

5-
// TODO(joyeecheung): merge ongc.js and gcUntil from common/index.js
6-
// into this.
9+
/**
10+
* Installs a garbage collection listener for the specified object.
11+
* Uses async_hooks for GC tracking, which may affect test functionality.
12+
* A full setImmediate() invocation passes between a global.gc() call and the listener being invoked.
13+
* @param {object} obj - The target object to track for garbage collection.
14+
* @param {object} gcListener - The listener object containing the ongc callback.
15+
* @param {Function} gcListener.ongc - The function to call when the target object is garbage collected.
16+
*/
17+
function onGC(obj, gcListener) {
18+
const async_hooks = require('async_hooks');
19+
20+
const onGcAsyncHook = async_hooks.createHook({
21+
init: common.mustCallAtLeast(function(id, type) {
22+
if (this.trackedId === undefined) {
23+
assert.strictEqual(type, gcTrackerTag);
24+
this.trackedId = id;
25+
}
26+
}),
27+
destroy(id) {
28+
assert.notStrictEqual(this.trackedId, -1);
29+
if (id === this.trackedId) {
30+
this.gcListener.ongc();
31+
onGcAsyncHook.disable();
32+
}
33+
},
34+
}).enable();
35+
onGcAsyncHook.gcListener = gcListener;
36+
37+
gcTrackerMap.set(obj, new async_hooks.AsyncResource(gcTrackerTag));
38+
obj = null;
39+
}
40+
41+
/**
42+
* Repeatedly triggers garbage collection until a specified condition is met or a maximum number of attempts is reached.
43+
* @param {string|Function} [name] - Optional name, used in the rejection message if the condition is not met.
44+
* @param {Function} condition - A function that returns true when the desired condition is met.
45+
* @returns {Promise} A promise that resolves when the condition is met, or rejects after 10 failed attempts.
46+
*/
47+
function gcUntil(name, condition) {
48+
if (typeof name === 'function') {
49+
condition = name;
50+
name = undefined;
51+
}
52+
return new Promise((resolve, reject) => {
53+
let count = 0;
54+
function gcAndCheck() {
55+
setImmediate(() => {
56+
count++;
57+
global.gc();
58+
if (condition()) {
59+
resolve();
60+
} else if (count < 10) {
61+
gcAndCheck();
62+
} else {
63+
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
64+
}
65+
});
66+
}
67+
gcAndCheck();
68+
});
69+
}
770

871
// This function can be used to check if an object factor leaks or not,
972
// but it needs to be used with care:
@@ -124,4 +187,6 @@ module.exports = {
124187
checkIfCollectable,
125188
runAndBreathe,
126189
checkIfCollectableByCounting,
190+
onGC,
191+
gcUntil,
127192
};

test/common/index.js

-25
Original file line numberDiff line numberDiff line change
@@ -851,30 +851,6 @@ function skipIfDumbTerminal() {
851851
}
852852
}
853853

854-
function gcUntil(name, condition) {
855-
if (typeof name === 'function') {
856-
condition = name;
857-
name = undefined;
858-
}
859-
return new Promise((resolve, reject) => {
860-
let count = 0;
861-
function gcAndCheck() {
862-
setImmediate(() => {
863-
count++;
864-
global.gc();
865-
if (condition()) {
866-
resolve();
867-
} else if (count < 10) {
868-
gcAndCheck();
869-
} else {
870-
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
871-
}
872-
});
873-
}
874-
gcAndCheck();
875-
});
876-
}
877-
878854
function requireNoPackageJSONAbove(dir = __dirname) {
879855
let possiblePackage = path.join(dir, '..', 'package.json');
880856
let lastPackage = null;
@@ -985,7 +961,6 @@ const common = {
985961
expectsError,
986962
expectRequiredModule,
987963
expectWarning,
988-
gcUntil,
989964
getArrayBufferViews,
990965
getBufferSources,
991966
getCallSite,

test/common/ongc.js

-32
This file was deleted.

test/js-native-api/6_object_wrap/test-object-wrap-ref.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
'use strict';
44
const common = require('../../common');
55
const addon = require(`./build/${common.buildType}/6_object_wrap`);
6+
const { gcUntil } = require('../../common/gc');
67

78
(function scope() {
89
addon.objectWrapDanglingReference({});
910
})();
1011

11-
common.gcUntil('object-wrap-ref', () => {
12+
gcUntil('object-wrap-ref', () => {
1213
return addon.objectWrapDanglingReferenceTest();
1314
});

test/js-native-api/7_factory_wrap/test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../../common');
55
const assert = require('assert');
66
const test = require(`./build/${common.buildType}/7_factory_wrap`);
7+
const { gcUntil } = require('../../common/gc');
78

89
assert.strictEqual(test.finalizeCount, 0);
910
async function runGCTests() {
@@ -13,14 +14,14 @@ async function runGCTests() {
1314
assert.strictEqual(obj.plusOne(), 12);
1415
assert.strictEqual(obj.plusOne(), 13);
1516
})();
16-
await common.gcUntil('test 1', () => (test.finalizeCount === 1));
17+
await gcUntil('test 1', () => (test.finalizeCount === 1));
1718

1819
(() => {
1920
const obj2 = test.createObject(20);
2021
assert.strictEqual(obj2.plusOne(), 21);
2122
assert.strictEqual(obj2.plusOne(), 22);
2223
assert.strictEqual(obj2.plusOne(), 23);
2324
})();
24-
await common.gcUntil('test 2', () => (test.finalizeCount === 2));
25+
await gcUntil('test 2', () => (test.finalizeCount === 2));
2526
}
2627
runGCTests();

test/js-native-api/8_passing_wrapped/test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../../common');
55
const assert = require('assert');
66
const addon = require(`./build/${common.buildType}/8_passing_wrapped`);
7+
const { gcUntil } = require('../../common/gc');
78

89
async function runTest() {
910
let obj1 = addon.createObject(10);
@@ -14,7 +15,7 @@ async function runTest() {
1415
// Make sure the native destructor gets called.
1516
obj1 = null;
1617
obj2 = null;
17-
await common.gcUntil('8_passing_wrapped',
18-
() => (addon.finalizeCount() === 2));
18+
await gcUntil('8_passing_wrapped',
19+
() => (addon.finalizeCount() === 2));
1920
}
2021
runTest();

test/js-native-api/test_finalizer/test.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ const common = require('../../common');
55
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
66
const assert = require('assert');
77

8+
const { gcUntil } = require('../../common/gc');
9+
810
// The goal of this test is to show that we can run "pure" finalizers in the
9-
// current JS loop tick. Thus, we do not use common.gcUntil function works
11+
// current JS loop tick. Thus, we do not use gcUntil function works
1012
// asynchronously using micro tasks.
1113
// We use IIFE for the obj scope instead of {} to be compatible with
1214
// non-V8 JS engines that do not support scoped variables.
@@ -25,7 +27,7 @@ for (let i = 0; i < 10; ++i) {
2527
assert.strictEqual(test_finalizer.getFinalizerCallCount(), 1);
2628

2729
// The finalizer that access JS cannot run synchronously. They are run in the
28-
// next JS loop tick. Thus, we must use common.gcUntil.
30+
// next JS loop tick. Thus, we must use gcUntil.
2931
async function runAsyncTests() {
3032
// We do not use common.mustCall() because we want to see the finalizer
3133
// called in response to GC and not as a part of env destruction.
@@ -36,8 +38,8 @@ async function runAsyncTests() {
3638
const obj = {};
3739
test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; });
3840
})();
39-
await common.gcUntil('ensure JS finalizer called',
40-
() => (test_finalizer.getFinalizerCallCount() === 2));
41+
await gcUntil('ensure JS finalizer called',
42+
() => (test_finalizer.getFinalizerCallCount() === 2));
4143
assert(js_is_called);
4244
}
4345
runAsyncTests();

test/js-native-api/test_general/test.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../../common');
55
const test_general = require(`./build/${common.buildType}/test_general`);
66
const assert = require('assert');
7+
const { gcUntil } = require('../../common/gc');
78

89
const val1 = '1';
910
const val2 = 1;
@@ -79,17 +80,17 @@ async function runGCTests() {
7980
assert.strictEqual(test_general.derefItemWasCalled(), false);
8081

8182
(() => test_general.wrap({}))();
82-
await common.gcUntil('deref_item() was called upon garbage collecting a ' +
83+
await gcUntil('deref_item() was called upon garbage collecting a ' +
8384
'wrapped object.',
84-
() => test_general.derefItemWasCalled());
85+
() => test_general.derefItemWasCalled());
8586

8687
// Ensure that removing a wrap and garbage collecting does not fire the
8788
// finalize callback.
8889
let z = {};
8990
test_general.testFinalizeWrap(z);
9091
test_general.removeWrap(z);
9192
z = null;
92-
await common.gcUntil(
93+
await gcUntil(
9394
'finalize callback was not called upon garbage collection.',
9495
() => (!test_general.finalizeWasCalled()));
9596
}

test/js-native-api/test_general/testFinalizer.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../../common');
55
const test_general = require(`./build/${common.buildType}/test_general`);
66
const assert = require('assert');
7+
const { gcUntil } = require('../../common/gc');
78

89
let finalized = {};
910
const callback = common.mustCall(2);
@@ -30,7 +31,7 @@ async function testFinalizeAndWrap() {
3031
test_general.wrap(finalizeAndWrap);
3132
test_general.addFinalizerOnly(finalizeAndWrap, common.mustCall());
3233
finalizeAndWrap = null;
33-
await common.gcUntil('test finalize and wrap',
34-
() => test_general.derefItemWasCalled());
34+
await gcUntil('test finalize and wrap',
35+
() => test_general.derefItemWasCalled());
3536
}
3637
testFinalizeAndWrap();

test/js-native-api/test_reference/test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
22
// Flags: --expose-gc
33

4-
const { gcUntil, buildType } = require('../../common');
4+
const { buildType } = require('../../common');
5+
const { gcUntil } = require('../../common/gc');
56
const assert = require('assert');
67

78
const test_reference = require(`./build/${buildType}/test_reference`);

test/node-api/test_reference_by_node_api_version/test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
// and symbol types, while in newer versions they can be created for
88
// any value type.
99
//
10-
const { gcUntil, buildType } = require('../../common');
10+
const { buildType } = require('../../common');
11+
const { gcUntil } = require('../../common/gc');
1112
const assert = require('assert');
1213
const addon_v8 = require(`./build/${buildType}/test_reference_obj_only`);
1314
const addon_new = require(`./build/${buildType}/test_reference_all_types`);

test/parallel/test-common-gc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22
// Flags: --expose-gc
33
const common = require('../common');
4-
const onGC = require('../common/ongc');
4+
const { onGC } = require('../common/gc');
55

66
{
77
onGC({}, { ongc: common.mustCall() });

test/parallel/test-domain-async-id-map-leak.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Flags: --expose-gc
22
'use strict';
33
const common = require('../common');
4-
const onGC = require('../common/ongc');
4+
const { onGC } = require('../common/gc');
5+
const { gcUntil } = require('../common/gc');
56
const assert = require('assert');
67
const async_hooks = require('async_hooks');
78
const domain = require('domain');
@@ -40,7 +41,7 @@ d.run(() => {
4041
d = null;
4142

4243
async function main() {
43-
await common.gcUntil(
44+
await gcUntil(
4445
'All objects garbage collected',
4546
() => resourceGCed && domainGCed && emitterGCed);
4647
}

test/parallel/test-gc-http-client-connaborted.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// but aborting every connection that comes in.
55

66
const common = require('../common');
7-
const onGC = require('../common/ongc');
7+
const { onGC } = require('../common/gc');
88
const http = require('http');
99
const os = require('os');
1010

0 commit comments

Comments
 (0)