summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2018-05-02 16:21:40 +0200
committerRuben Bridgewater <ruben@bridgewater.de>2018-05-10 13:39:19 +0200
commitb304096a14f4b266738faeaded8ea31215528ea0 (patch)
tree018595b6f5f88e6f706d3fc33324fdfcf779ac77
parent4c6df73ae4e3c7ce0b2346bc38c04c6501239b02 (diff)
downloadandroid-node-v8-b304096a14f4b266738faeaded8ea31215528ea0.tar.gz
android-node-v8-b304096a14f4b266738faeaded8ea31215528ea0.tar.bz2
android-node-v8-b304096a14f4b266738faeaded8ea31215528ea0.zip
assert: move AssertionError into own file
This moves the `assert` parts from `internal/errors` into an own file. `internal/errors` got bigger and bigger and it was difficult to keep a good overview of what was going on. While doing so it also removes the `internalAssert` function and just lazy loads `assert`. PR-URL: https://github.com/nodejs/node/pull/20486 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
-rw-r--r--lib/assert.js15
-rw-r--r--lib/internal/assert.js275
-rw-r--r--lib/internal/errors.js314
-rw-r--r--node.gyp1
-rw-r--r--test/parallel/test-assert.js2
5 files changed, 301 insertions, 306 deletions
diff --git a/lib/assert.js b/lib/assert.js
index ac9fd30793..27e71c2751 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -25,15 +25,12 @@ const {
isDeepEqual,
isDeepStrictEqual
} = require('internal/util/comparisons');
-const {
- AssertionError,
- errorCache,
- codes: {
- ERR_AMBIGUOUS_ARGUMENT,
- ERR_INVALID_ARG_TYPE,
- ERR_INVALID_RETURN_VALUE
- }
-} = require('internal/errors');
+const { codes: {
+ ERR_AMBIGUOUS_ARGUMENT,
+ ERR_INVALID_ARG_TYPE,
+ ERR_INVALID_RETURN_VALUE
+} } = require('internal/errors');
+const { AssertionError, errorCache } = require('internal/assert');
const { openSync, closeSync, readSync } = require('fs');
const { inspect, types: { isPromise, isRegExp } } = require('util');
const { EOL } = require('os');
diff --git a/lib/internal/assert.js b/lib/internal/assert.js
new file mode 100644
index 0000000000..990065a937
--- /dev/null
+++ b/lib/internal/assert.js
@@ -0,0 +1,275 @@
+'use strict';
+
+const { inspect } = require('util');
+const { codes: {
+ ERR_INVALID_ARG_TYPE
+} } = require('internal/errors');
+
+let blue = '';
+let green = '';
+let red = '';
+let white = '';
+
+const READABLE_OPERATOR = {
+ deepStrictEqual: 'Input A expected to strictly deep-equal input B',
+ notDeepStrictEqual: 'Input A expected to strictly not deep-equal input B',
+ strictEqual: 'Input A expected to strictly equal input B',
+ notStrictEqual: 'Input A expected to strictly not equal input B'
+};
+
+function copyError(source) {
+ const keys = Object.keys(source);
+ const target = Object.create(Object.getPrototypeOf(source));
+ for (const key of keys) {
+ target[key] = source[key];
+ }
+ Object.defineProperty(target, 'message', { value: source.message });
+ return target;
+}
+
+function inspectValue(val) {
+ // The util.inspect default values could be changed. This makes sure the
+ // error messages contain the necessary information nevertheless.
+ return inspect(
+ val,
+ {
+ compact: false,
+ customInspect: false,
+ depth: 1000,
+ maxArrayLength: Infinity,
+ // Assert compares only enumerable properties (with a few exceptions).
+ showHidden: false,
+ // Having a long line as error is better than wrapping the line for
+ // comparison.
+ breakLength: Infinity,
+ // Assert does not detect proxies currently.
+ showProxy: false
+ }
+ ).split('\n');
+}
+
+function createErrDiff(actual, expected, operator) {
+ var other = '';
+ var res = '';
+ var lastPos = 0;
+ var end = '';
+ var skipped = false;
+ const actualLines = inspectValue(actual);
+ const expectedLines = inspectValue(expected);
+ const msg = READABLE_OPERATOR[operator] +
+ `:\n${green}+ expected${white} ${red}- actual${white}`;
+ const skippedMsg = ` ${blue}...${white} Lines skipped`;
+
+ // Remove all ending lines that match (this optimizes the output for
+ // readability by reducing the number of total changed lines).
+ var a = actualLines[actualLines.length - 1];
+ var b = expectedLines[expectedLines.length - 1];
+ var i = 0;
+ while (a === b) {
+ if (i++ < 2) {
+ end = `\n ${a}${end}`;
+ } else {
+ other = a;
+ }
+ actualLines.pop();
+ expectedLines.pop();
+ if (actualLines.length === 0 || expectedLines.length === 0)
+ break;
+ a = actualLines[actualLines.length - 1];
+ b = expectedLines[expectedLines.length - 1];
+ }
+ if (i > 3) {
+ end = `\n${blue}...${white}${end}`;
+ skipped = true;
+ }
+ if (other !== '') {
+ end = `\n ${other}${end}`;
+ other = '';
+ }
+
+ const maxLines = Math.max(actualLines.length, expectedLines.length);
+ var printedLines = 0;
+ var identical = 0;
+ for (i = 0; i < maxLines; i++) {
+ // Only extra expected lines exist
+ const cur = i - lastPos;
+ if (actualLines.length < i + 1) {
+ if (cur > 1 && i > 2) {
+ if (cur > 4) {
+ res += `\n${blue}...${white}`;
+ skipped = true;
+ } else if (cur > 3) {
+ res += `\n ${expectedLines[i - 2]}`;
+ printedLines++;
+ }
+ res += `\n ${expectedLines[i - 1]}`;
+ printedLines++;
+ }
+ lastPos = i;
+ other += `\n${green}+${white} ${expectedLines[i]}`;
+ printedLines++;
+ // Only extra actual lines exist
+ } else if (expectedLines.length < i + 1) {
+ if (cur > 1 && i > 2) {
+ if (cur > 4) {
+ res += `\n${blue}...${white}`;
+ skipped = true;
+ } else if (cur > 3) {
+ res += `\n ${actualLines[i - 2]}`;
+ printedLines++;
+ }
+ res += `\n ${actualLines[i - 1]}`;
+ printedLines++;
+ }
+ lastPos = i;
+ res += `\n${red}-${white} ${actualLines[i]}`;
+ printedLines++;
+ // Lines diverge
+ } else if (actualLines[i] !== expectedLines[i]) {
+ if (cur > 1 && i > 2) {
+ if (cur > 4) {
+ res += `\n${blue}...${white}`;
+ skipped = true;
+ } else if (cur > 3) {
+ res += `\n ${actualLines[i - 2]}`;
+ printedLines++;
+ }
+ res += `\n ${actualLines[i - 1]}`;
+ printedLines++;
+ }
+ lastPos = i;
+ res += `\n${red}-${white} ${actualLines[i]}`;
+ other += `\n${green}+${white} ${expectedLines[i]}`;
+ printedLines += 2;
+ // Lines are identical
+ } else {
+ res += other;
+ other = '';
+ if (cur === 1 || i === 0) {
+ res += `\n ${actualLines[i]}`;
+ printedLines++;
+ }
+ identical++;
+ }
+ // Inspected object to big (Show ~20 rows max)
+ if (printedLines > 20 && i < maxLines - 2) {
+ return `${msg}${skippedMsg}\n${res}\n${blue}...${white}${other}\n` +
+ `${blue}...${white}`;
+ }
+ }
+
+ // Strict equal with identical objects that are not identical by reference.
+ if (identical === maxLines) {
+ // E.g., assert.deepStrictEqual(Symbol(), Symbol())
+ const base = operator === 'strictEqual' ?
+ 'Input objects identical but not reference equal:' :
+ 'Input objects not identical:';
+
+ // We have to get the result again. The lines were all removed before.
+ const actualLines = inspectValue(actual);
+
+ // Only remove lines in case it makes sense to collapse those.
+ // TODO: Accept env to always show the full error.
+ if (actualLines.length > 30) {
+ actualLines[26] = `${blue}...${white}`;
+ while (actualLines.length > 27) {
+ actualLines.pop();
+ }
+ }
+
+ return `${base}\n\n${actualLines.join('\n')}\n`;
+ }
+ return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`;
+}
+
+class AssertionError extends Error {
+ constructor(options) {
+ if (typeof options !== 'object' || options === null) {
+ throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
+ }
+ var {
+ actual,
+ expected,
+ message,
+ operator,
+ stackStartFn
+ } = options;
+
+ if (message != null) {
+ super(message);
+ } else {
+ if (process.stdout.isTTY) {
+ // Reset on each call to make sure we handle dynamically set environment
+ // variables correct.
+ if (process.stdout.getColorDepth() !== 1) {
+ blue = '\u001b[34m';
+ green = '\u001b[32m';
+ white = '\u001b[39m';
+ red = '\u001b[31m';
+ } else {
+ blue = '';
+ green = '';
+ white = '';
+ red = '';
+ }
+ }
+ // Prevent the error stack from being visible by duplicating the error
+ // in a very close way to the original in case both sides are actually
+ // instances of Error.
+ if (typeof actual === 'object' && actual !== null &&
+ typeof expected === 'object' && expected !== null &&
+ 'stack' in actual && actual instanceof Error &&
+ 'stack' in expected && expected instanceof Error) {
+ actual = copyError(actual);
+ expected = copyError(expected);
+ }
+
+ if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
+ super(createErrDiff(actual, expected, operator));
+ } else if (operator === 'notDeepStrictEqual' ||
+ operator === 'notStrictEqual') {
+ // In case the objects are equal but the operator requires unequal, show
+ // the first object and say A equals B
+ const res = inspectValue(actual);
+ const base = `Identical input passed to ${operator}:`;
+
+ // Only remove lines in case it makes sense to collapse those.
+ // TODO: Accept env to always show the full error.
+ if (res.length > 30) {
+ res[26] = `${blue}...${white}`;
+ while (res.length > 27) {
+ res.pop();
+ }
+ }
+
+ // Only print a single input.
+ if (res.length === 1) {
+ super(`${base} ${res[0]}`);
+ } else {
+ super(`${base}\n\n${res.join('\n')}\n`);
+ }
+ } else {
+ let res = inspect(actual);
+ let other = inspect(expected);
+ if (res.length > 128)
+ res = `${res.slice(0, 125)}...`;
+ if (other.length > 128)
+ other = `${other.slice(0, 125)}...`;
+ super(`${res} ${operator} ${other}`);
+ }
+ }
+
+ this.generatedMessage = !message;
+ this.name = 'AssertionError [ERR_ASSERTION]';
+ this.code = 'ERR_ASSERTION';
+ this.actual = actual;
+ this.expected = expected;
+ this.operator = operator;
+ Error.captureStackTrace(this, stackStartFn);
+ }
+}
+
+module.exports = {
+ AssertionError,
+ errorCache: new Map()
+};
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index de4a566af4..6af266eaee 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -15,18 +15,6 @@ const kInfo = Symbol('info');
const messages = new Map();
const codes = {};
-let blue = '';
-let green = '';
-let red = '';
-let white = '';
-
-const READABLE_OPERATOR = {
- deepStrictEqual: 'Input A expected to strictly deep-equal input B',
- notDeepStrictEqual: 'Input A expected to strictly not deep-equal input B',
- strictEqual: 'Input A expected to strictly equal input B',
- notStrictEqual: 'Input A expected to strictly not equal input B'
-};
-
const {
errmap,
UV_EAI_NODATA,
@@ -36,10 +24,10 @@ const { kMaxLength } = process.binding('buffer');
const { defineProperty } = Object;
// Lazily loaded
-var util;
-var buffer;
+let util;
+let assert;
-var internalUtil = null;
+let internalUtil = null;
function lazyInternalUtil() {
if (!internalUtil) {
internalUtil = require('internal/util');
@@ -47,35 +35,11 @@ function lazyInternalUtil() {
return internalUtil;
}
-function copyError(source) {
- const keys = Object.keys(source);
- const target = Object.create(Object.getPrototypeOf(source));
- for (const key of keys) {
- target[key] = source[key];
- }
- Object.defineProperty(target, 'message', { value: source.message });
- return target;
-}
-
-function inspectValue(val) {
- // The util.inspect default values could be changed. This makes sure the
- // error messages contain the necessary information nevertheless.
- return util.inspect(
- val,
- {
- compact: false,
- customInspect: false,
- depth: 1000,
- maxArrayLength: Infinity,
- // Assert compares only enumerable properties (with a few exceptions).
- showHidden: false,
- // Having a long line as error is better than wrapping the line for
- // comparison.
- breakLength: Infinity,
- // Assert does not detect proxies currently.
- showProxy: false
- }
- ).split('\n');
+let buffer;
+function lazyBuffer() {
+ if (buffer === undefined)
+ buffer = require('buffer').Buffer;
+ return buffer;
}
// A specialized Error that includes an additional info property with
@@ -240,254 +204,14 @@ function E(sym, val, def, ...otherClasses) {
codes[sym] = def;
}
-function lazyBuffer() {
- if (buffer === undefined)
- buffer = require('buffer').Buffer;
- return buffer;
-}
-
-function createErrDiff(actual, expected, operator) {
- var other = '';
- var res = '';
- var lastPos = 0;
- var end = '';
- var skipped = false;
- if (util === undefined) util = require('util');
- const actualLines = inspectValue(actual);
- const expectedLines = inspectValue(expected);
- const msg = READABLE_OPERATOR[operator] +
- `:\n${green}+ expected${white} ${red}- actual${white}`;
- const skippedMsg = ` ${blue}...${white} Lines skipped`;
-
- // Remove all ending lines that match (this optimizes the output for
- // readability by reducing the number of total changed lines).
- var a = actualLines[actualLines.length - 1];
- var b = expectedLines[expectedLines.length - 1];
- var i = 0;
- while (a === b) {
- if (i++ < 2) {
- end = `\n ${a}${end}`;
- } else {
- other = a;
- }
- actualLines.pop();
- expectedLines.pop();
- if (actualLines.length === 0 || expectedLines.length === 0)
- break;
- a = actualLines[actualLines.length - 1];
- b = expectedLines[expectedLines.length - 1];
- }
- if (i > 3) {
- end = `\n${blue}...${white}${end}`;
- skipped = true;
- }
- if (other !== '') {
- end = `\n ${other}${end}`;
- other = '';
- }
-
- const maxLines = Math.max(actualLines.length, expectedLines.length);
- var printedLines = 0;
- var identical = 0;
- for (i = 0; i < maxLines; i++) {
- // Only extra expected lines exist
- const cur = i - lastPos;
- if (actualLines.length < i + 1) {
- if (cur > 1 && i > 2) {
- if (cur > 4) {
- res += `\n${blue}...${white}`;
- skipped = true;
- } else if (cur > 3) {
- res += `\n ${expectedLines[i - 2]}`;
- printedLines++;
- }
- res += `\n ${expectedLines[i - 1]}`;
- printedLines++;
- }
- lastPos = i;
- other += `\n${green}+${white} ${expectedLines[i]}`;
- printedLines++;
- // Only extra actual lines exist
- } else if (expectedLines.length < i + 1) {
- if (cur > 1 && i > 2) {
- if (cur > 4) {
- res += `\n${blue}...${white}`;
- skipped = true;
- } else if (cur > 3) {
- res += `\n ${actualLines[i - 2]}`;
- printedLines++;
- }
- res += `\n ${actualLines[i - 1]}`;
- printedLines++;
- }
- lastPos = i;
- res += `\n${red}-${white} ${actualLines[i]}`;
- printedLines++;
- // Lines diverge
- } else if (actualLines[i] !== expectedLines[i]) {
- if (cur > 1 && i > 2) {
- if (cur > 4) {
- res += `\n${blue}...${white}`;
- skipped = true;
- } else if (cur > 3) {
- res += `\n ${actualLines[i - 2]}`;
- printedLines++;
- }
- res += `\n ${actualLines[i - 1]}`;
- printedLines++;
- }
- lastPos = i;
- res += `\n${red}-${white} ${actualLines[i]}`;
- other += `\n${green}+${white} ${expectedLines[i]}`;
- printedLines += 2;
- // Lines are identical
- } else {
- res += other;
- other = '';
- if (cur === 1 || i === 0) {
- res += `\n ${actualLines[i]}`;
- printedLines++;
- }
- identical++;
- }
- // Inspected object to big (Show ~20 rows max)
- if (printedLines > 20 && i < maxLines - 2) {
- return `${msg}${skippedMsg}\n${res}\n${blue}...${white}${other}\n` +
- `${blue}...${white}`;
- }
- }
-
- // Strict equal with identical objects that are not identical by reference.
- if (identical === maxLines) {
- // E.g., assert.deepStrictEqual(Symbol(), Symbol())
- const base = operator === 'strictEqual' ?
- 'Input objects identical but not reference equal:' :
- 'Input objects not identical:';
-
- // We have to get the result again. The lines were all removed before.
- const actualLines = inspectValue(actual);
-
- // Only remove lines in case it makes sense to collapse those.
- // TODO: Accept env to always show the full error.
- if (actualLines.length > 30) {
- actualLines[26] = `${blue}...${white}`;
- while (actualLines.length > 27) {
- actualLines.pop();
- }
- }
-
- return `${base}\n\n${actualLines.join('\n')}\n`;
- }
- return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`;
-}
-
-class AssertionError extends Error {
- constructor(options) {
- if (typeof options !== 'object' || options === null) {
- throw new codes.ERR_INVALID_ARG_TYPE('options', 'Object', options);
- }
- var {
- actual,
- expected,
- message,
- operator,
- stackStartFn
- } = options;
-
- if (message != null) {
- super(message);
- } else {
- if (process.stdout.isTTY) {
- // Reset on each call to make sure we handle dynamically set environment
- // variables correct.
- if (process.stdout.getColorDepth() !== 1) {
- blue = '\u001b[34m';
- green = '\u001b[32m';
- white = '\u001b[39m';
- red = '\u001b[31m';
- } else {
- blue = '';
- green = '';
- white = '';
- red = '';
- }
- }
- if (util === undefined) util = require('util');
- // Prevent the error stack from being visible by duplicating the error
- // in a very close way to the original in case both sides are actually
- // instances of Error.
- if (typeof actual === 'object' && actual !== null &&
- typeof expected === 'object' && expected !== null &&
- 'stack' in actual && actual instanceof Error &&
- 'stack' in expected && expected instanceof Error) {
- actual = copyError(actual);
- expected = copyError(expected);
- }
-
- if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
- super(createErrDiff(actual, expected, operator));
- } else if (operator === 'notDeepStrictEqual' ||
- operator === 'notStrictEqual') {
- // In case the objects are equal but the operator requires unequal, show
- // the first object and say A equals B
- const res = inspectValue(actual);
- const base = `Identical input passed to ${operator}:`;
-
- // Only remove lines in case it makes sense to collapse those.
- // TODO: Accept env to always show the full error.
- if (res.length > 30) {
- res[26] = `${blue}...${white}`;
- while (res.length > 27) {
- res.pop();
- }
- }
-
- // Only print a single input.
- if (res.length === 1) {
- super(`${base} ${res[0]}`);
- } else {
- super(`${base}\n\n${res.join('\n')}\n`);
- }
- } else {
- let res = util.inspect(actual);
- let other = util.inspect(expected);
- if (res.length > 128)
- res = `${res.slice(0, 125)}...`;
- if (other.length > 128)
- other = `${other.slice(0, 125)}...`;
- super(`${res} ${operator} ${other}`);
- }
- }
-
- this.generatedMessage = !message;
- this.name = 'AssertionError [ERR_ASSERTION]';
- this.code = 'ERR_ASSERTION';
- this.actual = actual;
- this.expected = expected;
- this.operator = operator;
- Error.captureStackTrace(this, stackStartFn);
- }
-}
-
-// This is defined here instead of using the assert module to avoid a
-// circular dependency. The effect is largely the same.
-function internalAssert(condition, message) {
- if (!condition) {
- throw new AssertionError({
- message,
- actual: false,
- expected: true,
- operator: '=='
- });
- }
-}
-
function getMessage(key, args) {
const msg = messages.get(key);
+
if (util === undefined) util = require('util');
+ if (assert === undefined) assert = require('assert');
if (typeof msg === 'function') {
- internalAssert(
+ assert(
msg.length <= args.length, // Default options do not count.
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
`match the required ones (${msg.length}).`
@@ -496,7 +220,7 @@ function getMessage(key, args) {
}
const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length;
- internalAssert(
+ assert(
expectedLength === args.length,
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
`match the required ones (${expectedLength}).`
@@ -690,11 +414,9 @@ module.exports = {
uvException,
isStackOverflowError,
getMessage,
- AssertionError,
SystemError,
codes,
- E, // This is exported only to facilitate testing.
- errorCache: new Map() // This is in here only to facilitate testing.
+ E // This is exported only to facilitate testing.
};
// To declare an error message, use the E(sym, val, def) function above. The sym
@@ -1038,7 +760,7 @@ E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed', Error);
function invalidArgType(name, expected, actual) {
- internalAssert(typeof name === 'string', 'name must be a string');
+ assert(typeof name === 'string', "'name' must be a string");
// determiner: 'must be' or 'must not be'
let determiner;
@@ -1064,7 +786,7 @@ function invalidArgType(name, expected, actual) {
}
function missingArgs(...args) {
- internalAssert(args.length > 0, 'At least one arg needs to be specified');
+ assert(args.length > 0, 'At least one arg needs to be specified');
let msg = 'The ';
const len = args.length;
args = args.map((a) => `"${a}"`);
@@ -1084,11 +806,11 @@ function missingArgs(...args) {
}
function oneOf(expected, thing) {
- internalAssert(typeof thing === 'string', '`thing` has to be of type string');
+ assert(typeof thing === 'string', '`thing` has to be of type string');
if (Array.isArray(expected)) {
const len = expected.length;
- internalAssert(len > 0,
- 'At least one expected value needs to be specified');
+ assert(len > 0,
+ 'At least one expected value needs to be specified');
expected = expected.map((i) => String(i));
if (len > 2) {
return `one of ${thing} ${expected.slice(0, len - 1).join(', ')}, or ` +
diff --git a/node.gyp b/node.gyp
index aeb558d566..54f22a33da 100644
--- a/node.gyp
+++ b/node.gyp
@@ -79,6 +79,7 @@
'lib/v8.js',
'lib/vm.js',
'lib/zlib.js',
+ 'lib/internal/assert.js',
'lib/internal/async_hooks.js',
'lib/internal/buffer.js',
'lib/internal/cli_table.js',
diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js
index cbcda17ab7..cab6fb33e1 100644
--- a/test/parallel/test-assert.js
+++ b/test/parallel/test-assert.js
@@ -29,7 +29,7 @@ const common = require('../common');
const assert = require('assert');
const { EOL } = require('os');
const EventEmitter = require('events');
-const { errorCache } = require('internal/errors');
+const { errorCache } = require('internal/assert');
const { writeFileSync, unlinkSync } = require('fs');
const { inspect } = require('util');
const a = assert;