Skip to content

Commit cd7a682

Browse files
committed
Merge pull request #60 from browserstack/better_logging
Fixes #51 + Refactoring for command line arguments
2 parents bd6d2a7 + 9292152 commit cd7a682

File tree

8 files changed

+334
-278
lines changed

8 files changed

+334
-278
lines changed

bin/cli.js

Lines changed: 253 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,261 @@
11
#! /usr/bin/env node
22

3-
var executable = process.argv[2] || 'runner';
3+
if (process.argv[2] == '--verbose')
4+
global.logLevel = "debug";
5+
else
6+
global.logLevel = "info";
7+
8+
var Log = require('../lib/logger'),
9+
logger = new Log(global.logLevel),
10+
BrowserStack = require('browserstack'),
11+
fs = require('fs'),
12+
chalk = require('chalk'),
13+
config = require('../lib/config'),
14+
utils = require('../lib/utils'),
15+
Server = require('../lib/server').Server,
16+
Tunnel = require('../lib/local').Tunnel,
17+
ConfigParser = require('../lib/configParser').ConfigParser,
18+
serverPort = 8888,
19+
timeout,
20+
activityTimeout,
21+
workers = {},
22+
workerKeys = {},
23+
logLevel,
24+
tunnel;
25+
26+
function cleanUp(signal) {
27+
try {
28+
server.close();
29+
} catch (e) {
30+
logger.debug("Server already closed");
31+
}
32+
33+
logger.info("Exiting");
34+
35+
for (var key in workers) {
36+
var worker = workers[key];
37+
if (workers.hasOwnProperty(key)) {
38+
client.terminateWorker(worker.id, function () {
39+
if (!workers[key]) {
40+
return;
41+
}
42+
43+
logger.debug('[%s] Terminated', worker.string);
44+
clearTimeout(worker.activityTimeout);
45+
delete workers[key];
46+
delete workerKeys[worker.id];
47+
});
48+
}
49+
}
50+
if (statusPoller) statusPoller.stop();
51+
52+
try {
53+
process.kill(tunnel.process.pid, 'SIGKILL');
54+
} catch (e) {
55+
logger.debug("Non existent tunnel");
56+
}
57+
try {
58+
fs.unlinkSync(pid_file);
59+
} catch (e) {
60+
logger.debug("Non existent pid file.");
61+
}
62+
if (signal) {
63+
process.kill(process.pid, 'SIGTERM');
64+
}
65+
}
66+
67+
function launchServer() {
68+
logger.debug("Launching server on port:", serverPort);
69+
70+
var server = new Server(client, workers);
71+
server.listen(parseInt(serverPort, 10));
72+
}
73+
74+
function launchBrowser(browser, url) {
75+
var browserString = utils.browserString(browser);
76+
logger.debug("[%s] Launching", browserString);
77+
78+
var key = utils.uuid();
79+
80+
if (url.indexOf('?') > 0) {
81+
url += '&';
82+
} else {
83+
url += '?';
84+
}
85+
86+
url += '_worker_key=' + key + '&_browser_string=' + browserString;
87+
browser['url'] = url;
88+
89+
if (config.project) {
90+
browser.project = config.project;
91+
}
92+
if (config.build) {
93+
browser.build = config.build;
94+
}
95+
96+
if(config.tunnelIdentifier) {
97+
browser["tunnel_identifier"] = config.tunnelIdentifier;
98+
}
99+
100+
timeout = parseInt(config.timeout);
101+
if(! isNaN(timeout)) {
102+
browser.timeout = timeout;
103+
} else {
104+
timeout = 300;
105+
}
106+
activityTimeout = timeout - 10;
107+
108+
client.createWorker(browser, function (err, worker) {
109+
if (err || typeof worker !== 'object') {
110+
logger.info("Error from BrowserStack: ", err);
111+
utils.alertBrowserStack("Failed to launch worker",
112+
"Arguments: " + JSON.stringify({
113+
err: err,
114+
worker: worker
115+
}, null, 4));
116+
return;
117+
}
118+
119+
worker.config = browser;
120+
worker.string = browserString;
121+
workers[key] = worker;
122+
workerKeys[worker.id] = {key: key, marked: false};
123+
});
124+
125+
}
126+
127+
function launchBrowsers(config, browser) {
128+
setTimeout(function () {
129+
if(Object.prototype.toString.call(config.test_path) === '[object Array]'){
130+
config.test_path.forEach(function(path){
131+
var url = 'http://localhost:' + serverPort.toString() + '/' + path;
132+
launchBrowser(browser,url);
133+
});
134+
} else {
135+
var url = 'http://localhost:' + serverPort.toString() + '/' + config.test_path;
136+
launchBrowser(browser,url);
137+
}
138+
}, 100);
139+
}
140+
141+
var statusPoller = {
142+
poller: null,
143+
144+
start: function() {
145+
statusPoller.poller = setInterval(function () {
146+
client.getWorkers(function (err, _workers) {
147+
_workers = _workers.filter(function(currentValue, index, array) {
148+
return currentValue.status == 'running' && workerKeys[currentValue.id] && !workerKeys[currentValue.id].marked;
149+
});
150+
for (var i in _workers) {
151+
var _worker = _workers[i];
152+
var workerData = workerKeys[_worker.id];
153+
var worker = workers[workerData.key];
154+
if (worker.launched) {
155+
return;
156+
}
157+
158+
if (_worker.status === 'running') {
159+
//clearInterval(statusPoller);
160+
logger.debug('[%s] Launched', worker.string);
161+
worker.launched = true;
162+
workerData.marked = true;
163+
164+
worker.activityTimeout = setTimeout(function () {
165+
if (!worker.acknowledged) {
166+
var subject = "Worker inactive for too long: " + worker.string;
167+
var content = "Worker details:\n" + JSON.stringify(worker.config, null, 4);
168+
utils.alertBrowserStack(subject, content, null, function(){});
169+
delete workers[workerData.key];
170+
delete workerKeys[worker.id];
171+
config.status += 1;
172+
if (utils.objectSize(workers) === 0) {
173+
var color = config.status > 0 ? "red" : "green";
174+
logger.info(chalk[color]("All tests done, failures: %d."), config.status);
175+
176+
if (config.status > 0) {
177+
config.status = 1;
178+
}
179+
180+
process.exit(config.status);
181+
}
182+
}
183+
}, activityTimeout * 1000);
184+
185+
setTimeout(function () {
186+
if (worker.acknowledged) {
187+
var subject = "Tests timed out on: " + worker.string;
188+
var content = "Worker details:\n" + JSON.stringify(worker.config, null, 4);
189+
utils.alertBrowserStack(subject, content, null, function(){});
190+
delete workers[workerData.key];
191+
delete workerKeys[worker.id];
192+
config.status += 1;
193+
if (utils.objectSize(workers) === 0) {
194+
var color = config.status > 0 ? "red" : "green";
195+
logger.info(chalk[color]("All tests done, failures: %d."), config.status);
196+
197+
if (config.status > 0) {
198+
config.status = 1;
199+
}
200+
201+
process.exit(config.status);
202+
}
203+
}
204+
}, (activityTimeout * 1000));
205+
}
206+
}
207+
});
208+
}, 2000);
209+
},
210+
211+
stop: function() {
212+
clearInterval(statusPoller.poller);
213+
}
214+
};
215+
216+
function runTests() {
217+
if (config.browsers && config.browsers.length > 0) {
218+
ConfigParser.parse(client, config.browsers, function(browsers){
219+
launchServer();
220+
tunnel = new Tunnel(config.key, serverPort, config.tunnelIdentifier, function () {
221+
statusPoller.start();
222+
logger.info("Launching " + browsers.length + " workers");
223+
browsers.forEach(function(browser) {
224+
if (browser.browser_version === "latest") {
225+
logger.debug("[%s] Finding version.", utils.browserString(browser));
226+
227+
client.getLatest(browser, function(err, version) {
228+
logger.debug("[%s] Version is %s.",
229+
utils.browserString(browser), version);
230+
browser.browser_version = version;
231+
// So that all latest logs come in together
232+
launchBrowsers(config, browser);
233+
});
234+
} else {
235+
launchBrowsers(config, browser);
236+
}
237+
});
238+
});
239+
});
240+
}
241+
}
4242

5243
try {
6-
require('./' + executable + '.js');
244+
if (process.argv[2] == 'init') {
245+
require('./init.js');
246+
} else {
247+
var client = BrowserStack.createClient({
248+
username: config.username,
249+
password: config.key
250+
});
251+
runTests();
252+
var pid_file = process.cwd() + '/browserstack-run.pid';
253+
fs.writeFileSync(pid_file, process.pid, 'utf-8')
254+
process.on('exit', function() {cleanUp(false)});
255+
process.on('SIGINT', function() {cleanUp(true)});
256+
}
7257
} catch (e) {
8258
console.log(e);
9259
console.log('Invalid command.');
10260
}
261+

0 commit comments

Comments
 (0)