-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #342 set child process debug port to available #613
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ var resolveCwd = require('resolve-cwd'); | |
var uniqueTempDir = require('unique-temp-dir'); | ||
var findCacheDir = require('find-cache-dir'); | ||
var slash = require('slash'); | ||
var getPort = require('get-port'); | ||
var AvaError = require('./lib/ava-error'); | ||
var fork = require('./lib/fork'); | ||
var formatter = require('./lib/enhance-assert').formatter(); | ||
|
@@ -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) | ||
var execArgv = process.execArgv.slice(); | ||
var debugArgIndex = -1; | ||
for (var i = 0; i < execArgv.length; i++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just use Array#some here. |
||
if (execArgv[i].indexOf('--debug-brk=') === 0 || execArgv[i].indexOf('--debug=') === 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't you just use the https://github.com/sindresorhus/has-flag dependency here? (we already depend on it) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why you need the position? These are flag arguments, not positional arguments, and can be in any order. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we don't append flag, but change existing. By default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps extract this into a memoizing function? Less logic in this function and no need to recompute for every file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think memoization would be a premature optimization for this as execArv usually contains zero or one elements. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also doesn't handle the case where the user would do |
||
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,13 +217,30 @@ Api.prototype.run = function (files) { | |
self.fileCount = files.length; | ||
self.base = path.relative('.', commonPathPrefix(files)) + path.sep; | ||
|
||
var tests = files.map(self._runFile); | ||
|
||
return new Promise(function (resolve) { | ||
// receive test count from all files and then run the tests | ||
var statsCount = 0; | ||
|
||
return new Promise(function (resolve) { | ||
tests.forEach(function (test) { | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is asynchronous whereas |
||
tests[index] = { | ||
run: function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would |
||
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'); | ||
|
@@ -208,7 +251,7 @@ Api.prototype.run = function (files) { | |
}; | ||
|
||
resolve(Promise[method](files, function (file, index) { | ||
return tests[index].run(options).catch(function (err) { | ||
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. | ||
|
@@ -225,10 +268,6 @@ Api.prototype.run = function (files) { | |
})); | ||
} | ||
} | ||
|
||
test.on('stats', tryRun); | ||
test.catch(tryRun); | ||
}); | ||
}); | ||
}) | ||
.then(function (results) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,37 +116,38 @@ 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; | ||
|
||
ps.on('stats', function () { | ||
isReady = true; | ||
}); | ||
|
||
promise.run = function (options) { | ||
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 promise; | ||
return this; | ||
} | ||
|
||
ps.on('stats', function () { | ||
send(ps, 'run', options); | ||
}); | ||
|
||
return promise; | ||
return this; | ||
}, | ||
then: function () { | ||
// only to simplify test code, in production use .promise | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should just bite the bullet and use |
||
return promise.then.apply(promise, arguments); | ||
} | ||
}; | ||
|
||
return promise; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a weird combination of callbacks and promises. Can we clean it up to just use promises?