Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / karma / lib / server.js
1 var io = require('socket.io')
2 var di = require('di')
3
4 var cfg = require('./config')
5 var logger = require('./logger')
6 var constant = require('./constants')
7 var watcher = require('./watcher')
8 var plugin = require('./plugin')
9
10 var ws = require('./web-server')
11 var preprocessor = require('./preprocessor')
12 var Launcher = require('./launcher').Launcher
13 var FileList = require('./file_list').List
14 var reporter = require('./reporter')
15 var helper = require('./helper')
16 var events = require('./events')
17 var EventEmitter = events.EventEmitter
18 var Executor = require('./executor')
19 var Browser = require('./browser')
20 var BrowserCollection = require('./browser_collection')
21 var EmitterWrapper = require('./emitter_wrapper')
22 var processWrapper = new EmitterWrapper(process)
23
24 var log = logger.create()
25
26 var start = function (injector, config, launcher, globalEmitter, preprocess, fileList, webServer,
27   capturedBrowsers, socketServer, executor, done) {
28   config.frameworks.forEach(function (framework) {
29     injector.get('framework:' + framework)
30   })
31
32   // A map of launched browsers.
33   var singleRunDoneBrowsers = Object.create(null)
34
35   // Passing fake event emitter, so that it does not emit on the global,
36   // we don't care about these changes.
37   var singleRunBrowsers = new BrowserCollection(new EventEmitter())
38
39   // Some browsers did not get captured.
40   var singleRunBrowserNotCaptured = false
41
42   webServer.on('error', function (e) {
43     if (e.code === 'EADDRINUSE') {
44       log.warn('Port %d in use', config.port)
45       config.port++
46       webServer.listen(config.port)
47     } else {
48       throw e
49     }
50   })
51
52   var afterPreprocess = function () {
53     if (config.autoWatch) {
54       injector.invoke(watcher.watch)
55     }
56
57     webServer.listen(config.port, function () {
58       log.info('Karma v%s server started at http://%s:%s%s', constant.VERSION, config.hostname,
59         config.port, config.urlRoot)
60
61       if (config.browsers && config.browsers.length) {
62         injector.invoke(launcher.launch, launcher).forEach(function (browserLauncher) {
63           singleRunDoneBrowsers[browserLauncher.id] = false
64         })
65       }
66     })
67   }
68
69   fileList.refresh().then(afterPreprocess, afterPreprocess)
70
71   globalEmitter.on('browsers_change', function () {
72     // TODO(vojta): send only to interested browsers
73     socketServer.sockets.emit('info', capturedBrowsers.serialize())
74   })
75
76   globalEmitter.on('browser_register', function (browser) {
77     launcher.markCaptured(browser.id)
78
79     // TODO(vojta): This is lame, browser can get captured and then crash (before other browsers get
80     // captured).
81     if (config.autoWatch && launcher.areAllCaptured()) {
82       executor.schedule()
83     }
84   })
85
86   var EVENTS_TO_REPLY = ['start', 'info', 'error', 'result', 'complete']
87   socketServer.sockets.on('connection', function (socket) {
88     log.debug('A browser has connected on socket ' + socket.id)
89
90     var replySocketEvents = events.bufferEvents(socket, EVENTS_TO_REPLY)
91
92     socket.on('complete', function (data, ack) {
93       ack()
94     })
95
96     socket.on('register', function (info) {
97       var newBrowser
98       var isRestart
99
100       if (info.id) {
101         newBrowser = capturedBrowsers.getById(info.id) || singleRunBrowsers.getById(info.id)
102       }
103
104       if (newBrowser) {
105         isRestart = newBrowser.state === Browser.STATE_DISCONNECTED
106         newBrowser.reconnect(socket)
107
108         // We are restarting a previously disconnected browser.
109         if (isRestart && config.singleRun) {
110           newBrowser.execute(config.client)
111         }
112       } else {
113         newBrowser = injector.createChild([{
114           id: ['value', info.id || null],
115           fullName: ['value', info.name],
116           socket: ['value', socket]
117         }]).instantiate(Browser)
118
119         newBrowser.init()
120
121         // execute in this browser immediately
122         if (config.singleRun) {
123           newBrowser.execute(config.client)
124           singleRunBrowsers.add(newBrowser)
125         }
126       }
127
128       replySocketEvents()
129     })
130   })
131
132   var emitRunCompleteIfAllBrowsersDone = function () {
133     // all browsers done
134     var isDone = Object.keys(singleRunDoneBrowsers).reduce(function (isDone, id) {
135       return isDone && singleRunDoneBrowsers[id]
136     }, true)
137
138     if (isDone) {
139       var results = singleRunBrowsers.getResults()
140       if (singleRunBrowserNotCaptured) {
141         results.exitCode = 1
142       }
143
144       globalEmitter.emit('run_complete', singleRunBrowsers, results)
145     }
146   }
147
148   if (config.singleRun) {
149     globalEmitter.on('browser_complete', function (completedBrowser) {
150       if (completedBrowser.lastResult.disconnected &&
151         completedBrowser.disconnectsCount <= config.browserDisconnectTolerance) {
152         log.info('Restarting %s (%d of %d attempts)', completedBrowser.name,
153           completedBrowser.disconnectsCount, config.browserDisconnectTolerance)
154         if (!launcher.restart(completedBrowser.id)) {
155           singleRunDoneBrowsers[completedBrowser.id] = true
156           emitRunCompleteIfAllBrowsersDone()
157         }
158       } else {
159         singleRunDoneBrowsers[completedBrowser.id] = true
160
161         if (launcher.kill(completedBrowser.id)) {
162           // workaround to supress "disconnect" warning
163           completedBrowser.state = Browser.STATE_DISCONNECTED
164         }
165
166         emitRunCompleteIfAllBrowsersDone()
167       }
168     })
169
170     globalEmitter.on('browser_process_failure', function (browserLauncher) {
171       singleRunDoneBrowsers[browserLauncher.id] = true
172       singleRunBrowserNotCaptured = true
173
174       emitRunCompleteIfAllBrowsersDone()
175     })
176
177     globalEmitter.on('run_complete', function (browsers, results) {
178       log.debug('Run complete, exiting.')
179       disconnectBrowsers(results.exitCode)
180     })
181
182     globalEmitter.emit('run_start', singleRunBrowsers)
183   }
184
185   if (config.autoWatch) {
186     globalEmitter.on('file_list_modified', function () {
187       log.debug('List of files has changed, trying to execute')
188       executor.schedule()
189     })
190   }
191
192   var webServerCloseTimeout = 3000
193   var disconnectBrowsers = function (code) {
194     // Slightly hacky way of removing disconnect listeners
195     // to suppress "browser disconnect" warnings
196     // TODO(vojta): change the client to not send the event (if disconnected by purpose)
197     var sockets = socketServer.sockets.sockets
198     Object.getOwnPropertyNames(sockets).forEach(function (key) {
199       var socket = sockets[key]
200       socket.removeAllListeners('disconnect')
201       if (!socket.disconnected) {
202         socket.disconnect()
203       }
204     })
205
206     var removeAllListenersDone = false
207     var removeAllListeners = function () {
208       // make sure we don't execute cleanup twice
209       if (removeAllListenersDone) {
210         return
211       }
212       removeAllListenersDone = true
213       webServer.removeAllListeners()
214       processWrapper.removeAllListeners()
215       done(code || 0)
216     }
217
218     globalEmitter.emitAsync('exit').then(function () {
219       // don't wait forever on webServer.close() because
220       // pending client connections prevent it from closing.
221       var closeTimeout = setTimeout(removeAllListeners, webServerCloseTimeout)
222
223       // shutdown the server...
224       webServer.close(function () {
225         clearTimeout(closeTimeout)
226         removeAllListeners()
227       })
228
229       // shutdown socket.io flash transport, if defined
230       if (socketServer.flashPolicyServer) {
231         socketServer.flashPolicyServer.close()
232       }
233     })
234   }
235
236   try {
237     processWrapper.on('SIGINT', disconnectBrowsers)
238     processWrapper.on('SIGTERM', disconnectBrowsers)
239   } catch (e) {
240     // Windows doesn't support signals yet, so they simply don't get this handling.
241     // https://github.com/joyent/node/issues/1553
242   }
243
244   // Handle all unhandled exceptions, so we don't just exit but
245   // disconnect the browsers before exiting.
246   processWrapper.on('uncaughtException', function (error) {
247     log.error(error)
248     disconnectBrowsers(1)
249   })
250 }
251 start.$inject = ['injector', 'config', 'launcher', 'emitter', 'preprocess', 'fileList',
252   'webServer', 'capturedBrowsers', 'socketServer', 'executor', 'done']
253
254 var createSocketIoServer = function (webServer, executor, config) {
255   var server = io.listen(webServer, {
256     // avoid destroying http upgrades from socket.io to get proxied websockets working
257     'destroy upgrade': false,
258     // socket.io has a timeout (15s by default) before destroying a store (a data structure where
259     // data associated with a socket are stored). Unfortunately this timeout is not cleared
260     // properly on socket.io shutdown and this timeout prevents karma from exiting cleanly.
261     // We change this timeout to 0 to make Karma exit just after all tests were executed.
262     'client store expiration': 0,
263     logger: logger.create('socket.io', constant.LOG_ERROR),
264     resource: config.urlRoot + 'socket.io',
265     transports: config.transports
266   })
267
268   // hack to overcome circular dependency
269   executor.socketIoSockets = server.sockets
270
271   return server
272 }
273
274 exports.start = function (cliOptions, done) {
275   // apply the default logger config (and config from CLI) as soon as we can
276   logger.setup(cliOptions.logLevel || constant.LOG_INFO,
277     helper.isDefined(cliOptions.colors) ? cliOptions.colors : true, [constant.CONSOLE_APPENDER])
278
279   var config = cfg.parseConfig(cliOptions.configFile, cliOptions)
280   var modules = [{
281     helper: ['value', helper],
282     logger: ['value', logger],
283     done: ['value', done || process.exit],
284     emitter: ['type', EventEmitter],
285     launcher: ['type', Launcher],
286     config: ['value', config],
287     preprocess: ['factory', preprocessor.createPreprocessor],
288     fileList: ['type', FileList],
289     webServer: ['factory', ws.create],
290     socketServer: ['factory', createSocketIoServer],
291     executor: ['type', Executor],
292     // TODO(vojta): remove
293     customFileHandlers: ['value', []],
294     // TODO(vojta): remove, once karma-dart does not rely on it
295     customScriptTypes: ['value', []],
296     reporter: ['factory', reporter.createReporters],
297     capturedBrowsers: ['type', BrowserCollection],
298     args: ['value', {}],
299     timer: ['value', {setTimeout: setTimeout, clearTimeout: clearTimeout}]
300   }]
301
302   // load the plugins
303   modules = modules.concat(plugin.resolve(config.plugins))
304
305   var injector = new di.Injector(modules)
306
307   injector.invoke(start)
308 }