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