Skip to content

Commit f0c0d7d

Browse files
author
Gregg Van Hove
committed
Merge branch 'wood1986-features/concurrent-v4'
- Merges #153 from @wood1986
2 parents c2e0f30 + b78c149 commit f0c0d7d

16 files changed

+444
-70
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ build
1919
node_modules
2020
package-lock.json
2121
yarn.lock
22+
.DS_Store

bin/jasmine.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
#!/usr/bin/env node
22

3-
var path = require('path'),
4-
Command = require('../lib/command.js'),
5-
Jasmine = require('../lib/jasmine.js');
3+
var cluster = require('cluster'),
4+
path = require('path'),
5+
Jasmine = require("../lib/jasmine");
66

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

11-
command.run(jasmine, process.argv.slice(2));
9+
if (cluster.isMaster) {
10+
var Command = require('../lib/command.js');
11+
12+
var examplesDir = path.join(path.dirname(require.resolve('jasmine-core')), 'jasmine-core', 'example', 'node_example');
13+
var command = new Command(path.resolve(), examplesDir, console.log);
14+
15+
command.run(jasmine, process.argv.slice(2));
16+
} else if (cluster.isWorker) {
17+
var loadConfig = require('../lib/loadConfig');
18+
var runWorkerJasmine = require('../lib/worker');
19+
runWorkerJasmine(jasmine, loadConfig);
20+
}

