Skip to content

Commit

Permalink
Merge pull request #1854 from budde377/feature-stop-karma-server
Browse files Browse the repository at this point in the history
Add possibility to stop a karma server
  • Loading branch information
dignifiedquire committed Feb 22, 2016
2 parents eddd9de + 0cb6204 commit 6fe5a77
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 21 deletions.
12 changes: 12 additions & 0 deletions docs/config/01-configuration-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ customHeaders: [{
}]
```


## detached
**Type:** Boolean

**Default:** `false`

**CLI:** `--detached`

**Description:** When true, this will start the karma server in another process, writing no output to the console.
The server can be stopped using the `karma stop` command.


## exclude
**Type:** Array

Expand Down
16 changes: 16 additions & 0 deletions docs/dev/04-public-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ runner.run({port: 9876}, function(exitCode) {
})
```

## karma.stopper

### **stopper.stop(options, [callback=process.exit])**

This function will signal a running server to stop. Equivalent of `karma stop`.

```javascript
var stopper = require('karma').stopper
runner.stop({port: 9876}, function(exitCode) {
if (exitCode === 0) {
console.log('Server stop as initiated')
}
process.exit(exitCode)
})
```

## Callback function notes

- If there is an error, the error code will be provided as the second parameter to the error callback.
19 changes: 19 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ var describeStart = function () {
' $0 start [<configFile>] [<options>]')
.describe('port', '<integer> Port where the server is running.')
.describe('auto-watch', 'Auto watch source files and run on change.')
.describe('detached', 'Detach the server.')
.describe('no-auto-watch', 'Do not watch source files.')
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
.describe('colors', 'Use colors when reporting and printing logs.')
Expand Down Expand Up @@ -187,6 +188,17 @@ var describeRun = function () {
.describe('help', 'Print usage.')
}

var describeStop = function () {
optimist
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'STOP - Stop the server (requires running server).\n\n' +
'Usage:\n' +
' $0 run [<configFile>] [<options>]')
.describe('port', '<integer> Port where the server is listening.')
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
.describe('help', 'Print usage.')
}

