test: refacor spawn[Sync]Pwd

* extract the gist into common.pwdCommand
* Merge test-child-process-buffering.js into test-child-process-stdio.js

PR-URL: https://github.com/nodejs/node/pull/22522
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Refael Ackermann 2018-08-25 12:23:53 -04:00
parent 6a689c8aa3
commit 8569f4a417
7 changed files with 109 additions and 149 deletions

View File

@ -317,6 +317,17 @@ A port number for tests to use if one is needed.
Logs '1..0 # Skipped: ' + `msg` Logs '1..0 # Skipped: ' + `msg`
### pwdCommand
* [&lt;array>] First two argument for the `spawn`/`exec` functions.
Platform normalized `pwd` command options. Usage example:
```js
const common = require('../common');
const { spawn } = require('child_process');
spawn(...common.pwdCommand, { stdio: ['pipe'] });
```
### rootDir ### rootDir
* [&lt;string>] * [&lt;string>]
@ -350,18 +361,6 @@ was disabled at compile time.
Skip the rest of the tests in the current file when the Node.js executable Skip the rest of the tests in the current file when the Node.js executable
was compiled with a pointer size smaller than 64 bits. was compiled with a pointer size smaller than 64 bits.
### spawnPwd(options)
* `options` [&lt;Object>]
* return [&lt;Object>]
Platform normalizes the `pwd` command.
### spawnSyncPwd(options)
* `options` [&lt;Object>]
* return [&lt;Object>]
Synchronous version of `spawnPwd`.
## ArrayStream Module ## ArrayStream Module
The `ArrayStream` module provides a simple `Stream` that pushes elements from The `ArrayStream` module provides a simple `Stream` that pushes elements from

View File

