Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
refactor(launcher): make launcher use child process messages instead …
Browse files Browse the repository at this point in the history
…of messing with the env
  • Loading branch information
juliemr committed Feb 20, 2014
1 parent 3879af3 commit cca82ca
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 97 deletions.
174 changes: 97 additions & 77 deletions lib/launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,42 @@ var util = require('util'),
child = require('child_process'),
ConfigParser = require('./configParser');

var launcherPrefix = '[launcher] '

var reportHeader_ = function(proc, env) {
var capability = JSON.parse(env.capability);
var log_ = function(stuff) {
console.log(launcherPrefix + stuff);
}

var noLineLog_ = function(stuff) {
process.stdout.write(launcherPrefix + stuff);
}


var reportHeader_ = function(childFork) {
var capability = childFork.capability;
var eol = require('os').EOL;

var outputHeader = eol + '------------------------------' + eol;
outputHeader += 'PID: ' + proc.pid + ' (capability: ';
var outputHeader = eol + '------------------------------------' + eol;;
outputHeader += 'PID: ' + childFork.process.pid + ' (capability: ';
outputHeader += (capability.browserName) ?
capability.browserName : '';
outputHeader += (capability.version) ?
capability.version : '';
outputHeader += (capability.platform) ?
capability.platform : '';
outputHeader += (env.runNumber) ?
' #' + env.runNumber : '';
outputHeader += (childFork.runNumber) ?
' #' + childFork.runNumber : '';
outputHeader += ')' + eol;
outputHeader += '------------------------------';
outputHeader += '------------------------------------' + eol;


util.puts(outputHeader);
};

var makeChildEnv_ = function(additional) {
var newEnv = {};
for (var v in process.env) {
newEnv[v] = process.env[v];
}
for (var a in additional) {
newEnv[a] = additional[a];
}
return newEnv;
};

/**
* Initialize and run the tests.
*
* @param {Object} argv - Optimist parsed arguments
* @param {Object} argv Optimist parsed arguments.
*/
var init = function(argv) {

Expand All @@ -61,8 +60,20 @@ var init = function(argv) {
addConfig(argv).
getConfig();

var listRemainingForks = function() {
var remaining = 0;
childForks.forEach(function(childFork) {
if (!childFork.done) {
remaining++;
}
});
if (remaining) {
noLineLog_(remaining + ' instance(s) of WebDriver still running');
}
}

if (config.multiCapabilities.length) {
console.log('Running using config.multiCapabilities - ' +
log_('Running using config.multiCapabilities - ' +
'config.capabilities will be ignored');
}

Expand All @@ -80,79 +91,88 @@ var init = function(argv) {

// Fork the child runners.
for (var j = 0; j < capabilityRunCount; j++) {

// We use an environment variable to retain the optimist parsed args and
// to tell the runner which capability its responsible for.
// The child environment inherits from the parent environment.
childForks.push(makeChildEnv_({
cliArgs: JSON.stringify(argv),
capability: JSON.stringify(config.multiCapabilities[i]),
childForks.push({
cliArgs: argv,
capability: config.multiCapabilities[i],
runNumber: j + 1
}));
});
}
}

// If we're launching multiple runners, aggregate output until completion.
// Otherwise, there are multiple runners, let's pipe the output straight
// Otherwise, there is a single runner, let's pipe the output straight
// through to maintain realtime reporting.
if (childForks.length === 1) {
var childEnv = childForks.pop(),
childProc = child.fork(__dirname + "/runFromLauncher.js", [],
{env: childEnv,
cwd: process.cwd()});
reportHeader_(childProc, childEnv);
childProc.on('error', function(err) {
util.puts('Runner Process(' + childProc.pid + ') Error: ' + err);
var childFork = childForks[0];
childFork.process = child.fork(
__dirname + "/runFromLauncher.js", [], {cwd: process.cwd()});
reportHeader_(childFork);

childFork.process.send({
command: 'run',
cliArgs: childFork.cliArgs,
capability: childFork.capability
});

childFork.process.on('error', function(err) {
log_('Runner Process(' + childFork.process.pid + ') Error: ' + err);
});

childProc.on('exit', function(code, signal) {
childFork.process.on('exit', function(code, signal) {
if (code) {
util.puts('Runner Process Exited With Error Code: ' + code);
log_('Runner Process Exited With Error Code: ' + code);
launcherExitCode = 1;
}
});
} else {
process.stdout.write('Running ' + childForks.length + ' instances of WebDriver');
noLineLog_('Running ' + childForks.length +
' instances of WebDriver');

// Launch each fork and set up listeners
childForks.forEach(function(childEnv) {

var childProc = child.fork(__dirname + "/runFromLauncher.js", [],
{env: childEnv, silent: true, cwd: process.cwd()});

// Force evaluation to protect from loop changing closure in callbacks
(function(childProc_, childEnv_) {
childProc_.output = '';

// stdin pipe
childProc_.stdout.on('data', function(chunk) {
// Output something so processes know we haven't stalled.
// TODO - consider replacing this with a message system which would
// output a dot per test.
process.stdout.write('.');
childProc_.output += chunk;
});

// stderr pipe
childProc_.stderr.on('data', function(chunk) {
childProc_.output += chunk;
});

// err handler
childProc_.on('error', function(err) {
util.puts('Runner Process(' + childProc_.pid + ') Error: ' + err);
});

// exit handlers
childProc_.on('exit', function(code, signal) {
if (code) {
util.puts('Runner Process Exited With Error Code: ' + code);
launcherExitCode = 1;
}
reportHeader_(childProc_, childEnv_);
util.puts(childProc_.output);
});
})(childProc, childEnv);
childForks.forEach(function(childFork) {

childFork.process = child.fork(
__dirname + "/runFromLauncher.js", [],
{silent: true, cwd: process.cwd()});

childFork.output = '';

// stdin pipe
childFork.process.stdout.on('data', function(chunk) {
// Output something so processes know we haven't stalled.
// TODO - consider replacing this with a message system which would
// output a dot per test.
process.stdout.write('.');
childFork.output += chunk;
});

// stderr pipe
childFork.process.stderr.on('data', function(chunk) {
childFork.output += chunk;
});

// err handler
childFork.process.on('error', function(err) {
log_('Runner Process(' + childFork.process.pid + ') Error: ' + err);
});

// exit handlers
childFork.process.on('exit', function(code, signal) {
if (code) {
log_('Runner Process Exited With Error Code: ' + code);
launcherExitCode = 1;
}
reportHeader_(childFork);
util.puts(childFork.output);
childFork.done = true;
listRemainingForks();
});

childFork.process.send({
command: 'run',
cliArgs: childFork.cliArgs,
capability: childFork.capability
});
});
}

Expand Down
47 changes: 27 additions & 20 deletions lib/runFromLauncher.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
/**
* This serves as the main function for starting a test run that has been
* requested by the launcher. It assumes that the process environment has
* been set with the following properties:
* cliArgs - a stringified JSON object representing the CLI args passed
* to this test run.
* capability - a stringified JSON object representing the particular
* capability to be used for this run.
* numNumber - an identifier for this run, in case multiple runs of the same
* capability were requested.
* requested by the launcher.
*/

var ConfigParser = require('./configParser');
var Runner = require('./runner');

var config, argv;

// Merge in config file options.
argv = JSON.parse(process.env.cliArgs);
config = new ConfigParser().
addFileConfig(argv._[0]).
addConfig(argv).
getConfig();
process.on('message', function(m) {
switch (m.command) {
case 'run':
if (!m.capability) {
throw new Error('Run message missing capability');
}
if (!m.cliArgs) {
throw new Error('Run message missing cliArgs');
}
// Merge in config file options.
argv = m.cliArgs;
config = new ConfigParser().
addFileConfig(argv._[0]).
addConfig(argv).
getConfig();

// Grab capability to run from launcher.
config.capabilities = JSON.parse(process.env.capability);
// Grab capability to run from launcher.
config.capabilities = m.capability;

// Launch test run.
var runner = new Runner(config);
runner.run().then(function(exitCode) {
process.exit(exitCode);
// Launch test run.
var runner = new Runner(config);
runner.run().then(function(exitCode) {
process.exit(exitCode);
});
break;
default:
throw new Error('command ' + m.command + ' is invalid');
}
});

0 comments on commit cca82ca

Please sign in to comment.