var child_process = require('child_process'), | |
assert = require('assert') | |
var isChild = !!(process.send), | |
isMaster = ((!isChild) && (process.argv.length > 2)), | |
isTopLevel = (!isMaster && !isChild) | |
if( isTopLevel ) { | |
//** | |
//* TopLevel - The program was called with no arguments. We'll run the test | |
//* once for each stop-signal ('T' or 'A') in the list. (except, we'll skip | |
//* SIGUSR1, because node's debugger owns that one. | |
//* | |
(function top_level(){ | |
var all_signals = [ | |
// A list of all posix-defined signals. "The following signals shall be supported on | |
// all implementations". | |
// See: <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html> | |
// | |
// the `action` field shows 'default action', which the OS will (should) take unless | |
// the signal is trapped and handled differently. | |
// T = Abnormal termination of the process. | |
// A = Abnormal termination of the process (with additional actions). | |
// I = Ignore the signal | |
// S = Stop the process | |
// C = Continue the process, if it is stopped; otherwise, ignore the signal. | |
// | |
{ name: 'SIGABRT', action: 'A', desc: 'Process abort signal.' }, | |
{ name: 'SIGALRM', action: 'T', desc: 'Alarm clock.' }, | |
{ name: 'SIGBUS', action: 'A', desc: 'Access to an undefined portion of a memory object.' }, | |
{ name: 'SIGCHLD', action: 'I', desc: 'Child process terminated, stopped, or continued. ' }, | |
{ name: 'SIGCONT', action: 'C', desc: 'Continue executing, if stopped.' }, | |
{ name: 'SIGFPE', action: 'A', desc: 'Erroneous arithmetic operation.' }, | |
{ name: 'SIGHUP', action: 'T', desc: 'Hangup.' }, | |
{ name: 'SIGILL', action: 'A', desc: 'Illegal instruction.' }, | |
{ name: 'SIGINT', action: 'T', desc: 'Terminal interrupt signal.' }, | |
{ name: 'SIGKILL', action: 'T', desc: 'Kill (cannot be caught or ignored).' }, | |
{ name: 'SIGPIPE', action: 'T', desc: 'Write on a pipe with no one to read it.' }, | |
{ name: 'SIGQUIT', action: 'A', desc: 'Terminal quit signal.' }, | |
{ name: 'SIGSEGV', action: 'A', desc: 'Invalid memory reference.' }, | |
{ name: 'SIGSTOP', action: 'S', desc: 'Stop executing (cannot be caught or ignored).' }, | |
{ name: 'SIGTERM', action: 'T', desc: 'Termination signal.' }, | |
{ name: 'SIGTSTP', action: 'S', desc: 'Terminal stop signal.' }, | |
{ name: 'SIGTTIN', action: 'S', desc: 'Background process attempting read.' }, | |
{ name: 'SIGTTOU', action: 'S', desc: 'Background process attempting write.' }, | |
{ name: 'SIGUSR1', action: 'T', desc: 'User-defined signal 1.' }, | |
{ name: 'SIGUSR2', action: 'T', desc: 'User-defined signal 2.' }, | |
{ name: 'SIGPOLL', action: 'T', desc: 'Pollable event. ' }, | |
{ name: 'SIGPROF', action: 'T', desc: 'Profiling timer expired. ' }, | |
{ name: 'SIGSYS', action: 'A', desc: 'Bad system call.' }, | |
{ name: 'SIGTRAP', action: 'A', desc: 'Trace/breakpoint trap. ' }, | |
{ name: 'SIGURG', action: 'I', desc: 'High bandwidth data is available at a socket.' }, | |
{ name: 'SIGVTALRM', action: 'T', desc: 'Virtual timer expired.' }, | |
{ name: 'SIGXCPU', action: 'A', desc: 'CPU time limit exceeded.' }, | |
{ name: 'SIGXFSZ', action: 'A', desc: 'File size limit exceeded. ' } | |
]; | |
var selectedActions = {T:true, A:true}, | |
globalResult = 0; | |
function finish() { | |
process.exit(globalResult); | |
} | |
(function testSignal(idx) { | |
idx = idx||0; | |
var sig = all_signals[idx]; | |
if( !sig ) { finish(); return; } | |
function nextTest() { | |
process.nextTick(function() { testSignal(idx+1); }); | |
}; | |
// ignore signals that do not match the "selected actions" | |
if( ! (sig.action in selectedActions) ) { | |
nextTest(); | |
return; | |
} | |
// run the test in its own process. | |
// the argument `sig.name` tells the script to be a "master" | |
// and test the given signal. (see "Master" below). | |
child_process.execFile( | |
process.execPath, [process.argv[1], sig.name], null, | |
function(code, stdout, stderr) { | |
process.stdout.write(stdout); | |
process.stderr.write(stderr); | |
globalResult = globalResult || code; | |
nextTest() | |
} | |
) | |
}()); | |
}()); //end: toplevel | |
} else if( isMaster ) { | |
//** | |
//* Master (Test Stop Signal) - The first cmdline argument will be the | |
//* name of the signal to be tested. Test it, and exit. | |
//* | |
(function do_master() { | |
var excludedSignals = { 'SIGUSR1': 'used by v8/node debugger' }, | |
testSig = process.argv[2], | |
tmr0 = null, | |
timeout = 5000, | |
reportText; | |
function clean_exit(code) { | |
process.removeAllListeners(); | |
clearTimeout(tmr0); | |
process.exit(code); | |
} | |
process.on('uncaughtException', function (err) { | |
var msg = ''; | |
if( err.name === 'AssertionError' ) { | |
msg = err.message; | |
} else { | |
//throw err; | |
msg = err; | |
} | |
if( reportText ) { msg = reportText + msg; } | |
print('FAIL: '+ msg + '\n'); | |
clean_exit(1); | |
}); | |
if( testSig ) { | |
print(testSig + ' ...'); | |
} else { | |
// disable the timeout if no signal was specified... | |
timeout = 0; | |
} | |
if( testSig && (testSig in excludedSignals) ) { | |
// skip "excluded" signals (treat them as ok) | |
print( 'OK (skipped) ' + excludedSignals[testSig] + '\n'); | |
clean_exit(0); | |
} else { | |
// -- run the test -- // | |
var didExit = false | |
, didDisconnect = false | |
if( timeout ) { | |
tmr0 = setTimeout( function() { | |
assert.ok( false, 'timeout ('+(timeout/1000)+'s): child did not respond to signal.'); | |
}, timeout); | |
} | |
child = child_process.fork(process.argv[1]); | |
// child messages us, once it's up and running | |
child.on('message', function(msg) { | |
process.nextTick( start ); | |
}); | |
// performs the action that was requested of this test run. | |
// (ie. send a signal, tell child exit, or just wait) | |
function start() { | |
if( testSig ) { | |
child.kill(testSig); | |
} else { | |
print('master ['+process.pid+']\n'); | |
print('child ['+child.pid+']\n'); | |
print('waiting for child to exit... '); | |
} | |
} | |
child.on('disconnect', function() { | |
didDisconnect = true; | |
}); | |
child.on('exit', function(exitCode, sigCode) { | |
clearTimeout(tmr0); | |
didExit = true; | |
reportText = '[exitCode='+exitCode+', signal='+sigCode+'] '; | |
//print(reportText); | |
if( testSig ) { | |
// verify that we got the signal we were expecting... | |
assert.equal( sigCode, testSig, 'exit: reported incorrect signal'); | |
assert.equal( exitCode , null, 'exit: reported incorrect exitCode.') | |
} else { | |
assert( (exitCode != null && sigCode==null) || (exitCode == null && sigCode != null)); | |
} | |
assert.ok( didDisconnect, '(exit) missing disconnect event, or events emitted out-of-order.'); | |
}); | |
process.on('exit', function() { | |
assert.ok( didExit, 'no \'exit\' event was fired for child process' ); | |
assert.ok( didDisconnect , 'no \'disconnect\' event was fired for child process' ); | |
print('OK\n'); | |
}); | |
process.on('SIGINT', function() { | |
print('interrupted!\n'); | |
clean_exit(-1); | |
}); | |
} | |
}()); //end: master | |
} else if( isChild ) { | |
//** | |
//* Child - created by the master, to be the recipient of the signal | |
//* | |
(function do_child() { | |
process.send('helo'); | |
// child stays alive, because of ipc channel. | |
}()); //end: child | |
} else { | |
// should not happen. | |
assert.ok( false, "the test script is unable to identify whether it is "+ | |
"master, child, or toplevel. This should never happen"); | |
} | |
// a few shared utilities | |
function print(str) { | |
process.stderr.write(str); | |
} |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment Hide comment
The following results were obtained using node v0.7.9-pre (6f82b9f), on OS-X lion.
Analysis:
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The following results were obtained using node v0.7.9-pre (6f82b9f), on OS-X lion.
Analysis:
-D_POSIX_C_SOURCE
might fix this, but that seems to break lots of other stuff)._exit(1)
. This causes the child_process to exit "normally" (with a non-zero exit code), rather than exiting "abnormally" (with a signal code).