Skip to content

Commit

Permalink
parallelise all the things!
Browse files Browse the repository at this point in the history
  • Loading branch information
arealmaas committed Feb 11, 2016
1 parent 49bf3ba commit 4c67c17
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 119 deletions.
68 changes: 41 additions & 27 deletions bin/parallel-mocha
Original file line number Diff line number Diff line change
@@ -1,49 +1,63 @@
#!/usr/bin/env node
'use strict';

const program = require('commander');
const fs = require('fs');
const glob = require('glob');
const Runner = require('../lib/runner');
const async = require('async');
const chalk = require('chalk');

program
.version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version)
.usage('[options] <files...>')
.option('-p, --processes <n>', 'Set number of processes', parseInt)
.option('-t, --timeout <n>', 'Timeout between testes', parseInt)
.option('-s, --slow <n>', 'Wait for slow tests', parseInt)
.parse(process.argv);

// TODO: how can i make sure workers is integer?
.version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version)
.usage('[options] <files...>')
.option('-p, --processes <n>', 'Set number of processes', parseInt)
.option('-t, --timeout <n>', 'Timeout between testes', parseInt)
.option('-s, --slow <n>', 'Wait for slow tests', parseInt)
.parse(process.argv);

let processes = program.processes || 20;
let timeout = program.timeout || 10000;
let slow = program.slow || 2000;
let files = program.args;
let environment = program.environment || 'test';

let resolveFilesQueue = files.map(
file=>(cb)=> {
glob(file, cb)
}
file=>(cb)=> {
glob(file, cb)
}
);

async.parallel(resolveFilesQueue, (err, files)=> {
if (err) {
throw err;
}
if (err) {
throw err;
}

files = files.reduce((files, file)=> {
return files.concat(file);
}, []);

var runner = new Runner(files, {
processes: files.length,
timeout: timeout,
slow: slow
});

runner.run((error) => {
if (error) {
console.log('==== Failed: %s', error.message);
} else {
console.log('==== Pass!');
}
});
var runner = new Runner(files, {
processes: files.length,
timeout: timeout,
slow: slow,
env: environment
});

process.stderr.on('data', (data)=> {
console.log(chalk.red(data));
})

runner.run((error) => {
if (error) {
console.log(chalk.red(`Failed: ${error.message}`));
// Force epic fail
process.exit(1);
} else {
console.log(chalk.green('All test passed!!'));
process.exit(0);
}
});
});


Expand Down
67 changes: 33 additions & 34 deletions lib/pool.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
var EventEmitter = require('events').EventEmitter
, util = require('util')
, DoneCriteria = require('done-criteria')
, underscore = require('underscore');
'use strict';

function Pool(paths, workers) {
EventEmitter.call(this);
const EventEmitter = require('events').EventEmitter;
const DoneCriteria = require('done-criteria');
const underscore = require('underscore');

this.paths = paths;
this.workers = workers;
}

util.inherits(Pool, EventEmitter);
class Pool extends EventEmitter {

Pool.prototype.start = function() {
var self = this
, doneCriteria = new DoneCriteria(this.paths, function() {
self.emit('done');
});
constructor(paths, workers) {
super(Pool);
this.paths = paths;
this.workers = workers;
}

self.on('next', function() {
self.next(function(path) {
doneCriteria.done(path);
start() {
let doneCriteria = new DoneCriteria(this.paths, () => {
this.emit('done');
});
});


underscore.range(this.workers).forEach(function() {
self.next(function(path) {
doneCriteria.done(path);
self.emit('next');
this.on('next', ()=> {
this.next((path)=> {
doneCriteria.done(path);
});
});
});
};

Pool.prototype.next = function(callback) {
var path = this.paths.shift();
if (path) {
this.emit('ready', path, function() {
return callback(path);

underscore.range(this.workers).forEach(()=> {
this.next((path)=> {
doneCriteria.done(path);
this.emit('next');
});
});
};

next(callback) {
let path = this.paths.shift();
if (path) {
this.emit('ready', path, ()=> {
return callback(path);
});
}
}
};
}

module.exports = Pool;
107 changes: 49 additions & 58 deletions lib/runner.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,73 @@
var spawn = require('child_process').spawn
var fs = require('fs')
var underscore = require('underscore');
'use strict';

var Pool = require('./pool');
const spawn = require('child_process').spawn;
const fs = require('fs');
const underscore = require('underscore');

function Runner(paths, config) {
const Pool = require('./pool');

class Runner {

constructor(paths, config) {
this.paths = paths;
this.config = underscore.extend(
{
bin: ['./node_modules/.bin/mocha']
, processes: 2
}
, config
{
bin: ['./node_modules/.bin/mocha']
, processes: 2
}
, config
);
this.mocha = null;
this.exitCodes = [];
}
}

Runner.prototype.run = function (callback) {
var self = this
, pool = new Pool(self.paths, self.config.processes);
run(callback) {
let pool = new Pool(this.paths, this.config.processes);
let exitCodes = [];

pool.on('done', function () {
var success = underscore.every(self.exitCodes, function (code) {
return code === 0;
});
// TODO: return better result rather than just error or not
var error = success ? null : new Error('Not all tests passed');
return callback(error);
});
pool.on('ready', (path, callback) => {
var args = {
path: path,
timeout: this.config.timeout,
slow: this.config.slow,
env: this.config.env
};

pool.on('ready', function (path, callback) {
var args = {
path: path,
timeout: self.config.timeout,
slow: self.config.slow
};
this.spawn(args, (code)=> {
exitCodes.push(code);
callback();
});
});

self.spawn(args, callback);
pool.on('done', () => {
var success = underscore.every(this.exitCodes, code=>code === 0);
var error = success ? null : new Error('Not all tests passed');
return callback(error);
});

pool.start();
};
}

Runner.prototype.spawn = function (args, callback) {
var self = this
, mocha = self.findMocha()
, child = spawn(mocha, [
spawn(args, callback) {
let mocha = this.findMocha();
let child = spawn(mocha, [
'--timeout', args.timeout,
'--slow', args.timeout,
args.path
], {stdio: "inherit", env: underscore.extend(process.env, {NODE_ENV: 'test'})});

/*child.stdout.once('data', function (data) {
process.stdout.write('.');
self.exitCodes.push(0);
return callback(null);
});
child.stderr.on('data', function (data) {
process.stdout.write('%s', data);
process.exit();
});*/

child.on('exit', function (code) {

});
],
{stdio: "inherit", env: underscore.extend(process.env, {NODE_ENV: args.env})}
);

};
child.on('exit', callback);
}

Runner.prototype.findMocha = function () {
findMocha() {
if (!this.mocha) {
this.mocha = underscore.find(this.config.bin, function (bin) {
return fs.existsSync(bin);
});
this.mocha = this.mocha || 'mocha';
this.mocha = underscore.find(this.config.bin, bin =>fs.existsSync(bin));
this.mocha = this.mocha || 'mocha';
}

return this.mocha;
};
}
}

module.exports = Runner;

0 comments on commit 4c67c17

Please sign in to comment.