| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 063fbd8 commit 3d89331
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -37,7 +37,7 @@ const { | |||
| 37 | 37 | }, | |
| 38 | 38 | } = require('internal/errors'); | |
| 39 | 39 | 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'); | ||
| 41 | 41 | ||
| 42 | 42 | const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/; | |
| 43 | 43 | const kIgnoreRegex = /\/\* node:coverage ignore next (?<count>\d+ )?\*\//; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,77 +1,24 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | const { | |
| 3 | - AtomicsNotify, | ||
| 4 | - AtomicsStore, | ||
| 5 | 3 | JSONStringify, | |
| 6 | 4 | SafeMap, | |
| 7 | 5 | } = primordials; | |
| 8 | - const { | ||
| 9 | - kBadExportsMessage, | ||
| 10 | - kMockSearchParam, | ||
| 11 | - kMockSuccess, | ||
| 12 | - kMockExists, | ||
| 13 | - kMockUnknownMessage, | ||
| 14 | - } = require('internal/test_runner/mock/mock'); | ||
| 6 | + | ||
| 7 | + const kMockSearchParam = 'node-test-mock'; | ||
| 8 | + const kBadExportsMessage = 'Cannot create mock because named exports ' + | ||
| 9 | + 'cannot be applied to the provided default export.'; | ||
| 10 | + | ||
| 15 | 11 | const { URL, URLParse } = require('internal/url'); | |
| 16 | 12 | let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => { | |
| 17 | 13 | debug = fn; | |
| 18 | 14 | }); | |
| 19 | 15 | ||
| 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 | - | ||
| 26 | 16 | const mocks = new SafeMap(); | |
| 27 | 17 | ||
| 28 | - async function initialize(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 | - const mock = mocks.get(baseURL); | ||
| 35 | - | ||
| 36 | - if (mock?.active) { | ||
| 37 | - debug('already mocking "%s"', baseURL); | ||
| 38 | - sendAck(payload.ack, kMockExists); | ||
| 39 | - return; | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | - const localVersion = 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 | - } else if (type === 'node:test:unregister') { | ||
| 57 | - const mock = 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 | - async function resolve(specifier, context, nextResolve) { | ||
| 18 | + function resolve(specifier, context, nextResolve) { | ||
| 72 | 19 | debug('resolve hook entry, specifier = "%s", context = %o', specifier, context); | |
| 73 | 20 | ||
| 74 | - const nextResolveResult = await nextResolve(specifier, context); | ||
| 21 | + const nextResolveResult = nextResolve(specifier, context); | ||
| 75 | 22 | const mockSpecifier = nextResolveResult.url; | |
| 76 | 23 | ||
| 77 | 24 | const mock = mocks.get(mockSpecifier); | |
@@ -95,7 +42,7 @@ async function resolve(specifier, context, nextResolve) { | |||
| 95 | 42 | return { __proto__: null, url: href, format: nextResolveResult.format }; | |
| 96 | 43 | } | |
| 97 | 44 | ||
| 98 | - async function load(url, context, nextLoad) { | ||
| 45 | + function load(url, context, nextLoad) { | ||
| 99 | 46 | debug('load hook entry, url = "%s", context = %o', url, context); | |
| 100 | 47 | const parsedURL = URLParse(url); | |
| 101 | 48 | if (parsedURL) { | |
@@ -105,7 +52,7 @@ async function load(url, context, nextLoad) { | |||
| 105 | 52 | const baseURL = parsedURL ? parsedURL.href : url; | |
| 106 | 53 | const mock = mocks.get(baseURL); | |
| 107 | 54 | ||
| 108 | - const original = await nextLoad(url, context); | ||
| 55 | + const original = nextLoad(url, context); | ||
| 109 | 56 | debug('load hook, mock = %o', mock); | |
| 110 | 57 | if (mock?.active !== true) { | |
| 111 | 58 | return original; | |
@@ -130,14 +77,14 @@ async function load(url, context, nextLoad) { | |||
| 130 | 77 | __proto__: null, | |
| 131 | 78 | format, | |
| 132 | 79 | shortCircuit: true, | |
| 133 | - source: await createSourceFromMock(mock, format), | ||
| 80 | + source: createSourceFromMock(mock, format), | ||
| 134 | 81 | }; | |
| 135 | 82 | ||
| 136 | 83 | debug('load hook finished, result = %o', result); | |
| 137 | 84 | return result; | |
| 138 | 85 | } | |
| 139 | 86 | ||
| 140 | - async function createSourceFromMock(mock, format) { | ||
| 87 | + function createSourceFromMock(mock, format) { | ||
| 141 | 88 | // Create mock implementation from provided exports. | |
| 142 | 89 | const { exportNames, hasDefaultExport, url } = mock; | |
| 143 | 90 | const useESM = format === 'module' || format === 'module-typescript'; | |
@@ -196,9 +143,12 @@ if (module.exports === null || typeof module.exports !== 'object') { | |||
| 196 | 143 | return source; | |
| 197 | 144 | } | |
| 198 | 145 | ||
| 199 | - function sendAck(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 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,12 +2,9 @@ | |||
| 2 | 2 | const { | |
| 3 | 3 | ArrayPrototypePush, | |
| 4 | 4 | ArrayPrototypeSlice, | |
| 5 | - AtomicsStore, | ||
| 6 | - AtomicsWait, | ||
| 7 | 5 | Error, | |
| 8 | 6 | FunctionPrototypeBind, | |
| 9 | 7 | FunctionPrototypeCall, | |
| 10 | - Int32Array, | ||
| 11 | 8 | ObjectDefineProperty, | |
| 12 | 9 | ObjectGetOwnPropertyDescriptor, | |
| 13 | 10 | ObjectGetPrototypeOf, | |
@@ -19,9 +16,6 @@ const { | |||
| 19 | 16 | SafeMap, | |
| 20 | 17 | StringPrototypeSlice, | |
| 21 | 18 | StringPrototypeStartsWith, | |
| 22 | - globalThis: { | ||
| 23 | - SharedArrayBuffer, | ||
| 24 | - }, | ||
| 25 | 19 | } = primordials; | |
| 26 | 20 | const { | |
| 27 | 21 | codes: { | |
@@ -54,19 +48,10 @@ const { | |||
| 54 | 48 | validateOneOf, | |
| 55 | 49 | } = require('internal/validators'); | |
| 56 | 50 | const { MockTimers } = require('internal/test_runner/mock/mock_timers'); | |
| 57 | - const { strictEqual, notStrictEqual } = require('assert'); | ||
| 58 | 51 | const { Module } = require('internal/modules/cjs/loader'); | |
| 59 | - const { MessageChannel } = require('worker_threads'); | ||
| 60 | 52 | const { _load, _nodeModulePaths, _resolveFilename, isBuiltin } = Module; | |
| 61 | 53 | function kDefaultFunction() {} | |
| 62 | 54 | const enableModuleMocking = getOptionValue('--experimental-test-module-mocks'); | |
| 63 | - const kMockSearchParam = 'node-test-mock'; | ||
| 64 | - const kMockSuccess = 1; | ||
| 65 | - const kMockExists = 2; | ||
| 66 | - const kMockUnknownMessage = 3; | ||
| 67 | - const kWaitTimeout = 5_000; | ||
| 68 | - const kBadExportsMessage = 'Cannot create mock because named exports ' + | ||
| 69 | - 'cannot be applied to the provided default export.'; | ||
| 70 | 55 | const kSupportedFormats = [ | |
| 71 | 56 | 'builtin', | |
| 72 | 57 | 'commonjs-typescript', | |
@@ -76,6 +61,11 @@ const kSupportedFormats = [ | |||
| 76 | 61 | 'module', | |
| 77 | 62 | ]; | |
| 78 | 63 | let sharedModuleState; | |
| 64 | + const { | ||
| 65 | + hooks: mockHooks, | ||
| 66 | + mocks, | ||
| 67 | + constants: { kBadExportsMessage, kMockSearchParam }, | ||
| 68 | + } = require('internal/test_runner/mock/loader'); | ||
| 79 | 69 | ||
| 80 | 70 | class MockFunctionContext { | |
| 81 | 71 | #calls; | |
@@ -201,8 +191,8 @@ class MockModuleContext { | |||
| 201 | 191 | hasDefaultExport, | |
| 202 | 192 | namedExports, | |
| 203 | 193 | sharedState, | |
| 194 | + specifier, | ||
| 204 | 195 | }) { | |
| 205 | - const ack = new Int32Array(new SharedArrayBuffer(4)); | ||
| 206 | 196 | const config = { | |
| 207 | 197 | __proto__: null, | |
| 208 | 198 | cache, | |
@@ -218,28 +208,36 @@ class MockModuleContext { | |||
| 218 | 208 | this.#sharedState = sharedState; | |
| 219 | 209 | this.#restore = { | |
| 220 | 210 | __proto__: null, | |
| 221 | - ack, | ||
| 222 | 211 | baseURL, | |
| 223 | 212 | cached: fullPath in Module._cache, | |
| 224 | 213 | format, | |
| 225 | 214 | fullPath, | |
| 226 | 215 | value: Module._cache[fullPath], | |
| 227 | 216 | }; | |
| 228 | 217 | ||
| 229 | - sharedState.loaderPort.postMessage({ | ||
| 230 | - __proto__: null, | ||
| 231 | - type: 'node:test:register', | ||
| 232 | - payload: { | ||
| 218 | + const mock = mocks.get(baseURL); | ||
| 219 | + | ||
| 220 | + if (mock?.active) { | ||
| 221 | + debug('already mocking "%s"', baseURL); | ||
| 222 | + throw new ERR_INVALID_STATE( | ||
| 223 | + `Cannot mock '${specifier}'. The module is already mocked.`, | ||
| 224 | + ); | ||
| 225 | + } else { | ||
| 226 | + const localVersion = mock?.localVersion ?? 0; | ||
| 227 | + | ||
| 228 | + debug('new mock version %d for "%s"', localVersion, baseURL); | ||
| 229 | + mocks.set(baseURL, { | ||
| 233 | 230 | __proto__: null, | |
| 234 | - ack, | ||
| 235 | - baseURL, | ||
| 231 | + url: baseURL, | ||
| 236 | 232 | cache, | |
| 237 | 233 | exportNames: ObjectKeys(namedExports), | |
| 238 | 234 | hasDefaultExport, | |
| 239 | 235 | format, | |
| 240 | - }, | ||
| 241 | - }); | ||
| 242 | - waitForAck(ack); | ||
| 236 | + localVersion, | ||
| 237 | + active: true, | ||
| 238 | + }); | ||
| 239 | + } | ||
| 240 | + | ||
| 243 | 241 | delete Module._cache[fullPath]; | |
| 244 | 242 | sharedState.mockExports.set(baseURL, { | |
| 245 | 243 | __proto__: null, | |
@@ -261,17 +259,12 @@ class MockModuleContext { | |||
| 261 | 259 | Module._cache[this.#restore.fullPath] = this.#restore.value; | |
| 262 | 260 | } | |
| 263 | 261 | ||
| 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 | + const mock = mocks.get(this.#restore.baseURL); | ||
| 263 | + | ||
| 264 | + if (mock !== undefined) { | ||
| 265 | + mock.active = false; | ||
| 266 | + mock.localVersion++; | ||
| 267 | + } | ||
| 275 | 268 | ||
| 276 | 269 | this.#sharedState.mockMap.delete(this.#restore.baseURL); | |
| 277 | 270 | this.#sharedState.mockMap.delete(this.#restore.fullPath); | |
@@ -654,7 +647,7 @@ class MockTracker { | |||
| 654 | 647 | const hasFileProtocol = StringPrototypeStartsWith(filename, 'file://'); | |
| 655 | 648 | const caller = hasFileProtocol ? filename : pathToFileURL(filename).href; | |
| 656 | 649 | const { format, url } = sharedState.moduleLoader.resolveSync( | |
| 657 | - mockSpecifier, caller, null, | ||
| 650 | + mockSpecifier, caller, kEmptyObject, | ||
| 658 | 651 | ); | |
| 659 | 652 | debug('module mock, url = "%s", format = "%s", caller = "%s"', url, format, caller); | |
| 660 | 653 | if (format) { // Format is not yet known for ambiguous files when detection is enabled. | |
@@ -828,20 +821,13 @@ function setupSharedModuleState() { | |||
| 828 | 821 | if (sharedModuleState === undefined) { | |
| 829 | 822 | const { mock } = require('test'); | |
| 830 | 823 | const mockExports = new SafeMap(); | |
| 831 | - const { port1, port2 } = new MessageChannel(); | ||
| 824 | + const { registerHooks } = require('internal/modules/customization_hooks'); | ||
| 832 | 825 | const moduleLoader = esmLoader.getOrInitializeCascadedLoader(); | |
| 833 | 826 | ||
| 834 | - moduleLoader.register( | ||
| 835 | - 'internal/test_runner/mock/loader', | ||
| 836 | - 'node:', | ||
| 837 | - { __proto__: null, port: port2 }, | ||
| 838 | - [port2], | ||
| 839 | - true, | ||
| 840 | - ); | ||
| 827 | + registerHooks(mockHooks); | ||
| 841 | 828 | ||
| 842 | 829 | sharedModuleState = { | |
| 843 | 830 | __proto__: null, | |
| 844 | - loaderPort: port1, | ||
| 845 | 831 | mockExports, | |
| 846 | 832 | mockMap: new SafeMap(), | |
| 847 | 833 | moduleLoader, | |
@@ -941,13 +927,6 @@ function findMethodOnPrototypeChain(instance, methodName) { | |||
| 941 | 927 | return descriptor; | |
| 942 | 928 | } | |
| 943 | 929 | ||
| 944 | - function waitForAck(buf) { | ||
| 945 | - const result = AtomicsWait(buf, 0, 0, kWaitTimeout); | ||
| 946 | - | ||
| 947 | - notStrictEqual(result, 'timed-out', 'test mocking synchronization failed'); | ||
| 948 | - strictEqual(buf[0], kMockSuccess); | ||
| 949 | - } | ||
| 950 | - | ||
| 951 | 930 | function ensureNodeScheme(specifier) { | |
| 952 | 931 | if (!StringPrototypeStartsWith(specifier, 'node:')) { | |
| 953 | 932 | return `node:${specifier}`; | |
@@ -962,10 +941,5 @@ if (!enableModuleMocking) { | |||
| 962 | 941 | ||
| 963 | 942 | module.exports = { | |
| 964 | 943 | ensureNodeScheme, | |
| 965 | - kBadExportsMessage, | ||
| 966 | - kMockSearchParam, | ||
| 967 | - kMockSuccess, | ||
| 968 | - kMockExists, | ||
| 969 | - kMockUnknownMessage, | ||
| 970 | 944 | MockTracker, | |
| 971 | 945 | }; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments