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

chore(launcher): reorganize launcher #1485

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions lib/RunnerFork.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
var child = require('child_process');
var q = require('q');
var TaskLogger = require('./TaskLogger.js');
var EventEmitter = require('events').EventEmitter;
var util = require('util');


/**
* A fork of a runner for running a specified task. The RunnerFork will
* start a new process that calls on '/runFromLauncher.js'.
*
* @constructor
* @param {object} task Task to run.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add param annotations for the other parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't change any comments yet. I'll do that as a next iteration, but I just want to see if you like the general feel of the refactor or you think it's excessive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha - will keep my feedback on that level then.

*/
var RunnerFork = function(configFile, additionalConfig, task, runInFork) {
this.configFile = configFile;
this.additionalConfig = additionalConfig;
this.task = task;
this.runInFork = runInFork;
};
util.inherits(RunnerFork, EventEmitter);

/**
* Sends the run command.
*/
RunnerFork.prototype.run = function() {
var self = this;

var runResults = {
taskId: this.task.taskId,
specs: this.task.specs,
capabilities: this.task.capabilities,
// The following are populated while running the test:
failedCount: 0,
exitCode: -1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull this out as some sort of sentinel constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by this? The -1 isn't really a "constant" as it has no meaning and will always be replaced (just a placeholder)

specResults: []
}

if (this.runInFork) {
var deferred = q.defer();

var childProcess = child.fork(
__dirname + '/runFromLauncher.js',
process.argv.slice(2), {
cwd: process.cwd(),
silent: true
}
);
var taskLogger = new TaskLogger(this.task, childProcess.pid);

// stdout pipe
childProcess.stdout.on('data', function(data) {
taskLogger.log(data);
});

// stderr pipe
childProcess.stderr.on('data', function(data) {
taskLogger.log(data);
});

childProcess.on('message', function(m) {
switch (m.event) {
case 'testPass':
process.stdout.write('.');
break;
case 'testFail':
process.stdout.write('F');
break;
case 'testsDone':
runResults.failedCount = m.results.failedCount;
runResults.specResults = m.results.specResults;
break;
}
})
.on('error', function(err) {
taskLogger.flush();
deferred.reject(err);
})
.on('exit', function(code) {
taskLogger.flush();
runResults.exitCode = code;
deferred.resolve(runResults);
});

childProcess.send({
command: 'run',
configFile: this.configFile,
additionalConfig: this.additionalConfig,
capabilities: this.task.capabilities,
specs: this.task.specs
});

return deferred.promise;
} else {
var ConfigParser = require('./configParser');
var configParser = new ConfigParser();
if (this.configFile) {
configParser.addFileConfig(this.configFile);
}
if (this.additionalConfig) {
configParser.addConfig(this.additionalConfig);
}
var config = configParser.getConfig();
config.capabilities = this.task.capabilities;
config.specs = this.task.specs;

var Runner = require('./runner');
var runner = new Runner(config);

runner.on('testsDone', function(results) {
runResults.failedCount = results.failedCount;
runResults.specResults = results.specResults;
})

return runner.run().then(function(exitCode) {
runResults.exitCode = exitCode;
return runResults;
});
}
};

module.exports = RunnerFork;
88 changes: 88 additions & 0 deletions lib/TaskLogger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
var EOL = require('os').EOL;

/**
* Log output such that metadata are appended.
* Calling log(data) will not flush to console until you call flush()
*
* @constructor
* @param {object} task Task that is being reported.
* @param {number} pid PID of process running the task.
*/
var TaskLogger = function(task, pid) {
this.task = task;
this.pid = pid;
this.buffer = '';
this.insertTag = true;

this.logHeader_();
};

/**
* Report the header for the current task including information such as
* PID, browser name/version, task Id, specs being run.
*
* @private
*/
TaskLogger.prototype.logHeader_ = function() {
var output = 'PID: ' + this.pid + EOL;
if (this.task.specs.length === 1) {
output += 'Specs: '+ this.task.specs.toString() + EOL + EOL;
}
this.log(output);
};


/**
* Flushes the buffer to stdout.
*/
TaskLogger.prototype.flush = function() {
if (this.buffer) {
// Flush buffer if nonempty
process.stdout.write(EOL + '------------------------------------' + EOL);
process.stdout.write(this.buffer);
process.stdout.write(EOL);
this.buffer = '';
}
};

