test: add mustCallAtLeast

PR-URL: https://github.com/nodejs/node/pull/12935
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Refael Ackermann 2017-05-09 17:16:52 -04:00
parent 46e773c5db
commit fccc0bf6e6
5 changed files with 88 additions and 17 deletions

View File

@ -182,9 +182,9 @@ Gets IP of localhost
Array of IPV6 hosts.
### mustCall([fn][, expected])
* fn [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
* expected [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
### mustCall([fn][, exact])
* `fn` [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) default = `common.noop`
* `exact` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
* return [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
Returns a function that calls `fn`. If the returned function has not been called
@ -193,6 +193,17 @@ fail.
If `fn` is not provided, `common.noop` will be used.
### mustCallAtLeast([fn][, minimum])
* `fn` [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) default = `common.noop`
* `minimum` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) default = 1
* return [&lt;Function>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
Returns a function that calls `fn`. If the returned function has not been called
at least `minimum` number of times when the test is complete, then the test will
fail.
If `fn` is not provided, `common.noop` will be used.
### nodeProcessAborted(exitCode, signal)
* `exitCode` [&lt;Number>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type)
* `signal` [&lt;String>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)

View File

@ -459,13 +459,19 @@ function runCallChecks(exitCode) {
if (exitCode !== 0) return;
const failed = mustCallChecks.filter(function(context) {
return context.actual !== context.expected;
if ('minimum' in context) {
context.messageSegment = `at least ${context.minimum}`;
return context.actual < context.minimum;
} else {
context.messageSegment = `exactly ${context.exact}`;
return context.actual !== context.exact;
}
});
failed.forEach(function(context) {
console.log('Mismatched %s function calls. Expected %d, actual %d.',
console.log('Mismatched %s function calls. Expected %s, actual %d.',
context.name,
context.expected,
context.messageSegment,
context.actual);
console.log(context.stack.split('\n').slice(2).join('\n'));
});
@ -473,22 +479,29 @@ function runCallChecks(exitCode) {
if (failed.length) process.exit(1);
}
exports.mustCall = function(fn, exact) {
return _mustCallInner(fn, exact, 'exact');
};
exports.mustCall = function(fn, expected) {
exports.mustCallAtLeast = function(fn, minimum) {
return _mustCallInner(fn, minimum, 'minimum');
};
function _mustCallInner(fn, criteria, field) {
if (typeof fn === 'number') {
expected = fn;
criteria = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}
if (expected === undefined)
expected = 1;
else if (typeof expected !== 'number')
throw new TypeError(`Invalid expected value: ${expected}`);
if (criteria === undefined)
criteria = 1;
else if (typeof criteria !== 'number')
throw new TypeError(`Invalid ${field} value: ${criteria}`);
const context = {
expected: expected,
[field]: criteria,
actual: 0,
stack: (new Error()).stack,
name: fn.name || '<anonymous>'
@ -503,7 +516,7 @@ exports.mustCall = function(fn, expected) {
context.actual++;
return fn.apply(this, arguments);
};
};
}
exports.hasMultiLocalhost = function hasMultiLocalhost() {
const TCP = process.binding('tcp_wrap').TCP;

3
test/fixtures/failmustcall1.js vendored Normal file
View File

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

3
test/fixtures/failmustcall2.js vendored Normal file
View File

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

View File

@ -22,7 +22,8 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const {join} = require('path');
const {execFile} = require('child_process');
// test for leaked global detection
global.gc = 42; // Not a valid global unless --expose_gc is set.
@ -33,12 +34,15 @@ delete global.gc;
// common.mustCall() tests
assert.throws(function() {
common.mustCall(function() {}, 'foo');
}, /^TypeError: Invalid expected value: foo$/);
}, /^TypeError: Invalid exact value: foo$/);
assert.throws(function() {
common.mustCall(function() {}, /foo/);
}, /^TypeError: Invalid expected value: \/foo\/$/);
}, /^TypeError: Invalid exact value: \/foo\/$/);
assert.throws(function() {
common.mustCallAtLeast(function() {}, /foo/);
}, /^TypeError: Invalid minimum value: \/foo\/$/);
// assert.fail() tests
assert.throws(
@ -47,3 +51,40 @@ assert.throws(
code: 'ERR_ASSERTION',
message: /^fhqwhgads$/
}));
const fnOnce = common.mustCall(() => {});
fnOnce();
const fnTwice = common.mustCall(() => {}, 2);
fnTwice();
fnTwice();
const fnAtLeast1Called1 = common.mustCallAtLeast(() => {}, 1);
fnAtLeast1Called1();
const fnAtLeast1Called2 = common.mustCallAtLeast(() => {}, 1);
fnAtLeast1Called2();
fnAtLeast1Called2();
const fnAtLeast2Called2 = common.mustCallAtLeast(() => {}, 2);
fnAtLeast2Called2();
fnAtLeast2Called2();
const fnAtLeast2Called3 = common.mustCallAtLeast(() => {}, 2);
fnAtLeast2Called3();
fnAtLeast2Called3();
fnAtLeast2Called3();
const failFixtures = [
[
join(common.fixturesDir, 'failmustcall1.js'),
'Mismatched <anonymous> function calls. Expected exactly 2, actual 1.'
], [
join(common.fixturesDir, 'failmustcall2.js'),
'Mismatched <anonymous> function calls. Expected at least 2, actual 1.'
]
];
for (const p of failFixtures) {
const [file, expected] = p;
execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => {
assert.ok(ex);
assert.strictEqual(stderr, '');
const firstLine = stdout.split('\n').shift();
assert.strictEqual(firstLine, expected);
}));
}