Skip to content

Commit 9d0f841

Browse files
committed
closes #98 implemented child block detection
1 parent 81fc757 commit 9d0f841

7 files changed

Lines changed: 70 additions & 24 deletions

File tree

lib/child.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ var options = JSON.parse(process.argv[2]),
1616
currentModule = path.basename(options.code.path, '.js'),
1717
currentTest;
1818

19+
// send ping messages to when child is blocked.
20+
// after I sent the first ping, testrunner will start to except the next ping
21+
// within maxBlockDuration, otherwise this process will be killed
22+
process.send({event: 'ping'});
23+
setInterval(function() {
24+
process.send({event: 'ping'});
25+
}, Math.floor(options.maxBlockDuration / 2));
26+
1927
process.on('uncaughtException', function(err) {
2028
if (QUnit.config.current) {
2129
QUnit.ok(false, 'Test threw unexpected exception: ' + err.message);
@@ -168,3 +176,4 @@ _require(options.code, true);
168176
options.tests.forEach(function(test) {
169177
_require(test, false);
170178
});
179+

lib/testrunner.js

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ options = exports.options = {
4545
deps: null,
4646

4747
// define namespace your code will be attached to on global['your namespace']
48-
namespace: null
48+
namespace: null,
49+
50+
// max amount of ms child can be blocked, after that we assume running an infinite loop
51+
maxBlockDuration: 1000
4952
};
5053

5154
/**
@@ -55,6 +58,7 @@ options = exports.options = {
5558
*/
5659
function runOne(opts, callback) {
5760
var child;
61+
var pingCheckTimeoutId;
5862

5963
child = cp.fork(
6064
__dirname + '/child.js',
@@ -67,28 +71,44 @@ function runOne(opts, callback) {
6771
child.kill();
6872
}
6973

74+
function complete(err, data) {
75+
kill();
76+
clearTimeout(pingCheckTimeoutId);
77+
callback(err, data)
78+
}
79+
7080
child.on('message', function(msg) {
71-
if (msg.event === 'assertionDone') {
72-
log.add('assertions', msg.data);
73-
} else if (msg.event === 'testDone') {
74-
log.add('tests', msg.data);
75-
} else if (msg.event === 'done') {
76-
msg.data.code = opts.code.path;
77-
log.add('summaries', msg.data);
78-
if (opts.coverage) {
79-
coverage.add(msg.data.coverage);
80-
msg.data.coverage = coverage.get();
81-
msg.data.coverage.code = msg.data.code;
82-
log.add('coverages', msg.data.coverage);
83-
}
84-
if (opts.log.testing) {
85-
console.log('done');
86-
}
87-
callback(null, msg.data);
88-
kill();
89-
} else if (msg.event === 'uncaughtException') {
90-
callback(_.extend(new Error(), msg.data));
91-
kill();
81+
switch (msg.event) {
82+
case 'ping':
83+
clearTimeout(pingCheckTimeoutId);
84+
pingCheckTimeoutId = setTimeout(function() {
85+
complete(new Error('Process blocked for too long'));
86+
}, opts.maxBlockDuration);
87+
break;
88+
case 'assertionDone':
89+
log.add('assertions', msg.data);
90+
break;
91+
case 'testDone':
92+
log.add('tests', msg.data);
93+
break;
94+
case 'done':
95+
clearTimeout(pingCheckTimeoutId);
96+
msg.data.code = opts.code.path;
97+
log.add('summaries', msg.data);
98+
if (opts.coverage) {
99+
coverage.add(msg.data.coverage);
100+
msg.data.coverage = coverage.get();
101+
msg.data.coverage.code = msg.data.code;
102+
log.add('coverages', msg.data.coverage);
103+
}
104+
if (opts.log.testing) {
105+
console.log('done');
106+
}
107+
complete(null, msg.data);
108+
break;
109+
case 'uncaughtException':
110+
complete(_.extend(new Error(), msg.data));
111+
break;
92112
}
93113
});
94114

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "qunit",
33
"description": "QUnit testing framework for nodejs",
4-
"version": "0.7.2",
4+
"version": "0.7.3",
55
"author": "Oleg Slobodskoi <oleg008@gmail.com>",
66
"contributors": [
77
{

readme.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ var testrunner = require("qunit");
9797
deps: null,
9898

9999
// define namespace your code will be attached to on global['your namespace']
100-
namespace: null
100+
namespace: null,
101+
102+
// max amount of ms child can be blocked, after that we assume running an infinite loop
103+
maxBlockDuration: 1000
101104
}
102105
```
103106

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
while(1) {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test('infinite loop', function() {
2+
ok(true)
3+
})

test/testrunner.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ chain.add('uncaught exception', function() {
153153
});
154154
});
155155

156+
chain.add('infinite loop', function() {
157+
tr.run({
158+
code: fixtures + '/infinite-loop-code.js',
159+
tests: fixtures + '/infinite-loop-test.js',
160+
}, function(err, res) {
161+
a.ok(err instanceof Error, 'error was forwarded');
162+
chain.next();
163+
});
164+
});
165+
156166
chain.add('coverage', function() {
157167
tr.options.coverage = true;
158168
tr.run({

0 commit comments

Comments
 (0)