Skip to content

Commit

Permalink
Parallel selenium for grid
Browse files Browse the repository at this point in the history
  • Loading branch information
zzo committed Sep 14, 2011
1 parent ceb75da commit 1590ad1
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 36 deletions.
15 changes: 15 additions & 0 deletions backend/nodejute/examples/clientSide/toolbar.js
@@ -1,6 +1,21 @@
YUI().add('toolbar', function(Y) {
Y.Toolbar = function Toolbar() {
this.message = "I am a toolbar!";
function hidden(testme) {
var ret = testme + ' TESTED';
return ret;
}
};

Y.Toolbar.prototype = {
zop: function() {
this.zop = 'ZOP';
}
};

function testme(y) {
return y * 55;
}

}, '1.0.0' ,{requires:['attribute', 'event-custom-base', 'common-utils']});

11 changes: 6 additions & 5 deletions backend/nodejute/jute/actions/common.js
Expand Up @@ -99,23 +99,24 @@ module.exports = {
}

},
takeSeleniumSnapshot: function(test, filename) {
takeSeleniumSnapshot: function(test, filename, component) {
var soda = require('soda'), i
b = soda.createClient({ host: test.sel_host });

if (!test.seleniumID) return;

b.sid = test.seleniumID;
b.sid = test.seleniumID;
filename = path.join(hub.config.outputDir, (common.makeSaneNames(component))[0], filename);

b.chain.windowFocus().getEval("window.moveTo(1,0); window.resizeTo(screen.availWidth, screen.availHeight);").end(function(err) {
if (!err) {
b.command('captureScreenshotToString', [], function(err, body, res) {
if (!err) {
var msg;
try {
var bb = new Buffer(body, 'base64');
var bb = new Buffer(body, 'base64'),
msg = "Dumped snapshot for " + test.url + ' to ' + filename + "\n";
fs.writeFileSync(filename, bb, 0, bb.length);
var msg = "Dumped snapshot for " + test.url + ' to ' + filename + "\n";
common.addTestOutput(test, msg);
hub.emit(hub.LOG, hub.INFO, msg);
} catch(e) {
Expand All @@ -133,7 +134,7 @@ module.exports = {
},
addTestOutput: function(test, msg) {
var lines = msg.split(/\n/),
now = new Date(),
now = new Date(),
output = '',
format;

Expand Down
48 changes: 34 additions & 14 deletions backend/nodejute/jute/actions/runTest.js
Expand Up @@ -102,7 +102,15 @@ module.exports = {
}

var pushed = false, v8Tests = '',
seleniumUUID = uuid(), requestKey = uuid();
requestKey = uuid(), seleniumIDs = [];

// Generate Selenium IDs
if (obj.sel_host) {
var seleniums = parseInt(obj.seleniums, 10) || 1;
for (var i = 0; i < seleniums; i++) {
seleniumIDs.push(uuid());
}
}

for (var i = 0; i < tests.length; i++) {
var test = tests[i],
Expand Down Expand Up @@ -140,7 +148,9 @@ module.exports = {

// Only pass these tests out to selenium hosts started by this
// this is how we keep track
obj.uuid = test_obj.browser = seleniumUUID;
// hand this off to next SeleniumID
console.log('choosing selenium id: ' + (i % seleniumIDs.length));
test_obj.browser = seleniumIDs[i % seleniumIDs.length];

common.addTestOutput(test_obj, 'Selenium test');

Expand All @@ -149,7 +159,7 @@ module.exports = {
} else {
if (multipleFromUI) {
// Only run these tests in THIS browser from the UI

test_obj.browser = req.session.uuid;

common.addTestOutput(test_obj, 'Multiple in this browser test');
Expand Down Expand Up @@ -178,18 +188,31 @@ module.exports = {
if (pushed) {
if (obj.sel_host) {
// Start up for a Selenium browser & Listen for results
hub.once('action:seleniumDone', function(err) {
var totalError = '';
hub.on('action:seleniumDone', function(err, selID) {
seleniumIDs.pop();
var done = !seleniumIDs.length;
if (done) hub.removeListener('action:seleniumDone', arguments.callee);

if (err) {
hub.emit(hub.LOG, hub.ERROR, 'ERROR running Selenium tests: ' + err);
res.end("" + err);
hub.emit(hub.LOG, hub.ERROR, 'ERROR running Selenium tests (' + selID + '): ' + err);
totalError += err;
if (done) {
res.end(totalError);
}
} else {
hub.once('action:checkedResults', function(results) {
res.end('Final Selenium Results: ' + JSON.stringify(results));
});
hub.emit('action:checkResults');
if (done) {
hub.once('action:checkedResults', function(results) {
res.end('Final Selenium Results: ' + JSON.stringify(results));
});
hub.emit('action:checkResults');
}
}
});
hub.emit('action:seleniumStart', req, res);

seleniumIDs.forEach(function(selID) {
hub.emit('action:seleniumStart', selID, req, res);
});
} else {
// UI wants to run multiple tests - redirect to it!
if (multipleFromUI) {
Expand Down Expand Up @@ -232,9 +255,6 @@ module.exports = {
}
}
}

function waitTests(res) {
}
}
};

21 changes: 10 additions & 11 deletions backend/nodejute/jute/actions/startSelenium.js
Expand Up @@ -45,7 +45,7 @@ module.exports = {
// Events I care about
hub.addListener('action:seleniumStart', startSelenium);

function startSelenium(req, res) {
function startSelenium(selID, req, res) {
var soda = require('soda'), cb,
body = req.body,
browser
Expand All @@ -58,7 +58,7 @@ module.exports = {
browser: body.sel_browser
})
} catch(e) {
hub.emit('action:seleniumDone', 'Cannot connect to Selenium server at ' + body.sel_host + ': ' + e);
hub.emit('action:seleniumDone', 'Cannot connect to Selenium server at ' + body.sel_host + ': ' + e, selID);
return;
}

Expand All @@ -68,39 +68,38 @@ module.exports = {
cb();
});

cache.connections[selID] = res; // our link back to the requesting client for status messages

// called when all Selenium tests are complete for this browser
// && keep track of requesting client for debug messages back...
cache.connections[body.uuid] = res; // our link back to the requesting client for status messages

// Callback for when the Selenium session is done
cb = function(err) {
if (!err) {
browser.chain.testComplete().end(function(err) {
delete cache.connections[body.uuid]; // done with status updates
hub.emit('action:seleniumDone', err);
delete cache.connections[selID]; // done with status updates
hub.emit('action:seleniumDone', err, selID);
});
} else {
hub.emit('action:seleniumDone', err);
hub.emit('action:seleniumDone', err, selID);
}
};
cb = hub.once('seleniumTestsFinished', cb);

var oo;
browser.
chain.
session().
open('/?selenium=' + body.uuid).
open('/?selenium=' + selID).
waitForPageToLoad(10000).
end(function(err) {
if (err) {
var msg = 'Error starting/waiting for Selenium page to load: ' + err;
hub.emit('seleniumTestsFinished', err);
} else {
hub.emit(hub.LOG, hub.INFO, "Selenium up and running: " + browser.sid);
// If this is one of the tests that are going to run in thie
// If this is one of the tests that are going to run in the
// Selenium session, tag it with the Selenium token
cache.tests_to_run.forEach(function(test) {
if (test.browser === body.uuid) {
if (test.browser === selID) {
test.seleniumID = browser.sid;
}
});
Expand Down
2 changes: 1 addition & 1 deletion backend/nodejute/jute/actions/testReport.js
Expand Up @@ -174,7 +174,7 @@ module.exports = {
// only if a test fails
if ((!succeeded || test.snapshot) && req.session.selenium) {
hub.emit(hub.LOG, hub.INFO, 'Taking a Selenium snapshot of: ' + test.url);
common.takeSeleniumSnapshot(test, path.join(names[1], path.basename(names[0], 'xml')) + '.png');
common.takeSeleniumSnapshot(test, path.basename(names[0], 'xml') + '.png', obj.name);
} else {
hub.emit('action:doneDone', null, test);
}
Expand Down
9 changes: 6 additions & 3 deletions backend/nodejute/jute/server.js
Expand Up @@ -115,6 +115,9 @@ Create: function(hub) {
url = url.replace(/\?.*/,''); // get rid of any query string
url = url.replace(hub.config.testDirWeb,''); // get rid of any query string

try { fs.statSync(path); } catch(e) { res.writeHeader(404); res.end('Cannot find: ' + path ); return; } // 404 this bad boy

/*
if (req.query._one_shot && path.match(/\.js$/)) {
// A V8 test!!
exec('JUTE_DEBUG=1 ' + p.join(__dirname, '..', 'jute_v8.js') + ' ' + url + '?do_coverage=' + req.query.do_coverage, function(error, stdout, stderr) {
Expand All @@ -124,7 +127,8 @@ Create: function(hub) {
res.end(stdout);
}
});
} else if (req.query.coverage) { // && (!req.headers.referer || req.headers.referer.match('do_coverage=1'))) {
} else */
if (req.query.coverage) { // && (!req.headers.referer || req.headers.referer.match('do_coverage=1'))) {
// Coverage this bad boy!
var tempFile = p.join('/tmp', p.basename(path));
hub.emit(hub.LOG, hub.INFO, "Generating coverage file " + tempFile + " for " + path);
Expand Down Expand Up @@ -174,8 +178,7 @@ YUI().use('io-base', 'json-stringify', function(Y) {\
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }\
}\
);\
});}",
yuiConfig = "YUI_config = {loadErrorFn: function(a,b,c) { " + efun + "}, errorFn: function(a, b, c) { " + efun + " } };";
});}";

fs.stat(path, function(err, stat) {
var type, charset,
Expand Down
2 changes: 1 addition & 1 deletion backend/nodejute/package.json
Expand Up @@ -2,7 +2,7 @@
"name": "jute",
"description": "Javascript Unit Test Environment",
"keywords": ["selenium", "test", "testing", "unit", "tests"],
"version": "0.0.51",
"version": "0.0.52",
"author": "Mark Ethan Trostler <mark@zzo.com>",
"preferGlobal": true,
"bin" : {
Expand Down
6 changes: 5 additions & 1 deletion backend/nodejute/submit_test.js
Expand Up @@ -41,12 +41,13 @@ var config = (require('./getConfig'))(),
events = require("events"),
eventHubF = function() { events.EventEmitter.call(this); },
args = opt
.usage('Usage: $0 --test [testfile] [ --test [another testfile] ] [ --host [JUTE host] ] [ --port [JUTE host port] ] [ --sel_host [Selenium host] ] [ --sel_browser [Selenium browser spec] [ --load ] ] [ --send_output ] [ --wait ] [ --clear_results ] [ -v8 ] [ --status ] [ --snapshot ] [ --retry ]')
.usage('Usage: $0 --test [testfile] [ --test [another testfile] ] [ --host [JUTE host] ] [ --port [JUTE host port] ] [ --sel_host [Selenium host] ] [ --sel_browser [Selenium browser spec] ] [ --seleniums # ] [ --load ] ] [ --send_output ] [ --wait ] [ --clear_results ] [ -v8 ] [ --status ] [ --snapshot ] [ --retry ]')
.alias('t', 'test')
.alias('h', 'host')
.alias('p', 'port')
.alias('sh', 'sel_host')
.alias('sb', 'sel_browser')
.alias('se', 'seleniums')
.alias('l', 'load')
.alias('s', 'status')
.alias('sn', 'snapshot')
Expand All @@ -61,13 +62,15 @@ var config = (require('./getConfig'))(),
.default('snapshot', false)
.default('load', false)
.default('clear_results', false)
.default('seleniums', 1)
.default('sel_browser', '*firefox')
.default('retry', 0)
.describe('test', 'Test file to run - relative to docRoot/testDir (npm set jute.testDir) - can specify multiple of these')
.describe('host', 'Hostname of JUTE server')
.describe('port', 'Port of JUTE server')
.describe('sel_host', 'Hostname of Selenium RC or Grid Server (if not specified test(s) will run in all CURRENTLY captured browsers)')
.describe('sel_browser', 'Selenium browser specification')
.describe('seleniums', 'Number of Selenium browsers to run tests in parallel')
.describe('load', 'Load up tests but do not run them immediately')
.describe('snapshot', 'Dump a snapshot at end of test (Selenium only!)')
.describe('send_output', 'For Selenium tests ONLY - send status messages back while running')
Expand Down Expand Up @@ -148,6 +151,7 @@ eventHub.on('tests', function(tests) {
if (args.sel_host) {
juteArgs.sel_host = args.sel_host;
juteArgs.sel_browser = args.sel_browser;
juteArgs.seleniums = args.seleniums;
if (args.snapshot) {
juteArgs.snapshot = 1;
}
Expand Down

0 comments on commit 1590ad1

Please sign in to comment.