test: add hijackStdout and hijackStderr

Add `common.hijackStdout` and `common.hijackStderr` to provide monitor
for console output.

PR-URL: https://github.com/nodejs/node/pull/13439
Reviewed-By: Refael Ackermann <refack@gmail.com>
This commit is contained in:
XadillaX 2017-06-03 13:24:05 -04:00 committed by Refael Ackermann
parent 27de36926b
commit d00e5f1a04
8 changed files with 114 additions and 41 deletions

View File

@ -116,6 +116,22 @@ Checks whether `IPv6` is supported on this platform.
Checks if there are multiple localhosts available.
### hijackStderr(listener)
* `listener` [&lt;Function>][MDN-Function]: a listener with a single parameter called `data`.
Eavesdrop to `process.stderr.write` calls. Once `process.stderr.write` is
called, `listener` will also be called and the `data` of `write` function will
be passed to `listener`. What's more, `process.stderr.writeTimes` is a count of
the number of calls.
### hijackStdout(listener)
* `listener` [&lt;Function>][MDN-Function]: a listener with a single parameter called `data`.
Eavesdrop to `process.stdout.write` calls. Once `process.stdout.write` is
called, `listener` will also be called and the `data` of `write` function will
be passed to `listener`. What's more, `process.stdout.writeTimes` is a count of
the number of calls.
### inFreeBSDJail
* return [&lt;Boolean>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type)
@ -256,6 +272,14 @@ Port tests are running on.
Deletes the 'tmp' dir and recreates it
### restoreStderr()
Restore the original `process.stderr.write`.
### restoreStdout()
Restore the original `process.stdout.write`.
### rootDir
* return [&lt;String>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)
@ -296,3 +320,5 @@ Node.js
[WHATWG URL API](https://nodejs.org/api/url.html#url_the_whatwg_url_api)
implementation with tests from
[W3C Web Platform Tests](https://github.com/w3c/web-platform-tests).
[MDN-Function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Normal_objects_and_functions

View File

@ -759,3 +759,27 @@ exports.getTTYfd = function getTTYfd() {
}
return tty_fd;
};
// Hijack stdout and stderr
const stdWrite = {};
function hijackStdWritable(name, listener) {
const stream = process[name];
const _write = stdWrite[name] = stream.write;
stream.writeTimes = 0;
stream.write = function(data, callback) {
listener(data);
_write.call(stream, data, callback);
stream.writeTimes++;
};
}
function restoreWritable(name) {
process[name].write = stdWrite[name];
delete process[name].writeTimes;
}
exports.hijackStdout = hijackStdWritable.bind(null, 'stdout');
exports.hijackStderr = hijackStdWritable.bind(null, 'stderr');
exports.restoreStdout = restoreWritable.bind(null, 'stdout');
exports.restoreStderr = restoreWritable.bind(null, 'stderr');

View File

@ -26,7 +26,7 @@ const fs = require('fs');
process.stdout.write('hello world\r\n');
var stdin = process.openStdin();
const stdin = process.openStdin();
stdin.on('data', function(data) {
process.stdout.write(data.toString());

View File

@ -88,3 +88,23 @@ for (const p of failFixtures) {
assert.strictEqual(firstLine, expected);
}));
}
// hijackStderr and hijackStdout
const HIJACK_TEST_ARRAY = [ 'foo\n', 'bar\n', 'baz\n' ];
[ 'err', 'out' ].forEach((txt) => {
const stream = process[`std${txt}`];
const originalWrite = stream.write;
common[`hijackStd${txt}`](common.mustCall(function(data) {
assert.strictEqual(data, HIJACK_TEST_ARRAY[stream.writeTimes]);
}, HIJACK_TEST_ARRAY.length));
assert.notStrictEqual(originalWrite, stream.write);
HIJACK_TEST_ARRAY.forEach((val) => {
stream.write(val, common.mustCall(common.noop));
});
assert.strictEqual(HIJACK_TEST_ARRAY.length, stream.writeTimes);
common[`restoreStd${txt}`]();
assert.strictEqual(originalWrite, stream.write);
});

View File

@ -45,16 +45,14 @@ assert.doesNotThrow(function() {
// an Object with a custom .inspect() function
const custom_inspect = { foo: 'bar', inspect: () => 'inspect' };
const stdout_write = global.process.stdout.write;
const stderr_write = global.process.stderr.write;
const strings = [];
const errStrings = [];
global.process.stdout.write = function(string) {
strings.push(string);
};
global.process.stderr.write = function(string) {
errStrings.push(string);
};
common.hijackStdout(function(data) {
strings.push(data);
});
common.hijackStderr(function(data) {
errStrings.push(data);
});
// test console.log() goes to stdout
console.log('foo');
@ -105,8 +103,10 @@ console.timeEnd('constructor');
console.time('hasOwnProperty');
console.timeEnd('hasOwnProperty');
global.process.stdout.write = stdout_write;
global.process.stderr.write = stderr_write;
assert.strictEqual(strings.length, process.stdout.writeTimes);
assert.strictEqual(errStrings.length, process.stderr.writeTimes);
common.restoreStdout();
common.restoreStderr();
// verify that console.timeEnd() doesn't leave dead links
const timesMapSize = console._times.size;
@ -146,9 +146,6 @@ assert.ok(/^hasOwnProperty: \d+\.\d{3}ms$/.test(strings.shift().trim()));
assert.strictEqual('Trace: This is a {"formatted":"trace"} 10 foo',
errStrings.shift().split('\n').shift());
assert.strictEqual(strings.length, 0);
assert.strictEqual(errStrings.length, 0);
assert.throws(() => {
console.assert(false, 'should throw');
}, common.expectsError({
@ -159,3 +156,14 @@ assert.throws(() => {
assert.doesNotThrow(() => {
console.assert(true, 'this should not throw');
});
// hijack stderr to catch `process.emitWarning` which is using
// `process.nextTick`
common.hijackStderr(common.mustCall(function(data) {
common.restoreStderr();
// stderr.write will catch sync error, so use `process.nextTick` here
process.nextTick(function() {
assert.strictEqual(data.includes('no such label'), true);
});
}));

View File

@ -7,31 +7,28 @@
const common = require('../common');
const assert = require('assert');
const EventEmitter = require('events');
const leak_warning = /EventEmitter memory leak detected\. 2 hello listeners/;
const leakWarning = /EventEmitter memory leak detected\. 2 hello listeners/;
let write_calls = 0;
common.hijackStderr(common.mustCall(function(data) {
if (process.stderr.writeTimes === 0) {
assert.ok(data.match(leakWarning));
} else {
assert.fail('stderr.write should be called only once');
}
}));
process.on('warning', (warning) => {
process.on('warning', function(warning) {
// This will be called after the default internal
// process warning handler is called. The default
// process warning writes to the console, which will
// invoke the monkeypatched process.stderr.write
// below.
assert.strictEqual(write_calls, 1);
EventEmitter.defaultMaxListeners = old_default;
assert.strictEqual(process.stderr.writeTimes, 1);
EventEmitter.defaultMaxListeners = oldDefault;
// when we get here, we should be done
});
process.stderr.write = (data) => {
if (write_calls === 0)
assert.ok(data.match(leak_warning));
else
assert.fail('stderr.write should be called only once');
write_calls++;
};
const old_default = EventEmitter.defaultMaxListeners;
const oldDefault = EventEmitter.defaultMaxListeners;
EventEmitter.defaultMaxListeners = 1;
const e = new EventEmitter();

View File

@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const os = require('os');
@ -63,10 +63,7 @@ function child() {
throw new Error('No ticking!');
};
const stderr = process.stderr;
stderr.write = function() {
throw new Error('No writing to stderr!');
};
common.hijackStderr(common.mustNotCall('stderr.write must not be called.'));
process._rawDebug('I can still %s!', 'debug');
}

View File

@ -20,19 +20,18 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const util = require('util');
assert.ok(process.stdout.writable);
assert.ok(process.stderr.writable);
const stdout_write = global.process.stdout.write;
const strings = [];
global.process.stdout.write = function(string) {
strings.push(string);
};
console._stderr = process.stdout;
common.hijackStdout(function(data) {
strings.push(data);
});
common.hijackStderr(common.mustNotCall('stderr.write must not be called'));
const tests = [
{input: 'foo', output: 'foo'},
@ -56,4 +55,6 @@ tests.forEach(function(test) {
assert.strictEqual(match[1], test.output);
});
global.process.stdout.write = stdout_write;
assert.strictEqual(process.stdout.writeTimes, tests.length);
common.restoreStdout();