Skip to content

Commit 10bea1c

Browse files
tannaltargos
authored andcommittedOct 2, 2024
test: merge ongc and gcutil into gc.js
PR-URL: #54355 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 2f5b98e commit 10bea1c

31 files changed

+119
-99
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
@@ -837,30 +837,6 @@ function skipIfDumbTerminal() {
837837
}
838838
}
839839

840-
function gcUntil(name, condition) {
841-
if (typeof name === 'function') {
842-
condition = name;
843-
name = undefined;
844-
}
845-
return new Promise((resolve, reject) => {
846-
let count = 0;
847-
function gcAndCheck() {
848-
setImmediate(() => {
849-
count++;
850-
global.gc();
851-
if (condition()) {
852-
resolve();
853-
} else if (count < 10) {
854-
gcAndCheck();
855-
} else {
856-
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
857-
}
858-
});
859-
}
860-
gcAndCheck();
861-
});
862-
}
863-
864840
function requireNoPackageJSONAbove(dir = __dirname) {
865841
let possiblePackage = path.join(dir, '..', 'package.json');
866842
let lastPackage = null;
@@ -966,7 +942,6 @@ const common = {
966942
expectsError,
967943
expectRequiredModule,
968944
expectWarning,
969-
gcUntil,
970945
getArrayBufferViews,
971946
getBufferSources,
972947
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

‎test/parallel/test-gc-net-timeout.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// but using a net server/client instead
55

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

‎test/parallel/test-gc-tls-external-memory.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ if (!common.hasCrypto)
99
common.skip('missing crypto');
1010

1111
const { duplexPair } = require('stream');
12-
const onGC = require('../common/ongc');
12+
const { onGC } = require('../common/gc');
1313
const assert = require('assert');
1414
const tls = require('tls');
1515

‎test/parallel/test-http-server-connections-checking-leak.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Check that creating a server without listening does not leak resources.
66

77
require('../common');
8-
const onGC = require('../common/ongc');
8+
const { onGC } = require('../common/gc');
99
const Countdown = require('../common/countdown');
1010

1111
const http = require('http');

‎test/parallel/test-http-server-keepalive-req-gc.js

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

‎test/parallel/test-https-server-connections-checking-leak.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if (!common.hasCrypto) {
1010
common.skip('missing crypto');
1111
}
1212

13-
const onGC = require('../common/ongc');
13+
const { onGC } = require('../common/gc');
1414
const Countdown = require('../common/countdown');
1515

1616
const https = require('https');

‎test/parallel/test-internal-util-weakreference.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Flags: --expose-internals --expose-gc
22
'use strict';
3-
const common = require('../common');
3+
require('../common');
4+
const { gcUntil } = require('../common/gc');
45
const assert = require('assert');
56
const { WeakReference } = require('internal/util');
67

@@ -10,7 +11,7 @@ assert.strictEqual(ref.get(), obj);
1011

1112
async function main() {
1213
obj = null;
13-
await common.gcUntil(
14+
await gcUntil(
1415
'Reference is garbage collected',
1516
() => ref.get() === undefined);
1617
}

‎test/parallel/test-net-connect-memleak.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
// Flags: --expose-gc
2424

2525
const common = require('../common');
26-
const onGC = require('../common/ongc');
26+
const { onGC } = require('../common/gc');
2727
const assert = require('assert');
2828
const net = require('net');
2929

‎test/parallel/test-primitive-timer-leak.js

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

66
// See https://github.com/nodejs/node/issues/53335
77
const poller = setInterval(() => {

‎test/parallel/test-tls-connect-memleak.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const common = require('../common');
2626
if (!common.hasCrypto)
2727
common.skip('missing crypto');
2828

29-
const onGC = require('../common/ongc');
29+
const { onGC } = require('../common/gc');
3030
const assert = require('assert');
3131
const tls = require('tls');
3232
const fixtures = require('../common/fixtures');

‎test/parallel/test-v8-serialize-leak.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Flags: --expose-gc
33

44
const common = require('../common');
5+
const { gcUntil } = require('../common/gc');
56

67
// On IBMi, the rss memory always returns zero
78
if (common.isIBMi)
@@ -16,7 +17,7 @@ for (let i = 0; i < 1000000; i++) {
1617
}
1718

1819
async function main() {
19-
await common.gcUntil('RSS should go down', () => {
20+
await gcUntil('RSS should go down', () => {
2021
const after = process.memoryUsage.rss();
2122
if (common.isASan) {
2223
console.log(`ASan: before=${before} after=${after}`);

‎test/parallel/test-zlib-invalid-input-memory.js

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

‎test/sequential/test-gc-http-client-onerror.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// but with an on('error') handler that does nothing.
55

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

99
const cpus = require('os').availableParallelism();
1010

‎test/sequential/test-gc-http-client-timeout.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Like test-gc-http-client.js, but with a timeout set.
44

55
const common = require('../common');
6-
const onGC = require('../common/ongc');
6+
const { onGC } = require('../common/gc');
77
const http = require('http');
88

99
function serverHandler(req, res) {

‎test/sequential/test-gc-http-client.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// just a simple http server and client.
44

55
const common = require('../common');
6-
const onGC = require('../common/ongc');
6+
const { onGC } = require('../common/gc');
77

88
const cpus = require('os').availableParallelism();
99

0 commit comments

Comments
 (0)
Please sign in to comment.