Skip to content

Commit fccc0bf

Browse files
committed
test: add mustCallAtLeast
PR-URL: #12935 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 46e773c commit fccc0bf

File tree

5 files changed

+88
-17
lines changed

5 files changed

+88
-17
lines changed

test/common/README.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ Gets IP of localhost
182182

183183
Array of IPV6 hosts.
184184

185-
### mustCall([fn][, expected])
186-
* fn [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
187-
* expected [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
185+
### mustCall([fn][, exact])
186+
* `fn` [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) default = `common.noop`
187+
* `exact` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
188188
* return [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
189189

190190
Returns a function that calls `fn`. If the returned function has not been called
@@ -193,6 +193,17 @@ fail.
193193

194194
If `fn` is not provided, `common.noop` will be used.
195195

196+
### mustCallAtLeast([fn][, minimum])
197+
* `fn` [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) default = `common.noop`
198+
* `minimum` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
199+
* return [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
200+
201+
Returns a function that calls `fn`. If the returned function has not been called
202+
at least `minimum` number of times when the test is complete, then the test will
203+
fail.
204+
205+
If `fn` is not provided, `common.noop` will be used.
206+
196207
### nodeProcessAborted(exitCode, signal)
197208
* `exitCode` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type)
198209
* `signal` [&lt;String>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)

test/common/index.js

+24-11
Original file line numberDiff line numberDiff line change
@@ -459,36 +459,49 @@ function runCallChecks(exitCode) {
459459
if (exitCode !== 0) return;
460460

461461
const failed = mustCallChecks.filter(function(context) {
462-
return context.actual !== context.expected;
462+
if ('minimum' in context) {
463+
context.messageSegment = `at least ${context.minimum}`;
464+
return context.actual < context.minimum;
465+
} else {
466+
context.messageSegment = `exactly ${context.exact}`;
467+
return context.actual !== context.exact;
468+
}
463469
});
464470

465471
failed.forEach(function(context) {
466-
console.log('Mismatched %s function calls. Expected %d, actual %d.',
472+
console.log('Mismatched %s function calls. Expected %s, actual %d.',
467473
context.name,
468-
context.expected,
474+
context.messageSegment,
469475
context.actual);
470476
console.log(context.stack.split('\n').slice(2).join('\n'));
471477
});
472478

473479
if (failed.length) process.exit(1);
474480
}
475481

482+
exports.mustCall = function(fn, exact) {
483+
return _mustCallInner(fn, exact, 'exact');
484+
};
476485

477-
exports.mustCall = function(fn, expected) {
486+
exports.mustCallAtLeast = function(fn, minimum) {
487+
return _mustCallInner(fn, minimum, 'minimum');
488+
};
489+
490+
function _mustCallInner(fn, criteria, field) {
478491
if (typeof fn === 'number') {
479-
expected = fn;
492+
criteria = fn;
480493
fn = noop;
481494
} else if (fn === undefined) {
482495
fn = noop;
483496
}
484497

485-
if (expected === undefined)
486-
expected = 1;
487-
else if (typeof expected !== 'number')
488-
throw new TypeError(`Invalid expected value: ${expected}`);
498+
if (criteria === undefined)
499+
criteria = 1;
500+
else if (typeof criteria !== 'number')
501+
throw new TypeError(`Invalid ${field} value: ${criteria}`);
489502

490503
const context = {
491-
expected: expected,
504+
[field]: criteria,
492505
actual: 0,
493506
stack: (new Error()).stack,
494507
name: fn.name || '<anonymous>'
@@ -503,7 +516,7 @@ exports.mustCall = function(fn, expected) {
503516
context.actual++;
504517
return fn.apply(this, arguments);
505518
};
506-
};
519+
}
507520

508521
exports.hasMultiLocalhost = function hasMultiLocalhost() {
509522
const TCP = process.binding('tcp_wrap').TCP;

test/fixtures/failmustcall1.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const common = require('../common');
2+
const f = common.mustCall( () => {}, 2);
3+
f();

test/fixtures/failmustcall2.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const common = require('../common');
2+
const f = common.mustCallAtLeast(() => {}, 2);
3+
f();

test/parallel/test-common.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
'use strict';
2323
const common = require('../common');
2424
const assert = require('assert');
25-
25+
const {join} = require('path');
26+
const {execFile} = require('child_process');
2627

2728
// test for leaked global detection
2829
global.gc = 42; // Not a valid global unless --expose_gc is set.
@@ -33,12 +34,15 @@ delete global.gc;
3334
// common.mustCall() tests
3435
assert.throws(function() {
3536
common.mustCall(function() {}, 'foo');
36-
}, /^TypeError: Invalid expected value: foo$/);
37+
}, /^TypeError: Invalid exact value: foo$/);
3738

3839
assert.throws(function() {
3940
common.mustCall(function() {}, /foo/);
40-
}, /^TypeError: Invalid expected value: \/foo\/$/);
41+
}, /^TypeError: Invalid exact value: \/foo\/$/);
4142

43+
assert.throws(function() {
44+
common.mustCallAtLeast(function() {}, /foo/);
45+
}, /^TypeError: Invalid minimum value: \/foo\/$/);
4246

4347
// assert.fail() tests
4448
assert.throws(
@@ -47,3 +51,40 @@ assert.throws(
4751
code: 'ERR_ASSERTION',
4852
message: /^fhqwhgads$/
4953
}));
54+
55+
const fnOnce = common.mustCall(() => {});
56+
fnOnce();
57+
const fnTwice = common.mustCall(() => {}, 2);
58+
fnTwice();
59+
fnTwice();
60+
const fnAtLeast1Called1 = common.mustCallAtLeast(() => {}, 1);
61+
fnAtLeast1Called1();
62+
const fnAtLeast1Called2 = common.mustCallAtLeast(() => {}, 1);
63+
fnAtLeast1Called2();
64+
fnAtLeast1Called2();
65+
const fnAtLeast2Called2 = common.mustCallAtLeast(() => {}, 2);
66+
fnAtLeast2Called2();
67+
fnAtLeast2Called2();
68+
const fnAtLeast2Called3 = common.mustCallAtLeast(() => {}, 2);
69+
fnAtLeast2Called3();
70+
fnAtLeast2Called3();
71+
fnAtLeast2Called3();
72+
73+
const failFixtures = [
74+
[
75+
join(common.fixturesDir, 'failmustcall1.js'),
76+
'Mismatched <anonymous> function calls. Expected exactly 2, actual 1.'
77+
], [
78+
join(common.fixturesDir, 'failmustcall2.js'),
79+
'Mismatched <anonymous> function calls. Expected at least 2, actual 1.'
80+
]
81+
];
82+
for (const p of failFixtures) {
83+
const [file, expected] = p;
84+
execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => {
85+
assert.ok(ex);
86+
assert.strictEqual(stderr, '');
87+
const firstLine = stdout.split('\n').shift();
88+
assert.strictEqual(firstLine, expected);
89+
}));
90+
}

0 commit comments

Comments
 (0)