lib/test-campaign/test-campaign.js
/*
* Copyright 2012 Amadeus s.a.s. Licensed under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
var util = require("util");
var events = require("events");
var connect = require('connect');
//var coverageServer = require('node-coverage').admin;
var Logger = require('../logging/logger.js');
var Coverage = require('./coverage.js');
var Resources = require('../middlewares/resources.js');
var Browser = require('./browser.js');
var AllTests = require('../test-type/all-tests.js');
var pad2 = function (number) {
var res = number + "";
if (res.length == 1) {
return "0" + res;
} else {
return res;
}
};
var counter = 0;
var createCampaignId = function (date) {
counter++;
return [date.getFullYear(), pad2(date.getMonth() + 1), pad2(date.getDate()), pad2(date.getHours()), pad2(date.getMinutes()), pad2(date.getSeconds()), pad2(Math.floor(Math.random() * 100)), pad2(counter)].join('');
};
var TestCampaign = function (config, parentLogger) {
this.results = config.liveResults ? [] : null;
this.startTime = new Date();
this.campaignNumber = config.campaignNumber;
this.id = createCampaignId(this.startTime);
this.baseURL = "/campaign" + (config.predictableUrls ? this.campaignNumber : this.id);
this.logger = new Logger('TestCampaign', this.campaignNumber, parentLogger);
this.logger.logInfo("Initializing campaign " + this.id + "...");
var rootDirectory = config.rootDirectory || "";
this.rootDirectory = rootDirectory;
this.config = config;
var browsersCfg = config.browsers;
if (browsersCfg && browsersCfg.length > 0) {
this.logger.logInfo("Expecting the following browsers to connect: ");
} else {
this.logger.logInfo("No specific browsers expected in the campaign");
browsersCfg = [""];
}
var browsers = [];
for (var i = 0, l = browsersCfg.length; i < l; i++) {
browsers[i] = new Browser(browsersCfg[i]);
if (browsers[i].name) {
this.logger.logInfo("- " + browsers[i].name);
}
}
this.browsers = browsers;
var resources = new Resources({
baseDirectory: rootDirectory,
contexts: config.resources || {}
}, this.logger);
this.resources = resources;
var coverage = null;
var coverageConfig = config.coverage;
if (coverageConfig) {
coverage = new Coverage(coverageConfig, this.logger, rootDirectory);
this.coverage = coverage;
}
this.tests = new AllTests(this, config.tests, this.logger);
var app = connect();
app.use(resources.pathResolver());
if (coverage) {
app.use(coverage.instrumentedSender());
}
app.use(resources.staticSender());
app.use(this.tests.handleRequest);
this.handleRequest = app.handle.bind(app);
this.tasks = [];
this.tasksTrees = [];
this.remainingTasks = null;
this.finished = false;
this.initFinished = false;
};
util.inherits(TestCampaign, events.EventEmitter);
TestCampaign.prototype.init = function (callback) {
// fills the array of tasks
var self = this;
var waitingCb = 1;
var endInit = function () {
waitingCb--;
if (waitingCb !== 0) {
return;
}
self.tasks = self.tests.tasks;
self.tasksTrees = self.tests.tasksTrees;
self.remainingTasks = self.tasks.length;
self.addResult({
event: "tasksList",
campaignId: self.id,
tasks: self.tasksTrees
});
var initResults = self.tests.initResults;
for (var i = 0, l = initResults.length; i < l; i++) {
self.addResult(initResults[i]);
}
self.initFinished = true;
process.nextTick(callback);
};
if (this.coverage) {
waitingCb++;
this.coverage.init(endInit);
}
this.tests.init(endInit);
};
TestCampaign.prototype.addResult = function (event) {
if (!event.time) {
event.time = new Date().getTime();
}
var eventName = event.event;
if ((eventName == "taskFinished" && !event.restartPlanned) || eventName == "taskIgnored") {
this.remainingTasks--;
this.checkFinished();
}
if (this.results) {
this.results.push(event);
}
this.emit('result-' + eventName, event);
this.emit('result', event);
};
TestCampaign.prototype.checkFinished = function () {
if (this.remainingTasks === 0 && !this.finished && this.initFinished) {
var self = this;
process.nextTick(function () {
if (self.finished) {
return;
}
// this flag makes sure the campaignFinished event can be raised only once
self.finished = true;
self.addResult({
event: 'campaignFinished'
});
var elapsed = self.getElapsedTime();
self.logger.logInfo("Campaign finished. Time: %dmin %dsec", [elapsed.min, elapsed.sec]);
self.emit('finished');
});
}
};
TestCampaign.prototype.addCoverageResult = function (taskId, coverage) {
// taskId is not used currently, but it would be possible in the future to keep the link between a coverage result
// and the test which produced it, or perhaps to build, for each file, the list of tests which use it
if (this.coverage) {
this.coverage.addResult(coverage);
}
this.checkFinished();
};
TestCampaign.prototype.getCoverageResult = function () {
if (this.coverage) {
return this.coverage.getResult();
}
};
TestCampaign.prototype.getElapsedTime = function () {
var elapsedSec = Math.ceil((new Date() - this.startTime) / 1000);
var elapsedMin = Math.floor(elapsedSec / 60);
elapsedSec = elapsedSec % 60;
return {
min: elapsedMin,
sec: elapsedSec
};
};
TestCampaign.prototype.dispose = function () {
this.logger.logDebug("Disposing campaign " + this.id);
this.tests.dispose();
// No need to dispose coverage
this.resources.dispose();
// No need to dispose browsers
this.logger.dispose();
this.finished = true;
};
module.exports = TestCampaign;