@ -26,7 +26,7 @@ const path = require('path');
const fs = require('fs'); const fs = require('fs');
const assert = require('assert'); const assert = require('assert');
const os = require('os'); const os = require('os');
const { exec, execSync, spawn, spawnSync } = require('child_process'); const { exec, execSync, spawnSync } = require('child_process');
const util = require('util'); const util = require('util');
const { fixturesDir } = require('./fixtures'); const { fixturesDir } = require('./fixtures');
const tmpdir = require('./tmpdir'); const tmpdir = require('./tmpdir');
@ -268,23 +268,11 @@ exports.ddCommand = function(filename, kilobytes) {
}; };
exports.spawnPwd = function(options) { exports.pwdCommand = exports.isWindows ?
if (exports.isWindows) { ['cmd.exe', ['/d', '/c', 'cd']] :
return spawn('cmd.exe', ['/d', '/c', 'cd'], options); ['pwd', []];
} else {
return spawn('pwd', [], options);
}
};
exports.spawnSyncPwd = function(options) {
if (exports.isWindows) {
return spawnSync('cmd.exe', ['/d', '/c', 'cd'], options);
} else {
return spawnSync('pwd', [], options);
}
};
exports.platformTimeout = function(ms) { exports.platformTimeout = function(ms) {
if (process.features.debug) if (process.features.debug)
ms = 2 * ms; ms = 2 * ms;

View File

@ -1,51 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
function pwd(callback) {
let output = '';
const child = common.spawnPwd();
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(s) {
console.log(`stdout: ${JSON.stringify(s)}`);
output += s;
});
child.on('exit', common.mustCall(function(c) {
console.log(`exit: ${c}`);
assert.strictEqual(0, c);
}));
child.on('close', common.mustCall(function() {
callback(output);
}));
}
pwd(function(result) {
console.dir(result);
assert.strictEqual(true, result.length > 1);
assert.strictEqual('\n', result[result.length - 1]);
});

View File

@ -2,19 +2,22 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const { spawnSync } = require('child_process');
const internalCp = require('internal/child_process'); const internalCp = require('internal/child_process');
const oldSpawnSync = internalCp.spawnSync;
if (!common.isMainThread) if (!common.isMainThread)
common.skip('stdio is not associated with file descriptors in Workers'); common.skip('stdio is not associated with file descriptors in Workers');
// This test uses the deprecated `customFds` option. We expect a deprecation
// warning, but only once (per node process).
const msg = 'child_process: options.customFds option is deprecated. ' +
'Use options.stdio instead.';
common.expectWarning('DeprecationWarning', msg, 'DEP0006');
// Verify that customFds is used if stdio is not provided. // Verify that customFds is used if stdio is not provided.
{ {
const msg = 'child_process: options.customFds option is deprecated. ' +
'Use options.stdio instead.';
common.expectWarning('DeprecationWarning', msg, 'DEP0006');
const customFds = [-1, process.stdout.fd, process.stderr.fd]; const customFds = [-1, process.stdout.fd, process.stderr.fd];
const oldSpawnSync = internalCp.spawnSync;
internalCp.spawnSync = common.mustCall(function(opts) { internalCp.spawnSync = common.mustCall(function(opts) {
assert.deepStrictEqual(opts.options.customFds, customFds); assert.deepStrictEqual(opts.options.customFds, customFds);
assert.deepStrictEqual(opts.options.stdio, [ assert.deepStrictEqual(opts.options.stdio, [
@ -23,13 +26,14 @@ if (!common.isMainThread)
{ type: 'fd', fd: process.stderr.fd } { type: 'fd', fd: process.stderr.fd }
]); ]);
}); });
common.spawnSyncPwd({ customFds }); spawnSync(...common.pwdCommand, { customFds });
internalCp.spawnSync = oldSpawnSync; internalCp.spawnSync = oldSpawnSync;
} }
// Verify that customFds is ignored when stdio is present. // Verify that customFds is ignored when stdio is present.
{ {
const customFds = [0, 1, 2]; const customFds = [0, 1, 2];
const oldSpawnSync = internalCp.spawnSync;
internalCp.spawnSync = common.mustCall(function(opts) { internalCp.spawnSync = common.mustCall(function(opts) {
assert.deepStrictEqual(opts.options.customFds, customFds); assert.deepStrictEqual(opts.options.customFds, customFds);
assert.deepStrictEqual(opts.options.stdio, [ assert.deepStrictEqual(opts.options.stdio, [
@ -38,6 +42,6 @@ if (!common.isMainThread)
{ type: 'pipe', readable: false, writable: true } { type: 'pipe', readable: false, writable: true }
]); ]);
}); });
common.spawnSyncPwd({ customFds, stdio: 'pipe' }); spawnSync(...common.pwdCommand, { customFds, stdio: 'pipe' });
internalCp.spawnSync = oldSpawnSync; internalCp.spawnSync = oldSpawnSync;
} }

View File

@ -22,47 +22,37 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const { spawn } = require('child_process');
let returns = 0;
/* /*
Spawns 'pwd' with given options, then test Spawns 'pwd' with given options, then test
- whether the exit code equals forCode, - whether the exit code equals expectCode,
- optionally whether the stdout result matches forData - optionally whether the trimmed stdout result matches expectData
(after removing trailing whitespace)
*/ */
function testCwd(options, forCode, forData) { function testCwd(options, expectCode = 0, expectData) {
let data = ''; const child = spawn(...common.pwdCommand, options);
const child = common.spawnPwd(options);
child.stdout.setEncoding('utf8'); child.stdout.setEncoding('utf8');
// No need to assert callback since `data` is asserted.
let data = '';
child.stdout.on('data', function(chunk) { child.stdout.on('data', function(chunk) {
data += chunk; data += chunk;
}); });
// Can't assert callback, as stayed in to API:
// _The 'exit' event may or may not fire after an error has occurred._
child.on('exit', function(code, signal) { child.on('exit', function(code, signal) {
assert.strictEqual(forCode, code); assert.strictEqual(expectCode, code);
}); });
child.on('close', function() { child.on('close', common.mustCall(function() {
forData && assert.strictEqual(forData, data.replace(/[\s\r\n]+$/, '')); expectData && assert.strictEqual(data.trim(), expectData);
returns--; }));
});
returns++;
return child; return child;
} }
// Assume these exist, and 'pwd' gives us the right directory back
testCwd({ cwd: common.rootDir }, 0, common.rootDir);
if (common.isWindows) {
testCwd({ cwd: process.env.windir }, 0, process.env.windir);
} else {
testCwd({ cwd: '/dev' }, 0, '/dev');
}
// Assume does-not-exist doesn't exist, expect exitCode=-1 and errno=ENOENT // Assume does-not-exist doesn't exist, expect exitCode=-1 and errno=ENOENT
{ {
@ -72,15 +62,12 @@ if (common.isWindows) {
})); }));
} }
// Spawn() shouldn't try to chdir() so this should just work // Assume these exist, and 'pwd' gives us the right directory back
testCwd(undefined, 0); testCwd({ cwd: common.rootDir }, 0, common.rootDir);
testCwd({}, 0); const shouldExistDir = common.isWindows ? process.env.windir : '/dev';
testCwd({ cwd: '' }, 0); testCwd({ cwd: shouldExistDir }, 0, shouldExistDir);
testCwd({ cwd: undefined }, 0);
testCwd({ cwd: null }, 0);
// Check whether all tests actually returned // Spawn() shouldn't try to chdir() to invalid arg, so this should just work
assert.notStrictEqual(returns, 0); testCwd({ cwd: '' });
process.on('exit', function() { testCwd({ cwd: undefined });
assert.strictEqual(returns, 0); testCwd({ cwd: null });
});

