Skip to content

Commit 3d89331

Browse files
joyeecheungaduh95
authored andcommitted
test_runner: use module.registerHooks in module mocks
Migrate away from module.register(). This no longer needs to deal with the worker synchronization. PR-URL: #60326 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]>
1 parent 063fbd8 commit 3d89331

File tree

5 files changed

+54
-190
lines changed

5 files changed

+54
-190
lines changed

‎lib/internal/test_runner/coverage.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const{
3737
},
3838
}=require('internal/errors');
3939
const{ matchGlobPattern }=require('internal/fs/glob');
40-
const{ kMockSearchParam }=require('internal/test_runner/mock/mock');
40+
const{constants: {kMockSearchParam }}=require('internal/test_runner/mock/loader');
4141

4242
constkCoverageFileRegex=/^coverage-(\d+)-(\d{13})-(\d+)\.json$/;
4343
constkIgnoreRegex=/\/\*node:coverageignorenext(?<count>\d+)?\*\//;

‎lib/internal/test_runner/mock/loader.js‎

Lines changed: 20 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,24 @@
11
'use strict';
22
const{
3-
AtomicsNotify,
4-
AtomicsStore,
53
JSONStringify,
64
SafeMap,
75
}=primordials;
8-
const{
9-
kBadExportsMessage,
10-
kMockSearchParam,
11-
kMockSuccess,
12-
kMockExists,
13-
kMockUnknownMessage,
14-
}=require('internal/test_runner/mock/mock');
6+
7+
constkMockSearchParam='node-test-mock';
8+
constkBadExportsMessage='Cannot create mock because named exports '+
9+
'cannot be applied to the provided default export.';
10+
1511
const{URL, URLParse }=require('internal/url');
1612
letdebug=require('internal/util/debuglog').debuglog('test_runner',(fn)=>{
1713
debug=fn;
1814
});
1915

20-
// TODO(cjihrig): The mocks need to be thread aware because the exports are
21-
// evaluated on the thread that creates the mock. Before marking this API as
22-
// stable, one of the following issues needs to be implemented:
23-
// https://github.com/nodejs/node/issues/49472
24-
// or https://github.com/nodejs/node/issues/52219
25-
2616
constmocks=newSafeMap();
2717

