Skip to content

Commit f578c9b

Browse files
refackMylesBorins
authored andcommitted
test: add mustCallAtLeast
PR-URL: #12935 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 7bb200f commit f578c9b

5 files changed

Lines changed: 86 additions & 16 deletions

File tree

‎test/common/README.md‎

Lines changed: 14 additions & 3 deletions
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‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -406,36 +406,49 @@ function runCallChecks(exitCode) {
406406
if(exitCode!==0)return;
407407

408408
constfailed=mustCallChecks.filter(function(context){
409-
returncontext.actual!==context.expected;
409+
if('minimum'incontext){
410+
context.messageSegment=`at least ${context.minimum}`;
411+
returncontext.actual<context.minimum;
412+
}else{
413+
context.messageSegment=`exactly ${context.exact}`;
414+
returncontext.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(typeoffn==='number'){
426-
expected=fn;
439+
criteria=fn;
427440
fn=noop;
428441
}elseif(fn===undefined){
429442
fn=noop;
430443
}
431444

432-
if(expected===undefined)
433-
expected=1;
434-
elseif(typeofexpected!=='number')
435-
thrownewTypeError(`Invalid expected value: ${expected}`);
445+
if(criteria===undefined)
446+
criteria=1;
447+
elseif(typeofcriteria!=='number')
448+
thrownewTypeError(`Invalid ${field} value: ${criteria}`);
436449

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

455468
exports.hasMultiLocalhost=functionhasMultiLocalhost(){
456469
constTCP=process.binding('tcp_wrap').TCP;

‎test/fixtures/failmustcall1.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
constcommon=require('../common');
2+
constf=common.mustCall(()=>{},2);
3+
f();

‎test/fixtures/failmustcall2.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
constcommon=require('../common');
2+
constf=common.mustCallAtLeast(()=>{},2);
3+
f();

‎test/parallel/test-common.js‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,54 @@
22
constcommon=require('../common');
33
constassert=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:Invalidexpectedvalue:foo$/);
14+
},/^TypeError:Invalidexactvalue:foo$/);
1215

1316
assert.throws(function(){
1417
common.mustCall(common.noop,/foo/);
15-
},/^TypeError:Invalidexpectedvalue:\/foo\/$/);
18+
},/^TypeError:Invalidexactvalue:\/foo\/$/);
19+
20+
constfnOnce=common.mustCall(()=>{});
21+
fnOnce();
22+
constfnTwice=common.mustCall(()=>{},2);
23+
fnTwice();
24+
fnTwice();
25+
constfnAtLeast1Called1=common.mustCallAtLeast(()=>{},1);
26+
fnAtLeast1Called1();
27+
constfnAtLeast1Called2=common.mustCallAtLeast(()=>{},1);
28+
fnAtLeast1Called2();
29+
fnAtLeast1Called2();
30+
constfnAtLeast2Called2=common.mustCallAtLeast(()=>{},2);
31+
fnAtLeast2Called2();
32+
fnAtLeast2Called2();
33+
constfnAtLeast2Called3=common.mustCallAtLeast(()=>{},2);
34+
fnAtLeast2Called3();
35+
fnAtLeast2Called3();
36+
fnAtLeast2Called3();
37+
38+
constfailFixtures=[
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(constpoffailFixtures){
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+
constfirstLine=stdout.split('\n').shift();
53+
assert.strictEqual(firstLine,expected);
54+
}));
55+
}

0 commit comments

Comments
 (0)