ickyrr-gagarin
Version:
A current updated fork of gagarin
166 lines (144 loc) • 4.48 kB
JavaScript
var spawn = require('child_process').spawn;
var processCounter = 0;
/**
* Creates a smart meteor instance, suitable for usage with the testing framework.
*
* @param {string} path to node executable
* @param {string} path to applciation main file
* @param {object} environment variables for the new process
* @param {object} prototype for the returned object
* @param {object} options; may contain startupTimeout value
*
* The prototype may implement the following methods
* - onStart ()
* - onExit ()
* - onData (data, { isError: true/false })
*/
module.exports = function createMeteorProcess (node, main, env, myPrototype, options) {
"use strict";
options = options || {};
var instance = Object.create(myPrototype);
var meteor = null;
var lastError = "";
var lastErrorAt = "nowhere";
var startupTimeout = options.startupTimeout;
var startupHandle = null;
var hasFirstOutput = false;
instance.env = env;
instance.pid = processCounter++; // the only requirement is that it stays unique "locally"
instance.kill = kill;
setTimeout(function () {
try {
meteor = spawn(node, [ main ], { env: env });
} catch (err) {
return instance.onStart && instance.onStart(err);
}
startupHandle = setTimeout(function () {
kill(function (err) {
if (instance.onStart) {
if (err) {
instance.onStart(err);
} else {
instance.onStart(new Error(startupTimeout +' ms startup timeout exceeded when waiting for the first server output;' +
' please try increasing it with -T option'));
}
}
});
}, startupTimeout);
//just for internal test
if (options.startupTimeout2 !== undefined) {
startupTimeout = options.startupTimeout2;
}
meteor.stdout.on('data', function (data) {
if (!hasFirstOutput) {
clearTimeout(startupHandle);
hasFirstOutput = true;
startupHandle = setTimeout(function () {
kill(function (err) {
if (instance.onStart) {
if (err) {
instance.onStart(err);
} else {
instance.onStart(new Error(startupTimeout +' ms startup timeout exceeded when waiting for server startup;' +
' please try to increasing it using -T option'));
}
}
});
}, startupTimeout);
}
if (/Поехали!/.test(data.toString())) {
clearTimeout(startupHandle);
//-------------------------------------
instance.onStart && instance.onStart();
}
instance.onData && instance.onData(data);
});
// make sure we kill meteor on process exit
process.once('exit', onProcessExit);
meteor.stderr.on('data', function (data) { // look for errors
instance.onData && instance.onData(data, { isError: true });
data.toString().split('\n').forEach(function (line) {
var hasMatch = [
{
regExp: /Error\:\s*(.*)/,
action: function (match) {
lastError = match[1];
lastErrorAt = '';
},
},
{
regExp: /at\s.*/,
action: function (match) {
if (!lastErrorAt) {
lastErrorAt = match[0];
}
},
},
].some(function (options) {
var match = options.regExp.exec(line);
if (match) {
options.action.call(null, match);
return true;
}
});
if (lastError && !hasMatch) {
lastError += '\n' + line;
}
});
});
meteor.on('exit', function (code) {
clearTimeout(startupHandle);
//-------------------------
if (instance.onExit) {
instance.onExit(code, lastError, lastErrorAt);
}
meteor = null;
});
});
/**
* Kill the meteor process and cleanup.
*
* @param {Function} cb
*/
function kill (cb) {
if (!meteor) {
return cb();
}
meteor.once('exit', function (code) {
if (!code || code === 0 || code === 130) {
cb();
} else {
cb(new Error('exited with code ' + code));
}
});
meteor.kill('SIGINT');
meteor = null;
//--------------------------------------------
process.removeListener('exit', onProcessExit);
}
function onProcessExit() {
meteor && meteor.kill();
meteor = null;
}
return instance;
}