Skip to content

Commit d6b932b

Browse files
author
vdemedes
committed
clean up forking and messages
1 parent 578173d commit d6b932b

File tree

10 files changed

+126
-95
lines changed

10 files changed

+126
-95
lines changed

cli.js

+14-13
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ var cli = meow({
4343
]
4444
});
4545

46+
var rejectionCount = 0;
47+
var exceptionCount = 0;
4648
var testCount = 0;
4749
var fileCount = 0;
48-
var unhandledRejectionCount = 0;
49-
var uncaughtExceptionCount = 0;
5050
var errors = [];
5151

5252
function error(err) {
@@ -120,22 +120,23 @@ function run(file) {
120120
return fork(args)
121121
.on('stats', stats)
122122
.on('test', test)
123-
.on('unhandledRejections', rejections)
124-
.on('uncaughtException', uncaughtException)
123+
.on('unhandledRejections', handleRejections)
124+
.on('uncaughtException', handleExceptions)
125125
.on('data', function (data) {
126126
process.stdout.write(data);
127127
});
128128
}
129129

130-
function rejections(data) {
131-
var unhandled = data.unhandledRejections;
132-
log.unhandledRejections(data.file, unhandled);
133-
unhandledRejectionCount += unhandled.length;
130+
function handleRejections(data) {
131+
log.unhandledRejections(data.file, data.rejections);
132+
133+
rejectionCount += data.rejections.length;
134134
}
135135

136-
function uncaughtException(data) {
137-
uncaughtExceptionCount++;
138-
log.uncaughtException(data.file, data.uncaughtException);
136+
function handleExceptions(data) {
137+
log.uncaughtException(data.file, data.exception);
138+
139+
exceptionCount++;
139140
}
140141

141142
function sum(arr, key) {
@@ -162,7 +163,7 @@ function exit(results) {
162163
var failed = sum(stats, 'failCount');
163164

164165
log.write();
165-
log.report(passed, failed, unhandledRejectionCount, uncaughtExceptionCount);
166+
log.report(passed, failed, rejectionCount, exceptionCount);
166167
log.write();
167168

168169
if (failed > 0) {
@@ -172,7 +173,7 @@ function exit(results) {
172173
process.stdout.write('');
173174

174175
flushIoAndExit(
175-
failed > 0 || unhandledRejectionCount > 0 || uncaughtExceptionCount > 0 ? 1 : 0
176+
failed > 0 || rejectionCount > 0 || exceptionCount > 0 ? 1 : 0
176177
);
177178
}
178179

index.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
'use strict';
2-
require('./lib/babel').avaRequired();
32
var setImmediate = require('set-immediate-shim');
3+
var relative = require('path').relative;
44
var hasFlag = require('has-flag');
55
var chalk = require('chalk');
6-
var relative = require('path').relative;
76
var serializeError = require('./lib/serialize-value');
87
var Runner = require('./lib/runner');
8+
var send = require('./lib/send');
99
var log = require('./lib/logger');
10+
1011
var runner = new Runner();
1112

13+
// note that test files have require('ava')
14+
require('./lib/babel').avaRequired = true;
15+
1216
// check if the test is being run without AVA cli
1317
var isForked = typeof process.send === 'function';
1418

@@ -39,10 +43,7 @@ function test(props) {
3943

4044
props.error = props.error ? serializeError(props.error) : {};
4145

42-
process.send({
43-
name: 'test',
44-
data: props
45-
});
46+
send('test', props);
4647

4748
if (props.error && hasFlag('fail-fast')) {
4849
isFailed = true;
@@ -58,12 +59,9 @@ function exit() {
5859
}
5960
});
6061

61-
process.send({
62-
name: 'results',
63-
data: {
64-
stats: runner.stats,
65-
tests: runner.results
66-
}
62+
send('results', {
63+
stats: runner.stats,
64+
tests: runner.results
6765
});
6866
}
6967

lib/babel.js

+44-35
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
'use strict';
2-
var loudRejection = require('loud-rejection/api')(process);
3-
var resolveFrom = require('resolve-from');
42
var createEspowerPlugin = require('babel-plugin-espower/create');
53
var requireFromString = require('require-from-string');
4+
var loudRejection = require('loud-rejection/api')(process);
5+
var resolveFrom = require('resolve-from');
66
var serializeValue = require('./serialize-value');
7+
var send = require('./send');
78

9+
// if node's major version part is >= 1, generators are supported
810
var hasGenerators = parseInt(process.version.slice(1), 10) > 0;
911
var testPath = process.argv[2];
12+
13+
// include local babel and fallback to ava's babel
1014
var babel;
1115

1216
try {
@@ -16,64 +20,69 @@ try {
1620
babel = require('babel-core');
1721
}
1822

23+
// initialize power-assert
24+
var powerAssert = createEspowerPlugin(babel, {
25+
patterns: require('./enhance-assert').PATTERNS
26+
});
27+
28+
// if generators are not supported, use regenerator
1929
var options = {
2030
blacklist: hasGenerators ? ['regenerator'] : [],
2131
optional: hasGenerators ? ['asyncToGenerator', 'runtime'] : ['runtime'],
22-
plugins: [
23-
createEspowerPlugin(babel, {
24-
patterns: require('./enhance-assert').PATTERNS
25-
})
26-
]
32+
plugins: [powerAssert]
2733
};
2834

29-
var avaRequired;
30-
31-
module.exports = {
32-
avaRequired: function () {
33-
avaRequired = true;
34-
}
35-
};
36-
37-
function send(name, data) {
38-
process.send({name: name, data: data});
39-
}
35+
// check if test files required ava and show error, when they didn't
36+
exports.avaRequired = false;
4037

4138
process.on('uncaughtException', function (exception) {
42-
send('uncaughtException', {uncaughtException: serializeValue(exception)});
39+
send('uncaughtException', {exception: serializeValue(exception)});
4340
});
4441

42+
// include test file
4543
var transpiled = babel.transformFileSync(testPath, options);
4644
requireFromString(transpiled.code, testPath, {
4745
appendPaths: module.paths
4846
});
4947

50-
if (!avaRequired) {
48+
// if ava was not required, show an error
49+
if (!exports.avaRequired) {
5150
throw new Error('No tests found in ' + testPath + ', make sure to import "ava" at the top of your test file');
5251
}
5352

53+
// parse and re-emit ava messages
5454
process.on('message', function (message) {
55-
var command = message['ava-child-process-command'];
56-
if (command) {
57-
process.emit('ava-' + command, message.data);
55+
if (!message.ava) {
56+
return;
5857
}
58+
59+
process.emit(message.name, message.data);
5960
});
6061

61-
process.on('ava-kill', function () {
62+
process.on('ava-exit', function () {
63+
// use a little delay when running on AppVeyor (because it's shit)
64+
var delay = process.env.AVA_APPVEYOR ? 100 : 0;
65+
6266
setTimeout(function () {
6367
process.exit(0);
64-
}, process.env.AVA_APPVEYOR ? 100 : 0);
68+
}, delay);
6569
});
6670

67-
process.on('ava-cleanup', function () {
68-
var unhandled = loudRejection.currentlyUnhandled();
69-
if (unhandled.length) {
70-
unhandled = unhandled.map(function (entry) {
71-
return serializeValue(entry.reason);
72-
});
73-
send('unhandledRejections', {unhandledRejections: unhandled});
71+
process.on('ava-teardown', function () {
72+
var rejections = loudRejection.currentlyUnhandled();
73+
74+
if (rejections.length === 0) {
75+
return exit();
7476
}
7577

76-
setTimeout(function () {
77-
send('cleaned-up', {});
78-
}, 100);
78+
rejections = rejections.map(function (rejection) {
79+
return serializeValue(rejection.reason);
80+
});
81+
82+
send('unhandledRejections', {rejections: rejections});
83+
setTimeout(exit, 100);
7984
});
85+
86+
function exit() {
87+
send('teardown');
88+
}

lib/fork.js

+28-24
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,78 @@
22
var childProcess = require('child_process');
33
var Promise = require('bluebird');
44
var path = require('path');
5+
var send = require('./send');
56

67
module.exports = function (args) {
78
if (!Array.isArray(args)) {
89
args = [args];
910
}
1011

11-
var babel = path.join(__dirname, 'babel.js');
12+
var filepath = path.join(__dirname, 'babel.js');
1213
var file = args[0];
1314

1415
var options = {
1516
silent: true,
1617
cwd: path.dirname(file)
1718
};
1819

19-
var ps = childProcess.fork(babel, args, options);
20-
21-
function send(command, data) {
22-
ps.send({'ava-child-process-command': command, 'data': data});
23-
}
20+
var ps = childProcess.fork(filepath, args, options);
2421

2522
var promise = new Promise(function (resolve, reject) {
2623
var testResults;
2724

25+
ps.on('error', reject);
2826
ps.on('results', function (results) {
2927
testResults = results;
3028

31-
// after all tests are finished and results received
32-
// kill the forked process, so AVA can exit safely
33-
send('cleanup', true);
34-
});
35-
36-
ps.on('cleaned-up', function () {
37-
send('kill', true);
29+
send(ps, 'teardown');
3830
});
3931

40-
ps.on('uncaughtException', function () {
41-
send('cleanup', true);
42-
});
43-
44-
ps.on('error', reject);
45-
4632
ps.on('exit', function (code) {
4733
if (code > 0 && code !== 143) {
48-
reject(new Error(file + ' exited with a non-zero exit code: ' + code));
49-
} else if (testResults) {
50-
if (!testResults.tests.length) {
51-
testResults.stats.failCount++;
34+
return reject(new Error(file + ' exited with a non-zero exit code: ' + code));
35+
}
36+
37+
if (testResults) {
38+
if (testResults.tests.length === 0) {
39+
testResults.stats.failCount = 1;
5240
testResults.tests.push({
5341
duration: 0,
5442
title: file,
5543
error: new Error('No tests for ' + file),
5644
type: 'test'
5745
});
5846
}
47+
5948
resolve(testResults);
6049
} else {
61-
reject(new Error('Never got test results from: ' + file));
50+
reject(new Error('Test results were not received from: ' + file));
6251
}
6352
});
6453
});
6554

6655
// emit `test` and `stats` events
6756
ps.on('message', function (event) {
57+
if (!event.ava) {
58+
return;
59+
}
60+
61+
event.name = event.name.replace(/^ava\-/, '');
6862
event.data.file = file;
6963

7064
ps.emit(event.name, event.data);
7165
});
7266

67+
// teardown finished, now exit
68+
ps.on('teardown', function () {
69+
send(ps, 'exit');
70+
});
71+
72+
// uncaught exception in fork, need to exit
73+
ps.on('uncaughtException', function () {
74+
send(ps, 'teardown');
75+
});
76+
7377
// emit data events on forked process' output
7478
ps.stdout.on('data', function (data) {
7579
ps.emit('data', data);

lib/runner.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use strict';
2-
var util = require('util');
32
var EventEmitter = require('events').EventEmitter;
3+
var util = require('util');
44
var Promise = require('bluebird');
55
var hasFlag = require('has-flag');
66
var Test = require('./test');
7+
var send = require('./send');
78

89
function noop() {}
910

@@ -192,10 +193,7 @@ Runner.prototype.run = function () {
192193

193194
// Runner is executed directly in tests, in that case process.send() == undefined
194195
if (process.send) {
195-
process.send({
196-
name: 'stats',
197-
data: stats
198-
});
196+
send('stats', stats);
199197
}
200198

201199
return eachSeries(tests.before, this._runTest.bind(this))

lib/send.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
// utility to send messages to processes
4+
function send(ps, name, data) {
5+
if (typeof ps === 'string') {
6+
data = name || {};
7+
name = ps;
8+
ps = process;
9+
}
10+
11+
ps.send({
12+
name: 'ava-' + name,
13+
data: data,
14+
ava: true
15+
});
16+
}
17+
18+
module.exports = send;

package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
"node": ">=0.10.0"
3333
},
3434
"scripts": {
35-
"test": "xo && nyc tap --timeout=150 test/*.js | tap-spec",
36-
"test-win": "tap --timeout=150 test/*.js | tap-spec",
35+
"test": "xo && tap --coverage --reporter=spec --timeout=150 test/*.js",
36+
"test-win": "tap --coverage --reporter=spec --timeout=150 test/*.js",
3737
"coveralls": "nyc report --reporter=text-lcov | coveralls"
3838
},
3939
"files": [
@@ -105,7 +105,6 @@
105105
"nyc": "^3.2.2",
106106
"signal-exit": "^2.1.2",
107107
"tap": "^2.2.1",
108-
"tap-spec": "^4.1.0",
109108
"xo": "*",
110109
"zen-observable": "^0.1.6"
111110
}

0 commit comments

Comments
 (0)