wallet-core/node_modules/ava/lib/fork.js
2017-05-28 00:40:43 +02:00

179 lines
3.9 KiB
JavaScript

'use strict';
const childProcess = require('child_process');
const path = require('path');
const fs = require('fs');
const Promise = require('bluebird');
const debug = require('debug')('ava');
const AvaError = require('./ava-error');
if (fs.realpathSync(__filename) !== __filename) {
console.warn('WARNING: `npm link ava` and the `--preserve-symlink` flag are incompatible. We have detected that AVA is linked via `npm link`, and that you are using either an early version of Node 6, or the `--preserve-symlink` flag. This breaks AVA. You should upgrade to Node 6.2.0+, avoid the `--preserve-symlink` flag, or avoid using `npm link ava`.');
}
let env = process.env;
// Ensure NODE_PATH paths are absolute
if (env.NODE_PATH) {
env = Object.assign({}, env);
env.NODE_PATH = env.NODE_PATH
.split(path.delimiter)
.map(x => path.resolve(x))
.join(path.delimiter);
}
// In case the test file imports a different AVA install,
// the presence of this variable allows it to require this one instead
env.AVA_PATH = path.resolve(__dirname, '..');
module.exports = (file, opts, execArgv) => {
opts = Object.assign({
file,
baseDir: process.cwd(),
tty: process.stdout.isTTY ? {
columns: process.stdout.columns,
rows: process.stdout.rows
} : false
}, opts);
const args = [JSON.stringify(opts), opts.color ? '--color' : '--no-color'];
const ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), args, {
cwd: opts.projectDir,
silent: true,
env,
execArgv: execArgv || process.execArgv
});
const relFile = path.relative('.', file);
let exiting = false;
const send = (name, data) => {
if (!exiting) {
// This seems to trigger a Node bug which kills the AVA master process, at
// least while running AVA's tests. See
// <https://github.com/novemberborn/_ava-tap-crash> for more details.
ps.send({
name: `ava-${name}`,
data,
ava: true
});
}
};
const testResults = [];
let results;
const promise = new Promise((resolve, reject) => {
ps.on('error', reject);
// Emit `test` and `stats` events
ps.on('message', event => {
if (!event.ava) {
return;
}
event.name = event.name.replace(/^ava-/, '');
event.data.file = relFile;
debug('ipc %s:\n%o', event.name, event.data);
ps.emit(event.name, event.data);
});
ps.on('test', props => {
testResults.push(props);
});
ps.on('results', data => {
results = data;
data.tests = testResults;
send('teardown');
});
ps.on('exit', (code, signal) => {
if (code > 0) {
return reject(new AvaError(`${relFile} exited with a non-zero exit code: ${code}`));
}
if (code === null && signal) {
return reject(new AvaError(`${relFile} exited due to ${signal}`));
}
if (results) {
resolve(results);
} else {
reject(new AvaError(`Test results were not received from ${relFile}`));
}
});
ps.on('no-tests', data => {
send('teardown');
let message = `No tests found in ${relFile}`;
if (!data.avaRequired) {
message += ', make sure to import "ava" at the top of your test file';
}
reject(new AvaError(message));
});
});
// Teardown finished, now exit
ps.on('teardown', () => {
send('exit');
exiting = true;
});
// Uncaught exception in fork, need to exit
ps.on('uncaughtException', () => {
send('teardown');
});
ps.stdout.on('data', data => {
ps.emit('stdout', data);
});
ps.stderr.on('data', data => {
ps.emit('stderr', data);
});
promise.on = function () {
ps.on.apply(ps, arguments);
return promise;
};
promise.send = (name, data) => {
send(name, data);
return promise;
};
promise.exit = () => {
send('init-exit');
return promise;
};
// Send 'run' event only when fork is listening for it
let isReady = false;
ps.on('stats', () => {
isReady = true;
});
promise.run = options => {
if (isReady) {
send('run', options);
return promise;
}
ps.on('stats', () => {
send('run', options);
});
return promise;
};
return promise;
};