From db5006cf6e7be47102041a743e2cd0e43b7db22a Mon Sep 17 00:00:00 2001 From: Vadim Tomnikov Date: Wed, 5 Nov 2014 00:19:34 +0200 Subject: [PATCH] Add test debugging support 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 #108. --- bin/cli.js | 12 +++++- lib/child.js | 14 ++++--- lib/testrunner.js | 93 +++++++++++++++++++++++++++++------------------ package.json | 3 +- 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index dade70a..d131ac0 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -8,7 +8,7 @@ var root = __dirname + '/..', args = argsparser.parse(), testrunner = require(root), o = testrunner.options, - code, tests, + code, tests, debug, help; help = '' @@ -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' @@ -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]; @@ -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); diff --git a/lib/child.js b/lib/child.js index a4a3411..7a710fe 100644 --- a/lib/child.js +++ b/lib/child.js @@ -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) { diff --git a/lib/testrunner.js b/lib/testrunner.js index 08d3f77..2dfe914 100644 --- a/lib/testrunner.js +++ b/lib/testrunner.js @@ -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, @@ -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 }; /** @@ -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); @@ -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); diff --git a/package.json b/package.json index 436325e..cf46ec7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "qunit", "description": "QUnit testing framework for nodejs", - "version": "0.7.5", + "version": "0.7.6", "author": "Oleg Slobodskoi ", "contributors": [ { @@ -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"