Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
c87ed21
assert: port common.mustCall() to assert
DavenportEmma Feb 27, 2020
4423304
vm: throw error when duplicated exportNames in SyntheticModule
himself65 Apr 13, 2020
8c40ffc
tools: update broken types in type parser
cjihrig Apr 25, 2020
8d5ac1b
wasi: rename __wasi_unstable_reactor_start()
cjihrig Apr 26, 2020
d7d9960
wasi: update start() behavior to match spec
cjihrig Apr 26, 2020
8325c29
deps: update to uvwasi 0.0.8
cjihrig Apr 26, 2020
ed45b51
path: fix comment grammar
thecodrr Apr 20, 2020
ec24577
v8: use AliasedBuffers for passing heap statistics around
joyeecheung Apr 19, 2020
542da43
lib: fix validateport error message when allowZero is false
rickyes Apr 15, 2020
aced1f5
test: flaky test-stdout-close-catch on freebsd
sam-github Apr 14, 2020
f79ef96
test: mark test flaky on freebsd
sam-github Apr 14, 2020
3cb1713
http2,doc: minor fixes
mildsunrise Apr 27, 2020
db293c4
cluster: fix error on worker disconnect/destroy
santigimeno Apr 1, 2020
637442f
crypto: key size must be int32 in DiffieHellman()
bnoordhuis Apr 10, 2020
e07cca6
crypto: generator must be int32 in DiffieHellman()
bnoordhuis Apr 10, 2020
83e165b
crypto: check DiffieHellman p and g params
bnoordhuis Apr 10, 2020
c49e3ea
console: support console constructor groupIndentation option
rickyes Apr 21, 2020
10a87c8
src: remove unnecessary fully qualified names
rickyes Apr 26, 2020
12426f5
tools: update [email protected]
Trott Apr 26, 2020
c5a2f9a
doc: fix markdown parsing on doc/api/os.md
juanarbol Apr 25, 2020
466213d
n-api: simplify uv_idle wrangling
bnoordhuis Apr 22, 2020
82e459d
doc: don't check links in tmp dirs
bnoordhuis Apr 22, 2020
92c7e06
test: check args on SourceTextModule cachedData
juanarbol Apr 20, 2020
b183d0a
stream: let Duplex re-use Writable properties
ronag Apr 26, 2020
289a5c8
doc: some grammar fixes
ChrisAHolland Apr 26, 2020
de15edc
doc: improve worker pool example
ranjan-purbey Apr 26, 2020
3c2f608
test: correct typo in test name
cjihrig Apr 27, 2020
c6d632a
worker: unify custom error creation
addaleax Apr 27, 2020
bc9e413
worker: add stack size resource limit option
addaleax Apr 27, 2020
bc40ed3
stream: add null check in Readable.from
rexagod Apr 16, 2020
2396219
doc: correct Nodejs to Node.js spelling
nschonni Apr 27, 2020
541ea03
src: separate out NgLibMemoryManagerBase
jasnell Apr 27, 2020
eab4be1
lib: cosmetic change to builtinLibs list for maintainability
jasnell Apr 27, 2020
f4e5ab1
src: crypto::UseSNIContext to use BaseObjectPtr
jasnell Apr 27, 2020
f61928b
src: return undefined when validation err == 0
jasnell Apr 27, 2020
3866dc1
wasi: use free() to release preopen array
addaleax Apr 28, 2020
de643bc
src: use unique_ptr for CachedData in ContextifyScript::New
addaleax Apr 28, 2020
9ccb6b2
test: add missing calls to napi_async_destroy
addaleax Apr 28, 2020
af7da46
test: fix out-of-bound reads from invalid sizeof usage
addaleax Apr 28, 2020
f62d92b
build: add --error-on-warn configure flag
danbev Apr 6, 2020
a87d371
tools: fix redundant-move warning in inspector
danbev Apr 6, 2020
5eccf1e
module: no type module resolver side effects
guybedford Apr 27, 2020
4cfa7e0
stream: simplify Readable push/unshift logic
himself65 Apr 17, 2020
d39254a
vm: fix vm.measureMemory() and introduce execution option
joyeecheung Apr 22, 2020
45032a3
stream: fix stream.finished on Duplex
ronag Apr 28, 2020
89ed7a5
test: move test-process-title to sequential
addaleax Apr 29, 2020
c5a4534
deps: V8: backport e29c62b74854
addaleax Apr 28, 2020
e276524
test: skip memory usage tests when ASAN is enabled
addaleax Apr 28, 2020
451993e
http: set default timeout in agent keepSocketAlive
omsmith Apr 28, 2020
a990137
doc: fix the spelling error in stream.md
yunnysunny Feb 1, 2020
36d5002
doc: clarify when not to run CI on docs
juanarbol Apr 27, 2020
d02ced8
doc: add util.types.isArrayBufferView()
kevinoid Apr 27, 2020
6734cc4
tools: bump remark-preset-lint-node to 1.15.0
Trott Apr 30, 2020
bb2d2f6
src: remove unused v8 Message namespace
edsadr Apr 30, 2020
7893bde
doc: fix a typo in crypto.generateKeyPairSync()
himself65 May 1, 2020
a4ec01c
http: simplify sending header
ronag May 1, 2020
2174159
esm: improve commonjs hint on module not found
dnlup Mar 21, 2020
413f5d3
doc: add missing deprecation not
ronag May 1, 2020
60ebbc4
test: update c8 ignore comment
Apr 30, 2020
74b0e8c
http: ensure client request emits close
ronag Apr 30, 2020
6925b35
doc: mark assert.CallTracker experimental
BridgeAR Apr 28, 2020
d68f78f
2020-05-05, Version 14.2.0 (Current)
targos May 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
assert: port common.mustCall() to assert
Fixes: #31392

