Skip to content

Commit f578c9b

Browse files
refackMylesBorins
authored andcommitted
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 7bb200f commit f578c9b

File tree

5 files changed

+86
-16
lines changed

5 files changed

+86
-16
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
@@ -406,36 +406,49 @@ function runCallChecks(exitCode) {
406406
if (exitCode !== 0) return;
407407

408408
const failed = mustCallChecks.filter(function(context) {
409-
return context.actual !== context.expected;
409+
if ('minimum' in context) {
410+
context.messageSegment = `at least ${context.minimum}`;
411+
return context.actual < context.minimum;
412+
} else {
413+
context.messageSegment = `exactly ${context.exact}`;
414+
return context.actual !== context.exact;
415+
}
410416
});
411417

412418
failed.forEach(function(context) {
413-
console.log('Mismatched %s function calls. Expected %d, actual %d.',
419+
console.log('Mismatched %s function calls. Expected %s, actual %d.',
414420
context.name,
415-
context.expected,
421+
context.messageSegment,
416422
context.actual);
417423
console.log(context.stack.split('\n').slice(2).join('\n'));
418424
});
419425

420426
if (failed.length) process.exit(1);
421427
}
422428

429+
exports.mustCall = function(fn, exact) {
430+
return _mustCallInner(fn, exact, 'exact');
431+
};
423432

424-
exports.mustCall = function(fn, expected) {
433+
exports.mustCallAtLeast = function(fn, minimum) {
434+
return _mustCallInner(fn, minimum, 'minimum');
435+
};
436+
437+
function _mustCallInner(fn, criteria, field) {
425438
if (typeof fn === 'number') {
426-
expected = fn;
439+
criteria = fn;
427440
fn = noop;
428441
} else if (fn === undefined) {
429442
fn = noop;
430443
}
431444

432-
if (expected === undefined)
433-
expected = 1;
434-
else if (typeof expected !== 'number')
435-
throw new TypeError(`Invalid expected value: ${expected}`);
445+
if (criteria === undefined)
446+
criteria = 1;
447+
else if (typeof criteria !== 'number')
448+
throw new TypeError(`Invalid ${field} value: ${criteria}`);
436449

437450
const context = {
438-
expected: expected,
451+
[field]: criteria,
439452
actual: 0,
440453
stack: (new Error()).stack,
441454
name: fn.name || '<anonymous>'
@@ -450,7 +463,7 @@ exports.mustCall = function(fn, expected) {
450463
context.actual++;
451464
return fn.apply(this, arguments);
452465
};
453-
};
466+
}
454467

455468
exports.hasMultiLocalhost = function hasMultiLocalhost() {
456469
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

+42-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,54 @@
22
const common = require('../common');
33
const assert = require('assert');
44

5+
const {join} = require('path');
6+
const {execFile} = require('child_process');
7+
58
common.globalCheck = false;
69
global.gc = 42; // Not a valid global unless --expose_gc is set.
710
assert.deepStrictEqual(common.leakedGlobals(), ['gc']);
811

912
assert.throws(function() {
1013
common.mustCall(common.noop, 'foo');
11-
}, /^TypeError: Invalid expected value: foo$/);
14+
}, /^TypeError: Invalid exact value: foo$/);
1215

1316
assert.throws(function() {
1417
common.mustCall(common.noop, /foo/);
15-
}, /^TypeError: Invalid expected value: \/foo\/$/);
18+
}, /^TypeError: Invalid exact value: \/foo\/$/);
19+
20+
const fnOnce = common.mustCall(() => {});
21+
fnOnce();
22+
const fnTwice = common.mustCall(() => {}, 2);
23+
fnTwice();
24+
fnTwice();
25+
const fnAtLeast1Called1 = common.mustCallAtLeast(() => {}, 1);
26+
fnAtLeast1Called1();
27+
const fnAtLeast1Called2 = common.mustCallAtLeast(() => {}, 1);
28+
fnAtLeast1Called2();
29+
fnAtLeast1Called2();
30+
const fnAtLeast2Called2 = common.mustCallAtLeast(() => {}, 2);
31+
fnAtLeast2Called2();
32+
fnAtLeast2Called2();
33+
const fnAtLeast2Called3 = common.mustCallAtLeast(() => {}, 2);
34+
fnAtLeast2Called3();
35+
fnAtLeast2Called3();
36+
fnAtLeast2Called3();
37+
38+
const failFixtures = [
39+
[
40+
join(common.fixturesDir, 'failmustcall1.js'),
41+
'Mismatched <anonymous> function calls. Expected exactly 2, actual 1.'
42+
], [
43+
join(common.fixturesDir, 'failmustcall2.js'),
44+
'Mismatched <anonymous> function calls. Expected at least 2, actual 1.'
45+
]
46+
];
47+
for (const p of failFixtures) {
48+
const [file, expected] = p;
49+
execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => {
50+
assert.ok(ex);
51+
assert.strictEqual(stderr, '');
52+
const firstLine = stdout.split('\n').shift();
53+
assert.strictEqual(firstLine, expected);
54+
}));
55+
}

0 commit comments

Comments
 (0)