28-
asyncfunctioninitialize(data){
29-
data?.port.on('message',({ type, payload })=>{
30-
debug('mock loader received message type "%s" with payload %o',type,payload);
31-
32-
if(type==='node:test:register'){
33-
const{ baseURL }=payload;
34-
constmock=mocks.get(baseURL);
35-
36-
if(mock?.active){
37-
debug('already mocking "%s"',baseURL);
38-
sendAck(payload.ack,kMockExists);
39-
return;
40-
}
41-
42-
constlocalVersion=mock?.localVersion??0;
43-
44-
debug('new mock version %d for "%s"',localVersion,baseURL);
45-
mocks.set(baseURL,{
46-
__proto__: null,
47-
active: true,
48-
cache: payload.cache,
49-
exportNames: payload.exportNames,
50-
format: payload.format,
51-
hasDefaultExport: payload.hasDefaultExport,
52-
localVersion,
53-
url: baseURL,
54-
});
55-
sendAck(payload.ack);
56-
}elseif(type==='node:test:unregister'){
57-
constmock=mocks.get(payload.baseURL);
58-
59-
if(mock!==undefined){
60-
mock.active=false;
61-
mock.localVersion++;
62-
}
63-
64-
sendAck(payload.ack);
65-
}else{
66-
sendAck(payload.ack,kMockUnknownMessage);
67-
}
68-
});
69-
}
70-
71-
asyncfunctionresolve(specifier,context,nextResolve){
18+
functionresolve(specifier,context,nextResolve){
7219
debug('resolve hook entry, specifier = "%s", context = %o',specifier,context);
7320

74-
constnextResolveResult=awaitnextResolve(specifier,context);
21+
constnextResolveResult=nextResolve(specifier,context);
7522
constmockSpecifier=nextResolveResult.url;
7623

7724
constmock=mocks.get(mockSpecifier);
@@ -95,7 +42,7 @@ async function resolve(specifier, context, nextResolve){
9542
return{__proto__: null,url: href,format: nextResolveResult.format};
9643
}
9744

98-
asyncfunctionload(url,context,nextLoad){
45+
functionload(url,context,nextLoad){
9946
debug('load hook entry, url = "%s", context = %o',url,context);
10047
constparsedURL=URLParse(url);
10148
if(parsedURL){
@@ -105,7 +52,7 @@ async function load(url, context, nextLoad){
10552
constbaseURL=parsedURL ? parsedURL.href : url;
10653
constmock=mocks.get(baseURL);
10754

108-
constoriginal=awaitnextLoad(url,context);
55+
constoriginal=nextLoad(url,context);
10956
debug('load hook, mock = %o',mock);
11057
if(mock?.active!==true){
11158
returnoriginal;
@@ -130,14 +77,14 @@ async function load(url, context, nextLoad){
13077
__proto__: null,
13178
format,
13279
shortCircuit: true,
133-
source: awaitcreateSourceFromMock(mock,format),
80+
source: createSourceFromMock(mock,format),
13481
};
13582

13683
debug('load hook finished, result = %o',result);
13784
returnresult;
13885
}
13986

140-
asyncfunctioncreateSourceFromMock(mock,format){
87+
functioncreateSourceFromMock(mock,format){
14188
// Create mock implementation from provided exports.
14289
const{ exportNames, hasDefaultExport, url }=mock;
14390
constuseESM=format==='module'||format==='module-typescript';
@@ -196,9 +143,12 @@ if (module.exports === null || typeof module.exports !== 'object'){
196143
returnsource;
197144
}
198145

199-
functionsendAck(buf,status=kMockSuccess){
200-
AtomicsStore(buf,0,status);
201-
AtomicsNotify(buf,0);
202-
}
203-
204-
module.exports={ initialize, load, resolve };
146+
module.exports={
147+
hooks: {__proto__: null, load, resolve },
148+
mocks,
149+
constants: {
150+
__proto__: null,
151+
kBadExportsMessage,
152+
kMockSearchParam,
153+
},
154+
};

‎lib/internal/test_runner/mock/mock.js‎

Lines changed: 33 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
const{
33
ArrayPrototypePush,
44
ArrayPrototypeSlice,
5-
AtomicsStore,
6-
AtomicsWait,
75
Error,
86
FunctionPrototypeBind,
97
FunctionPrototypeCall,
10-
Int32Array,
118
ObjectDefineProperty,
129
ObjectGetOwnPropertyDescriptor,
1310
ObjectGetPrototypeOf,
@@ -19,9 +16,6 @@ const{
1916
SafeMap,
2017
StringPrototypeSlice,
2118
StringPrototypeStartsWith,
22-
globalThis: {
23-
SharedArrayBuffer,
24-
},
2519
}=primordials;
2620
const{
2721
codes: {
@@ -54,19 +48,10 @@ const{
5448
validateOneOf,
5549
}=require('internal/validators');
5650
const{ MockTimers }=require('internal/test_runner/mock/mock_timers');
57-
const{ strictEqual, notStrictEqual }=require('assert');
5851
const{ Module }=require('internal/modules/cjs/loader');
59-
const{ MessageChannel }=require('worker_threads');
6052
const{ _load, _nodeModulePaths, _resolveFilename, isBuiltin }=Module;
6153
functionkDefaultFunction(){}
6254
constenableModuleMocking=getOptionValue('--experimental-test-module-mocks');
63-
constkMockSearchParam='node-test-mock';
64-
constkMockSuccess=1;
65-
constkMockExists=2;
66-
constkMockUnknownMessage=3;
67-
constkWaitTimeout=5_000;
68-
constkBadExportsMessage='Cannot create mock because named exports '+
69-
'cannot be applied to the provided default export.';
7055
constkSupportedFormats=[
7156
'builtin',
7257
'commonjs-typescript',
@@ -76,6 +61,11 @@ const kSupportedFormats = [
7661
'module',
7762
];
7863
letsharedModuleState;
64+
const{
65+
hooks: mockHooks,
66+
mocks,
67+
constants: { kBadExportsMessage, kMockSearchParam },
68+
}=require('internal/test_runner/mock/loader');
7969

8070
classMockFunctionContext{
8171
#calls;
@@ -201,8 +191,8 @@ class MockModuleContext{
201191
hasDefaultExport,
202192
namedExports,
203193
sharedState,
194+
specifier,
204195
}){
205-
constack=newInt32Array(newSharedArrayBuffer(4));
206196
constconfig={
207197
__proto__: null,
208198
cache,
@@ -218,28 +208,36 @@ class MockModuleContext{
218208
this.#sharedState =sharedState;
219209
this.#restore ={
220210
__proto__: null,
221-
ack,
222211
baseURL,
223212
cached: fullPathinModule._cache,
224213
format,
225214
fullPath,
226215
value: Module._cache[fullPath],
227216
};
228217

229-
sharedState.loaderPort.postMessage({
230-
__proto__: null,
231-
type: 'node:test:register',
232-
payload: {
218+
constmock=mocks.get(baseURL);
219+
220+
if(mock?.active){
221+
debug('already mocking "%s"',baseURL);
222+
thrownewERR_INVALID_STATE(
223+
`Cannot mock '${specifier}'. The module is already mocked.`,
224+
);
225+
}else{
226+
constlocalVersion=mock?.localVersion??0;
227+
228+
debug('new mock version %d for "%s"',localVersion,baseURL);
229+
mocks.set(baseURL,{
233230
__proto__: null,
234-
ack,
235-
baseURL,
231+
url: baseURL,
236232
cache,
237233
exportNames: ObjectKeys(namedExports),
238234
hasDefaultExport,
239235
format,
240-
},
241-
});
242-
waitForAck(ack);
236+
localVersion,
237+
active: true,
238+
});
239+
}
240+
243241
deleteModule._cache[fullPath];
244242
sharedState.mockExports.set(baseURL,{
245243
__proto__: null,
@@ -261,17 +259,12 @@ class MockModuleContext{
261259
Module._cache[this.#restore.fullPath]=this.#restore.value;
262260
}
263261

264-
AtomicsStore(this.#restore.ack,0,0);
265-
this.#sharedState.loaderPort.postMessage({
266-
__proto__: null,
267-
type: 'node:test:unregister',
268-
payload: {
269-
__proto__: null,
270-
ack: this.#restore.ack,
271-
baseURL: this.#restore.baseURL,
272-
},
273-
});
274-
waitForAck(this.#restore.ack);
262+
constmock=mocks.get(this.#restore.baseURL);
263+
264+
if(mock!==undefined){
265+
mock.active=false;
266+
mock.localVersion++;
267+
}
275268

276269
this.#sharedState.mockMap.delete(this.#restore.baseURL);
277270
this.#sharedState.mockMap.delete(this.#restore.fullPath);
@@ -654,7 +647,7 @@ class MockTracker{
654647
consthasFileProtocol=StringPrototypeStartsWith(filename,'file://');
655648
constcaller=hasFileProtocol ? filename : pathToFileURL(filename).href;
656649
const{ format, url }=sharedState.moduleLoader.resolveSync(
657-
mockSpecifier,caller,null,
650+
mockSpecifier,caller,kEmptyObject,
658651
);
659652
debug('module mock, url = "%s", format = "%s", caller = "%s"',url,format,caller);
660653
if(format){// Format is not yet known for ambiguous files when detection is enabled.
@@ -828,20 +821,13 @@ function setupSharedModuleState(){
828821
if(sharedModuleState===undefined){
829822
const{ mock }=require('test');
830823
constmockExports=newSafeMap();
831-
const{port1, port2 }=newMessageChannel();
824+
const{registerHooks }=require('internal/modules/customization_hooks');
832825
constmoduleLoader=esmLoader.getOrInitializeCascadedLoader();
833826

834-
moduleLoader.register(
835-
'internal/test_runner/mock/loader',
836-
'node:',
837-
{__proto__: null,port: port2},
838-
[port2],
839-
true,
840-
);
827+
registerHooks(mockHooks);
841828

842829
sharedModuleState={
843830
__proto__: null,
844-
loaderPort: port1,
845831
mockExports,
846832
mockMap: newSafeMap(),
847833
moduleLoader,
@@ -941,13 +927,6 @@ function findMethodOnPrototypeChain(instance, methodName){
941927
returndescriptor;
942928
}
943929

944-
functionwaitForAck(buf){
945-
constresult=AtomicsWait(buf,0,0,kWaitTimeout);
946-
947-
notStrictEqual(result,'timed-out','test mocking synchronization failed');
948-
strictEqual(buf[0],kMockSuccess);
949-
}
950-
951930
functionensureNodeScheme(specifier){
952931
if(!StringPrototypeStartsWith(specifier,'node:')){
953932
return`node:${specifier}`;
@@ -962,10 +941,5 @@ if (!enableModuleMocking){
962941

963942
module.exports={
964943
ensureNodeScheme,
965-
kBadExportsMessage,
966-
kMockSearchParam,
967-
kMockSuccess,
968-
kMockExists,
969-
kMockUnknownMessage,
970944
MockTracker,
971945
};

‎test/parallel/test-permission-dc-worker-threads.js‎

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

0 commit comments

Comments
(0)