diff --git a/.gitignore b/.gitignore index bc986b3c4c0659..85168ea6cefcd9 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,8 @@ _UpgradeReport_Files/ tools/*/*.i tools/*/*.i.tmp +test/common/knownGlobals.json + # === Rules for release artifacts === /*.tar.* /*.pkg diff --git a/Makefile b/Makefile index 4aace77c7c8c63..4e45fa369dad6b 100644 --- a/Makefile +++ b/Makefile @@ -261,7 +261,8 @@ coverage-report-js: .PHONY: cctest # Runs the C++ tests using the built `cctest` executable. -cctest: all +# knownGlobals.json is listed as order-only prerequisit to make it work from the tarball. +cctest: all | test/common/knownGlobals.json @out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER) @out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')" @@ -325,8 +326,8 @@ test-cov: all $(MAKE) build-addons $(MAKE) build-js-native-api-tests $(MAKE) build-node-api-tests - $(MAKE) cctest CI_SKIP_TESTS=$(COV_SKIP_TESTS) $(MAKE) jstest + $(MAKE) cctest .PHONY: test-parallel test-parallel: all @@ -1136,11 +1137,15 @@ pkg-upload: pkg scp -p $(TARNAME).pkg $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg.done" -$(TARBALL): release-only doc-only +test/common/knownGlobals.json: lib/.eslintrc.yaml + $(PYTHON) tools/test.py --create-knownGlobals-json + +$(TARBALL): test/common/knownGlobals.json release-only doc-only git checkout-index -a -f --prefix=$(TARNAME)/ mkdir -p $(TARNAME)/doc/api cp doc/node.1 $(TARNAME)/doc/node.1 cp -r out/doc/api/* $(TARNAME)/doc/api/ + cp $< $(TARNAME)/$< $(RM) -r $(TARNAME)/.editorconfig $(RM) -r $(TARNAME)/.git* $(RM) -r $(TARNAME)/.mailmap diff --git a/test/common/index.js b/test/common/index.js index b69d726e8ef323..c5aaa43a55dc1d 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -37,11 +37,6 @@ const bits = ['arm64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64'] .includes(process.arch) ? 64 : 32; const hasIntl = !!process.config.variables.v8_enable_i18n_support; -const { - atob, - btoa -} = require('buffer'); - // Some tests assume a umask of 0o022 so set that up front. Tests that need a // different umask will set it themselves. // @@ -257,61 +252,16 @@ function platformTimeout(ms) { return ms; // ARMv8+ } -let knownGlobals = [ - atob, - btoa, - clearImmediate, - clearInterval, - clearTimeout, - global, - setImmediate, - setInterval, - setTimeout, - queueMicrotask, -]; - -// TODO(@jasnell): This check can be temporary. AbortController is -// not currently supported in either Node.js 12 or 10, making it -// difficult to run tests comparatively on those versions. Once -// all supported versions have AbortController as a global, this -// check can be removed and AbortController can be added to the -// knownGlobals list above. -if (global.AbortController) - knownGlobals.push(global.AbortController); - -if (global.gc) { - knownGlobals.push(global.gc); -} - -if (global.performance) { - knownGlobals.push(global.performance); -} -if (global.PerformanceMark) { - knownGlobals.push(global.PerformanceMark); -} -if (global.PerformanceMeasure) { - knownGlobals.push(global.PerformanceMeasure); -} - -// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary -// until v16.x is EOL. Once all supported versions have structuredClone we -// can add this to the list above instead. -if (global.structuredClone) { - knownGlobals.push(global.structuredClone); -} - -if (global.fetch) { - knownGlobals.push(fetch); -} -if (hasCrypto && global.crypto) { - knownGlobals.push(global.crypto); - knownGlobals.push(global.Crypto); - knownGlobals.push(global.CryptoKey); - knownGlobals.push(global.SubtleCrypto); +let knownGlobals; +try { + knownGlobals = require('./knownGlobals.json'); +} catch (err) { + console.info('You may need to run `make test/common/knownGlobals.json`.'); + throw err; } function allowGlobals(...allowlist) { - knownGlobals = knownGlobals.concat(allowlist); + knownGlobals.push(...allowlist); } if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { @@ -323,9 +273,10 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { function leakedGlobals() { const leaked = []; - for (const val in global) { - if (!knownGlobals.includes(global[val])) { - leaked.push(val); + const globals = Object.getOwnPropertyDescriptors(global); + for (const val in globals) { + if (globals[val].configurable && !knownGlobals.includes(val) && !knownGlobals.includes(global[val])) { + leaked.push(val.toString()); } } @@ -335,7 +286,7 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { process.on('exit', function() { const leaked = leakedGlobals(); if (leaked.length > 0) { - assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`); + assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}. Add it to lib/.eslint.yaml or call common.allowGlobals().`); } }); } diff --git a/test/parallel/test-repl-autocomplete.js b/test/parallel/test-repl-autocomplete.js index b107053183080a..25a744b1543995 100644 --- a/test/parallel/test-repl-autocomplete.js +++ b/test/parallel/test-repl-autocomplete.js @@ -15,6 +15,8 @@ common.skipIfDumbTerminal(); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + process.throwDeprecation = true; const defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history'); diff --git a/test/parallel/test-repl-autolibs.js b/test/parallel/test-repl-autolibs.js index 5cf3b1497221d0..ff118e4da511cc 100644 --- a/test/parallel/test-repl-autolibs.js +++ b/test/parallel/test-repl-autolibs.js @@ -26,6 +26,8 @@ const assert = require('assert'); const util = require('util'); const repl = require('repl'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + const putIn = new ArrayStream(); repl.start('', putIn, null, true); diff --git a/test/parallel/test-repl-envvars.js b/test/parallel/test-repl-envvars.js index b9216bc4aa0303..5c69828e830d33 100644 --- a/test/parallel/test-repl-envvars.js +++ b/test/parallel/test-repl-envvars.js @@ -2,13 +2,15 @@ // Flags: --expose-internals -require('../common'); +const common = require('../common'); const stream = require('stream'); const REPL = require('internal/repl'); const assert = require('assert'); const inspect = require('util').inspect; const { REPL_MODE_SLOPPY, REPL_MODE_STRICT } = require('repl'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + const tests = [ { env: {}, diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js index 29cb7816f0feb0..37d4d7415eb635 100644 --- a/test/parallel/test-repl-history-navigation.js +++ b/test/parallel/test-repl-history-navigation.js @@ -18,6 +18,8 @@ tmpdir.refresh(); process.throwDeprecation = true; process.on('warning', common.mustNotCall()); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + const defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history'); // Create an input stream specialized for testing an array of actions diff --git a/test/parallel/test-repl-history-perm.js b/test/parallel/test-repl-history-perm.js index aeca832d430978..a105ddbebd7c22 100644 --- a/test/parallel/test-repl-history-perm.js +++ b/test/parallel/test-repl-history-perm.js @@ -20,6 +20,8 @@ const Duplex = require('stream').Duplex; // Invoking the REPL should create a repl history file at the specified path // and mode 600. +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + const stream = new Duplex(); stream.pause = stream.resume = () => {}; // ends immediately diff --git a/test/parallel/test-repl-let-process.js b/test/parallel/test-repl-let-process.js index d0524953d74650..a16c11482458b9 100644 --- a/test/parallel/test-repl-let-process.js +++ b/test/parallel/test-repl-let-process.js @@ -1,8 +1,10 @@ 'use strict'; -require('../common'); +const common = require('../common'); const ArrayStream = require('../common/arraystream'); const repl = require('repl'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + // Regression test for https://github.com/nodejs/node/issues/6802 const input = new ArrayStream(); repl.start({ input, output: process.stdout, useGlobal: true }); diff --git a/test/parallel/test-repl-options.js b/test/parallel/test-repl-options.js index 953255319cf9eb..d2dfa4010e3b2d 100644 --- a/test/parallel/test-repl-options.js +++ b/test/parallel/test-repl-options.js @@ -28,6 +28,8 @@ const assert = require('assert'); const repl = require('repl'); const cp = require('child_process'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + assert.strictEqual(repl.repl, undefined); repl._builtinLibs; // eslint-disable-line no-unused-expressions diff --git a/test/parallel/test-repl-persistent-history.js b/test/parallel/test-repl-persistent-history.js index b0cddf0a2bd020..2a24fa8d5c2b1a 100644 --- a/test/parallel/test-repl-persistent-history.js +++ b/test/parallel/test-repl-persistent-history.js @@ -17,6 +17,8 @@ common.skipIfDumbTerminal(); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + // Mock os.homedir() os.homedir = function() { return tmpdir.path; diff --git a/test/parallel/test-repl-reset-event.js b/test/parallel/test-repl-reset-event.js index 1f1347547e95f8..69844b14d7452e 100644 --- a/test/parallel/test-repl-reset-event.js +++ b/test/parallel/test-repl-reset-event.js @@ -26,7 +26,7 @@ const assert = require('assert'); const repl = require('repl'); const util = require('util'); -common.allowGlobals(42); +common.allowGlobals(42, 'require', '_', '_error', ...require('module').builtinModules); // Create a dummy stream that does nothing const dummy = new ArrayStream(); diff --git a/test/parallel/test-repl-reverse-search.js b/test/parallel/test-repl-reverse-search.js index 5165dc2820d2d6..68747e5d5511db 100644 --- a/test/parallel/test-repl-reverse-search.js +++ b/test/parallel/test-repl-reverse-search.js @@ -16,6 +16,8 @@ common.allowGlobals('aaaa'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + const defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history'); // Create an input stream specialized for testing an array of actions diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index 3abd01ba9d0cbc..130dbba9ad241e 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -1,10 +1,12 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const repl = require('repl'); const stream = require('stream'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + testSloppyMode(); testStrictMode(); testResetContext(); diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js index 3457d0c5ba7210..af2373b1aa06e9 100644 --- a/test/parallel/test-repl-use-global.js +++ b/test/parallel/test-repl-use-global.js @@ -7,6 +7,8 @@ const stream = require('stream'); const repl = require('internal/repl'); const assert = require('assert'); +common.allowGlobals('require', '_', '_error', ...require('module').builtinModules); + // Array of [useGlobal, expectedResult] pairs const globalTestCases = [ [false, 'undefined'], diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index b70fce93ba7c49..7c74b7a39ebe4c 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -39,6 +39,7 @@ const moduleFilename = fixtures.path('a'); global.invoke_me = function(arg) { return `invoked ${arg}`; }; +common.allowGlobals('invoke_me', 'require', 'a', '_', '_error', 'message', ...require('module').builtinModules); // Helpers for describing the expected output: const kArrow = /^ *\^+ *$/; // Arrow of ^ pointing to syntax error location diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index ebbbcbb6c937ef..4e6dc01d80deba 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.js @@ -278,6 +278,7 @@ assert.throws( assert.deepStrictEqual(children, { 'common/index.js': { + 'common/knownGlobals.json': {}, 'common/tmpdir.js': {} }, 'fixtures/not-main-module.js': {}, diff --git a/tools/test.py b/tools/test.py index f204004c7a63f1..cb86c5a30a7a37 100755 --- a/tools/test.py +++ b/tools/test.py @@ -43,6 +43,7 @@ import multiprocessing import errno import copy +import json if sys.version_info >= (3, 5): @@ -89,6 +90,49 @@ def get_module(name, path): os.umask(0o022) os.environ['NODE_OPTIONS'] = '' + +def createKnowGlobalsJSON(workspace, test_root): + eslintConfigFile = join(workspace, 'lib', '.eslintrc.yaml') + outputFile = join(test_root, 'common', 'knownGlobals.json') + searchLines = [ + ' no-restricted-globals:', + ' node-core/prefer-primordials:', + ] + isReadingGlobals = False + restrictedGlobalDeclaration = re.compile(r"^\s{4}- name:\s?([^#\s]+)") + closingSectionLine = re.compile(r"^\s{0,3}[^#\s]") + with open(eslintConfigFile, 'r') as eslintConfig, open(outputFile, 'w') as output: + output.write(u'["process"') + for line in eslintConfig.readlines(): + if isReadingGlobals: + match = restrictedGlobalDeclaration.match(line) + if match is not None: + output.write(u',{}'.format(json.dumps(match.group(1)))) + elif closingSectionLine.match(line) is not None: + isReadingGlobals = False + elif searchLines and line.rstrip() == searchLines[0]: + searchLines = searchLines[1:] + isReadingGlobals = True + output.write(u']') + +def createKnowGlobalsJSONIfPossible(workspace, test_root): + try: + # Python 3 + FileNotFoundError # noqa: F823 + except NameError: + # Python 2 + FileNotFoundError = IOError + try: + createKnowGlobalsJSON(workspace, test_root) + except FileNotFoundError: + # In the tarball, the .eslintrc.yaml file doesn't exist, and we cannot + # create the JSON file. However, in the tarball the JSON file has already + # been generated, so we can ignore this error. + # If the JSON file is not present, JavaScript will raise an error, so we can + # also ignore. + pass + + # --------------------------------------------- # --- P r o g r e s s I n d i c a t o r s --- # --------------------------------------------- @@ -1395,6 +1439,9 @@ def BuildOptions(): result.add_option("--type", help="Type of build (simple, fips, coverage)", default=None) + result.add_option('--create-knownGlobals-json', + help='Generates the knownGlobal.json file. No tests will be run when using this flag.', + default=False, action='store_true', dest='create_knownGlobal_json') return result @@ -1581,6 +1628,10 @@ def Main(): repositories = [TestRepository(join(test_root, name)) for name in suites] repositories += [TestRepository(a) for a in options.suite] + if options.create_knownGlobal_json: + createKnowGlobalsJSON(workspace, test_root) + return 0 + root = LiteralTestSuite(repositories, test_root) paths = ArgsToTestPaths(test_root, args, suites) @@ -1668,6 +1719,8 @@ def Main(): if has_crypto.stdout.rstrip() == 'undefined': context.node_has_crypto = False + createKnowGlobalsJSONIfPossible(workspace, test_root) + if options.cat: visited = set() for test in unclassified_tests: diff --git a/vcbuild.bat b/vcbuild.bat index d1a9e592551593..f2a943ee063095 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -596,6 +596,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% :: building addons setlocal set npm_config_nodedir=%~dp0 +python "%~dp0tools\test.py" --create-knownGlobals-json "%node_exe%" "%~dp0tools\build-addons.js" "%~dp0deps\npm\node_modules\node-gyp\bin\node-gyp.js" "%~dp0test\addons" if errorlevel 1 exit /b 1 endlocal @@ -614,6 +615,7 @@ for /d %%F in (test\js-native-api\??_*) do ( :: building js-native-api setlocal set npm_config_nodedir=%~dp0 +python "%~dp0tools\test.py" --create-knownGlobals-json "%node_exe%" "%~dp0tools\build-addons.js" "%~dp0deps\npm\node_modules\node-gyp\bin\node-gyp.js" "%~dp0test\js-native-api" if errorlevel 1 exit /b 1 endlocal @@ -633,6 +635,7 @@ for /d %%F in (test\node-api\??_*) do ( :: building node-api setlocal set npm_config_nodedir=%~dp0 +python "%~dp0tools\test.py" --create-knownGlobals-json "%node_exe%" "%~dp0tools\build-addons.js" "%~dp0deps\npm\node_modules\node-gyp\bin\node-gyp.js" "%~dp0test\node-api" if errorlevel 1 exit /b 1 endlocal