• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Originally from narwhal.js (http://narwhaljs.org)
2// Copyright (c) 2009 Thomas Robinson <280north.com>
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the 'Software'), to
6// deal in the Software without restriction, including without limitation the
7// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8// sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21'use strict';
22
23const {
24  ArrayPrototypeIndexOf,
25  ArrayPrototypeJoin,
26  ArrayPrototypePush,
27  ArrayPrototypeShift,
28  ArrayPrototypeSlice,
29  Error,
30  ErrorCaptureStackTrace,
31  FunctionPrototypeBind,
32  NumberIsNaN,
33  ObjectAssign,
34  ObjectIs,
35  ObjectKeys,
36  ObjectPrototypeIsPrototypeOf,
37  ReflectApply,
38  RegExpPrototypeTest,
39  SafeMap,
40  String,
41  StringPrototypeCharCodeAt,
42  StringPrototypeIncludes,
43  StringPrototypeIndexOf,
44  StringPrototypeReplace,
45  StringPrototypeSlice,
46  StringPrototypeSplit,
47} = primordials;
48
49const { Buffer } = require('buffer');
50const {
51  codes: {
52    ERR_AMBIGUOUS_ARGUMENT,
53    ERR_INVALID_ARG_TYPE,
54    ERR_INVALID_ARG_VALUE,
55    ERR_INVALID_RETURN_VALUE,
56    ERR_MISSING_ARGS,
57  },
58  overrideStackTrace,
59} = require('internal/errors');
60const AssertionError = require('internal/assert/assertion_error');
61const { openSync, closeSync, readSync } = require('fs');
62const { inspect } = require('internal/util/inspect');
63const { isPromise, isRegExp } = require('internal/util/types');
64const { EOL } = require('internal/constants');
65const { NativeModule } = require('internal/bootstrap/loaders');
66const { isError } = require('internal/util');
67
68const errorCache = new SafeMap();
69const CallTracker = require('internal/assert/calltracker');
70
71let isDeepEqual;
72let isDeepStrictEqual;
73let parseExpressionAt;
74let findNodeAround;
75let decoder;
76
77function lazyLoadComparison() {
78  const comparison = require('internal/util/comparisons');
79  isDeepEqual = comparison.isDeepEqual;
80  isDeepStrictEqual = comparison.isDeepStrictEqual;
81}
82
83// Escape control characters but not \n and \t to keep the line breaks and
84// indentation intact.
85// eslint-disable-next-line no-control-regex
86const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
87const meta = [
88  '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
89  '\\u0005', '\\u0006', '\\u0007', '\\b', '',
90  '', '\\u000b', '\\f', '', '\\u000e',
91  '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
92  '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
93  '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
94  '\\u001e', '\\u001f',
95];
96
97const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)];
98
99let warned = false;
100
101// The assert module provides functions that throw
102// AssertionError's when particular conditions are not met. The
103// assert module must conform to the following interface.
104
105const assert = module.exports = ok;
106
107const NO_EXCEPTION_SENTINEL = {};
108
109// All of the following functions must throw an AssertionError
110// when a corresponding condition is not met, with a message that
111// may be undefined if not provided. All assertion methods provide
112// both the actual and expected values to the assertion error for
113// display purposes.
114
115function innerFail(obj) {
116  if (obj.message instanceof Error) throw obj.message;
117
118  throw new AssertionError(obj);
119}
120
121/**
122 * @param {any} actual
123 * @param {any} expected
124 * @param {string | Error} [message]
125 * @param {string} [operator]
126 * @param {Function} [stackStartFn]
127 * @returns {never}
128 */
129function fail(actual, expected, message, operator, stackStartFn) {
130  const argsLen = arguments.length;
131
132  let internalMessage = false;
133  if (actual == null && argsLen <= 1) {
134    internalMessage = true;
135    message = 'Failed';
136  } else if (argsLen === 1) {
137    message = actual;
138    actual = undefined;
139  } else {
140    if (warned === false) {
141      warned = true;
142      process.emitWarning(
143        'assert.fail() with more than one argument is deprecated. ' +
144          'Please use assert.strictEqual() instead or only pass a message.',
145        'DeprecationWarning',
146        'DEP0094'
147      );
148    }
149    if (argsLen === 2)
150      operator = '!=';
151  }
152
153  if (message instanceof Error) throw message;
154
155  const errArgs = {
156    actual,
157    expected,
158    operator: operator === undefined ? 'fail' : operator,
159    stackStartFn: stackStartFn || fail,
160    message
161  };
162  const err = new AssertionError(errArgs);
163  if (internalMessage) {
164    err.generatedMessage = true;
165  }
166  throw err;
167}
168
169assert.fail = fail;
170
171// The AssertionError is defined in internal/error.
172assert.AssertionError = AssertionError;
173
174function findColumn(fd, column, code) {
175  if (code.length > column + 100) {
176    try {
177      return parseCode(code, column);
178    } catch {
179      // End recursion in case no code could be parsed. The expression should
180      // have been found after 2500 characters, so stop trying.
181      if (code.length - column > 2500) {
182        // eslint-disable-next-line no-throw-literal
183        throw null;
184      }
185    }
186  }
187  // Read up to 2500 bytes more than necessary in columns. That way we address
188  // multi byte characters and read enough data to parse the code.
189  const bytesToRead = column - code.length + 2500;
190  const buffer = Buffer.allocUnsafe(bytesToRead);
191  const bytesRead = readSync(fd, buffer, 0, bytesToRead);
192  code += decoder.write(buffer.slice(0, bytesRead));
193  // EOF: fast path.
194  if (bytesRead < bytesToRead) {
195    return parseCode(code, column);
196  }
197  // Read potentially missing code.
198  return findColumn(fd, column, code);
199}
200
201function getCode(fd, line, column) {
202  let bytesRead = 0;
203  if (line === 0) {
204    // Special handle line number one. This is more efficient and simplifies the
205    // rest of the algorithm. Read more than the regular column number in bytes
206    // to prevent multiple reads in case multi byte characters are used.
207    return findColumn(fd, column, '');
208  }
209  let lines = 0;
210  // Prevent blocking the event loop by limiting the maximum amount of
211  // data that may be read.
212  let maxReads = 32; // bytesPerRead * maxReads = 512 kb
213  const bytesPerRead = 16384;
214  // Use a single buffer up front that is reused until the call site is found.
215  let buffer = Buffer.allocUnsafe(bytesPerRead);
216  while (maxReads-- !== 0) {
217    // Only allocate a new buffer in case the needed line is found. All data
218    // before that can be discarded.
219    buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead);
220    bytesRead = readSync(fd, buffer, 0, bytesPerRead);
221    // Read the buffer until the required code line is found.
222    for (let i = 0; i < bytesRead; i++) {
223      if (buffer[i] === 10 && ++lines === line) {
224        // If the end of file is reached, directly parse the code and return.
225        if (bytesRead < bytesPerRead) {
226          return parseCode(buffer.toString('utf8', i + 1, bytesRead), column);
227        }
228        // Check if the read code is sufficient or read more until the whole
229        // expression is read. Make sure multi byte characters are preserved
230        // properly by using the decoder.
231        const code = decoder.write(buffer.slice(i + 1, bytesRead));
232        return findColumn(fd, column, code);
233      }
234    }
235  }
236}
237
238function parseCode(code, offset) {
239  // Lazy load acorn.
240  if (parseExpressionAt === undefined) {
241    const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
242    ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk'));
243
244    parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser);
245  }
246  let node;
247  let start = 0;
248  // Parse the read code until the correct expression is found.
249  do {
250    try {
251      node = parseExpressionAt(code, start, { ecmaVersion: 'latest' });
252      start = node.end + 1 || start;
253      // Find the CallExpression in the tree.
254      node = findNodeAround(node, offset, 'CallExpression');
255    } catch (err) {
256      // Unexpected token error and the like.
257      start += err.raisedAt || 1;
258      if (start > offset) {
259        // No matching expression found. This could happen if the assert
260        // expression is bigger than the provided buffer.
261        // eslint-disable-next-line no-throw-literal
262        throw null;
263      }
264    }
265  } while (node === undefined || node.node.end < offset);
266
267  return [
268    node.node.start,
269    StringPrototypeReplace(StringPrototypeSlice(code,
270                                                node.node.start, node.node.end),
271                           escapeSequencesRegExp, escapeFn),
272  ];
273}
274
275function getErrMessage(message, fn) {
276  const tmpLimit = Error.stackTraceLimit;
277  // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
278  // does to much work.
279  Error.stackTraceLimit = 1;
280  // We only need the stack trace. To minimize the overhead use an object
281  // instead of an error.
282  const err = {};
283  ErrorCaptureStackTrace(err, fn);
284  Error.stackTraceLimit = tmpLimit;
285
286  overrideStackTrace.set(err, (_, stack) => stack);
287  const call = err.stack[0];
288
289  const filename = call.getFileName();
290  const line = call.getLineNumber() - 1;
291  let column = call.getColumnNumber() - 1;
292  let identifier;
293  let code;
294
295  if (filename) {
296    identifier = `${filename}${line}${column}`;
297
298    // Skip Node.js modules!
299    if (filename.endsWith('.js') &&
300        NativeModule.exists(filename.slice(0, -3))) {
301      errorCache.set(identifier, undefined);
302      return;
303    }
304  } else {
305    const fn = call.getFunction();
306    if (!fn) {
307      return message;
308    }
309    code = String(fn);
310    identifier = `${code}${line}${column}`;
311  }
312
313  if (errorCache.has(identifier)) {
314    return errorCache.get(identifier);
315  }
316
317  let fd;
318  try {
319    // Set the stack trace limit to zero. This makes sure unexpected token
320    // errors are handled faster.
321    Error.stackTraceLimit = 0;
322
323    if (filename) {
324      if (decoder === undefined) {
325        const { StringDecoder } = require('string_decoder');
326        decoder = new StringDecoder('utf8');
327      }
328      fd = openSync(filename, 'r', 0o666);
329      // Reset column and message.
330      ({ 0: column, 1: message } = getCode(fd, line, column));
331      // Flush unfinished multi byte characters.
332      decoder.end();
333    } else {
334      for (let i = 0; i < line; i++) {
335        code = StringPrototypeSlice(code,
336                                    StringPrototypeIndexOf(code, '\n') + 1);
337      }
338      ({ 0: column, 1: message } = parseCode(code, column));
339    }
340    // Always normalize indentation, otherwise the message could look weird.
341    if (StringPrototypeIncludes(message, '\n')) {
342      if (EOL === '\r\n') {
343        message = StringPrototypeReplace(message, /\r\n/g, '\n');
344      }
345      const frames = StringPrototypeSplit(message, '\n');
346      message = ArrayPrototypeShift(frames);
347      for (const frame of frames) {
348        let pos = 0;
349        while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
350          pos++;
351        }
352        message += `\n  ${StringPrototypeSlice(frame, pos)}`;
353      }
354    }
355    message = `The expression evaluated to a falsy value:\n\n  ${message}\n`;
356    // Make sure to always set the cache! No matter if the message is
357    // undefined or not
358    errorCache.set(identifier, message);
359
360    return message;
361  } catch {
362    // Invalidate cache to prevent trying to read this part again.
363    errorCache.set(identifier, undefined);
364  } finally {
365    // Reset limit.
366    Error.stackTraceLimit = tmpLimit;
367    if (fd !== undefined)
368      closeSync(fd);
369  }
370}
371
372function innerOk(fn, argLen, value, message) {
373  if (!value) {
374    let generatedMessage = false;
375
376    if (argLen === 0) {
377      generatedMessage = true;
378      message = 'No value argument passed to `assert.ok()`';
379    } else if (message == null) {
380      generatedMessage = true;
381      message = getErrMessage(message, fn);
382    } else if (message instanceof Error) {
383      throw message;
384    }
385
386    const err = new AssertionError({
387      actual: value,
388      expected: true,
389      message,
390      operator: '==',
391      stackStartFn: fn
392    });
393    err.generatedMessage = generatedMessage;
394    throw err;
395  }
396}
397
398/**
399 * Pure assertion tests whether a value is truthy, as determined
400 * by !!value.
401 * @param {...any} args
402 * @returns {void}
403 */
404function ok(...args) {
405  innerOk(ok, args.length, ...args);
406}
407assert.ok = ok;
408
409/**
410 * The equality assertion tests shallow, coercive equality with ==.
411 * @param {any} actual
412 * @param {any} expected
413 * @param {string | Error} [message]
414 * @returns {void}
415 */
416/* eslint-disable no-restricted-properties */
417assert.equal = function equal(actual, expected, message) {
418  if (arguments.length < 2) {
419    throw new ERR_MISSING_ARGS('actual', 'expected');
420  }
421  // eslint-disable-next-line eqeqeq
422  if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) {
423    innerFail({
424      actual,
425      expected,
426      message,
427      operator: '==',
428      stackStartFn: equal
429    });
430  }
431};
432
433/**
434 * The non-equality assertion tests for whether two objects are not
435 * equal with !=.
436 * @param {any} actual
437 * @param {any} expected
438 * @param {string | Error} [message]
439 * @returns {void}
440 */
441assert.notEqual = function notEqual(actual, expected, message) {
442  if (arguments.length < 2) {
443    throw new ERR_MISSING_ARGS('actual', 'expected');
444  }
445  // eslint-disable-next-line eqeqeq
446  if (actual == expected || (NumberIsNaN(actual) && NumberIsNaN(expected))) {
447    innerFail({
448      actual,
449      expected,
450      message,
451      operator: '!=',
452      stackStartFn: notEqual
453    });
454  }
455};
456
457/**
458 * The deep equivalence assertion tests a deep equality relation.
459 * @param {any} actual
460 * @param {any} expected
461 * @param {string | Error} [message]
462 * @returns {void}
463 */
464assert.deepEqual = function deepEqual(actual, expected, message) {
465  if (arguments.length < 2) {
466    throw new ERR_MISSING_ARGS('actual', 'expected');
467  }
468  if (isDeepEqual === undefined) lazyLoadComparison();
469  if (!isDeepEqual(actual, expected)) {
470    innerFail({
471      actual,
472      expected,
473      message,
474      operator: 'deepEqual',
475      stackStartFn: deepEqual
476    });
477  }
478};
479
480/**
481 * The deep non-equivalence assertion tests for any deep inequality.
482 * @param {any} actual
483 * @param {any} expected
484 * @param {string | Error} [message]
485 * @returns {void}
486 */
487assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
488  if (arguments.length < 2) {
489    throw new ERR_MISSING_ARGS('actual', 'expected');
490  }
491  if (isDeepEqual === undefined) lazyLoadComparison();
492  if (isDeepEqual(actual, expected)) {
493    innerFail({
494      actual,
495      expected,
496      message,
497      operator: 'notDeepEqual',
498      stackStartFn: notDeepEqual
499    });
500  }
501};
502/* eslint-enable */
503
504/**
505 * The deep strict equivalence assertion tests a deep strict equality
506 * relation.
507 * @param {any} actual
508 * @param {any} expected
509 * @param {string | Error} [message]
510 * @returns {void}
511 */
512assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
513  if (arguments.length < 2) {
514    throw new ERR_MISSING_ARGS('actual', 'expected');
515  }
516  if (isDeepEqual === undefined) lazyLoadComparison();
517  if (!isDeepStrictEqual(actual, expected)) {
518    innerFail({
519      actual,
520      expected,
521      message,
522      operator: 'deepStrictEqual',
523      stackStartFn: deepStrictEqual
524    });
525  }
526};
527
528/**
529 * The deep strict non-equivalence assertion tests for any deep strict
530 * inequality.
531 * @param {any} actual
532 * @param {any} expected
533 * @param {string | Error} [message]
534 * @returns {void}
535 */
536assert.notDeepStrictEqual = notDeepStrictEqual;
537function notDeepStrictEqual(actual, expected, message) {
538  if (arguments.length < 2) {
539    throw new ERR_MISSING_ARGS('actual', 'expected');
540  }
541  if (isDeepEqual === undefined) lazyLoadComparison();
542  if (isDeepStrictEqual(actual, expected)) {
543    innerFail({
544      actual,
545      expected,
546      message,
547      operator: 'notDeepStrictEqual',
548      stackStartFn: notDeepStrictEqual
549    });
550  }
551}
552
553/**
554 * The strict equivalence assertion tests a strict equality relation.
555 * @param {any} actual
556 * @param {any} expected
557 * @param {string | Error} [message]
558 * @returns {void}
559 */
560assert.strictEqual = function strictEqual(actual, expected, message) {
561  if (arguments.length < 2) {
562    throw new ERR_MISSING_ARGS('actual', 'expected');
563  }
564  if (!ObjectIs(actual, expected)) {
565    innerFail({
566      actual,
567      expected,
568      message,
569      operator: 'strictEqual',
570      stackStartFn: strictEqual
571    });
572  }
573};
574
575/**
576 * The strict non-equivalence assertion tests for any strict inequality.
577 * @param {any} actual
578 * @param {any} expected
579 * @param {string | Error} [message]
580 * @returns {void}
581 */
582assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
583  if (arguments.length < 2) {
584    throw new ERR_MISSING_ARGS('actual', 'expected');
585  }
586  if (ObjectIs(actual, expected)) {
587    innerFail({
588      actual,
589      expected,
590      message,
591      operator: 'notStrictEqual',
592      stackStartFn: notStrictEqual
593    });
594  }
595};
596
597class Comparison {
598  constructor(obj, keys, actual) {
599    for (const key of keys) {
600      if (key in obj) {
601        if (actual !== undefined &&
602            typeof actual[key] === 'string' &&
603            isRegExp(obj[key]) &&
604            RegExpPrototypeTest(obj[key], actual[key])) {
605          this[key] = actual[key];
606        } else {
607          this[key] = obj[key];
608        }
609      }
610    }
611  }
612}
613
614function compareExceptionKey(actual, expected, key, message, keys, fn) {
615  if (!(key in actual) || !isDeepStrictEqual(actual[key], expected[key])) {
616    if (!message) {
617      // Create placeholder objects to create a nice output.
618      const a = new Comparison(actual, keys);
619      const b = new Comparison(expected, keys, actual);
620
621      const err = new AssertionError({
622        actual: a,
623        expected: b,
624        operator: 'deepStrictEqual',
625        stackStartFn: fn
626      });
627      err.actual = actual;
628      err.expected = expected;
629      err.operator = fn.name;
630      throw err;
631    }
632    innerFail({
633      actual,
634      expected,
635      message,
636      operator: fn.name,
637      stackStartFn: fn
638    });
639  }
640}
641
642function expectedException(actual, expected, message, fn) {
643  let generatedMessage = false;
644  let throwError = false;
645
646  if (typeof expected !== 'function') {
647    // Handle regular expressions.
648    if (isRegExp(expected)) {
649      const str = String(actual);
650      if (RegExpPrototypeTest(expected, str))
651        return;
652
653      if (!message) {
654        generatedMessage = true;
655        message = 'The input did not match the regular expression ' +
656                  `${inspect(expected)}. Input:\n\n${inspect(str)}\n`;
657      }
658      throwError = true;
659      // Handle primitives properly.
660    } else if (typeof actual !== 'object' || actual === null) {
661      const err = new AssertionError({
662        actual,
663        expected,
664        message,
665        operator: 'deepStrictEqual',
666        stackStartFn: fn
667      });
668      err.operator = fn.name;
669      throw err;
670    } else {
671      // Handle validation objects.
672      const keys = ObjectKeys(expected);
673      // Special handle errors to make sure the name and the message are
674      // compared as well.
675      if (expected instanceof Error) {
676        ArrayPrototypePush(keys, 'name', 'message');
677      } else if (keys.length === 0) {
678        throw new ERR_INVALID_ARG_VALUE('error',
679                                        expected, 'may not be an empty object');
680      }
681      if (isDeepEqual === undefined) lazyLoadComparison();
682      for (const key of keys) {
683        if (typeof actual[key] === 'string' &&
684            isRegExp(expected[key]) &&
685            RegExpPrototypeTest(expected[key], actual[key])) {
686          continue;
687        }
688        compareExceptionKey(actual, expected, key, message, keys, fn);
689      }
690      return;
691    }
692  // Guard instanceof against arrow functions as they don't have a prototype.
693  // Check for matching Error classes.
694  } else if (expected.prototype !== undefined && actual instanceof expected) {
695    return;
696  } else if (ObjectPrototypeIsPrototypeOf(Error, expected)) {
697    if (!message) {
698      generatedMessage = true;
699      message = 'The error is expected to be an instance of ' +
700        `"${expected.name}". Received `;
701      if (isError(actual)) {
702        const name = (actual.constructor && actual.constructor.name) ||
703                     actual.name;
704        if (expected.name === name) {
705          message += 'an error with identical name but a different prototype.';
706        } else {
707          message += `"${name}"`;
708        }
709        if (actual.message) {
710          message += `\n\nError message:\n\n${actual.message}`;
711        }
712      } else {
713        message += `"${inspect(actual, { depth: -1 })}"`;
714      }
715    }
716    throwError = true;
717  } else {
718    // Check validation functions return value.
719    const res = ReflectApply(expected, {}, [actual]);
720    if (res !== true) {
721      if (!message) {
722        generatedMessage = true;
723        const name = expected.name ? `"${expected.name}" ` : '';
724        message = `The ${name}validation function is expected to return` +
725          ` "true". Received ${inspect(res)}`;
726
727        if (isError(actual)) {
728          message += `\n\nCaught error:\n\n${actual}`;
729        }
730      }
731      throwError = true;
732    }
733  }
734
735  if (throwError) {
736    const err = new AssertionError({
737      actual,
738      expected,
739      message,
740      operator: fn.name,
741      stackStartFn: fn
742    });
743    err.generatedMessage = generatedMessage;
744    throw err;
745  }
746}
747
748function getActual(fn) {
749  if (typeof fn !== 'function') {
750    throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn);
751  }
752  try {
753    fn();
754  } catch (e) {
755    return e;
756  }
757  return NO_EXCEPTION_SENTINEL;
758}
759
760function checkIsPromise(obj) {
761  // Accept native ES6 promises and promises that are implemented in a similar
762  // way. Do not accept thenables that use a function as `obj` and that have no
763  // `catch` handler.
764  return isPromise(obj) ||
765    (obj !== null && typeof obj === 'object' &&
766    typeof obj.then === 'function' &&
767    typeof obj.catch === 'function');
768}
769
770async function waitForActual(promiseFn) {
771  let resultPromise;
772  if (typeof promiseFn === 'function') {
773    // Return a rejected promise if `promiseFn` throws synchronously.
774    resultPromise = promiseFn();
775    // Fail in case no promise is returned.
776    if (!checkIsPromise(resultPromise)) {
777      throw new ERR_INVALID_RETURN_VALUE('instance of Promise',
778                                         'promiseFn', resultPromise);
779    }
780  } else if (checkIsPromise(promiseFn)) {
781    resultPromise = promiseFn;
782  } else {
783    throw new ERR_INVALID_ARG_TYPE(
784      'promiseFn', ['Function', 'Promise'], promiseFn);
785  }
786
787  try {
788    await resultPromise;
789  } catch (e) {
790    return e;
791  }
792  return NO_EXCEPTION_SENTINEL;
793}
794
795function expectsError(stackStartFn, actual, error, message) {
796  if (typeof error === 'string') {
797    if (arguments.length === 4) {
798      throw new ERR_INVALID_ARG_TYPE('error',
799                                     ['Object', 'Error', 'Function', 'RegExp'],
800                                     error);
801    }
802    if (typeof actual === 'object' && actual !== null) {
803      if (actual.message === error) {
804        throw new ERR_AMBIGUOUS_ARGUMENT(
805          'error/message',
806          `The error message "${actual.message}" is identical to the message.`
807        );
808      }
809    } else if (actual === error) {
810      throw new ERR_AMBIGUOUS_ARGUMENT(
811        'error/message',
812        `The error "${actual}" is identical to the message.`
813      );
814    }
815    message = error;
816    error = undefined;
817  } else if (error != null &&
818             typeof error !== 'object' &&
819             typeof error !== 'function') {
820    throw new ERR_INVALID_ARG_TYPE('error',
821                                   ['Object', 'Error', 'Function', 'RegExp'],
822                                   error);
823  }
824
825  if (actual === NO_EXCEPTION_SENTINEL) {
826    let details = '';
827    if (error && error.name) {
828      details += ` (${error.name})`;
829    }
830    details += message ? `: ${message}` : '.';
831    const fnType = stackStartFn.name === 'rejects' ? 'rejection' : 'exception';
832    innerFail({
833      actual: undefined,
834      expected: error,
835      operator: stackStartFn.name,
836      message: `Missing expected ${fnType}${details}`,
837      stackStartFn
838    });
839  }
840
841  if (!error)
842    return;
843
844  expectedException(actual, error, message, stackStartFn);
845}
846
847function hasMatchingError(actual, expected) {
848  if (typeof expected !== 'function') {
849    if (isRegExp(expected)) {
850      const str = String(actual);
851      return RegExpPrototypeTest(expected, str);
852    }
853    throw new ERR_INVALID_ARG_TYPE(
854      'expected', ['Function', 'RegExp'], expected
855    );
856  }
857  // Guard instanceof against arrow functions as they don't have a prototype.
858  if (expected.prototype !== undefined && actual instanceof expected) {
859    return true;
860  }
861  if (ObjectPrototypeIsPrototypeOf(Error, expected)) {
862    return false;
863  }
864  return ReflectApply(expected, {}, [actual]) === true;
865}
866
867function expectsNoError(stackStartFn, actual, error, message) {
868  if (actual === NO_EXCEPTION_SENTINEL)
869    return;
870
871  if (typeof error === 'string') {
872    message = error;
873    error = undefined;
874  }
875
876  if (!error || hasMatchingError(actual, error)) {
877    const details = message ? `: ${message}` : '.';
878    const fnType = stackStartFn.name === 'doesNotReject' ?
879      'rejection' : 'exception';
880    innerFail({
881      actual,
882      expected: error,
883      operator: stackStartFn.name,
884      message: `Got unwanted ${fnType}${details}\n` +
885               `Actual message: "${actual && actual.message}"`,
886      stackStartFn
887    });
888  }
889  throw actual;
890}
891
892/**
893 * Expects the function `promiseFn` to throw an error.
894 * @param {() => any} promiseFn
895 * @param {...any} [args]
896 * @returns {void}
897 */
898assert.throws = function throws(promiseFn, ...args) {
899  expectsError(throws, getActual(promiseFn), ...args);
900};
901
902/**
903 * Expects `promiseFn` function or its value to reject.
904 * @param {() => Promise<any>} promiseFn
905 * @param {...any} [args]
906 * @returns {Promise<void>}
907 */
908assert.rejects = async function rejects(promiseFn, ...args) {
909  expectsError(rejects, await waitForActual(promiseFn), ...args);
910};
911
912/**
913 * Asserts that the function `fn` does not throw an error.
914 * @param {() => any} fn
915 * @param {...any} [args]
916 * @returns {void}
917 */
918assert.doesNotThrow = function doesNotThrow(fn, ...args) {
919  expectsNoError(doesNotThrow, getActual(fn), ...args);
920};
921
922/**
923 * Expects `fn` or its value to not reject.
924 * @param {() => Promise<any>} fn
925 * @param {...any} [args]
926 * @returns {Promise<void>}
927 */
928assert.doesNotReject = async function doesNotReject(fn, ...args) {
929  expectsNoError(doesNotReject, await waitForActual(fn), ...args);
930};
931
932/**
933 * Throws `value` if the value is not `null` or `undefined`.
934 * @param {any} err
935 * @returns {void}
936 */
937assert.ifError = function ifError(err) {
938  if (err !== null && err !== undefined) {
939    let message = 'ifError got unwanted exception: ';
940    if (typeof err === 'object' && typeof err.message === 'string') {
941      if (err.message.length === 0 && err.constructor) {
942        message += err.constructor.name;
943      } else {
944        message += err.message;
945      }
946    } else {
947      message += inspect(err);
948    }
949
950    const newErr = new AssertionError({
951      actual: err,
952      expected: null,
953      operator: 'ifError',
954      message,
955      stackStartFn: ifError
956    });
957
958    // Make sure we actually have a stack trace!
959    const origStack = err.stack;
960
961    if (typeof origStack === 'string') {
962      // This will remove any duplicated frames from the error frames taken
963      // from within `ifError` and add the original error frames to the newly
964      // created ones.
965      const tmp2 = StringPrototypeSplit(origStack, '\n');
966      ArrayPrototypeShift(tmp2);
967      // Filter all frames existing in err.stack.
968      let tmp1 = StringPrototypeSplit(newErr.stack, '\n');
969      for (const errFrame of tmp2) {
970        // Find the first occurrence of the frame.
971        const pos = ArrayPrototypeIndexOf(tmp1, errFrame);
972        if (pos !== -1) {
973          // Only keep new frames.
974          tmp1 = ArrayPrototypeSlice(tmp1, 0, pos);
975          break;
976        }
977      }
978      newErr.stack =
979        `${ArrayPrototypeJoin(tmp1, '\n')}\n${ArrayPrototypeJoin(tmp2, '\n')}`;
980    }
981
982    throw newErr;
983  }
984};
985
986function internalMatch(string, regexp, message, fn) {
987  if (!isRegExp(regexp)) {
988    throw new ERR_INVALID_ARG_TYPE(
989      'regexp', 'RegExp', regexp
990    );
991  }
992  const match = fn.name === 'match';
993  if (typeof string !== 'string' ||
994      RegExpPrototypeTest(regexp, string) !== match) {
995    if (message instanceof Error) {
996      throw message;
997    }
998
999    const generatedMessage = !message;
1000
1001    // 'The input was expected to not match the regular expression ' +
1002    message = message || (typeof string !== 'string' ?
1003      'The "string" argument must be of type string. Received type ' +
1004        `${typeof string} (${inspect(string)})` :
1005      (match ?
1006        'The input did not match the regular expression ' :
1007        'The input was expected to not match the regular expression ') +
1008          `${inspect(regexp)}. Input:\n\n${inspect(string)}\n`);
1009    const err = new AssertionError({
1010      actual: string,
1011      expected: regexp,
1012      message,
1013      operator: fn.name,
1014      stackStartFn: fn
1015    });
1016    err.generatedMessage = generatedMessage;
1017    throw err;
1018  }
1019}
1020
1021/**
1022 * Expects the `string` input to match the regular expression.
1023 * @param {string} string
1024 * @param {RegExp} regexp
1025 * @param {string | Error} [message]
1026 * @returns {void}
1027 */
1028assert.match = function match(string, regexp, message) {
1029  internalMatch(string, regexp, message, match);
1030};
1031
1032/**
1033 * Expects the `string` input not to match the regular expression.
1034 * @param {string} string
1035 * @param {RegExp} regexp
1036 * @param {string | Error} [message]
1037 * @returns {void}
1038 */
1039assert.doesNotMatch = function doesNotMatch(string, regexp, message) {
1040  internalMatch(string, regexp, message, doesNotMatch);
1041};
1042
1043assert.CallTracker = CallTracker;
1044
1045/**
1046 * Expose a strict only variant of assert.
1047 * @param {...any} args
1048 * @returns {void}
1049 */
1050function strict(...args) {
1051  innerOk(strict, args.length, ...args);
1052}
1053
1054assert.strict = ObjectAssign(strict, assert, {
1055  equal: assert.strictEqual,
1056  deepEqual: assert.deepStrictEqual,
1057  notEqual: assert.notStrictEqual,
1058  notDeepEqual: assert.notDeepStrictEqual
1059});
1060
1061assert.strict.strict = assert.strict;
1062