diff --git a/test/common/README.md b/test/common/README.md index 01d19116bdac29..3e1e1bbe9c96af 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -116,6 +116,22 @@ Checks whether `IPv6` is supported on this platform. Checks if there are multiple localhosts available. +### hijackStderr(listener) +* `listener` [<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` [<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 [<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 [<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 diff --git a/test/common/index.js b/test/common/index.js index d657e36f06c481..75a1edd447a534 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -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'); diff --git a/test/fixtures/echo-close-check.js b/test/fixtures/echo-close-check.js index f58515b6b77f1e..f03a35727c6eb3 100644 --- a/test/fixtures/echo-close-check.js +++ b/test/fixtures/echo-close-check.js @@ -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()); diff --git a/test/parallel/test-common.js b/test/parallel/test-common.js index 0a4e1d72f0f487..47ed7d9f3138da 100644 --- a/test/parallel/test-common.js +++ b/test/parallel/test-common.js @@ -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); +}); diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 0c413410159199..eecfd8334e3ff2 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -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); + }); +})); diff --git a/test/parallel/test-global-console-exists.js b/test/parallel/test-global-console-exists.js index d18ecb77c36f5a..425eac077aba43 100644 --- a/test/parallel/test-global-console-exists.js +++ b/test/parallel/test-global-console-exists.js @@ -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(); diff --git a/test/parallel/test-process-raw-debug.js b/test/parallel/test-process-raw-debug.js index 04599014641456..7b89a8ad18eda7 100644 --- a/test/parallel/test-process-raw-debug.js +++ b/test/parallel/test-process-raw-debug.js @@ -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'); } diff --git a/test/parallel/test-util-log.js b/test/parallel/test-util-log.js index f32dcecd15a7bf..24e84e1be9247b 100644 --- a/test/parallel/test-util-log.js +++ b/test/parallel/test-util-log.js @@ -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();