/**
* Log the data in the argument. The data will be saved to a buffer
* until flush() is called.
*
* @param {string} data
*/
TaskLogger.prototype.log = function(data) {
var tag = '[';
var capabilities = this.task.capabilities;
tag += (capabilities.browserName) ?
capabilities.browserName : '';
tag += (capabilities.version) ?
(' ' + capabilities.version) : '';
tag += (capabilities.platform) ?
(' ' + capabilities.platform) : '';
tag += (' #' + this.task.taskId);
tag += '] ';

data = data.toString();
for ( var i = 0; i < data.length; i++ ) {
if (this.insertTag) {
this.insertTag = false;
// This ensures that the '\x1B[0m' appears before the tag, so that
// data remains correct when color is not processed.
// See https://github.com/angular/protractor/pull/1216
if (data[i] === '\x1B' && data.substring(i, i+4) === '\x1B[0m' ) {
this.buffer += ('\x1B[0m' + tag);
i += 3;
continue;
}

this.buffer += tag;
}
if (data[i] === '\n') {
this.insertTag = true;
}
this.buffer += data[i];
}
};

module.exports = TaskLogger;
1 change: 1 addition & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var optimist = require('optimist').
describe('stackTrace', 'Print stack trace on error').
describe('params', 'Param object to be passed to the tests').
describe('framework', 'Test framework to use: jasmine, cucumber or mocha').
describe('testResultOutput', 'Path to save JSON test result').
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add to docs/referenceConf - also, I'm not sure about this name. What about resultJsonOutputFile?

alias('browser', 'capabilities.browserName').
alias('name', 'capabilities.name').
alias('platform', 'capabilities.platform').
Expand Down
25 changes: 23 additions & 2 deletions lib/frameworks/jasmine.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,38 @@ exports.run = function(runner, specs) {
require('jasminewd');
/* global jasmine */

testResult = [];

var RunnerReporter = function(emitter) {
this.emitter = emitter;
};

RunnerReporter.prototype.reportRunnerStarting = function() {};
RunnerReporter.prototype.reportRunnerResults = function() {};
RunnerReporter.prototype.reportSuiteResults = function() {};
RunnerReporter.prototype.reportSpecStarting = function() {};
RunnerReporter.prototype.reportSpecStarting = function() {
this.startTime = new Date();
};
RunnerReporter.prototype.reportSpecResults = function(spec) {
if (spec.results().passed()) {
this.emitter.emit('testPass');
} else {
this.emitter.emit('testFail');
}

var entry = {
description: spec.results().description,
result: [],
duration: new Date().getTime() - this.startTime.getTime()
};
spec.results().getItems().forEach(function(item) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we find information about the rumored output format that javascript frameworks were supposedly discussing? Would be great to use something compatible with that.

entry.result.push({
passed: item.passed(),
errorMsg: item.passed() ? undefined : item.message,
stacktrace: item.passed() ? undefined : item.trace.stack
});
});
testResult.push(entry);
};
RunnerReporter.prototype.log = function() {};

Expand All @@ -47,7 +65,10 @@ exports.run = function(runner, specs) {
if (originalOnComplete) {
originalOnComplete(jasmineRunner, log);
}
resolve(jasmineRunner.results());
resolve({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update frameworks/README with more info about the changes to the resolved object.

failedCount: jasmineRunner.results().failedCount,
specResults: testResult
});
} catch(err) {
reject(err);
}
Expand Down
50 changes: 36 additions & 14 deletions lib/frameworks/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,11 @@ exports.run = function(runner, specs) {
global.it.only = global.iit = originalOnly;

global.it.skip = global.xit = mochaAdapters.xit;
}catch(err){
} catch(err) {
deferred.reject(err);
}
});

mocha.suite.on('pass', function() {
runner.emit('testPass');
});

mocha.suite.on('fail', function() {
runner.emit('testFail');
});

mocha.loadFiles();

runner.runTestPreparer().then(function() {
Expand All @@ -56,21 +48,51 @@ exports.run = function(runner, specs) {
mocha.addFile(file);
});

mocha.run(function(failures) {
var testResult = [];

var mochaRunner = mocha.run(function(failures) {
try {
if (runner.getConfig().onComplete) {
runner.getConfig().onComplete();
}
var resolvedObj = {
failedCount: failures
failedCount: failures,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing something here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like resolvedObj should just be removed.

};

deferred.resolve(resolvedObj);
}catch(err){
deferred.resolve({
failedCount: failures,
specResults: testResult
});
} catch(err) {
deferred.reject(err);
}
});
}).catch(function(reason){

mochaRunner.on('pass', function(test) {
runner.emit('testPass');
testResult.push({
description: test.title,
result: [{
passed: true
}],
duration: test.duration
});
});

mochaRunner.on('fail', function(test) {
runner.emit('testFail');
testResult.push({
description: test.title,
result: [{
passed: false,
errorMsg: test.err.message,
stacktrace: test.err.stack
}],
duration: test.duration
});
});
}).catch(function(reason) {
deferred.reject(reason);
});

Expand Down
Loading