@@ -20,6 +20,7 @@ var Log = require('../lib/logger'),
2020 logger = new Log ( global . logLevel ) ,
2121 BrowserStack = require ( 'browserstack' ) ,
2222 fs = require ( 'fs' ) ,
23+ qs = require ( 'querystring' ) ,
2324 chalk = require ( 'chalk' ) ,
2425 config = require ( '../lib/config' ) ,
2526 utils = require ( '../lib/utils' ) ,
@@ -32,6 +33,7 @@ var Log = require('../lib/logger'),
3233 server ,
3334 timeout ,
3435 activityTimeout ,
36+ ackTimeout ,
3537 workers = { } ,
3638 workerKeys = { } ,
3739 tunnelingAgent ,
@@ -43,6 +45,7 @@ function terminateAllWorkers(callback) {
4345 var worker = workers [ key ] ;
4446 if ( worker ) {
4547 logger . debug ( '[%s] Terminated' , worker . string ) ;
48+ clearTimeout ( worker . ackTimeout ) ;
4649 clearTimeout ( worker . activityTimeout ) ;
4750 clearTimeout ( worker . testActivityTimeout ) ;
4851 delete workers [ key ] ;
@@ -109,9 +112,24 @@ function getTestBrowserInfo(browserString, path) {
109112 if ( config . multipleTest ) {
110113 info += ', ' + path ;
111114 }
115+
112116 return info ;
113117}
114118
119+
120+ function buildTestUrl ( test_path , worker_key , browser_string ) {
121+ var url = 'http://localhost:' + serverPort + '/' + test_path ;
122+
123+ var querystring = qs . stringify ( {
124+ _worker_key : worker_key ,
125+ _browser_string : browser_string
126+ } ) ;
127+
128+ url += ( ( url . indexOf ( '?' ) > 0 ) ? '&' : '?' ) + querystring ;
129+ return url ;
130+ }
131+
132+
115133function launchServer ( ) {
116134 logger . debug ( 'Launching server on port:' , serverPort ) ;
117135
@@ -120,20 +138,12 @@ function launchServer() {
120138}
121139
122140function launchBrowser ( browser , path ) {
123- var url = 'http://localhost:' + serverPort . toString ( ) + '/' + path . replace ( / \\ / g, '/' ) ;
124- var browserString = utils . browserString ( browser ) ;
125- logger . debug ( '[%s] Launching' , getTestBrowserInfo ( browserString , path ) ) ;
126-
127141 var key = utils . uuid ( ) ;
142+ var browserString = utils . browserString ( browser ) ;
143+ var browserInfo = getTestBrowserInfo ( browserString , path ) ;
144+ logger . debug ( '[%s] Launching' , browserInfo ) ;
128145
129- if ( url . indexOf ( '?' ) > 0 ) {
130- url += '&' ;
131- } else {
132- url += '?' ;
133- }
134-
135- url += '_worker_key=' + key + '&_browser_string=' + browserString ;
136- browser [ 'url' ] = url ;
146+ browser . url = buildTestUrl ( path . replace ( / \\ / g, '/' ) , key , browserString ) ;
137147
138148 if ( config . project ) {
139149 browser . project = config . project ;
@@ -153,6 +163,7 @@ function launchBrowser(browser, path) {
153163 timeout = 300 ;
154164 }
155165 activityTimeout = timeout - 10 ;
166+ ackTimeout = parseInt ( config . ackTimeout ) || 60 ;
156167
157168 client . createWorker ( browser , function ( err , worker ) {
158169 if ( err || typeof worker !== 'object' ) {
@@ -169,10 +180,13 @@ function launchBrowser(browser, path) {
169180 worker . string = browserString ;
170181 worker . test_path = path ;
171182 worker . path_index = 0 ;
183+
184+ // attach helper methods to manage worker state
185+ attachWorkerHelpers ( worker ) ;
186+
172187 workers [ key ] = worker ;
173188 workerKeys [ worker . id ] = { key : key , marked : false } ;
174189 } ) ;
175-
176190}
177191
178192function launchBrowsers ( config , browser ) {
@@ -187,6 +201,63 @@ function launchBrowsers(config, browser) {
187201 } , 100 ) ;
188202}
189203
204+
205+ function attachWorkerHelpers ( worker ) {
206+ // TODO: Consider creating instances of a proper 'Worker' class
207+
208+ worker . buildUrl = function buildUrl ( test_path ) {
209+ return buildTestUrl ( test_path || this . test_path , this . _worker_key , this . getTestBrowserInfo ( ) ) ;
210+ } ;
211+
212+ worker . getTestBrowserInfo = function getTestBrowserInfo ( test_path ) {
213+ var info = this . string ;
214+ if ( config . multipleTest ) {
215+ info += ', ' + ( test_path || this . test_path ) ;
216+ }
217+ return info ;
218+ } ;
219+
220+ worker . awaitAck = function awaitAck ( ) {
221+ var self = this ;
222+
223+ if ( this . ackTimeout ) {
224+ // Already awaiting ack, or awaited ack once and failed
225+ return ;
226+ }
227+
228+ this . ackTimeout = setTimeout ( function ( ) {
229+ if ( self . isAckd ) {
230+ // Already ack'd
231+ return ;
232+ }
233+
234+ // worker has not acknowledged itself in 60 sec, reopen url
235+ client . changeUrl ( self . id , { url : self . buildUrl ( ) } , function ( ) {
236+ logger . debug ( "[%s] Sent Request to reload url" , self . getTestBrowserInfo ( ) ) ;
237+ } ) ;
238+
239+ } , ackTimeout * 1000 ) ;
240+
241+ logger . debug ( '[%s] Awaiting ack' , this . getTestBrowserInfo ( ) ) ;
242+ } ;
243+
244+ worker . markAckd = function markAckd ( ) {
245+ this . resetAck ( ) ;
246+ this . isAckd = true ;
247+
248+ logger . debug ( '[%s] Received ack' , this . getTestBrowserInfo ( ) ) ;
249+ } ;
250+
251+ worker . resetAck = function resetAck ( ) {
252+ clearTimeout ( this . ackTimeout ) ;
253+ this . ackTimeout = null ;
254+ this . isAckd = false ;
255+ } ;
256+
257+ return worker ;
258+ }
259+
260+
190261var statusPoller = {
191262 poller : null ,
192263
@@ -208,10 +279,13 @@ var statusPoller = {
208279
209280 if ( _worker . status === 'running' ) {
210281 //clearInterval(statusPoller);
211- logger . debug ( '[%s] Launched' , getTestBrowserInfo ( worker . string , worker . test_path ) ) ;
282+ logger . debug ( '[%s] Launched' , worker . getTestBrowserInfo ( ) ) ;
212283 worker . launched = true ;
213284 workerData . marked = true ;
214285
286+ // Await ack from browser-worker
287+ worker . awaitAck ( ) ;
288+
215289 worker . activityTimeout = setTimeout ( function ( ) {
216290 if ( ! worker . acknowledged ) {
217291 var subject = 'Worker inactive for too long: ' + worker . string ;
0 commit comments