Browse Source

child_process: js bits for spawnSync/execSync

This implements the user-facing APIs that lets one run a child process
and block until it exits.

Logic shared with the async counterpart of each function was refactored
to enable code reuse.

Docs and tests are included.
v0.11.12-release
Timothy J Fontaine 11 years ago
parent
commit
e8df267674
  1. 87
      doc/api/child_process.markdown
  2. 376
      lib/child_process.js
  3. 82
      test/simple/test-child-process-execsync.js
  4. 124
      test/simple/test-child-process-spawnsync-input.js
  5. 46
      test/simple/test-child-process-spawnsync-timeout.js
  6. 52
      test/simple/test-child-process-spawnsync.js

87
doc/api/child_process.markdown

@ -589,4 +589,91 @@ done with care and by default will talk over the fd represented an
environmental variable `NODE_CHANNEL_FD` on the child process. The input and environmental variable `NODE_CHANNEL_FD` on the child process. The input and
output on this fd is expected to be line delimited JSON objects. output on this fd is expected to be line delimited JSON objects.
## child_process.spawnSync(command, [args], [options])
* `command` {String} The command to run
* `args` {Array} List of string arguments
* `options` {Object}
* `cwd` {String} Current working directory of the child process
* `input` {String|Buffer} The value which will be passed as stdin to the spawned process
- supplying this value will override `stdio[0]`
* `stdio` {Array} Child's stdio configuration.
* `env` {Object} Environment key-value pairs
* `uid` {Number} Sets the user identity of the process. (See setuid(2).)
* `gid` {Number} Sets the group identity of the process. (See setgid(2).)
* `timeout` {Number} In milliseconds the maximum amount of time the process is allowed to run. (Default: undefined)
* `killSignal` {String} The signal value to be used when the spawned process will be killed. (Default: 'SIGTERM')
* `maxBuffer` {Number}
* `encoding` {String} The encoding used for all stdio inputs and outputs. (Default: 'buffer')
* return: {Object}
* `pid` {Number} Pid of the child process
* `output` {Array} Array of results from stdio output
* `stdout` {Buffer|String} The contents of `output[1]`
* `stderr` {Buffer|String} The contents of `output[2]`
* `status` {Number} The exit code of the child process
* `signal` {String} The signal used to kill the child process
* `error` {Error} The error object if the child process failed or timedout
`spawnSync` will not return until the child process has fully closed. When a
timeout has been encountered and `killSignal` is sent, the method won't return
until the process has completely exited. That is to say, if the process handles
the `SIGTERM` signal and doesn't exit, your process will wait until the child
process has exited.
## child_process.execFileSync(command, [args], [options])
* `command` {String} The command to run
* `args` {Array} List of string arguments
* `options` {Object}
* `cwd` {String} Current working directory of the child process
* `input` {String|Buffer} The value which will be passed as stdin to the spawned process
- supplying this value will override `stdio[0]`
* `stdio` {Array} Child's stdio configuration.
* `env` {Object} Environment key-value pairs
* `uid` {Number} Sets the user identity of the process. (See setuid(2).)
* `gid` {Number} Sets the group identity of the process. (See setgid(2).)
* `timeout` {Number} In milliseconds the maximum amount of time the process is allowed to run. (Default: undefined)
* `killSignal` {String} The signal value to be used when the spawned process will be killed. (Default: 'SIGTERM')
* `maxBuffer` {Number}
* `encoding` {String} The encoding used for all stdio inputs and outputs. (Default: 'buffer')
* return: {Buffer|String} The stdout from the command
`execFileSync` will not return until the child process has fully closed. When a
timeout has been encountered and `killSignal` is sent, the method won't return
until the process has completely exited. That is to say, if the process handles
the `SIGTERM` signal and doesn't exit, your process will wait until the child
process has exited.
If the process times out, or has a non-zero exit code, this method ***will***
throw. The `Error` object will contain the entire result from
[`child_process.spawnSync`](#child_process_child_process_spawnsync_command_args_options)
## child_process.execSync(command, [options])
* `command` {String} The command to run
* `options` {Object}
* `cwd` {String} Current working directory of the child process
* `input` {String|Buffer} The value which will be passed as stdin to the spawned process
- supplying this value will override `stdio[0]`
* `stdio` {Array} Child's stdio configuration.
* `env` {Object} Environment key-value pairs
* `uid` {Number} Sets the user identity of the process. (See setuid(2).)
* `gid` {Number} Sets the group identity of the process. (See setgid(2).)
* `timeout` {Number} In milliseconds the maximum amount of time the process is allowed to run. (Default: undefined)
* `killSignal` {String} The signal value to be used when the spawned process will be killed. (Default: 'SIGTERM')
* `maxBuffer` {Number}
* `encoding` {String} The encoding used for all stdio inputs and outputs. (Default: 'buffer')
* return: {Buffer|String} The stdout from the command
`execSync` will not return until the child process has fully closed. When a
timeout has been encountered and `killSignal` is sent, the method won't return
until the process has completely exited. That is to say, if the process handles
the `SIGTERM` signal and doesn't exit, your process will wait until the child
process has exited.
If the process times out, or has a non-zero exit code, this method ***will***
throw. The `Error` object will contain the entire result from
[`child_process.spawnSync`](#child_process_child_process_spawnsync_command_args_options)
[EventEmitter]: events.html#events_class_events_eventemitter [EventEmitter]: events.html#events_class_events_eventemitter

376
lib/child_process.js

@ -29,6 +29,7 @@ var util = require('util');
var Process = process.binding('process_wrap').Process; var Process = process.binding('process_wrap').Process;
var uv = process.binding('uv'); var uv = process.binding('uv');
var spawn_sync; // Lazy-loaded process.binding('spawn_sync')
var constants; // Lazy-loaded process.binding('constants') var constants; // Lazy-loaded process.binding('constants')
var errnoException = util._errnoException; var errnoException = util._errnoException;
@ -505,7 +506,7 @@ function setupChannel(target, channel) {
// queue is flushed. // queue is flushed.
if (!this._handleQueue) if (!this._handleQueue)
this._disconnect(); this._disconnect();
} };
target._disconnect = function() { target._disconnect = function() {
assert(this._channel); assert(this._channel);
@ -585,7 +586,7 @@ exports._forkChild = function(fd) {
}; };
exports.exec = function(command /*, options, callback */) { function normalizeExecArgs(command /*, options, callback */) {
var file, args, options, callback; var file, args, options, callback;
if (util.isFunction(arguments[1])) { if (util.isFunction(arguments[1])) {
@ -611,7 +612,22 @@ exports.exec = function(command /*, options, callback */) {
if (options && options.shell) if (options && options.shell)
file = options.shell; file = options.shell;
return exports.execFile(file, args, options, callback); return {
cmd: command,
file: file,
args: args,
options: options,
callback: callback
};
}
exports.exec = function(command /*, options, callback */) {
var opts = normalizeExecArgs.apply(null, arguments);
return exports.execFile(opts.file,
opts.args,
opts.options,
opts.callback);
}; };
@ -777,8 +793,132 @@ exports.execFile = function(file /* args, options, callback */) {
}; };
var spawn = exports.spawn = function(file /*, args, options*/) { function _convertCustomFds(options) {
if (options && options.customFds && !options.stdio) {
options.stdio = options.customFds.map(function(fd) {
return fd === -1 ? 'pipe' : fd;
});
}
}
function _validateStdio(stdio, sync) {
var ipc,
ipcFd;
// Replace shortcut with an array
if (util.isString(stdio)) {
switch (stdio) {
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
case 'inherit': stdio = [0, 1, 2]; break;
default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
}
} else if (!util.isArray(stdio)) {
throw new TypeError('Incorrect value of stdio option: ' +
util.inspect(stdio));
}
// At least 3 stdio will be created
// Don't concat() a new Array() because it would be sparse, and
// stdio.reduce() would skip the sparse elements of stdio.
// See http://stackoverflow.com/a/5501711/3561
while (stdio.length < 3) stdio.push(undefined);
// Translate stdio into C++-readable form
// (i.e. PipeWraps or fds)
stdio = stdio.reduce(function(acc, stdio, i) {
function cleanup() {
acc.filter(function(stdio) {
return stdio.type === 'pipe' || stdio.type === 'ipc';
}).forEach(function(stdio) {
if (stdio.handle)
stdio.handle.close();
});
}
// Defaults
if (util.isNullOrUndefined(stdio)) {
stdio = i < 3 ? 'pipe' : 'ignore';
}
if (stdio === null || stdio === 'ignore') {
acc.push({type: 'ignore'});
} else if (stdio === 'pipe' || util.isNumber(stdio) && stdio < 0) {
var a = {
type: 'pipe',
readable: i === 0,
writable: i !== 0
};
if (!sync)
a.handle = createPipe();
acc.push(a);
} else if (stdio === 'ipc') {
if (sync || !util.isUndefined(ipc)) {
// Cleanup previously created pipes
cleanup();
if (!sync)
throw Error('Child process can have only one IPC pipe');
else
throw Error('You cannot use IPC with synchronous forks');
}
ipc = createPipe(true);
ipcFd = i;
acc.push({
type: 'pipe',
handle: ipc,
ipc: true
});
} else if (stdio === 'inherit') {
acc.push({
type: 'inherit',
fd: i
});
} else if (util.isNumber(stdio) || util.isNumber(stdio.fd)) {
acc.push({
type: 'fd',
fd: stdio.fd || stdio
});
} else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
getHandleWrapType(stdio._handle)) {
var handle = getHandleWrapType(stdio) ?
stdio :
getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
acc.push({
type: 'wrap',
wrapType: getHandleWrapType(handle),
handle: handle
});
} else if (util.isBuffer(stdio) || util.isString(stdio)) {
if (!sync) {
cleanup();
throw new TypeError('Asynchronous forks do not support Buffer input: ' +
util.inspect(stdio));
}
} else {
// Cleanup
cleanup();
throw new TypeError('Incorrect value for stdio stream: ' +
util.inspect(stdio));
}
return acc;
}, []);
return {stdio: stdio, ipc: ipc, ipcFd: ipcFd};
}
function normalizeSpawnArguments(/*file, args, options*/) {
var args, options; var args, options;
var file = arguments[0];
if (Array.isArray(arguments[1])) { if (Array.isArray(arguments[1])) {
args = arguments[1].slice(0); args = arguments[1].slice(0);
options = arguments[2]; options = arguments[2];
@ -787,6 +927,9 @@ var spawn = exports.spawn = function(file /*, args, options*/) {
options = arguments[1]; options = arguments[1];
} }
if (!options)
options = {};
args.unshift(file); args.unshift(file);
var env = (options ? options.env : null) || process.env; var env = (options ? options.env : null) || process.env;
@ -795,12 +938,26 @@ var spawn = exports.spawn = function(file /*, args, options*/) {
envPairs.push(key + '=' + env[key]); envPairs.push(key + '=' + env[key]);
} }
_convertCustomFds(options);
return {
file: file,
args: args,
options: options,
envPairs: envPairs
};
}
var spawn = exports.spawn = function(/*file, args, options*/) {
var opts = normalizeSpawnArguments.apply(null, arguments);
var file = opts.file;
var args = opts.args;
var options = opts.options;
var envPairs = opts.envPairs;
var child = new ChildProcess(); var child = new ChildProcess();
if (options && options.customFds && !options.stdio) {
options.stdio = options.customFds.map(function(fd) {
return fd === -1 ? 'pipe' : fd;
});
}
child.spawn({ child.spawn({
file: file, file: file,
@ -923,78 +1080,11 @@ ChildProcess.prototype.spawn = function(options) {
// If no `stdio` option was given - use default // If no `stdio` option was given - use default
stdio = options.stdio || 'pipe'; stdio = options.stdio || 'pipe';
// Replace shortcut with an array stdio = _validateStdio(stdio, false);
if (util.isString(stdio)) {
switch (stdio) {
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
case 'inherit': stdio = [0, 1, 2]; break;
default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
}
} else if (!util.isArray(stdio)) {
throw new TypeError('Incorrect value of stdio option: ' + stdio);
}
// At least 3 stdio will be created
// Don't concat() a new Array() because it would be sparse, and
// stdio.reduce() would skip the sparse elements of stdio.
// See http://stackoverflow.com/a/5501711/3561
while (stdio.length < 3) stdio.push(undefined);
// Translate stdio into C++-readable form
// (i.e. PipeWraps or fds)
stdio = stdio.reduce(function(acc, stdio, i) {
function cleanup() {
acc.filter(function(stdio) {
return stdio.type === 'pipe' || stdio.type === 'ipc';
}).forEach(function(stdio) {
stdio.handle.close();
});
}
// Defaults
if (util.isNullOrUndefined(stdio)) {
stdio = i < 3 ? 'pipe' : 'ignore';
}
if (stdio === 'ignore') {
acc.push({type: 'ignore'});
} else if (stdio === 'pipe' || util.isNumber(stdio) && stdio < 0) {
acc.push({type: 'pipe', handle: createPipe()});
} else if (stdio === 'ipc') {
if (!util.isUndefined(ipc)) {
// Cleanup previously created pipes
cleanup();
throw Error('Child process can have only one IPC pipe');
}
ipc = createPipe(true);
ipcFd = i;
acc.push({ type: 'pipe', handle: ipc, ipc: true });
} else if (util.isNumber(stdio) || util.isNumber(stdio.fd)) {
acc.push({ type: 'fd', fd: stdio.fd || stdio });
} else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
getHandleWrapType(stdio._handle)) {
var handle = getHandleWrapType(stdio) ?
stdio :
getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
acc.push({
type: 'wrap',
wrapType: getHandleWrapType(handle),
handle: handle
});
} else {
// Cleanup
cleanup();
throw new TypeError('Incorrect value for stdio stream: ' + stdio);
}
return acc;
}, []);
options.stdio = stdio; ipc = stdio.ipc;
ipcFd = stdio.ipcFd;
stdio = options.stdio = stdio.stdio;
if (!util.isUndefined(ipc)) { if (!util.isUndefined(ipc)) {
// Let child process know about opened IPC channel // Let child process know about opened IPC channel
@ -1113,3 +1203,129 @@ ChildProcess.prototype.ref = function() {
ChildProcess.prototype.unref = function() { ChildProcess.prototype.unref = function() {
if (this._handle) this._handle.unref(); if (this._handle) this._handle.unref();
}; };
function lookupSignal(signal) {
if (typeof signal === 'number')
return signal;
if (!constants)
constants = process.binding('constants');
if (!(signal in constants))
throw new Error('Unknown signal: ' + signal);
return constants[signal];
}
function spawnSync(/*file, args, options*/) {
var opts = normalizeSpawnArguments.apply(null, arguments);
var options = opts.options;
var envPairs = opts.envPairs;
var i;
options.file = opts.file;
options.args = opts.args;
if (options.killSignal)
options.killSignal = lookupSignal(options.killSignal);
options.stdio = _validateStdio(options.stdio || 'pipe', true).stdio;
if (options.input) {
var stdin = options.stdio[0] = util._extend({}, options.stdio[0]);
stdin.input = options.input;
}
// We may want to pass data in on any given fd, ensure it is a valid buffer
for (i = 0; i < options.stdio.length; i++) {
var input = options.stdio[i] && options.stdio[i].input;
if (input != null) {
var pipe = options.stdio[i] = util._extend({}, options.stdio[i]);
if (Buffer.isBuffer(input))
pipe.input = input;
else if (util.isString(input))
pipe.input = new Buffer(input, options.encoding);
else
throw new TypeError(util.format(
'stdio[%d] should be Buffer or string not %s',
i,
typeof input));
}
}
if (!spawn_sync)
spawn_sync = process.binding('spawn_sync');
var result = spawn_sync.spawn(options);
if (result.output && options.encoding) {
for (i = 0; i < result.output.length; i++) {
if (!result.output[i])
continue;
result.output[i] = result.output[i].toString(options.encoding);
}
}
result.stdout = result.output && result.output[1];
result.stderr = result.output && result.output[2];
if (result.error)
result.error = errnoException(result.error, 'spawnSync');
util._extend(result, opts);
return result;
}
exports.spawnSync = spawnSync;
function checkExecSyncError(ret) {
if (ret.error || ret.status !== 0) {
var err = ret.error;
ret.error = null;
if (!err) {
var cmd = ret.cmd ? ret.cmd : ret.args.join(' ');
err = new Error(util.format('Command failed: %s\n%s',
cmd,
ret.stderr.toString()));
}
util._extend(err, ret);
return err;
}
return false;
}
function execFileSync(/*command, options*/) {
var ret = spawnSync.apply(null, arguments);
var err = checkExecSyncError(ret);
if (err)
throw err;
else
return ret.stdout;
}
exports.execFileSync = execFileSync;
function execSync(/*comand, options*/) {
var opts = normalizeExecArgs.apply(null, arguments);
var ret = spawnSync(opts.file, opts.args, opts.options);
ret.cmd = opts.cmd;
var err = checkExecSyncError(ret);
if (err)
throw err;
else
return ret.stdout;
}
exports.execSync = execSync;

82
test/simple/test-child-process-execsync.js

@ -0,0 +1,82 @@
// 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.
var common = require('../common');
var assert = require('assert');
var util = require('util');
var os = require('os');
var execSync = require('child_process').execSync;
var execFileSync = require('child_process').execFileSync;
var TIMER = 200;
var SLEEP = 1000;
var start = Date.now();
var err;
var caught = false;
try
{
var cmd = util.format('%s -e "setTimeout(function(){}, %d);"',
process.execPath, SLEEP);
var ret = execSync(cmd, {timeout: TIMER});
} catch (e) {
caught = true;
assert.strictEqual(e.errno, 'ETIMEDOUT');
err = e;
} finally {
assert.strictEqual(ret, undefined, 'we should not have a return value');
assert.strictEqual(caught, true, 'execSync should throw');
var end = Date.now() - start;
assert(end < SLEEP);
assert(err.status > 128 || err.signal);
}
assert.throws(function() {
execSync('iamabadcommand');
}, /Command failed: iamabadcommand/);
var msg = 'foobar';
var msgBuf = new Buffer(msg + '\n');
// console.log ends every line with just '\n', even on Windows.
cmd = util.format('%s -e "console.log(\'%s\');"', process.execPath, msg);
var ret = execSync(cmd);
assert.strictEqual(ret.length, msgBuf.length);
assert.deepEqual(ret, msgBuf, 'execSync result buffer should match');
ret = execSync(cmd, { encoding: 'utf8' });
assert.strictEqual(ret, msg + '\n', 'execSync encoding result should match');
var args = [
'-e',
util.format('console.log("%s");', msg)
];
ret = execFileSync(process.execPath, args);
assert.deepEqual(ret, msgBuf);
ret = execFileSync(process.execPath, args, { encoding: 'utf8' });
assert.strictEqual(ret, msg + '\n', 'execFileSync encoding result should match');

124
test/simple/test-child-process-spawnsync-input.js

@ -0,0 +1,124 @@
// 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.
var common = require('../common');
var assert = require('assert');
var os = require('os');
var util = require('util');
var spawnSync = require('child_process').spawnSync;
function checkRet(ret) {
assert.strictEqual(ret.status, 0);
assert.strictEqual(ret.error, undefined);
}
var msgOut = 'this is stdout';
var msgErr = 'this is stderr';
// this is actually not os.EOL?
var msgOutBuf = new Buffer(msgOut + '\n');
var msgErrBuf = new Buffer(msgErr + '\n');
var args = [
'-e',
util.format('console.log("%s"); console.error("%s");', msgOut, msgErr)
];
var ret;
if (process.argv.indexOf('spawnchild') !== -1) {
switch (process.argv[3]) {
case '1':
ret = spawnSync(process.execPath, args, { stdio: 'inherit' });
checkRet(ret);
break;
case '2':
ret = spawnSync(process.execPath, args, {
stdio: ['inherit', 'inherit', 'inherit']
});
checkRet(ret);
break;
}
process.exit(0);
return;
}
function verifyBufOutput(ret) {
checkRet(ret);
assert.deepEqual(ret.stdout, msgOutBuf);
assert.deepEqual(ret.stderr, msgErrBuf);
}
verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 1]));
verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 2]));
var options = {
input: 1234
};
assert.throws(function() {
spawnSync('cat', [], options);
}, /TypeError:.*should be Buffer or string not number/);
options = {
input: 'hello world'
};
ret = spawnSync('cat', [], options);
checkRet(ret);
assert.strictEqual(ret.stdout.toString('utf8'), options.input);
assert.strictEqual(ret.stderr.toString('utf8'), '');
options = {
input: new Buffer('hello world')
};
ret = spawnSync('cat', [], options);
checkRet(ret);
assert.deepEqual(ret.stdout, options.input);
assert.deepEqual(ret.stderr, new Buffer(''));
verifyBufOutput(spawnSync(process.execPath, args));
ret = spawnSync(process.execPath, args, { encoding: 'utf8' });
checkRet(ret);
assert.strictEqual(ret.stdout, msgOut + '\n');
assert.strictEqual(ret.stderr, msgErr + '\n');
options = {
maxBuffer: 1
};
ret = spawnSync(process.execPath, args, options);
assert.ok(ret.error, 'maxBuffer should error');
assert.strictEqual(ret.error.errno, 'ENOBUFS');
// we can have buffers larger than maxBuffer because underneath we alloc 64k
// that matches our read sizes
assert.deepEqual(ret.stdout, msgOutBuf);

46
test/simple/test-child-process-spawnsync-timeout.js

@ -0,0 +1,46 @@
// 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.
var common = require('../common');
var assert = require('assert');
var spawnSync = require('child_process').spawnSync;
var TIMER = 200;
var SLEEP = 1000;
switch (process.argv[2]) {
case 'child':
setTimeout(function() {
console.log('child fired');
process.exit(1);
}, SLEEP);
break;
default:
var start = Date.now();
var ret = spawnSync(process.execPath, [__filename, 'child'], {timeout: TIMER});
assert.strictEqual(ret.error.errno, 'ETIMEDOUT');
console.log(ret);
var end = Date.now() - start;
assert(end < SLEEP);
assert(ret.status > 128 || ret.signal);
break;
}

52
test/simple/test-child-process-spawnsync.js

@ -0,0 +1,52 @@
// 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.
var common = require('../common');
var assert = require('assert');
var spawnSync = require('child_process').spawnSync;
var TIMER = 100;
var SLEEP = 1000;
var start = Date.now();
var timeout = 0;
setTimeout(function() {
console.log('timer fired');
timeout = Date.now();
}, TIMER);
console.log('sleep started');
var ret = spawnSync('sleep', ['1']);
console.log('sleep exited');
process.on('exit', function() {
assert.strictEqual(ret.status, 0);
var delta = Date.now() - start;
var expected_timeout = start + TIMER;
var tdlta = timeout - expected_timeout;
assert(delta > SLEEP);
assert(tdlta > TIMER && tdlta < SLEEP);
});
Loading…
Cancel
Save