-
Notifications
You must be signed in to change notification settings - Fork 2.3k
chore(launcher): reorganize launcher #1485
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
*/ | ||
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pull this out as some sort of sentinel constant? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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'). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() {}; | ||
|
||
|
@@ -47,7 +65,10 @@ exports.run = function(runner, specs) { | |
if (originalOnComplete) { | ||
originalOnComplete(jasmineRunner, log); | ||
} | ||
resolve(jasmineRunner.results()); | ||
resolve({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() { | ||
|
@@ -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, | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing something here? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.