|
|
|
// Originally from narwhal.js (http://narwhaljs.org)
|
|
|
|
// Copyright (c) 2009 Thomas Robinson <280north.com>
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
// of this software and associated documentation files (the 'Software'), to
|
|
|
|
// deal in the Software without restriction, including without limitation the
|
|
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const { compare } = process.binding('buffer');
|
|
|
|
const util = require('util');
|
|
|
|
const Buffer = require('buffer').Buffer;
|
|
|
|
const pToString = (obj) => Object.prototype.toString.call(obj);
|
|
|
|
|
|
|
|
// The assert module provides functions that throw
|
|
|
|
// AssertionError's when particular conditions are not met. The
|
|
|
|
// assert module must conform to the following interface.
|
|
|
|
|
|
|
|
const assert = module.exports = ok;
|
|
|
|
|
|
|
|
// The AssertionError is defined in assert.
|
|
|
|
// new assert.AssertionError({ message: message,
|
|
|
|
// actual: actual,
|
|
|
|
// expected: expected });
|
|
|
|
|
|
|
|
assert.AssertionError = function AssertionError(options) {
|
|
|
|
this.name = 'AssertionError';
|
|
|
|
this.actual = options.actual;
|
|
|
|
this.expected = options.expected;
|
|
|
|
this.operator = options.operator;
|
|
|
|
if (options.message) {
|
|
|
|
this.message = options.message;
|
|
|
|
this.generatedMessage = false;
|
|
|
|
} else {
|
|
|
|
this.message = getMessage(this);
|
|
|
|
this.generatedMessage = true;
|
|
|
|
}
|
|
|
|
const stackStartFunction = options.stackStartFunction || fail;
|
|
|
|
Error.captureStackTrace(this, stackStartFunction);
|
|
|
|
};
|
|
|
|
|
|
|
|
// assert.AssertionError instanceof Error
|
|
|
|
util.inherits(assert.AssertionError, Error);
|
|
|
|
|
|
|
|
function truncate(s, n) {
|
|
|
|
return s.slice(0, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getMessage(self) {
|
|
|
|
return truncate(util.inspect(self.actual), 128) + ' ' +
|
|
|
|
self.operator + ' ' +
|
|
|
|
truncate(util.inspect(self.expected), 128);
|
|
|
|
}
|
|
|
|
|
|
|
|
// At present only the three keys mentioned above are used and
|
|
|
|
// understood by the spec. Implementations or sub modules can pass
|
|
|
|
// other keys to the AssertionError's constructor - they will be
|
|
|
|
// ignored.
|
|
|
|
|
|
|
|
// All of the following functions must throw an AssertionError
|
|
|
|
// when a corresponding condition is not met, with a message that
|
|
|
|
// may be undefined if not provided. All assertion methods provide
|
|
|
|
// both the actual and expected values to the assertion error for
|
|
|
|
// display purposes.
|
|
|
|
|
|
|
|
function fail(actual, expected, message, operator, stackStartFunction) {
|
|
|
|
if (arguments.length === 1)
|
|
|
|
message = actual;
|
|
|
|
if (arguments.length === 2)
|
|
|
|
operator = '!=';
|
|
|
|
throw new assert.AssertionError({
|
|
|
|
message: message,
|
|
|
|
actual: actual,
|
|
|
|
expected: expected,
|
|
|
|
operator: operator,
|
|
|
|
stackStartFunction: stackStartFunction
|
|
|
|
});
|
|
|
|
}
|
|
|
|
assert.fail = fail;
|
|
|
|
|
|
|
|
// Pure assertion tests whether a value is truthy, as determined
|
|
|
|
// by !!value.
|
|
|
|
function ok(value, message) {
|
|
|
|
if (!value) fail(value, true, message, '==', ok);
|
|
|
|
}
|
|
|
|
assert.ok = ok;
|
|
|
|
|
|
|
|
// The equality assertion tests shallow, coercive equality with ==.
|
|
|
|
/* eslint-disable no-restricted-properties */
|
|
|
|
assert.equal = function equal(actual, expected, message) {
|
|
|
|
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
|
|
|
|
};
|
|
|
|
|
|
|
|
// The non-equality assertion tests for whether two objects are not
|
|
|
|
// equal with !=.
|
|
|
|
assert.notEqual = function notEqual(actual, expected, message) {
|
|
|
|
if (actual == expected) {
|
|
|
|
fail(actual, expected, message, '!=', notEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// The equivalence assertion tests a deep equality relation.
|
|
|
|
assert.deepEqual = function deepEqual(actual, expected, message) {
|
|
|
|
if (!innerDeepEqual(actual, expected, false)) {
|
|
|
|
fail(actual, expected, message, 'deepEqual', deepEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/* eslint-enable */
|
|
|
|
|
|
|
|
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
|
|
|
|
if (!innerDeepEqual(actual, expected, true)) {
|
|
|
|
fail(actual, expected, message, 'deepStrictEqual', deepStrictEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function innerDeepEqual(actual, expected, strict, memos) {
|
|
|
|
// All identical values are equivalent, as determined by ===.
|
|
|
|
if (actual === expected) {
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// If both values are instances of buffers, equivalence is
|
|
|
|
// determined by comparing the values and ensuring the result
|
|
|
|
// === 0.
|
|
|
|
} else if (actual instanceof Buffer && expected instanceof Buffer) {
|
|
|
|
return compare(actual, expected) === 0;
|
|
|
|
|
|
|
|
// If the expected value is a Date object, the actual value is
|
|
|
|
// equivalent if it is also a Date object that refers to the same time.
|
|
|
|
} else if (util.isDate(actual) && util.isDate(expected)) {
|
|
|
|
return actual.getTime() === expected.getTime();
|
|
|
|
|
|
|
|
// If the expected value is a RegExp object, the actual value is
|
|
|
|
// equivalent if it is also a RegExp object with the same source and
|
|
|
|
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
|
|
|
|
} else if (util.isRegExp(actual) && util.isRegExp(expected)) {
|
|
|
|
return actual.source === expected.source &&
|
|
|
|
actual.global === expected.global &&
|
|
|
|
actual.multiline === expected.multiline &&
|
|
|
|
actual.lastIndex === expected.lastIndex &&
|
|
|
|
actual.ignoreCase === expected.ignoreCase;
|
|
|
|
|
|
|
|
// If both values are primitives, equivalence is determined by
|
|
|
|
// == or, if checking for strict equivalence, ===.
|
|
|
|
} else if ((actual === null || typeof actual !== 'object') &&
|
|
|
|
(expected === null || typeof expected !== 'object')) {
|
|
|
|
return strict ? actual === expected : actual == expected;
|
|
|
|
|
|
|
|
// If both values are instances of typed arrays, wrap their underlying
|
|
|
|
// ArrayBuffers in a Buffer to increase performance.
|
|
|
|
// This optimization requires the arrays to have the same type as checked by
|
|
|
|
// Object.prototype.toString (pToString). Never perform binary
|
|
|
|
// comparisons for Float*Arrays, though, since +0 === -0 is true despite the
|
|
|
|
// two values' bit patterns not being identical.
|
|
|
|
} else if (ArrayBuffer.isView(actual) && ArrayBuffer.isView(expected) &&
|
|
|
|
pToString(actual) === pToString(expected) &&
|
|
|
|
!(actual instanceof Float32Array ||
|
|
|
|
actual instanceof Float64Array)) {
|
|
|
|
return compare(Buffer.from(actual.buffer,
|
|
|
|
actual.byteOffset,
|
|
|
|
actual.byteLength),
|
|
|
|
Buffer.from(expected.buffer,
|
|
|
|
expected.byteOffset,
|
|
|
|
expected.byteLength)) === 0;
|
|
|
|
|
|
|
|
// For all other Object pairs, including Array objects, equivalence is
|
|
|
|
// determined by having the same number of owned properties (as verified
|
|
|
|
// with Object.prototype.hasOwnProperty.call), the same set of keys
|
|
|
|
// (although not necessarily the same order), equivalent values for every
|
|
|
|
// corresponding key, and an identical 'prototype' property. Note: this
|
|
|
|
// accounts for both named and indexed properties on Arrays.
|
|
|
|
} else {
|
|
|
|
memos = memos || {actual: [], expected: []};
|
|
|
|
|
|
|
|
const actualIndex = memos.actual.indexOf(actual);
|
|
|
|
if (actualIndex !== -1) {
|
|
|
|
if (actualIndex === memos.expected.indexOf(expected)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memos.actual.push(actual);
|
|
|
|
memos.expected.push(expected);
|
|
|
|
|
|
|
|
return objEquiv(actual, expected, strict, memos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function isArguments(object) {
|
|
|
|
return Object.prototype.toString.call(object) === '[object Arguments]';
|
|
|
|
}
|
|
|
|
|
|
|
|
function objEquiv(a, b, strict, actualVisitedObjects) {
|
|
|
|
if (a === null || a === undefined || b === null || b === undefined)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// If one is a primitive, the other must be the same.
|
|
|
|
if (util.isPrimitive(a) || util.isPrimitive(b))
|
|
|
|
return a === b;
|
|
|
|
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
|
|
|
|
return false;
|
|
|
|
const aIsArgs = isArguments(a);
|
|
|
|
const bIsArgs = isArguments(b);
|
|
|
|
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
|
|
|
|
return false;
|
|
|
|
const ka = Object.keys(a);
|
|
|
|
const kb = Object.keys(b);
|
|
|
|
var key, i;
|
|
|
|
|
|
|
|
// The pair must have the same number of owned properties (keys
|
|
|
|
// incorporates hasOwnProperty).
|
|
|
|
if (ka.length !== kb.length)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// The pair must have the same set of keys (although not
|
|
|
|
// necessarily in the same order).
|
|
|
|
ka.sort();
|
|
|
|
kb.sort();
|
|
|
|
// Cheap key test:
|
|
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
|
|
if (ka[i] !== kb[i])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// The pair must have equivalent values for every corresponding key.
|
|
|
|
// Possibly expensive deep test:
|
|
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
|
|
key = ka[i];
|
|
|
|
if (!innerDeepEqual(a[key], b[key], strict, actualVisitedObjects))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The non-equivalence assertion tests for any deep inequality.
|
|
|
|
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
|
|
|
if (innerDeepEqual(actual, expected, false)) {
|
|
|
|
fail(actual, expected, message, 'notDeepEqual', notDeepEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.notDeepStrictEqual = notDeepStrictEqual;
|
|
|
|
function notDeepStrictEqual(actual, expected, message) {
|
|
|
|
if (innerDeepEqual(actual, expected, true)) {
|
|
|
|
fail(actual, expected, message, 'notDeepStrictEqual',
|
|
|
|
notDeepStrictEqual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The strict equality assertion tests strict equality, as determined by ===.
|
|
|
|
assert.strictEqual = function strictEqual(actual, expected, message) {
|
|
|
|
if (actual !== expected) {
|
|
|
|
fail(actual, expected, message, '===', strictEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// The strict non-equality assertion tests for strict inequality, as
|
|
|
|
// determined by !==.
|
|
|
|
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
|
|
|
|
if (actual === expected) {
|
|
|
|
fail(actual, expected, message, '!==', notStrictEqual);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function expectedException(actual, expected) {
|
|
|
|
// actual is guaranteed to be an Error object, but we need to check expected.
|
|
|
|
if (!expected) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Object.prototype.toString.call(expected) === '[object RegExp]') {
|
|
|
|
return expected.test(actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (actual instanceof expected) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore. The instanceof check doesn't work for arrow functions.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Error.isPrototypeOf(expected)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return expected.call({}, actual) === true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function tryBlock(block) {
|
|
|
|
var error;
|
|
|
|
try {
|
|
|
|
block();
|
|
|
|
} catch (e) {
|
|
|
|
error = e;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
function innerThrows(shouldThrow, block, expected, message) {
|
|
|
|
if (typeof block !== 'function') {
|
|
|
|
throw new TypeError('"block" argument must be a function');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof expected === 'string') {
|
|
|
|
message = expected;
|
|
|
|
expected = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const actual = tryBlock(block);
|
|
|
|
|
|
|
|
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
|
|
|
|
(message ? ' ' + message : '.');
|
|
|
|
|
|
|
|
if (shouldThrow && !actual) {
|
|
|
|
fail(actual, expected, 'Missing expected exception' + message, fail);
|
|
|
|
}
|
|
|
|
|
|
|
|
const userProvidedMessage = typeof message === 'string';
|
|
|
|
const isUnwantedException = !shouldThrow && util.isError(actual);
|
|
|
|
const isUnexpectedException = !shouldThrow && actual && !expected;
|
|
|
|
|
|
|
|
if ((isUnwantedException &&
|
|
|
|
userProvidedMessage &&
|
|
|
|
expectedException(actual, expected)) ||
|
|
|
|
isUnexpectedException) {
|
|
|
|
fail(actual, expected, 'Got unwanted exception' + message, fail);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shouldThrow && actual && expected &&
|
|
|
|
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
|
|
|
|
throw actual;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expected to throw an error.
|
|
|
|
assert.throws = function throws(block, error, message) {
|
|
|
|
innerThrows(true, block, error, message);
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.doesNotThrow = function doesNotThrow(block, error, message) {
|
|
|
|
innerThrows(false, block, error, message);
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.ifError = function ifError(err) { if (err) throw err; };
|