node/test/parallel/test-cluster-worker-exit.js
Imran Iqbal 8b57b31299 test: Fix test-cluster-worker-exit.js for AIX
test fails intermittently due to the assertion that the 'disconnect'
event should come before the 'exit' event. This is caused be the
non-deteministic behaviour of pollset_poll[1] on AIX
(see deps/uv/src/unix/aix.c). This API makes no garauntee for the order
in which file descriptors are returned. On linux epoll_wait[2] is used,
which also does not make a garauntee on order of file descriptors
returned. In the failing case we recieve our file descriptor with a
callback of uv__signal_event (which causes JavaScript to receive the
exit event) before our file descriptor with uv__stream_io as its
callback (which in turn causes JavaScript receive the disconnect event).
This change simply removes the assertion that the disconnect event
happens before exit event and processes the test regardless of which
event comes first.

[1] https://www-01.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.ai
x.basetrf1/pollset.htm
[2] http://linux.die.net/man/2/epoll_pwait

PR-URL: https://github.com/nodejs/node/pull/3666
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2015-11-13 21:01:18 -08:00

128 lines
3.6 KiB
JavaScript

'use strict';
// test-cluster-worker-exit.js
// verifies that, when a child process exits (by calling `process.exit(code)`)
// - the parent receives the proper events in the proper order, no duplicates
// - the exitCode and signalCode are correct in the 'exit' event
// - the worker.suicide flag, and worker.state are correct
// - the worker process actually goes away
var common = require('../common');
var assert = require('assert');
var cluster = require('cluster');
var EXIT_CODE = 42;
if (cluster.isWorker) {
var http = require('http');
var server = http.Server(function() { });
server.once('listening', function() {
process.exit(EXIT_CODE);
});
server.listen(common.PORT, '127.0.0.1');
} else if (cluster.isMaster) {
var expected_results = {
cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"],
cluster_emitExit: [1, "the cluster did not emit 'exit'"],
cluster_exitCode: [EXIT_CODE, 'the cluster exited w/ incorrect exitCode'],
cluster_signalCode: [null, 'the cluster exited w/ incorrect signalCode'],
worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"],
worker_emitExit: [1, "the worker did not emit 'exit'"],
worker_state: ['disconnected', 'the worker state is incorrect'],
worker_suicideMode: [false, 'the worker.suicide flag is incorrect'],
worker_died: [true, 'the worker is still running'],
worker_exitCode: [EXIT_CODE, 'the worker exited w/ incorrect exitCode'],
worker_signalCode: [null, 'the worker exited w/ incorrect signalCode']
};
var results = {
cluster_emitDisconnect: 0,
cluster_emitExit: 0,
worker_emitDisconnect: 0,
worker_emitExit: 0
};
// start worker
var worker = cluster.fork();
worker.once('listening', function() {
// the worker is up and running...
});
// Check cluster events
cluster.on('disconnect', function() {
results.cluster_emitDisconnect += 1;
});
cluster.on('exit', function(worker) {
results.cluster_exitCode = worker.process.exitCode;
results.cluster_signalCode = worker.process.signalCode;
results.cluster_emitExit += 1;
});
// Check worker events and properties
worker.on('disconnect', function() {
results.worker_emitDisconnect += 1;
results.worker_suicideMode = worker.suicide;
results.worker_state = worker.state;
if (results.worker_emitExit > 0) {
process.nextTick(function() { finish_test(); });
}
});
// Check that the worker died
worker.once('exit', function(exitCode, signalCode) {
results.worker_exitCode = exitCode;
results.worker_signalCode = signalCode;
results.worker_emitExit += 1;
results.worker_died = !alive(worker.process.pid);
if (results.worker_emitDisconnect > 0) {
process.nextTick(function() { finish_test(); });
}
});
var finish_test = function() {
try {
checkResults(expected_results, results);
} catch (exc) {
console.error('FAIL: ' + exc.message);
if (exc.name != 'AssertionError') {
console.trace(exc);
}
process.exit(1);
return;
}
process.exit(0);
};
}
// some helper functions ...
function checkResults(expected_results, results) {
for (var k in expected_results) {
var actual = results[k],
expected = expected_results[k];
var msg = (expected[1] || '') +
(' [expected: ' + expected[0] + ' / actual: ' + actual + ']');
if (expected && expected.length) {
assert.equal(actual, expected[0], msg);
} else {
assert.equal(actual, expected, msg);
}
}
}
function alive(pid) {
try {
process.kill(pid, 'SIGCONT');
return true;
} catch (e) {
return false;
}
}