Skip to content

Commit

Permalink
feat: add the parallel functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
wood authored and Gregg Van Hove committed Sep 24, 2019
1 parent c2e0f30 commit b78c149
Show file tree
Hide file tree
Showing 16 changed files with 444 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ build
node_modules
package-lock.json
yarn.lock
.DS_Store
21 changes: 15 additions & 6 deletions bin/jasmine.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
#!/usr/bin/env node

var path = require('path'),
Command = require('../lib/command.js'),
Jasmine = require('../lib/jasmine.js');
var cluster = require('cluster'),
path = require('path'),
Jasmine = require("../lib/jasmine");

var jasmine = new Jasmine({ projectBaseDir: path.resolve() });
var examplesDir = path.join(path.dirname(require.resolve('jasmine-core')), 'jasmine-core', 'example', 'node_example');
var command = new Command(path.resolve(), examplesDir, console.log);

command.run(jasmine, process.argv.slice(2));
if (cluster.isMaster) {
var Command = require('../lib/command.js');

var examplesDir = path.join(path.dirname(require.resolve('jasmine-core')), 'jasmine-core', 'example', 'node_example');
var command = new Command(path.resolve(), examplesDir, console.log);

command.run(jasmine, process.argv.slice(2));
} else if (cluster.isWorker) {
var loadConfig = require('../lib/loadConfig');
var runWorkerJasmine = require('../lib/worker');
runWorkerJasmine(jasmine, loadConfig);
}
46 changes: 13 additions & 33 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@ function parseOptions(argv) {
stopOnFailure,
failFast,
random,
seed;
seed,
workerCount;

argv.forEach(function(arg) {
if (arg === '--no-color') {
color = false;
} else if (arg === '--color') {
color = true;
} else if (arg.match("^--worker-count=")) {
workerCount = parseInt(arg.match("^--worker-count=(.*)")[1]);
} else if (arg.match("^--filter=")) {
filter = arg.match("^--filter=(.*)")[1];
} else if (arg.match("^--helper=")) {
Expand Down Expand Up @@ -118,44 +121,20 @@ function parseOptions(argv) {
files: files,
random: random,
seed: seed,
workerCount: workerCount,
unknownOptions: unknownOptions
};
}

function runJasmine(jasmine, env, print) {
jasmine.loadConfigFile(env.configPath || process.env.JASMINE_CONFIG_PATH);
if (env.stopOnFailure !== undefined) {
jasmine.stopSpecOnExpectationFailure(env.stopOnFailure);
}
if (env.failFast !== undefined) {
jasmine.stopOnSpecFailure(env.failFast);
}
if (env.seed !== undefined) {
jasmine.seed(env.seed);
}
if (env.random !== undefined) {
jasmine.randomizeTests(env.random);
}
if (env.helpers !== undefined && env.helpers.length) {
jasmine.addHelperFiles(env.helpers);
}
if (env.requires !== undefined && env.requires.length) {
jasmine.addRequires(env.requires);
}
if (env.reporter !== undefined) {
try {
var Report = require(env.reporter);
var reporter = new Report();
jasmine.clearReporters();
jasmine.addReporter(reporter);
} catch(e) {
print('failed to register reporter "' + env.reporter + '"');
print(e.message);
print(e.stack);
}
var loadConfig = require('./loadConfig');
if (!env.workerCount || env.workerCount < 2) {
loadConfig(jasmine, env, print);
jasmine.execute(env.files, env.filter);
} else {
var runMasterJasmine = require('./master');
runMasterJasmine(jasmine, env, print, loadConfig);
}
jasmine.showColors(env.color);
jasmine.execute(env.files, env.filter);
}

function initJasmine(options) {
Expand Down Expand Up @@ -226,6 +205,7 @@ function help(options) {
print('%s\t[true|false] stop Jasmine execution on spec failure', lPad('--fail-fast=', 18));
print('%s\tpath to your optional jasmine.json', lPad('--config=', 18));
print('%s\tpath to reporter to use instead of the default Jasmine reporter', lPad('--reporter=', 18));
print('%s\tnumber of workers to run the tests in parallel. Default is 1', lPad('--worker-count=', 18));
print('');
print('The given arguments take precedence over options in your jasmine.json');
print('The path to your optional jasmine.json can also be configured by setting the JASMINE_CONFIG_PATH environment variable');
Expand Down
52 changes: 26 additions & 26 deletions lib/jasmine.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var path = require('path'),
util = require('util'),
glob = require('glob'),
fg = require('fast-glob'),
CompletionReporter = require('./reporters/completion_reporter'),
ConsoleSpecFilter = require('./filters/console_spec_filter');

Expand Down Expand Up @@ -86,18 +86,21 @@ Jasmine.prototype.addMatchers = function(matchers) {

Jasmine.prototype.loadSpecs = function() {
this.specFiles.forEach(function(file) {
delete require.cache[require.resolve(file)];
require(file);
});
};

Jasmine.prototype.loadHelpers = function() {
this.helperFiles.forEach(function(file) {
delete require.cache[require.resolve(file)];
require(file);
});
};

Jasmine.prototype.loadRequires = function() {
this.requires.forEach(function(r) {
delete require.cache[require.resolve(r)];
require(r);
});
};
Expand Down Expand Up @@ -159,35 +162,32 @@ Jasmine.prototype.addRequires = function(requires) {
function addFiles(kind) {
return function (files) {
var jasmineRunner = this;
var fileArr = this[kind];
files = files.map(function(file) {
var hasNegation = file[0] === "!";

var includeFiles = [];
var excludeFiles = [];
files.forEach(function(file) {
if (file.startsWith('!')) {
var excludeFile = file.substring(1);
if(!(path.isAbsolute && path.isAbsolute(excludeFile))) {
excludeFile = path.join(jasmineRunner.projectBaseDir, jasmineRunner.specDir, excludeFile);
}

excludeFiles.push(excludeFile);
} else {
includeFiles.push(file);
if (hasNegation) {
file = file.substring(1);
}
});

includeFiles.forEach(function(file) {
if(!(path.isAbsolute && path.isAbsolute(file))) {
if (!path.isAbsolute(file)) {
file = path.join(jasmineRunner.projectBaseDir, jasmineRunner.specDir, file);
}
var filePaths = glob.sync(file, { ignore: excludeFiles });
filePaths.forEach(function(filePath) {
// glob will always output '/' as a segment separator but the fileArr may use \ on windows
// fileArr needs to be checked for both versions
if(fileArr.indexOf(filePath) === -1 && fileArr.indexOf(path.normalize(filePath)) === -1) {
fileArr.push(filePath);
}
});

if (hasNegation) {
file = '!' + file;
}

return file;
});

var fileArr = this[kind];

fg.sync(this[kind].concat(files), { 'unique': true }).forEach(function(file) {
// glob will always output '/' as a segment separator but the fileArr may use \ on windows
// fileArr needs to be checked for both versions
if(fileArr.indexOf(file) === -1 && fileArr.indexOf(path.normalize(file)) === -1) {
fileArr.push(file);
}
});
};
}
Expand Down Expand Up @@ -241,7 +241,7 @@ Jasmine.prototype.execute = function(files, filterString) {
this.configureDefaultReporter({ showColors: this.showingColors });
}

if(filterString) {
if (filterString) {
var specFilter = new ConsoleSpecFilter({
filterString: filterString
});
Expand Down
34 changes: 34 additions & 0 deletions lib/loadConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = exports = function(jasmine, env, print) {
jasmine.loadConfigFile(env.configPath || process.env.JASMINE_CONFIG_PATH);
if (env.stopOnFailure !== undefined) {
jasmine.stopSpecOnExpectationFailure(env.stopOnFailure);
}
if (env.failFast !== undefined) {
jasmine.stopOnSpecFailure(env.failFast);
}
if (env.seed !== undefined) {
jasmine.seed(env.seed);
}
if (env.random !== undefined) {
jasmine.randomizeTests(env.random);
}
if (env.helpers !== undefined && env.helpers.length) {
jasmine.addHelperFiles(env.helpers);
}
if (env.requires !== undefined && env.requires.length) {
jasmine.addRequires(env.requires);
}
if (env.reporter !== undefined) {
try {
var Report = require(env.reporter);
var reporter = new Report();
jasmine.clearReporters();
jasmine.addReporter(reporter);
} catch(e) {
print('failed to register reporter "' + env.reporter + '"');
print(e.message);
print(e.stack);
}
}
jasmine.showColors(env.color);
};
59 changes: 59 additions & 0 deletions lib/master.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
var cluster = require('cluster');

module.exports = exports = function(jasmine, env, print, loadConfig) {
loadConfig(jasmine, env, print);

if (env.files && env.files.length > 0) {
jasmine.specDir = '';
jasmine.specFiles = [];
jasmine.addSpecFiles(env.files);
}

env.seed = env.seed || String(Math.random()).slice(-5);

var files = jasmine.specFiles.slice(0).reverse();
jasmine.configureDefaultReporter({ showColors: env.color });
clusterReporter(jasmine.reporter);

cluster.on('message', function(worker, message) {
if (message.kind === 'jasmineDone') {
if (files.length) {
worker.send(clusterEnv(env, files.pop()));
} else {
worker.kill();
}
}
});

for (var i = 0; i < env.workerCount; i++) {
cluster.fork().send(clusterEnv(env, files.pop()));
}
};

function clusterEnv(env, file) {
var clusterEnv = Object.assign({}, env);
clusterEnv.reporter = './reporters/worker_reporter.js';
clusterEnv.files = [file];
return clusterEnv;
}

function clusterReporter(reporter) {
var results = [];

cluster.on('message', function(worker, message) {
if (message.kind === 'jasmineDone') {
results.push(message.result);
} else if (reporter[message.kind]) {
reporter[message.kind](message.result);
if (message.kind === 'jasmineStarted') {
reporter[message.kind] = function(){};
}
}
});

cluster.on('exit', function() {
if (!Object.keys(cluster.workers).length) {
reporter.jasmineDone(results);
}
});
}
6 changes: 6 additions & 0 deletions lib/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var path = require('path'),
Jasmine = require('./jasmine');

module.exports = exports = function() {
return new Jasmine({ projectBaseDir: path.resolve() });
};
15 changes: 15 additions & 0 deletions lib/reporters/worker_reporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var kinds = ['jasmineStarted', 'jasmineDone', 'specStarted', 'specDone', 'suiteStarted', 'suiteDone'];

function WorkerReporter() {
var self = this;
kinds.forEach(function(kind) {
self[kind] = function(result) {
process.send({
kind: kind,
result: result
});
};
});
}

module.exports = exports = WorkerReporter;
7 changes: 7 additions & 0 deletions lib/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = exports = function(jasmine, loadConfig) {
process.on('message', function(env) {
jasmine.onComplete(function(){});
loadConfig(jasmine, env, console.log);
jasmine.execute(env.files, env.filter);
});
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test": "./node_modules/.bin/grunt && ./bin/jasmine.js"
},
"dependencies": {
"glob": "^7.1.4",
"fast-glob": "^2.2.6",
"jasmine-core": "~3.5.0"
},
"bin": "./bin/jasmine.js",
Expand Down
8 changes: 4 additions & 4 deletions spec/jasmine_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,27 @@ describe('Jasmine', function() {
it('add spec files with glob pattern', function() {
expect(this.testJasmine.specFiles).toEqual([]);
this.testJasmine.addSpecFiles(['spec/*.js']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'jasmine_spec.js']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'jasmine_spec.js', 'load_config_spec.js', 'master_spec.js', 'worker_spec.js']);
});

it('add spec files with excluded files', function() {
expect(this.testJasmine.specFiles).toEqual([]);
this.testJasmine.addSpecFiles(['spec/*.js', '!spec/command*']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['jasmine_spec.js']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['jasmine_spec.js', 'load_config_spec.js', 'master_spec.js', 'worker_spec.js']);
});

it('add spec files with glob pattern to existings files', function() {
var aFile = path.join(this.testJasmine.projectBaseDir, this.testJasmine.specDir, 'spec/command_spec.js');
this.testJasmine.specFiles = [aFile, 'b'];
this.testJasmine.addSpecFiles(['spec/*.js']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'b', 'jasmine_spec.js']);
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'b', 'jasmine_spec.js', 'load_config_spec.js', 'master_spec.js', 'worker_spec.js']);
});

it('add helper files with glob pattern to existings files', function() {
var aFile = path.join(this.testJasmine.projectBaseDir, this.testJasmine.specDir, 'spec/command_spec.js');
this.testJasmine.helperFiles = [aFile, 'b'];
this.testJasmine.addHelperFiles(['spec/*.js']);
expect(this.testJasmine.helperFiles.map(basename)).toEqual(['command_spec.js', 'b', 'jasmine_spec.js']);
expect(this.testJasmine.helperFiles.map(basename)).toEqual(['command_spec.js', 'b', 'jasmine_spec.js', 'load_config_spec.js', 'master_spec.js', 'worker_spec.js']);
});
});

Expand Down
Loading

0 comments on commit b78c149

Please sign in to comment.