View File

@ -22,10 +22,9 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const { spawnSync } = require('child_process');
const spawnSync = require('child_process').spawnSync; // `sleep` does different things on Windows and Unix, but in both cases, it does
// Echo does different things on Windows and Unix, but in both cases, it does
// more-or-less nothing if there are no parameters // more-or-less nothing if there are no parameters
const ret = spawnSync('sleep', ['0']); const ret = spawnSync('sleep', ['0']);
assert.strictEqual(ret.status, 0); assert.strictEqual(ret.status, 0);
@ -42,21 +41,23 @@ assert.deepStrictEqual(ret_err.spawnargs, ['bar']);
{ {
// Test the cwd option // Test the cwd option
const cwd = common.rootDir; const cwd = common.rootDir;
const response = common.spawnSyncPwd({ cwd }); const response = spawnSync(...common.pwdCommand, { cwd });
assert.strictEqual(response.stdout.toString().trim(), cwd); assert.strictEqual(response.stdout.toString().trim(), cwd);
} }
{
// Test the encoding option
const noEncoding = common.spawnSyncPwd();
const bufferEncoding = common.spawnSyncPwd({ encoding: 'buffer' });
const utf8Encoding = common.spawnSyncPwd({ encoding: 'utf8' });
assert.deepStrictEqual(noEncoding.output, bufferEncoding.output); {
assert.deepStrictEqual([ // Assert Buffer is the default encoding
const retDefault = spawnSync(...common.pwdCommand);
const retBuffer = spawnSync(...common.pwdCommand, { encoding: 'buffer' });
assert.deepStrictEqual(retDefault.output, retBuffer.output);
const retUTF8 = spawnSync(...common.pwdCommand, { encoding: 'utf8' });
const stringifiedDefault = [
null, null,
noEncoding.stdout.toString(), retDefault.stdout.toString(),
noEncoding.stderr.toString() retDefault.stderr.toString()
], utf8Encoding.output); ];
assert.deepStrictEqual(retUTF8.output, stringifiedDefault);
} }

View File

@ -22,24 +22,56 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const spawnSync = require('child_process').spawnSync; const { spawn } = require('child_process');
let options = { stdio: ['pipe'] }; // Test stdio piping.
let child = common.spawnPwd(options); {
const child = spawn(...common.pwdCommand, { stdio: ['pipe'] });
assert.notStrictEqual(child.stdout, null);
assert.notStrictEqual(child.stderr, null);
}
assert.notStrictEqual(child.stdout, null); // Test stdio ignoring.
assert.notStrictEqual(child.stderr, null); {
const child = spawn(...common.pwdCommand, { stdio: 'ignore' });
assert.strictEqual(child.stdout, null);
assert.strictEqual(child.stderr, null);
}
options = { stdio: 'ignore' }; // Asset options invariance.
child = common.spawnPwd(options); {
const options = { stdio: 'ignore' };
spawn(...common.pwdCommand, options);
assert.deepStrictEqual(options, { stdio: 'ignore' });
}
assert.strictEqual(child.stdout, null); // Test stdout buffering.
assert.strictEqual(child.stderr, null); {
let output = '';
const child = spawn(...common.pwdCommand);
options = { stdio: 'ignore' }; child.stdout.setEncoding('utf8');
child = spawnSync('cat', [], options); child.stdout.on('data', function(s) {
assert.deepStrictEqual(options, { stdio: 'ignore' }); output += s;
});
common.expectsError(() => { child.on('exit', common.mustCall(function(code) {
common.spawnPwd({ stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc'] }); assert.strictEqual(code, 0);
}, { code: 'ERR_IPC_ONE_PIPE', type: Error }); }));
child.on('close', common.mustCall(function() {
assert.strictEqual(true, output.length > 1);
assert.strictEqual('\n', output[output.length - 1]);
}));
}
// Assert only one IPC pipe allowed.
common.expectsError(
() => {
spawn(
...common.pwdCommand,
{ stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc'] }
);
},
{ code: 'ERR_IPC_ONE_PIPE', type: Error }
);