diff --git a/api.js b/api.js index cedd69db2..88ce3fba5 100644 --- a/api.js +++ b/api.js @@ -18,6 +18,7 @@ var AvaError = require('./lib/ava-error'); var fork = require('./lib/fork'); var formatter = require('./lib/enhance-assert').formatter(); var CachingPrecompiler = require('./lib/caching-precompiler'); +var getPort = require('get-port'); function Api(options) { if (!(this instanceof Api)) { @@ -61,19 +62,44 @@ Api.prototype._reset = function () { this.base = ''; }; -Api.prototype._runFile = function (file) { +Api.prototype._runFile = function (file, onForkStarting) { var options = objectAssign({}, this.options, { precompiled: this.precompiler.generateHashForFile(file) }); - return fork(file, options) - .on('teardown', this._handleTeardown) - .on('stats', this._handleStats) - .on('test', this._handleTest) - .on('unhandledRejections', this._handleRejections) - .on('uncaughtException', this._handleExceptions) - .on('stdout', this._handleOutput.bind(this, 'stdout')) - .on('stderr', this._handleOutput.bind(this, 'stderr')); + var execArgv = process.execArgv.slice(); + var debugArgIndex = -1; + for (var i = 0; i < execArgv.length; i++) { + if (execArgv[i].indexOf('--debug-brk=') === 0 || execArgv[i].indexOf('--debug=') === 0) { + debugArgIndex = i; + break; + } + } + + var execArgvPromise; + if (debugArgIndex === -1) { + execArgvPromise = Promise.resolve(execArgv); + } else { + execArgvPromise = getPort() + .then(function (port) { + execArgv[debugArgIndex] = '--debug-brk=' + port; + return execArgv; + }); + } + + return execArgvPromise + .then(function (execArgv) { + var result = fork(file, options, execArgv) + .on('teardown', this._handleTeardown) + .on('stats', this._handleStats) + .on('test', this._handleTest) + .on('unhandledRejections', this._handleRejections) + .on('uncaughtException', this._handleExceptions) + .on('stdout', this._handleOutput.bind(this, 'stdout')) + .on('stderr', this._handleOutput.bind(this, 'stderr')); + onForkStarting(result); + return result.promise; + }.bind(this)); }; Api.prototype._handleOutput = function (channel, data) { @@ -191,44 +217,57 @@ Api.prototype.run = function (files) { self.fileCount = files.length; self.base = path.relative('.', commonPathPrefix(files)) + path.sep; - var tests = files.map(self._runFile); - - // receive test count from all files and then run the tests - var statsCount = 0; - return new Promise(function (resolve) { - tests.forEach(function (test) { - function tryRun() { - if (++statsCount === self.fileCount) { - self.emit('ready'); - - var method = self.options.serial ? 'mapSeries' : 'map'; - var options = { - runOnlyExclusive: self.hasExclusive - }; - - resolve(Promise[method](files, function (file, index) { - return tests[index].run(options).catch(function (err) { - // The test failed catastrophically. Flag it up as an - // exception, then return an empty result. Other tests may - // continue to run. - self._handleExceptions({ - exception: err, - file: file - }); - - return { - stats: {passCount: 0, skipCount: 0, todoCount: 0, failCount: 0}, - tests: [] - }; + // receive test count from all files and then run the tests + var statsCount = 0; + + var tests = new Array(files.length); + files.forEach(function (file, index) { + self._runFile(file, function (forkManager) { + tests[index] = forkManager; + forkManager.on("stats", tryRun); + }) + .catch(function (error) { + tests[index] = { + run: function () { + return this; + }, + promise: Promise.reject(error), + then: function () { + return this.promise.then.apply(this.promise, arguments); + } + }; + tryRun(); + }); + }); + + function tryRun() { + if (++statsCount === self.fileCount) { + self.emit('ready'); + + var method = self.options.serial ? 'mapSeries' : 'map'; + var options = { + runOnlyExclusive: self.hasExclusive + }; + + resolve(Promise[method](files, function (file, index) { + return tests[index].run(options).promise.catch(function (err) { + // The test failed catastrophically. Flag it up as an + // exception, then return an empty result. Other tests may + // continue to run. + self._handleExceptions({ + exception: err, + file: file }); - })); - } - } - test.on('stats', tryRun); - test.catch(tryRun); - }); + return { + stats: {passCount: 0, skipCount: 0, todoCount: 0, failCount: 0}, + tests: [] + }; + }); + })); + } + } }); }) .then(function (results) { diff --git a/lib/fork.js b/lib/fork.js index d863a2a00..8a5135299 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -21,7 +21,7 @@ if (env.NODE_PATH) { .join(path.delimiter); } -module.exports = function (file, opts) { +module.exports = function (file, opts, execArgv) { opts = objectAssign({ file: file, tty: process.stdout.isTTY ? { @@ -33,7 +33,8 @@ module.exports = function (file, opts) { var ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), [JSON.stringify(opts)], { cwd: path.dirname(file), silent: true, - env: env + env: env, + execArgv: execArgv }); var relFile = path.relative('.', file); @@ -115,18 +116,6 @@ module.exports = function (file, opts) { } }); - promise.on = function () { - ps.on.apply(ps, arguments); - - return promise; - }; - - promise.send = function (name, data) { - send(ps, name, data); - - return promise; - }; - // send 'run' event only when fork is listening for it var isReady = false; @@ -134,18 +123,31 @@ module.exports = function (file, opts) { isReady = true; }); - promise.run = function (options) { - if (isReady) { - send(ps, 'run', options); - return promise; - } + return { + promise: promise, + on: function () { + ps.on.apply(ps, arguments); + return this; + }, + send: function (name, data) { + send(ps, name, data); + return this; + }, + run: function (options) { + if (isReady) { + send(ps, 'run', options); + return this; + } - ps.on('stats', function () { - send(ps, 'run', options); - }); + ps.on('stats', function () { + send(ps, 'run', options); + }); - return promise; + return this; + }, + then: function () { + // only to simplify test code, in production use .promise + return promise.then.apply(promise, arguments); + } }; - - return promise; }; diff --git a/package.json b/package.json index 6e19c80f1..00d9b7717 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "figures": "^1.4.0", "find-cache-dir": "^0.1.1", "fn-name": "^2.0.0", + "get-port": "^2.1.0", "globby": "^4.0.0", "ignore-by-default": "^1.0.0", "is-ci": "^1.0.7",