var describeCompletion = function () {
optimist
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
Expand All @@ -212,6 +224,10 @@ exports.process = function () {
options.clientArgs = parseClientArgs(process.argv)
break

case 'stop':
describeStop()
break

case 'init':
describeInit()
break
Expand Down Expand Up @@ -245,6 +261,9 @@ exports.run = function () {
case 'run':
require('./runner').run(config)
break
case 'stop':
require('./stopper').stop(config)
break
case 'init':
require('./init').init(config)
break
Expand Down
1 change: 1 addition & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ var Config = function () {
this.concurrency = Infinity
this.failOnEmptyTestSuite = true
this.retryLimit = 2
this.detached = false
}

var CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' +
Expand Down
9 changes: 9 additions & 0 deletions lib/detached.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var fs = require('fs')

var Server = require('./server')
var configurationFile = process.argv[2]
var fileContents = fs.readFileSync(configurationFile, 'utf-8')
fs.unlink(configurationFile, function () {})
var data = JSON.parse(fileContents)
var server = new Server(data)
server.start(data)
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var constants = require('./constants')
var Server = require('./server')
var runner = require('./runner')
var stopper = require('./stopper')
var launcher = require('./launcher')

// TODO: remove in 1.0
Expand All @@ -21,6 +22,7 @@ module.exports = {
VERSION: constants.VERSION,
Server: Server,
runner: runner,
stopper: stopper,
launcher: launcher,
server: oldServer
}
24 changes: 24 additions & 0 deletions lib/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ var setup = function (level, colors, appenders) {
})
}

// Setup the logger by passing in the config object. The function sets the
// `colors` and `logLevel` if they are defined. It takes two arguments:
//
// setupFromConfig(config, appenders)
//
// * `config`: *Object* The configuration object.
// * `appenders`: *Array* This will be passed as appenders to log4js
// to allow for fine grained configuration of log4js. For more information
// see https://github.com/nomiddlename/log4js-node.
var setupFromConfig = function (config, appenders) {
var useColors = true
var logLevel = constant.LOG_INFO

if (helper.isDefined(config.colors)) {
useColors = config.colors
}

if (helper.isDefined(config.logLevel)) {
logLevel = config.logLevel
}
setup(logLevel, useColors, appenders)
}

// Create a new logger. There are two optional arguments
// * `name`, which defaults to `karma` and
// If the `name = 'socket.io'` this will create a special wrapper
Expand All @@ -60,3 +83,4 @@ var create = function (name, level) {

exports.create = create
exports.setup = setup
exports.setupFromConfig = setupFromConfig
2 changes: 1 addition & 1 deletion lib/middleware/runner.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Runner middleware is reponsible for communication with `karma run`.
* Runner middleware is responsible for communication with `karma run`.
*
* It basically triggers a test run and streams stdout back.
*/
Expand Down
18 changes: 18 additions & 0 deletions lib/middleware/stopper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Stopper middleware is responsible for communicating with `karma stop`.
*/

var log = require('../logger').create('middleware:stopper')

var createStopperMiddleware = function (urlRoot) {
return function (request, response, next) {
if (request.url !== urlRoot + 'stop') return next()
response.writeHead(200)
log.info('Stopping server')
response.end('OK')
process.kill(process.pid, 'SIGINT')
}
}

createStopperMiddleware.$inject = ['config.urlRoot']
exports.create = createStopperMiddleware
7 changes: 6 additions & 1 deletion lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ var parseExitCode = function (buffer, defaultCode, failOnEmptyTestSuite) {

// TODO(vojta): read config file (port, host, urlRoot)
exports.run = function (config, done) {
config = config || {}

logger.setupFromConfig(config)

done = helper.isFunction(done) ? done : process.exit
config = cfg.parseConfig(config.configFile, config)

Expand Down Expand Up @@ -71,6 +75,7 @@ exports.run = function (config, done) {
removedFiles: config.removedFiles,
changedFiles: config.changedFiles,
addedFiles: config.addedFiles,
refresh: config.refresh
refresh: config.refresh,
colors: config.colors
}))
}
30 changes: 29 additions & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ var SocketIO = require('socket.io')
var di = require('di')
var util = require('util')
var Promise = require('bluebird')

var spawn = require('child_process').spawn
var tmp = require('tmp')
var fs = require('fs')
var path = require('path')
var root = global || window || this

var cfg = require('./config')
Expand Down Expand Up @@ -131,6 +134,10 @@ Server.prototype.refreshFiles = function () {
Server.prototype._start = function (config, launcher, preprocess, fileList, webServer,
capturedBrowsers, socketServer, executor, done) {
var self = this
if (config.detached) {
this._detach(config, done)
return
}

self._fileList = fileList

Expand Down Expand Up @@ -362,6 +369,27 @@ Server.prototype._start = function (config, launcher, preprocess, fileList, webS
})
}

Server.prototype._detach = function (config, done) {
var log = this.log
var tmpFile = tmp.fileSync({keep: true})
log.info('Starting karma detached')
log.info('Run "karma stop" to stop the server.')
log.debug('Writing config to tmp-file %s', tmpFile.name)
config.detached = false
try {
fs.writeFileSync(tmpFile.name, JSON.stringify(config), 'utf8')
} catch (e) {
log.error("Couldn't write temporary configuration file")
done(1)
return
}
var child = spawn(process.argv[0], [path.resolve(__dirname, '../lib/detached.js'), tmpFile.name], {
detached: true,
stdio: 'ignore'
})
child.unref()
}

// Export
// ------

Expand Down
43 changes: 43 additions & 0 deletions lib/stopper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
var http = require('http')

var cfg = require('./config')
var logger = require('./logger')
var helper = require('./helper')

exports.stop = function (config, done) {
config = config || {}
logger.setupFromConfig(config)
done = helper.isFunction(done) ? done : process.exit
var log = logger.create('stopper')
config = cfg.parseConfig(config.configFile, config)

var options = {
hostname: config.hostname,
path: config.urlRoot + 'stop',
port: config.port,
method: 'GET'
}

var request = http.request(options)

request.on('response', function (response) {
if (response.statusCode !== 200) {
log.error('Server returned status code: ' + response.statusCode)
done(1)
return
}

log.info('Server stopped.')
done(0)
})

request.on('error', function (e) {
if (e.code === 'ECONNREFUSED') {
log.error('There is no server listening on port %d', options.port)
done(1, e.code)
} else {
throw e
}
})
request.end()
}
2 changes: 2 additions & 0 deletions lib/web-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var Promise = require('bluebird')

var common = require('./middleware/common')
var runnerMiddleware = require('./middleware/runner')
var stopperMiddleware = require('./middleware/stopper')
var stripHostMiddleware = require('./middleware/strip_host')
var karmaMiddleware = require('./middleware/karma')
var sourceFilesMiddleware = require('./middleware/source_files')
Expand Down Expand Up @@ -56,6 +57,7 @@ var createWebServer = function (injector, emitter, fileList) {

var handler = connect()
.use(injector.invoke(runnerMiddleware.create))
.use(injector.invoke(stopperMiddleware.create))
.use(injector.invoke(stripHostMiddleware.create))
.use(injector.invoke(karmaMiddleware.create))
.use(injector.invoke(sourceFilesMiddleware.create))
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
"rimraf": "^2.3.3",
"socket.io": "^1.4.5",
"source-map": "^0.5.3",
"tmp": "0.0.28",
"useragent": "^2.1.6"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 6fe5a77

Please sign in to comment.