lib/command.js

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,16 @@ function parseOptions(argv) {
7575
stopOnFailure,
7676
failFast,
7777
random,
78-
seed;
78+
seed,
79+
workerCount;
7980

8081
argv.forEach(function(arg) {
8182
if (arg === '--no-color') {
8283
color = false;
8384
} else if (arg === '--color') {
8485
color = true;
86+
} else if (arg.match("^--worker-count=")) {
87+
workerCount = parseInt(arg.match("^--worker-count=(.*)")[1]);
8588
} else if (arg.match("^--filter=")) {
8689
filter = arg.match("^--filter=(.*)")[1];
8790
} else if (arg.match("^--helper=")) {
@@ -118,44 +121,20 @@ function parseOptions(argv) {
118121
files: files,
119122
random: random,
120123
seed: seed,
124+
workerCount: workerCount,
121125
unknownOptions: unknownOptions
122126
};
123127
}
124128

125129
function runJasmine(jasmine, env, print) {
126-
jasmine.loadConfigFile(env.configPath || process.env.JASMINE_CONFIG_PATH);
127-
if (env.stopOnFailure !== undefined) {
128-
jasmine.stopSpecOnExpectationFailure(env.stopOnFailure);
129-
}
130-
if (env.failFast !== undefined) {
131-
jasmine.stopOnSpecFailure(env.failFast);
132-
}
133-
if (env.seed !== undefined) {
134-
jasmine.seed(env.seed);
135-
}
136-
if (env.random !== undefined) {
137-
jasmine.randomizeTests(env.random);
138-
}
139-
if (env.helpers !== undefined && env.helpers.length) {
140-
jasmine.addHelperFiles(env.helpers);
141-
}
142-
if (env.requires !== undefined && env.requires.length) {
143-
jasmine.addRequires(env.requires);
144-
}
145-
if (env.reporter !== undefined) {
146-
try {
147-
var Report = require(env.reporter);
148-
var reporter = new Report();
149-
jasmine.clearReporters();
150-
jasmine.addReporter(reporter);
151-
} catch(e) {
152-
print('failed to register reporter "' + env.reporter + '"');
153-
print(e.message);
154-
print(e.stack);
155-
}
130+
var loadConfig = require('./loadConfig');
131+
if (!env.workerCount || env.workerCount < 2) {
132+
loadConfig(jasmine, env, print);
133+
jasmine.execute(env.files, env.filter);
134+
} else {
135+
var runMasterJasmine = require('./master');
136+
runMasterJasmine(jasmine, env, print, loadConfig);
156137
}
157-
jasmine.showColors(env.color);
158-
jasmine.execute(env.files, env.filter);
159138
}
160139

161140
function initJasmine(options) {
@@ -226,6 +205,7 @@ function help(options) {
226205
print('%s\t[true|false] stop Jasmine execution on spec failure', lPad('--fail-fast=', 18));
227206
print('%s\tpath to your optional jasmine.json', lPad('--config=', 18));
228207
print('%s\tpath to reporter to use instead of the default Jasmine reporter', lPad('--reporter=', 18));
208+
print('%s\tnumber of workers to run the tests in parallel. Default is 1', lPad('--worker-count=', 18));
229209
print('');
230210
print('The given arguments take precedence over options in your jasmine.json');
231211
print('The path to your optional jasmine.json can also be configured by setting the JASMINE_CONFIG_PATH environment variable');

lib/jasmine.js

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var path = require('path'),
22
util = require('util'),
3-
glob = require('glob'),
3+
fg = require('fast-glob'),
44
CompletionReporter = require('./reporters/completion_reporter'),
55
ConsoleSpecFilter = require('./filters/console_spec_filter');
66

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

8787
Jasmine.prototype.loadSpecs = function() {
8888
this.specFiles.forEach(function(file) {
89+
delete require.cache[require.resolve(file)];
8990
require(file);
9091
});
9192
};
9293

9394
Jasmine.prototype.loadHelpers = function() {
9495
this.helperFiles.forEach(function(file) {
96+
delete require.cache[require.resolve(file)];
9597
require(file);
9698
});
9799
};
98100

99101
Jasmine.prototype.loadRequires = function() {
100102
this.requires.forEach(function(r) {
103+
delete require.cache[require.resolve(r)];
101104
require(r);
102105
});
103106
};
@@ -159,35 +162,32 @@ Jasmine.prototype.addRequires = function(requires) {
159162
function addFiles(kind) {
160163
return function (files) {
161164
var jasmineRunner = this;
162-
var fileArr = this[kind];
165+
files = files.map(function(file) {
166+
var hasNegation = file[0] === "!";
163167

164-
var includeFiles = [];
165-
var excludeFiles = [];
166-
files.forEach(function(file) {
167-
if (file.startsWith('!')) {
168-
var excludeFile = file.substring(1);
169-
if(!(path.isAbsolute && path.isAbsolute(excludeFile))) {
170-
excludeFile = path.join(jasmineRunner.projectBaseDir, jasmineRunner.specDir, excludeFile);
171-
}
172-
173-
excludeFiles.push(excludeFile);
174-
} else {
175-
includeFiles.push(file);
168+
if (hasNegation) {
169+
file = file.substring(1);
176170
}
177-
});
178171

179-
includeFiles.forEach(function(file) {
180-
if(!(path.isAbsolute && path.isAbsolute(file))) {
172+
if (!path.isAbsolute(file)) {
181173
file = path.join(jasmineRunner.projectBaseDir, jasmineRunner.specDir, file);
182174
}
183-
var filePaths = glob.sync(file, { ignore: excludeFiles });
184-
filePaths.forEach(function(filePath) {
185-
// glob will always output '/' as a segment separator but the fileArr may use \ on windows
186-
// fileArr needs to be checked for both versions
187-
if(fileArr.indexOf(filePath) === -1 && fileArr.indexOf(path.normalize(filePath)) === -1) {
188-
fileArr.push(filePath);
189-
}
190-
});
175+
176+
if (hasNegation) {
177+
file = '!' + file;
178+
}
179+
180+
return file;
181+
});
182+
183+
var fileArr = this[kind];
184+
185+
fg.sync(this[kind].concat(files), { 'unique': true }).forEach(function(file) {
186+
// glob will always output '/' as a segment separator but the fileArr may use \ on windows
187+
// fileArr needs to be checked for both versions
188+
if(fileArr.indexOf(file) === -1 && fileArr.indexOf(path.normalize(file)) === -1) {
189+
fileArr.push(file);
190+
}
191191
});
192192
};
193193
}
@@ -241,7 +241,7 @@ Jasmine.prototype.execute = function(files, filterString) {
241241
this.configureDefaultReporter({ showColors: this.showingColors });
242242
}
243243

244-
if(filterString) {
244+
if (filterString) {
245245
var specFilter = new ConsoleSpecFilter({
246246
filterString: filterString
247247
});

lib/loadConfig.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module.exports = exports = function(jasmine, env, print) {
2+
jasmine.loadConfigFile(env.configPath || process.env.JASMINE_CONFIG_PATH);
3+
if (env.stopOnFailure !== undefined) {
4+
jasmine.stopSpecOnExpectationFailure(env.stopOnFailure);
5+
}
6+
if (env.failFast !== undefined) {
7+
jasmine.stopOnSpecFailure(env.failFast);
8+
}
9+
if (env.seed !== undefined) {
10+
jasmine.seed(env.seed);
11+
}
12+
if (env.random !== undefined) {
13+
jasmine.randomizeTests(env.random);
14+
}
15+
if (env.helpers !== undefined && env.helpers.length) {
16+
jasmine.addHelperFiles(env.helpers);
17+
}
18+
if (env.requires !== undefined && env.requires.length) {
19+
jasmine.addRequires(env.requires);
20+
}
21+
if (env.reporter !== undefined) {
22+
try {
23+
var Report = require(env.reporter);
24+
var reporter = new Report();
25+
jasmine.clearReporters();
26+
jasmine.addReporter(reporter);
27+
} catch(e) {
28+
print('failed to register reporter "' + env.reporter + '"');
29+
print(e.message);
30+
print(e.stack);
31+
}
32+
}
33+
jasmine.showColors(env.color);
34+
};

lib/master.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
var cluster = require('cluster');
2+
3+
module.exports = exports = function(jasmine, env, print, loadConfig) {
4+
loadConfig(jasmine, env, print);
5+
6+
if (env.files && env.files.length > 0) {
7+
jasmine.specDir = '';
8+
jasmine.specFiles = [];
9+
jasmine.addSpecFiles(env.files);
10+
}
11+
12+
env.seed = env.seed || String(Math.random()).slice(-5);
13+
14+
var files = jasmine.specFiles.slice(0).reverse();
15+
jasmine.configureDefaultReporter({ showColors: env.color });
16+
clusterReporter(jasmine.reporter);
17+
18+
cluster.on('message', function(worker, message) {
19+
if (message.kind === 'jasmineDone') {
20+
if (files.length) {
21+
worker.send(clusterEnv(env, files.pop()));
22+
} else {
23+
worker.kill();
24+
}
25+
}
26+
});
27+
28+
for (var i = 0; i < env.workerCount; i++) {
29+
cluster.fork().send(clusterEnv(env, files.pop()));
30+
}
31+
};
32+
33+
function clusterEnv(env, file) {
34+
var clusterEnv = Object.assign({}, env);
35+
clusterEnv.reporter = './reporters/worker_reporter.js';
36+
clusterEnv.files = [file];
37+
return clusterEnv;
38+
}
39+
40+
function clusterReporter(reporter) {
41+
var results = [];
42+
43+
cluster.on('message', function(worker, message) {
44+
if (message.kind === 'jasmineDone') {
45+
results.push(message.result);
46+
} else if (reporter[message.kind]) {
47+
reporter[message.kind](message.result);
48+
if (message.kind === 'jasmineStarted') {
49+
reporter[message.kind] = function(){};
50+
}
51+
}
52+
});
53+
54+
cluster.on('exit', function() {
55+
if (!Object.keys(cluster.workers).length) {
56+
reporter.jasmineDone(results);
57+
}
58+
});
59+
}

lib/new.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
var path = require('path'),
2+
Jasmine = require('./jasmine');
3+
4+
module.exports = exports = function() {
5+
return new Jasmine({ projectBaseDir: path.resolve() });
6+
};

lib/reporters/worker_reporter.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var kinds = ['jasmineStarted', 'jasmineDone', 'specStarted', 'specDone', 'suiteStarted', 'suiteDone'];
2+
3+
function WorkerReporter() {
4+
var self = this;
5+
kinds.forEach(function(kind) {
6+
self[kind] = function(result) {
7+
process.send({
8+
kind: kind,
9+
result: result
10+
});
11+
};
12+
});
13+
}
14+
15+
module.exports = exports = WorkerReporter;

lib/worker.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = exports = function(jasmine, loadConfig) {
2+
process.on('message', function(env) {
3+
jasmine.onComplete(function(){});
4+
loadConfig(jasmine, env, console.log);
5+
jasmine.execute(env.files, env.filter);
6+
});
7+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"test": "./node_modules/.bin/grunt && ./bin/jasmine.js"
1919
},
2020
"dependencies": {
21-
"glob": "^7.1.4",
21+
"fast-glob": "^2.2.6",
2222
"jasmine-core": "~3.5.0"
2323
},
2424
"bin": "./bin/jasmine.js",

spec/jasmine_spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,27 @@ describe('Jasmine', function() {
5252
it('add spec files with glob pattern', function() {
5353
expect(this.testJasmine.specFiles).toEqual([]);
5454
this.testJasmine.addSpecFiles(['spec/*.js']);
55-
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'jasmine_spec.js']);
55+
expect(this.testJasmine.specFiles.map(basename)).toEqual(['command_spec.js', 'jasmine_spec.js', 'load_config_spec.js', 'master_spec.js', 'worker_spec.js']);
5656
});
5757

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

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

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

0 commit comments

Comments
 (0)