PR-URL: #31982
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Denys Otrishko <[email protected]>
  • Loading branch information
DavenportEmma authored and targos committed May 4, 2020
commit c87ed21fdf6e393e6f7601bd867f61e529cc058d
135 changes: 135 additions & 0 deletions doc/api/assert.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,137 @@ try {
}
```

## Class: `assert.CallTracker`

### `new assert.CallTracker()`
<!-- YAML
added: REPLACEME
-->

Creates a new [`CallTracker`][] object which can be used to track if functions
were called a specific number of times. The `tracker.verify()` must be called
for the verification to take place. The usual pattern would be to call it in a
[`process.on('exit')`][] handler.

```js
const assert = require('assert');

const tracker = new assert.CallTracker();

function func() {}

// callsfunc() must be called exactly 1 time before tracker.verify().
const callsfunc = tracker.calls(func, 1);

callsfunc();

// Calls tracker.verify() and verifies if all tracker.calls() functions have
// been called exact times.
process.on('exit', () => {
tracker.verify();
});
```

### `tracker.calls([fn][, exact])`
<!-- YAML
added: REPLACEME
-->

* `fn` {Function} **Default** A no-op function.
* `exact` {number} **Default** `1`.
* Returns: {Function} that wraps `fn`.

The wrapper function is expected to be called exactly `exact` times. If the
function has not been called exactly `exact` times when
[`tracker.verify()`][] is called, then [`tracker.verify()`][] will throw an
error.

```js
const assert = require('assert');

// Creates call tracker.
const tracker = new assert.CallTracker();

function func() {}

// Returns a function that wraps func() that must be called exact times
// before tracker.verify().
const callsfunc = tracker.calls(func);
```

### `tracker.report()`
<!-- YAML
added: REPLACEME
-->

* Returns: {Array} of objects containing information about the wrapper functions
returned by [`tracker.calls()`][].
* Object {Object}
* `message` {string}
* `actual` {number} The actual number of times the function was called.
* `expected` {number} The number of times the function was expected to be
called.
* `operator` {string} The name of the function that is wrapped.
* `stack` {Object} A stack trace of the function.

The arrays contains information about the expected and actual number of calls of
the functions that have not been called the expected number of times.

```js
const assert = require('assert');

