Skip to content

Commit

Permalink
test: add mustCallAtLeast
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
refack committed May 19, 2017
1 parent 46e773c commit fccc0bf
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 17 deletions.
17 changes: 14 additions & 3 deletions test/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
35 changes: 24 additions & 11 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,36 +459,49 @@ 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'));
});

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>'
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/failmustcall1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const common = require('../common');
const f = common.mustCall( () => {}, 2);
f();
3 changes: 3 additions & 0 deletions test/fixtures/failmustcall2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const common = require('../common');
const f = common.mustCallAtLeast(() => {}, 2);
f();
47 changes: 44 additions & 3 deletions test/parallel/test-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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(
Expand All @@ -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);
}));
}

0 comments on commit fccc0bf

Please sign in to comment.