Skip to content

Commit

Permalink
Add test debugging support
Browse files Browse the repository at this point in the history
When cli.js executed by Node.js running in debug mode, child processes are also running in debug mode with different port.
It is possible to run cli.js in normal mode and debug only child processes using --debug option.
It is possible to specify debug port after --debug option.

Close qunitjs#108.
  • Loading branch information
micellius committed Nov 4, 2014
1 parent 7065bb8 commit db5006c
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 45 deletions.
12 changes: 10 additions & 2 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var root = __dirname + '/..',
args = argsparser.parse(),
testrunner = require(root),
o = testrunner.options,
code, tests,
code, tests, debug,
help;

help = ''
Expand All @@ -17,6 +17,7 @@ help = ''
+ '\nOptions:'
+ '\n -c, --code path to code you want to test'
+ '\n -t, --tests path to tests (space separated)'
+ '\n --debug debugging port'
+ '\n -d, --deps dependency paths - files required before code (space separated)'
+ '\n -l, --log logging options, json have to be used'
+ '\n --cov create tests coverage report'
Expand Down Expand Up @@ -65,6 +66,9 @@ for (var key in args) {
// of QUnit in browsers.
tests = args[key];
break;
case '--debug':
debug = args[key];
break;
case '-d':
case '--deps':
o.deps = args[key];
Expand Down Expand Up @@ -105,7 +109,11 @@ if(!code || !tests) {
return;
}

testrunner.run({ code: code, tests: tests, deps: o.deps, log: o.log }, function(err, stats) {
debug = debug || process.execArgv.reduce(function (prevArg, curArg) {
return prevArg || curArg.indexOf('--debug') === 0;
}, false);

testrunner.run({ code: code, tests: tests, deps: o.deps, log: o.log, debug: debug }, function(err, stats) {
if (err) {
console.error(err);
process.exit(1);
Expand Down
14 changes: 8 additions & 6 deletions lib/child.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ var options = JSON.parse(process.argv.pop()),
currentModule = path.basename(options.code.path, '.js'),
currentTest;

// send ping messages to when child is blocked.
// after I sent the first ping, testrunner will start to except the next ping
// within maxBlockDuration, otherwise this process will be killed
process.send({event: 'ping'});
setInterval(function() {
if(!options.debug) {
// send ping messages to when child is blocked.
// after I sent the first ping, testrunner will start to except the next ping
// within maxBlockDuration, otherwise this process will be killed
process.send({event: 'ping'});
}, Math.floor(options.maxBlockDuration / 2));
setInterval(function () {
process.send({event: 'ping'});
}, Math.floor(options.maxBlockDuration / 2));
}

process.on('uncaughtException', function(err) {
if (QUnit.config.current) {
Expand Down
93 changes: 57 additions & 36 deletions lib/testrunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var fs = require('fs'),
coverage = require('./coverage'),
cp = require('child_process'),
_ = require('underscore'),
freeport = require('freeport'),
log = exports.log = require('./log');

var options,
Expand Down Expand Up @@ -48,7 +49,10 @@ options = exports.options = {
namespace: null,

// max amount of ms child can be blocked, after that we assume running an infinite loop
maxBlockDuration: 2000
maxBlockDuration: 2000,

// port that should be used for debugging
debug: null
};

/**
Expand All @@ -60,9 +64,23 @@ function runOne(opts, callback) {
var child;
var pingCheckTimeoutId;
var argv = process.argv.slice();
var debug = opts.debug;

argv.push(JSON.stringify(opts));
child = cp.fork(__dirname + '/child.js', argv, {env: process.env});

if (debug) {
if (typeof debug === 'boolean') {
freeport(function (er, port) {
process.execArgv.push('--debug-brk=' + port);
fork();
});
} else {
process.execArgv.push('--debug-brk=' + debug);
fork();
}
} else {
fork();
}

function kill() {
process.removeListener('exit', kill);
Expand All @@ -75,40 +93,43 @@ function runOne(opts, callback) {
callback(err, data)
}

child.on('message', function(msg) {
switch (msg.event) {
case 'ping':
clearTimeout(pingCheckTimeoutId);
pingCheckTimeoutId = setTimeout(function() {
complete(new Error('Process blocked for too long'));
}, opts.maxBlockDuration);
break;
case 'assertionDone':
log.add('assertions', msg.data);
break;
case 'testDone':
log.add('tests', msg.data);
break;
case 'done':
clearTimeout(pingCheckTimeoutId);
msg.data.code = opts.code.path;
log.add('summaries', msg.data);
if (opts.coverage) {
coverage.add(msg.data.coverage);
msg.data.coverage = coverage.get();
msg.data.coverage.code = msg.data.code;
log.add('coverages', msg.data.coverage);
}
if (opts.log.testing) {
console.log('done');
}
complete(null, msg.data);
break;
case 'uncaughtException':
complete(_.extend(new Error(), msg.data));
break;
}
});
function fork() {
child = cp.fork(__dirname + '/child.js', argv, {env: process.env});
child.on('message', function (msg) {
switch (msg.event) {
case 'ping':
clearTimeout(pingCheckTimeoutId);
pingCheckTimeoutId = setTimeout(function () {
complete(new Error('Process blocked for too long'));
}, opts.maxBlockDuration);
break;
case 'assertionDone':
log.add('assertions', msg.data);
break;
case 'testDone':
log.add('tests', msg.data);
break;
case 'done':
clearTimeout(pingCheckTimeoutId);
msg.data.code = opts.code.path;
log.add('summaries', msg.data);
if (opts.coverage) {
coverage.add(msg.data.coverage);
msg.data.coverage = coverage.get();
msg.data.coverage.code = msg.data.code;
log.add('coverages', msg.data.coverage);
}
if (opts.log.testing) {
console.log('done');
}
complete(null, msg.data);
break;
case 'uncaughtException':
complete(_.extend(new Error(), msg.data));
break;
}
});
}

process.on('exit', kill);

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "qunit",
"description": "QUnit testing framework for nodejs",
"version": "0.7.5",
"version": "0.7.6",
"author": "Oleg Slobodskoi <[email protected]>",
"contributors": [
{
Expand Down Expand Up @@ -39,6 +39,7 @@
"argsparser": "^0.0.6",
"cli-table": "^0.3.0",
"co": "^3.0.6",
"freeport": "^1.0.3",
"qunitjs": "1.10.0",
"tracejs": "^0.1.8",
"underscore": "^1.6.0"
Expand Down

0 comments on commit db5006c

Please sign in to comment.