// Creates call tracker.
const tracker = new assert.CallTracker();

function func() {}

function foo() {}

// Returns a function that wraps func() that must be called exact times
// before tracker.verify().
const callsfunc = tracker.calls(func, 2);

// Returns an array containing information on callsfunc()
tracker.report();
// [
// {
// message: 'Expected the func function to be executed 2 time(s) but was
// executed 0 time(s).',
// actual: 0,
// expected: 2,
// operator: 'func',
// stack: stack trace
// }
// ]
```

### `tracker.verify()`
<!-- YAML
added: REPLACEME
-->

Iterates through the list of functions passed to
[`tracker.calls()`][] and will throw an error for functions that
have not been called the expected number of times.

```js
const assert = require('assert');

// Creates call tracker.
const tracker = new assert.CallTracker();

function func() {}

// Returns a function that wraps func() that must be called exact times
// before tracker.verify().
const callsfunc = tracker.calls(func, 2);

callsfunc();

// Will throw an error since callsfunc() was only called once.
tracker.verify();
```

## `assert(value[, message])`
<!-- YAML
added: v0.5.9
Expand Down Expand Up @@ -1429,6 +1560,7 @@ argument.
[`TypeError`]: errors.html#errors_class_typeerror
[`WeakMap`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
[`WeakSet`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet
[`CallTracker`]: #assert_class_assert_calltracker
[`assert.deepEqual()`]: #assert_assert_deepequal_actual_expected_message
[`assert.deepStrictEqual()`]: #assert_assert_deepstrictequal_actual_expected_message
[`assert.doesNotThrow()`]: #assert_assert_doesnotthrow_fn_error_message
Expand All @@ -1440,6 +1572,9 @@ argument.
[`assert.ok()`]: #assert_assert_ok_value_message
[`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message
[`assert.throws()`]: #assert_assert_throws_fn_error_message
[`process.on('exit')`]: process.html#process_event_exit
[`tracker.calls()`]: #assert_class_assert_CallTracker#tracker_calls
[`tracker.verify()`]: #assert_class_assert_CallTracker#tracker_verify
[strict assertion mode]: #assert_strict_assertion_mode
[Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
[Object wrappers]: https://developer.mozilla.org/en-US/docs/Glossary/Primitive#Primitive_wrapper_objects_in_JavaScript
Expand Down
7 changes: 7 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,12 @@ A `Transform` stream finished with data still in the write buffer.

The initialization of a TTY failed due to a system error.

<a id="ERR_UNAVAILABLE_DURING_EXIT"></a>
### `ERR_UNAVAILABLE_DURING_EXIT`

Function was called within a [`process.on('exit')`][] handler that shouldn't be
called within [`process.on('exit')`][] handler.

<a id="ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET"></a>
### `ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET`

Expand Down Expand Up @@ -2545,6 +2551,7 @@ such as `process.stdout.on('data')`.
[`net`]: net.html
[`new URL(input)`]: url.html#url_constructor_new_url_input_base
[`new URLSearchParams(iterable)`]: url.html#url_constructor_new_urlsearchparams_iterable
[`process.on('exit')`]: process.html#Event:-`'exit'`
[`process.send()`]: process.html#process_process_send_message_sendhandle_options_callback
[`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn
[`readable._read()`]: stream.html#stream_readable_read_size_1
Expand Down
3 changes: 3 additions & 0 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const { NativeModule } = require('internal/bootstrap/loaders');
const { isError } = require('internal/util');

const errorCache = new Map();
const CallTracker = require('internal/assert/calltracker');

let isDeepEqual;
let isDeepStrictEqual;
Expand Down Expand Up @@ -928,6 +929,8 @@ assert.doesNotMatch = function doesNotMatch(string, regexp, message) {
internalMatch(string, regexp, message, doesNotMatch);
};

assert.CallTracker = CallTracker;

// Expose a strict only variant of assert
function strict(...args) {
innerOk(strict, args.length, ...args);
Expand Down
20 changes: 17 additions & 3 deletions lib/internal/assert/assertion_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ class AssertionError extends Error {
message,
operator,
stackStartFn,
details,
// Compatibility with older versions.
stackStartFunction
} = options;
Expand Down Expand Up @@ -426,9 +427,22 @@ class AssertionError extends Error {
configurable: true
});
this.code = 'ERR_ASSERTION';
this.actual = actual;
this.expected = expected;
this.operator = operator;
if (details) {
this.actual = undefined;
this.expected = undefined;
this.operator = undefined;
for (let i = 0; i < details.length; i++) {
this['message ' + i] = details[i].message;
this['actual ' + i] = details[i].actual;
this['expected ' + i] = details[i].expected;
this['operator ' + i] = details[i].operator;
this['stack trace ' + i] = details[i].stack;
}
} else {
this.actual = actual;
this.expected = expected;
this.operator = operator;
}
// eslint-disable-next-line no-restricted-syntax
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
// Create error message including the error code in the name.
Expand Down
93 changes: 93 additions & 0 deletions lib/internal/assert/calltracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict';

const {
Error,
SafeSet,
} = primordials;

const {
codes: {
ERR_UNAVAILABLE_DURING_EXIT,
},
} = require('internal/errors');
const AssertionError = require('internal/assert/assertion_error');
const {
validateUint32,
} = require('internal/validators');

const noop = () => {};

class CallTracker {

#callChecks = new SafeSet()

calls(fn, exact = 1) {
if (process._exiting)
throw new ERR_UNAVAILABLE_DURING_EXIT();
if (typeof fn === 'number') {
exact = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}

validateUint32(exact, 'exact', true);

const context = {
exact,
actual: 0,
// eslint-disable-next-line no-restricted-syntax
stackTrace: new Error(),
name: fn.name || 'calls'
};
const callChecks = this.#callChecks;
callChecks.add(context);

return function() {
context.actual++;
if (context.actual === context.exact) {
// Once function has reached its call count remove it from
// callChecks set to prevent memory leaks.
callChecks.delete(context);
}
// If function has been called more than expected times, add back into
// callchecks.
if (context.actual === context.exact + 1) {
callChecks.add(context);
}
return fn.apply(this, arguments);
};
}

report() {
const errors = [];
for (const context of this.#callChecks) {
// If functions have not been called exact times
if (context.actual !== context.exact) {
const message = `Expected the ${context.name} function to be ` +
`executed ${context.exact} time(s) but was ` +
`executed ${context.actual} time(s).`;
errors.push({
message,
actual: context.actual,
expected: context.exact,
operator: context.name,
stack: context.stackTrace
});
}
}
return errors;
}

verify() {
const errors = this.report();
if (errors.length > 0) {
throw new AssertionError({
message: 'Function(s) were not called the expected number of times',
details: errors,
});
}
}
}

module.exports = CallTracker;
2 changes: 2 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,8 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING',
E('ERR_TRANSFORM_WITH_LENGTH_0',
'Calling transform done when writableState.length != 0', Error);
E('ERR_TTY_INIT_FAILED', 'TTY initialization failed', SystemError);
E('ERR_UNAVAILABLE_DURING_EXIT', 'Cannot call function in process exit ' +
'handler', Error);
E('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET',
'`process.setupUncaughtExceptionCapture()` was called while a capture ' +
'callback was already active',
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
'lib/zlib.js',
'lib/internal/assert.js',
'lib/internal/assert/assertion_error.js',
'lib/internal/assert/calltracker.js',
'lib/internal/async_hooks.js',
'lib/internal/buffer.js',
'lib/internal/cli_table.js',
Expand Down
Loading