diff --git a/test/common/README.md b/test/common/README.md index 4e7e955487bd3f..8a6ce5f60456c5 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -94,6 +94,12 @@ Path to the 'fixtures' directory. Returns an instance of all possible `ArrayBufferView`s of the provided Buffer. +### getCallSite(func) +* `func` [<Function>] +* return [<String>] + +Returns the file name and line number for the provided Function. + ### globalCheck * [<Boolean>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type) diff --git a/test/common/index.js b/test/common/index.js index 7747cb57cf3ae7..d762f4e0aa8498 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -480,9 +480,51 @@ exports.fileExists = function(pathname) { } }; +exports.canCreateSymLink = function() { + // On Windows, creating symlinks requires admin privileges. + // We'll only try to run symlink test if we have enough privileges. + // On other platforms, creating symlinks shouldn't need admin privileges + if (exports.isWindows) { + // whoami.exe needs to be the one from System32 + // If unix tools are in the path, they can shadow the one we want, + // so use the full path while executing whoami + const whoamiPath = path.join(process.env['SystemRoot'], + 'System32', 'whoami.exe'); + + let err = false; + let output = ''; + + try { + output = execSync(`${whoamiPath} /priv`, { timout: 1000 }); + } catch (e) { + err = true; + } finally { + if (err || !output.includes('SeCreateSymbolicLinkPrivilege')) { + return false; + } + } + } + + return true; +}; + +exports.getCallSite = function getCallSite(top) { + const originalStackFormatter = Error.prepareStackTrace; + Error.prepareStackTrace = (err, stack) => + `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; + const err = new Error(); + Error.captureStackTrace(err, top); + // with the V8 Error API, the stack is not formatted until it is accessed + err.stack; + Error.prepareStackTrace = originalStackFormatter; + return err.stack; +}; + exports.mustNotCall = function(msg) { + const callSite = exports.getCallSite(exports.mustNotCall); return function mustNotCall() { - assert.fail(msg || 'function should not have been called'); + assert.fail( + `${msg || 'function should not have been called'} at ${callSite}`); }; }; diff --git a/test/parallel/test-common-must-not-call.js b/test/parallel/test-common-must-not-call.js new file mode 100644 index 00000000000000..d70daabf0a4bd0 --- /dev/null +++ b/test/parallel/test-common-must-not-call.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +const message = 'message'; +const testFunction = common.mustNotCall(message); + +const validateError = common.mustCall((e) => { + const prefix = `${message} at `; + assert.ok(e.message.startsWith(prefix)); + if (process.platform === 'win32') { + e.message = e.message.substring(2); // remove 'C:' + } + const [ fileName, lineNumber ] = e.message + .substring(prefix.length).split(':'); + assert.strictEqual(path.basename(fileName), 'test-common-must-not-call.js'); + assert.strictEqual(lineNumber, '8'); +}); + +try { + testFunction(); +} catch (e) { + validateError(e); +}