You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			180 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			180 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
| 
								 
											6 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								var childProcess = require('child_process');
							 | 
						||
| 
								 | 
							
								var path = require('path');
							 | 
						||
| 
								 | 
							
								var fs = require('fs');
							 | 
						||
| 
								 | 
							
								var objectAssign = require('object-assign');
							 | 
						||
| 
								 | 
							
								var Promise = require('bluebird');
							 | 
						||
| 
								 | 
							
								var debug = require('debug')('ava');
							 | 
						||
| 
								 | 
							
								var AvaError = require('./ava-error');
							 | 
						||
| 
								 | 
							
								var doSend = require('./send');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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`.'
							 | 
						||
| 
								 | 
							
									);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var env = process.env;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ensure NODE_PATH paths are absolute
							 | 
						||
| 
								 | 
							
								if (env.NODE_PATH) {
							 | 
						||
| 
								 | 
							
									env = objectAssign({}, env);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									env.NODE_PATH = env.NODE_PATH
							 | 
						||
| 
								 | 
							
										.split(path.delimiter)
							 | 
						||
| 
								 | 
							
										.map(function (x) {
							 | 
						||
| 
								 | 
							
											return path.resolve(x);
							 | 
						||
| 
								 | 
							
										})
							 | 
						||
| 
								 | 
							
										.join(path.delimiter);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = function (file, opts) {
							 | 
						||
| 
								 | 
							
									opts = objectAssign({
							 | 
						||
| 
								 | 
							
										file: file,
							 | 
						||
| 
								 | 
							
										baseDir: process.cwd(),
							 | 
						||
| 
								 | 
							
										tty: process.stdout.isTTY ? {
							 | 
						||
| 
								 | 
							
											columns: process.stdout.columns,
							 | 
						||
| 
								 | 
							
											rows: process.stdout.rows
							 | 
						||
| 
								 | 
							
										} : false
							 | 
						||
| 
								 | 
							
									}, opts);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), [JSON.stringify(opts)], {
							 | 
						||
| 
								 | 
							
										cwd: path.dirname(file),
							 | 
						||
| 
								 | 
							
										silent: true,
							 | 
						||
| 
								 | 
							
										env: env
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var relFile = path.relative('.', file);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var exiting = false;
							 | 
						||
| 
								 | 
							
									var send = function (ps, 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.
							 | 
						||
| 
								 | 
							
											doSend(ps, name, data);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var testResults = [];
							 | 
						||
| 
								 | 
							
									var results;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var promise = new Promise(function (resolve, reject) {
							 | 
						||
| 
								 | 
							
										ps.on('error', reject);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// emit `test` and `stats` events
							 | 
						||
| 
								 | 
							
										ps.on('message', function (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', function (props) {
							 | 
						||
| 
								 | 
							
											testResults.push(props);
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										ps.on('results', function (data) {
							 | 
						||
| 
								 | 
							
											results = data;
							 | 
						||
| 
								 | 
							
											data.tests = testResults;
							 | 
						||
| 
								 | 
							
											send(ps, 'teardown');
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										ps.on('exit', function (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', function (data) {
							 | 
						||
| 
								 | 
							
											send(ps, 'teardown');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											var 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', function () {
							 | 
						||
| 
								 | 
							
										send(ps, 'exit');
							 | 
						||
| 
								 | 
							
										exiting = true;
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// uncaught exception in fork, need to exit
							 | 
						||
| 
								 | 
							
									ps.on('uncaughtException', function () {
							 | 
						||
| 
								 | 
							
										send(ps, 'teardown');
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ps.stdout.on('data', function (data) {
							 | 
						||
| 
								 | 
							
										ps.emit('stdout', data);
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ps.stderr.on('data', function (data) {
							 | 
						||
| 
								 | 
							
										ps.emit('stderr', data);
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									promise.on = function () {
							 | 
						||
| 
								 | 
							
										ps.on.apply(ps, arguments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return promise;
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									promise.send = function (name, data) {
							 | 
						||
| 
								 | 
							
										send(ps, name, data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return promise;
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									promise.exit = function () {
							 | 
						||
| 
								 | 
							
										send(ps, 'init-exit');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return promise;
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// send 'run' event only when fork is listening for it
							 | 
						||
| 
								 | 
							
									var isReady = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ps.on('stats', function () {
							 | 
						||
| 
								 | 
							
										isReady = true;
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									promise.run = function (options) {
							 | 
						||
| 
								 | 
							
										if (isReady) {
							 | 
						||
| 
								 | 
							
											send(ps, 'run', options);
							 | 
						||
| 
								 | 
							
											return promise;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										ps.on('stats', function () {
							 | 
						||
| 
								 | 
							
											send(ps, 'run', options);
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return promise;
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return promise;
							 | 
						||
| 
								 | 
							
								};
							 |