diff --git a/src/lib/saucelabs-manager.js b/src/lib/saucelabs-manager.js index 06f327b..627aa20 100644 --- a/src/lib/saucelabs-manager.js +++ b/src/lib/saucelabs-manager.js @@ -1,5 +1,6 @@ var request = require('request'), - log = require('./log'); + log = require('./log'), + _ = require('underscore'); /** * SessionManager Constructor @@ -21,6 +22,8 @@ function SauceLabsSessionManager(options) { this.retryDelay = 3000; this.retry = 0; + this.build = null; + log.debug('[chimp][saucelabs-session-manager] created a new SessionManager', options); } @@ -39,17 +42,25 @@ SauceLabsSessionManager.prototype.remote = function (webdriverOptions, callback) log.debug('[chimp][saucelabs-session-manager] creating webdriver remote '); var browser = this.webdriver.remote(webdriverOptions); + this.build = webdriverOptions.desiredCapabilities.build; + log.debug('[chimp][saucelabs-session-manager] build: ' + this.build); + callback(null, browser); return; }; +var DEFAULT_LIMIT = 100; + /** * Gets a list of sessions from the SauceLabs API based on Build ID * * @api private */ -SauceLabsSessionManager.prototype._getJobs = function (callback) { - var hub = this.options.sauceLabsUrl + '/jobs?full=:get_full_info&limit=10'; //default is 100 which seems like too much +SauceLabsSessionManager.prototype._getJobs = function (callback, limit, skip) { + var hub = this.options.sauceLabsUrl + '/jobs?full=true&limit=' + limit; + if (skip > 0) { + hub += '&skip=' + skip; + } log.debug('[chimp][saucelabs-session-manager]', 'requesting sessions from', hub); @@ -65,53 +76,89 @@ SauceLabsSessionManager.prototype._getJobs = function (callback) { }; /** - * Kills the 1st session found running on SauceLabs - * * @api public */ SauceLabsSessionManager.prototype.killCurrentSession = function (callback) { - this._getJobs(function (err, jobs) { - if (jobs && jobs.length) { - var job = jobs[0]; - - // This will stop the session, causing a 'User terminated' error. - // If we don't manually stop the session, we get a timed-out error. - var options = { - url: this.options.sauceLabsUrl + '/jobs/' + job.id + '/stop', - method: 'PUT' - }; - - request(options, function (error, response) { - if (!error && response.statusCode === 200) { - log.debug('[chimp][saucelabs-session-manager]', 'stopped session'); - callback(); - } else { - log.error('[chimp][saucelabs-session-manager]', 'received error', error); - callback(error); + var self = this; + var killSession = function (job) { + // This will stop the session, causing a 'User terminated' error. + // If we don't manually stop the session, we get a timed-out error. + var options = { + url: this.options.sauceLabsUrl + '/jobs/' + job.id + '/stop', + method: 'PUT' + }; + + request(options, function (error, response) { + if (!error && response.statusCode === 200) { + log.debug('[chimp][saucelabs-session-manager]', 'stopped session'); + callback(); + } else { + log.error('[chimp][saucelabs-session-manager]', 'received error', error); + callback(error); + } + }); + + // This will set the session to passing or else it will show as Errored out + // even though we stop it. + options = { + url: this.options.sauceLabsUrl + '/jobs/' + job.id, + method: 'PUT', + json: true, + body: { passed: true } + }; + + request(options, function (error, response) { + if (!error && response.statusCode === 200) { + log.debug('[chimp][saucelabs-session-manager]', 'updated session to passing state'); + callback(); + } else { + log.error('[chimp][saucelabs-session-manager]', 'received error', error); + callback(error); + } + }); + }.bind(this) + + var getJobForBuild = function(cb, skip) { + if (!self.build) { + // get one and kill it, this is the (flawed) default of chimp which will just randomly + // terminate sessions and you get an odd `Error: The test with session id XXX has already finished, and can't receive further commands.` error + console.warn('You should really consider setting a build, otherwise random sessions will be terminated!'); + this._getJobs(function(err, jobs) { + if (jobs.length) { + killSession(jobs[0]); } + }, 1); + return; + } + this._getJobs(function(err, jobs) { + // the original code never uses this error, + // probably because if we don't find anything we just leave the session alone + // we might want to revisit this behavior + // if (err) { + // cb(err); + // } + if (!jobs.length) { + // no more jobs found, let's exit + console.warn(`Couldn't find a job to terminate for build ${self.build}`); + callback(); + return; + } + var currentJob = _.find(jobs, function (b) { + return b.build === self.build; }); + if (!currentJob) { + // maybe it's in the next batch? + getJobForBuild(cb, skip + DEFAULT_LIMIT) + } else { + killSession(currentJob); + } + }, DEFAULT_LIMIT, skip); + }.bind(this) - // This will set the session to passing or else it will show as Errored out - // even though we stop it. - options = { - url: this.options.sauceLabsUrl + '/jobs/' + job.id, - method: 'PUT', - json: true, - body: { passed: true } - }; - - request(options, function (error, response) { - if (!error && response.statusCode === 200) { - log.debug('[chimp][saucelabs-session-manager]', 'updated session to passing state'); - callback(); - } else { - log.error('[chimp][saucelabs-session-manager]', 'received error', error); - callback(error); - } - }); - } - }.bind(this)); + + + getJobForBuild(killSession, 0); }; module.exports = SauceLabsSessionManager;