diff --git a/lib/assert.js b/lib/assert.js index a528e768747eb4..555b38f39e59b9 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -35,6 +35,10 @@ const { NativeModule } = require('internal/bootstrap/loaders'); let isDeepEqual; let isDeepStrictEqual; +let parseExpressionAt; +let findNodeAround; +let columnOffset = 0; +let decoder; function lazyLoadComparison() { const comparison = require('internal/util/comparisons'); @@ -114,52 +118,141 @@ function fail(actual, expected, message, operator, stackStartFn) { assert.fail = fail; // The AssertionError is defined in internal/error. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }); assert.AssertionError = AssertionError; -function getBuffer(fd, assertLine) { +function findColumn(fd, column, code) { + if (code.length > column + 100) { + try { + return parseCode(code, column); + } catch { + // End recursion in case no code could be parsed. The expression should + // have been found after 2500 characters, so stop trying. + if (code.length - column > 2500) { + // eslint-disable-next-line no-throw-literal + throw null; + } + } + } + // Read up to 2500 bytes more than necessary in columns. That way we address + // multi byte characters and read enough data to parse the code. + const bytesToRead = column - code.length + 2500; + const buffer = Buffer.allocUnsafe(bytesToRead); + const bytesRead = readSync(fd, buffer, 0, bytesToRead); + code += decoder.write(buffer.slice(0, bytesRead)); + // EOF: fast path. + if (bytesRead < bytesToRead) { + return parseCode(code, column); + } + // Read potentially missing code. + return findColumn(fd, column, code); +} + +function getCode(fd, line, column) { + let bytesRead = 0; + if (line === 0) { + // Special handle line number one. This is more efficient and simplifies the + // rest of the algorithm. Read more than the regular column number in bytes + // to prevent multiple reads in case multi byte characters are used. + return findColumn(fd, column, ''); + } let lines = 0; // Prevent blocking the event loop by limiting the maximum amount of // data that may be read. let maxReads = 64; // bytesPerRead * maxReads = 512 kb - let bytesRead = 0; - let startBuffer = 0; // Start reading from that char on const bytesPerRead = 8192; - const buffers = []; - do { - const buffer = Buffer.allocUnsafe(bytesPerRead); + // Use a single buffer up front that is reused until the call site is found. + let buffer = Buffer.allocUnsafe(bytesPerRead); + while (maxReads-- !== 0) { + // Only allocate a new buffer in case the needed line is found. All data + // before that can be discarded. + buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead); bytesRead = readSync(fd, buffer, 0, bytesPerRead); + // Read the buffer until the required code line is found. for (var i = 0; i < bytesRead; i++) { - if (buffer[i] === 10) { - lines++; - if (lines === assertLine) { - startBuffer = i + 1; - // Read up to 15 more lines to make sure all code gets matched - } else if (lines === assertLine + 16) { - buffers.push(buffer.slice(startBuffer, i)); - return buffers; + if (buffer[i] === 10 && ++lines === line) { + // If the end of file is reached, directly parse the code and return. + if (bytesRead < bytesPerRead) { + return parseCode(buffer.toString('utf8', i + 1, bytesRead), column); } + // Check if the read code is sufficient or read more until the whole + // expression is read. Make sure multi byte characters are preserved + // properly by using the decoder. + const code = decoder.write(buffer.slice(i + 1, bytesRead)); + return findColumn(fd, column, code); } } - if (lines >= assertLine) { - buffers.push(buffer.slice(startBuffer, bytesRead)); - // Reset the startBuffer in case we need more than one chunk - startBuffer = 0; + } +} + +function parseCode(code, offset) { + // Lazy load acorn. + if (parseExpressionAt === undefined) { + ({ parseExpressionAt } = require('internal/deps/acorn/dist/acorn')); + ({ findNodeAround } = require('internal/deps/acorn/dist/walk')); + } + let node; + let start = 0; + // Parse the read code until the correct expression is found. + do { + try { + node = parseExpressionAt(code, start); + start = node.end + 1 || start; + // Find the CallExpression in the tree. + node = findNodeAround(node, offset, 'CallExpression'); + } catch (err) { + // Unexpected token error and the like. + start += err.raisedAt || 1; + if (start > offset) { + // No matching expression found. This could happen if the assert + // expression is bigger than the provided buffer. + // eslint-disable-next-line no-throw-literal + throw null; + } } - } while (--maxReads !== 0 && bytesRead !== 0); - return buffers; + } while (node === undefined || node.node.end < offset); + + return [ + node.node.start, + code.slice(node.node.start, node.node.end) + .replace(escapeSequencesRegExp, escapeFn) + ]; } -function getErrMessage(call) { +function getErrMessage(message, fn) { + const tmpLimit = Error.stackTraceLimit; + // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it + // does to much work. + Error.stackTraceLimit = 1; + // We only need the stack trace. To minimize the overhead use an object + // instead of an error. + const err = {}; + Error.captureStackTrace(err, fn); + Error.stackTraceLimit = tmpLimit; + + const tmpPrepare = Error.prepareStackTrace; + Error.prepareStackTrace = (_, stack) => stack; + const call = err.stack[0]; + Error.prepareStackTrace = tmpPrepare; + const filename = call.getFileName(); + if (!filename) { - return; + return message; } const line = call.getLineNumber() - 1; - const column = call.getColumnNumber() - 1; + let column = call.getColumnNumber() - 1; + + // Line number one reports the wrong column due to being wrapped in a + // function. Remove that offset to get the actual call. + if (line === 0) { + if (columnOffset === 0) { + const { wrapper } = require('internal/modules/cjs/loader'); + columnOffset = wrapper[0].length; + } + column -= columnOffset; + } + const identifier = `${filename}${line}${column}`; if (errorCache.has(identifier)) { @@ -172,57 +265,49 @@ function getErrMessage(call) { return; } - let fd, message; + let fd; try { + // Set the stack trace limit to zero. This makes sure unexpected token + // errors are handled faster. + Error.stackTraceLimit = 0; + + if (decoder === undefined) { + const { StringDecoder } = require('string_decoder'); + decoder = new StringDecoder('utf8'); + } + fd = openSync(filename, 'r', 0o666); - const buffers = getBuffer(fd, line); - const code = Buffer.concat(buffers).toString('utf8'); - // Lazy load acorn. - const { parseExpressionAt } = require('internal/deps/acorn/dist/acorn'); - const nodes = parseExpressionAt(code, column); - // Node type should be "CallExpression" and some times - // "SequenceExpression". - const node = nodes.type === 'CallExpression' ? nodes : nodes.expressions[0]; - const name = node.callee.name; - // Calling `ok` with .apply or .call is uncommon but we use a simple - // safeguard nevertheless. - if (name !== 'apply' && name !== 'call') { - // Only use `assert` and `assert.ok` to reference the "real API" and - // not user defined function names. - const ok = name === 'ok' ? '.ok' : ''; - const args = node.arguments; - message = code - .slice(args[0].start, args[args.length - 1].end) - .replace(escapeSequencesRegExp, escapeFn); + // Reset column and message. + [column, message] = getCode(fd, line, column); + // Flush unfinished multi byte characters. + decoder.end(); + // Always normalize indentation, otherwise the message could look weird. + if (message.indexOf('\n') !== -1) { if (EOL === '\r\n') { message = message.replace(/\r\n/g, '\n'); } - // Always normalize indentation, otherwise the message could look weird. - if (message.indexOf('\n') !== -1) { - const tmp = message.split('\n'); - message = tmp[0]; - for (var i = 1; i < tmp.length; i++) { - let pos = 0; - while (pos < column && - (tmp[i][pos] === ' ' || tmp[i][pos] === '\t')) { - pos++; - } - message += `\n ${tmp[i].slice(pos)}`; + const frames = message.split('\n'); + message = frames.shift(); + for (const frame of frames) { + let pos = 0; + while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) { + pos++; } + message += `\n ${frame.slice(pos)}`; } - message = 'The expression evaluated to a falsy value:' + - `\n\n assert${ok}(${message})\n`; } + message = `The expression evaluated to a falsy value:\n\n ${message}\n`; // Make sure to always set the cache! No matter if the message is // undefined or not errorCache.set(identifier, message); return message; - } catch (e) { // Invalidate cache to prevent trying to read this part again. errorCache.set(identifier, undefined); } finally { + // Reset limit. + Error.stackTraceLimit = tmpLimit; if (fd !== undefined) closeSync(fd); } @@ -236,25 +321,8 @@ function innerOk(fn, argLen, value, message) { generatedMessage = true; message = 'No value argument passed to `assert.ok()`'; } else if (message == null) { - // Use the call as error message if possible. - // This does not work with e.g. the repl. - // eslint-disable-next-line no-restricted-syntax - const err = new Error(); - // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it - // does to much work. - const tmpLimit = Error.stackTraceLimit; - Error.stackTraceLimit = 1; - Error.captureStackTrace(err, fn); - Error.stackTraceLimit = tmpLimit; - - const tmpPrepare = Error.prepareStackTrace; - Error.prepareStackTrace = (_, stack) => stack; - const call = err.stack[0]; - Error.prepareStackTrace = tmpPrepare; - - // Make sure it would be "null" in case that is used. - message = getErrMessage(call) || message; generatedMessage = true; + message = getErrMessage(message, fn); } else if (message instanceof Error) { throw message; } diff --git a/test/fixtures/assert-first-line.js b/test/fixtures/assert-first-line.js new file mode 100644 index 00000000000000..8a65113559c981 --- /dev/null +++ b/test/fixtures/assert-first-line.js @@ -0,0 +1,2 @@ +'use strict'; const ässört = require('assert'); ässört(true); ässört.ok(''); ässört(null); +// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(false); diff --git a/test/fixtures/assert-long-line.js b/test/fixtures/assert-long-line.js new file mode 100644 index 00000000000000..cab3507ac8d779 --- /dev/null +++ b/test/fixtures/assert-long-line.js @@ -0,0 +1 @@ +'use strict'; /* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa */ const assert = require('assert'); assert(true); assert.ok(''); assert(null); diff --git a/test/parallel/test-assert-first-line.js b/test/parallel/test-assert-first-line.js new file mode 100644 index 00000000000000..32eadbf74156c3 --- /dev/null +++ b/test/parallel/test-assert-first-line.js @@ -0,0 +1,23 @@ +'use strict'; + +// Verify that asserting in the very first line produces the expected result. + +require('../common'); +const assert = require('assert'); +const { path } = require('../common/fixtures'); + +assert.throws( + () => require(path('assert-first-line')), + { + name: 'AssertionError [ERR_ASSERTION]', + message: "The expression evaluated to a falsy value:\n\n ässört.ok('')\n" + } +); + +assert.throws( + () => require(path('assert-long-line')), + { + name: 'AssertionError [ERR_ASSERTION]', + message: "The expression evaluated to a falsy value:\n\n assert.ok('')\n" + } +); diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 12ae82475726a0..bc96dc57bcd5c0 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -457,7 +457,7 @@ assert.throws( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert.ok(typeof 123 === 'string')\n" + "assert.ok(\n typeof 123 === 'string'\n )\n" } ); Error.stackTraceLimit = tmpLimit; @@ -661,7 +661,7 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert(Buffer.from('test') instanceof Error)\n" + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" } ); common.expectsError( @@ -670,7 +670,7 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert(Buffer.from('test') instanceof Error)\n" + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" } ); fs.close = tmp; @@ -690,11 +690,13 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((() => \'string\')()\n' + + ' a(\n' + + ' (() => \'string\')()\n' + ' // eslint-disable-next-line\n' + ' ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); @@ -712,11 +714,13 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((() => \'string\')()\n' + + ' a(\n' + + ' (() => \'string\')()\n' + ' // eslint-disable-next-line\n' + ' ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); @@ -731,16 +735,19 @@ Buffer code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((\n' + + ' a((\n' + ' () => \'string\')() ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); /* eslint-enable indent */ common.expectsError( - () => assert(null, undefined), + () => { + assert(true); assert(null, undefined); + }, { code: 'ERR_ASSERTION', type: assert.AssertionError, @@ -750,11 +757,38 @@ common.expectsError( ); common.expectsError( - () => assert.ok.apply(null, [0]), + () => { + assert + .ok(null, undefined); + }, { code: 'ERR_ASSERTION', type: assert.AssertionError, - message: '0 == true' + message: 'The expression evaluated to a falsy value:\n\n ' + + 'ok(null, undefined)\n' + } +); + +common.expectsError( + // eslint-disable-next-line dot-notation, quotes + () => assert['ok']["apply"](null, [0]), + { + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert[\'ok\']["apply"](null, [0])\n' + } +); + +common.expectsError( + () => { + const wrapper = (fn, value) => fn(value); + wrapper(assert, false); + }, + { + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n fn(value)\n' } ); @@ -763,7 +797,8 @@ common.expectsError( { code: 'ERR_ASSERTION', type: assert.AssertionError, - message: '0 == true', + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert.ok.call(null, 0)\n', generatedMessage: true } ); @@ -778,7 +813,7 @@ common.expectsError( } ); -// works in eval +// Works in eval. common.expectsError( () => new Function('assert', 'assert(1 === 2);')(assert), {