Skip to content

Commit

Permalink
added some improvements for parallell test running mode; added global…
Browse files Browse the repository at this point in the history
… disable_colors property and parallel_process_delay property to specify the delay between starting child processes
  • Loading branch information
beatfactor committed Nov 22, 2014
1 parent 37ac619 commit d11e3de
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 44 deletions.
2 changes: 2 additions & 0 deletions bin/nightwatch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"custom_assertions_path" : "",
"globals_path" : "",
"live_output" : false,
"parallel_process_delay" : 10,
"disable_colors": false,

"selenium" : {
"start_process" : false,
Expand Down
61 changes: 35 additions & 26 deletions lib/runner/cli/clirunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ CliRunner.prototype = {
try {
this.settings = require(this.argv.config);
this.replaceEnvVariables();

this.manageSelenium = !this.isParallelMode() && this.settings.selenium &&
this.settings.selenium.start_process || false;

Expand Down Expand Up @@ -159,9 +160,10 @@ CliRunner.prototype = {
if (!err.message) {
err.message = 'There was an error while running the test.';
}
if (this.test_settings.output) {
process.stderr.write('\n' + Logger.colors.red(err.message) + '\n');
}


process.stderr.write('\n' + Logger.colors.red(err.message) + '\n');

finished(false);
process.exit(1);
} else {
Expand Down Expand Up @@ -249,9 +251,8 @@ CliRunner.prototype = {
beforeGlobal.call(globalsContext, function() {
Selenium.startServer(self.settings, function(error, child, error_out, exitcode) {
if (error) {
if (self.test_settings.output) {
Logger.error('There was an error while starting the Selenium server:');
}
console.error('There was an error while starting the Selenium server:');

self.globalErrorHandler({
message : error_out
});
Expand Down Expand Up @@ -331,9 +332,10 @@ CliRunner.prototype = {

/**
* Validates and parses the test settings
* @param {function} [done]
* @returns {CliRunner}
*/
parseTestSettings : function(callback) {
parseTestSettings : function(done) {
// checking if the env passed is valid
if (!this.settings.test_settings) {
throw new Error('No testing environment specified.');
Expand All @@ -347,7 +349,7 @@ CliRunner.prototype = {
}

if (envs.length > 1) {
this.setupParallelMode(envs, callback);
this.setupParallelMode(envs, done);
return this;
}

Expand Down Expand Up @@ -404,7 +406,7 @@ CliRunner.prototype = {
this.test_settings.tag_filter = this.argv.tag;
}

if (this.test_settings.disable_colors) {
if (this.settings.disable_colors || this.test_settings.disable_colors) {
Logger.disableColors();
}

Expand Down Expand Up @@ -452,18 +454,18 @@ CliRunner.prototype = {
/**
* Enables parallel execution mode
* @param {Array} envs
* @param {function} [callback]
* @param {function} [done]
* @returns {CliRunner}
*/
setupParallelMode : function(envs, callback) {
setupParallelMode : function(envs, done) {
this.parallelMode = true;
var self = this;

this.startSelenium(function() {
self.startChildProcesses(envs, function(o, code) {
self.stopSelenium();
if (callback) {
callback();
if (done) {
done(o, code);
}
if (code) {
process.exit(code);
Expand Down Expand Up @@ -517,17 +519,19 @@ CliRunner.prototype = {
/**
* Start a new child process for each environment
* @param {Array} envs
* @param {function} finishCallback
* @param {function} doneCallback
*/
startChildProcesses : function(envs, finishCallback) {
startChildProcesses : function(envs, doneCallback) {
var execFile = require('child_process').execFile, child, self = this;
var mainModule = process.mainModule.filename;
finishCallback = finishCallback || function() {};
doneCallback = doneCallback || function() {};

var availColors = this.getAvailableColors();
var prevIndex = 0;
var output = {};
var globalExitCode = 0;
var processStartDelay = this.settings.parallel_process_delay || 10;

var writeToSdtout = function(data, item, index) {
data = data.replace(/^\s+|\s+$/g, '');
output[item] = output[item] || [];
Expand All @@ -541,8 +545,12 @@ CliRunner.prototype = {
}
}

env_output += Logger.colors[color_pair[1]](' ' + item + ' ',
Logger.colors.background[color_pair[0]]);
if (self.settings.disable_colors) {
env_output += ' ' + item + ' ';
} else {
env_output += Logger.colors[color_pair[1]](' ' + item + ' ',
Logger.colors.background[color_pair[0]]);
}

if (self.settings.live_output) {
env_output += ' ' + data;
Expand All @@ -561,6 +569,7 @@ CliRunner.prototype = {
var cliArgs = self.getChildProcessArgs(mainModule);
cliArgs.push('--env', item, '--parallel-mode');
var env = process.env;
var itemKey = item + '_' + (index+1);

setTimeout(function() {
env.__NIGHTWATCH_PARALLEL_MODE = 1;
Expand All @@ -572,21 +581,21 @@ CliRunner.prototype = {
env : env
}, function (error, stdout, stderr) {});

self.runningProcesses[item] = true;
self.runningProcesses[itemKey] = true;
console.log('Started child process for env:',
Logger.colors.yellow(' ' + item + ' ', Logger.colors.background.black), '\n');
self.settings.disable_colors ? (' ' + itemKey + ' ') : (Logger.colors.yellow(' ' + itemKey + ' ', Logger.colors.background.black)), '\n');

child.stdout.on('data', function (data) {
writeToSdtout(data, item, index);
writeToSdtout(data, itemKey, index);
});

child.stderr.on('data', function (data) {
writeToSdtout(data, item, index);
writeToSdtout(data, itemKey, index);
});

child.on('close', function(code) {
if (!self.processesRunning()) {
finishCallback(output, globalExitCode);
doneCallback(output, globalExitCode);
}
});

Expand All @@ -595,16 +604,16 @@ CliRunner.prototype = {
globalExitCode = 2;
}
if (!self.settings.live_output) {
var child_output = output[item] || '';
var child_output = output[itemKey] || '';
for (var i = 0; i < child_output.length; i++) {
process.stdout.write(child_output[i]);
}
console.log('');
}

self.runningProcesses[item] = false;
self.runningProcesses[itemKey] = false;
});
}, index * 10);
}, index * processStartDelay);
});
},

Expand Down
68 changes: 50 additions & 18 deletions tests/src/runner/testParallelExecution.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,14 @@ var mockery = require('mockery');

module.exports = {
setUp: function(callback) {
var self = this;
this.allArgs = [];
this.allOpts = [];
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
callback();
},

tearDown: function(callback) {
mockery.deregisterAll();
mockery.resetCache();
mockery.disable();
callback();
},

testParallelExecution : function(test) {
var allArgs = [], allOpts = [];
mockery.registerMock('child_process', {
execFile : function(path, args, opts) {
allArgs.push(args);
allOpts.push(opts);
self.allArgs.push(args);
self.allOpts.push(opts);

function Stdout() {}
function Stderr() {}
Expand All @@ -44,22 +35,63 @@ module.exports = {
return new Child();
}
});

mockery.registerMock('../lib/runner/run.js', {
run : function(source, settings, opts, callback) {}
});

callback();
},

tearDown: function(callback) {
mockery.deregisterAll();
mockery.resetCache();
mockery.disable();
callback();
},

testParallelExecution : function(test) {
var self = this;
var CliRunner = require('../../../' + BASE_PATH + '/../lib/runner/cli/clirunner.js');
var runner = new CliRunner({
config : './extra/nightwatch.json',
env : 'default,mixed'
});

runner.init(function(output, code) {
test.ok(runner.isParallelMode());
test.equals(code, 0);
test.deepEqual(output, {});
test.equals(self.allArgs.length, 2);
test.equals(self.allArgs[0][2], 'default');
test.equals(self.allArgs[1][2], 'mixed');

test.ok('default_1' in runner.runningProcesses);
test.ok('mixed_2' in runner.runningProcesses);
test.equals(runner.runningProcesses['default_1'], false);
test.equals(runner.runningProcesses['mixed_2'], false);
test.done();
});

test.ok(runner.parallelMode);
},

testParallelExecutionSameEnv : function(test) {
var self = this;
var CliRunner = require('../../../' + BASE_PATH + '/../lib/runner/cli/clirunner.js');
var runner = new CliRunner({
config : './extra/nightwatch.json',
env : 'mixed,mixed'
});

runner.init(function() {
test.ok(runner.isParallelMode());
test.equals(allArgs.length, 2);
test.equals(allArgs[0][2], 'default');
test.equals(allArgs[1][2], 'mixed');
test.equals(self.allArgs.length, 2);
test.equals(self.allArgs[0][2], 'mixed');
test.equals(self.allArgs[1][2], 'mixed');
test.ok('mixed_1' in runner.runningProcesses);
test.ok('mixed_2' in runner.runningProcesses);
test.equals(runner.runningProcesses['mixed_1'], false);
test.equals(runner.runningProcesses['mixed_2'], false);
test.done();
});

Expand Down

0 comments on commit d11e3de

Please sign in to comment.