yeti
Version:
220 lines (185 loc) • 5.3 kB
JavaScript
"use strict";
var util = require("util");
var EventEmitter2 = require("../event-emitter");
var EventYoshi = require("eventyoshi");
var parseUA = require("./ua");
var makeURLFromComponents = require("./url-builder");
var NullTests = require("./null-tests");
/**
* An Target is a collection of Agents
* that share the load of testing a Batch.
*
* @class Target
* @constructor
* @inherits EventEmitter2
* @param {Batch} batch Batch associated with this Target.
* @param {String} ua User-Agent string for this Target.
*/
function Target(batch, ua) {
this.batch = batch;
this.agents = {};
this.id = String(Date.now()) + String(Math.random() * 0x100000 | 0);
this.ua = ua;
this.name = parseUA(ua);
EventEmitter2.call(this);
this.batchId = null;
this.testSpec = null;
this.nullTests = new NullTests.createForMountpoint(batch.allBatches.hub.mountpoint);
this.tests = this.nullTests;
this.agentEmitter = new EventYoshi();
this.agentEmitter.proxy("setTarget");
this.setupEvents();
}
util.inherits(Target, EventEmitter2);
Target.prototype.isAgentAllowed = function (agent) {
var permittedIds;
if (agent.ua !== this.ua) { return false; }
permittedIds = this.batch.getAgentWhitelist();
if (!permittedIds.length) { return true; }
return permittedIds.some(function (id) {
return id === agent.id;
});
};
Target.prototype.appendAgentIfAllowed = function (agent) {
if (this.isAgentAllowed(agent)) {
this.appendAgent(agent);
return true;
}
return false;
};
Target.prototype.appendAgent = function (agent) {
agent.pipeLog(this);
agent.setTarget(this);
this.agents[agent.id] = agent;
this.agentEmitter.add(agent);
};
/**
* Append agents to agent list.
*
* @param {Agent[]} Array of Agent objects.
*/
Target.prototype.appendAgents = function (agents) {
agents.forEach(this.appendAgentIfAllowed.bind(this));
};
/**
* Get this agent's human-readable name.
*
* @method getName
* @return {String} Agent name.
*/
Target.prototype.getName = function () {
return this.name;
};
/**
* Get an Agent from this group.
*
* @param {String} agentId Agent ID.
* @return {Agent} Agent object.
*/
Target.prototype.getAgent = function (agentId) {
if (this.destroyed) {
return;
}
return this.agents[agentId];
};
/**
* Get this agent's ID.
*
* @method getId
* @return {String} Numeric agent ID.
*/
Target.prototype.getId = function () {
return this.id;
};
/**
* Setup events.
*
* @method setupEvents
* @private
*/
Target.prototype.setupEvents = function () {
var self = this;
self.agentEmitter.on("results", function (data) {
if (self.destroyed) { return; }
var test = self.tests.getByUrl(data.url);
if (!test.isExecuting()) { return; }
test.setResults(true);
test.setExecuting(false);
self.emit("results", data);
this.child.next();
});
self.agentEmitter.on("scriptError", function (data) {
if (self.destroyed) { return; }
var test = self.tests.getByUrl(data.url);
// TODO: Set results should include error message.
data.url = test.location;
self.emit("scriptError", data);
});
self.agentEmitter.on("beat", self.emit.bind(self, "beat"));
self.agentEmitter.on("agentError", self.emit.bind(self, "agentError"));
// TODO handle disconnects
self.agentEmitter.on("disconnect", function () {
if (self.destroyed) { return; }
self.emit("disconnect", this.child);
self.agentEmitter.remove(this.child);
if (self.agentEmitter.children.length === 0) {
self.emit("agentError", "All " + self.name + " browsers disconnected, giving up on this browser.");
self.complete();
}
});
};
/**
* @method complete
*/
Target.prototype.complete = function () {
if (this.destroyed) {
return;
}
this.destroyed = true;
this.emit("complete");
this.batchId = null;
this.tests = null;
this.batch = null;
this.agents = null;
this.agentEmitter = null;
};
/**
* Get the next Test.
*
* Fires our complete event when no more
* URLs are in the queue, then returns
* the capture page URL.
*
* @method nextTest
* @param {String} agentId Agent ID.
* @return {Test} Next test. May be a NullTest if we're done.
*/
Target.prototype.nextTest = function (agentId) {
var didComplete = this.tests.didComplete(),
next = this.tests.next();
this.emit("progress", {
total: this.tests.totalSubmitted(),
current: this.tests.totalSubmitted() - this.tests.totalPending()
});
if (didComplete) {
this.complete();
}
return next;
};
/**
* Set the URL queue to the given URL array
* and advance the browser to the first test.
*
* @method dispatch
* @param {String} batchId Batch ID.
* @param {TestSpecification} testSpec Test specification.
*/
Target.prototype.dispatch = function (batchId, testSpec) {
this.batchId = batchId;
this.testSpec = testSpec;
this.ttl = testSpec.timeout * 1000;
this.tests = testSpec.createTests();
this.debug("Emit dispatch for batchId", batchId);
this.emit("dispatch", batchId);
};
module.exports = Target;