diff --git a/doc/config.md b/doc/config.md index 063044c6d..42c56ae97 100644 --- a/doc/config.md +++ b/doc/config.md @@ -65,6 +65,7 @@ system: teamcity: true debug: true parallelLimit: 3 + workers: 4 diffColor: '#ff0000' coverage: enabled: true @@ -125,6 +126,9 @@ exclude: option to limit the number of browsers that `gemini` will try to run in parallel. +* `workers` — by default, `gemini` will run as many parallel + workers as cores available, this option allows you to limit it. + * `diffColor` — specifies color which will be used to highlight differences between images. Specified in hexadecimal RGB (`#RRGGBB`). Magenta by default (`#FF00FF`). diff --git a/lib/config/options.js b/lib/config/options.js index 9113025c5..865e7d27d 100644 --- a/lib/config/options.js +++ b/lib/config/options.js @@ -1,5 +1,6 @@ 'use strict'; +const os = require('os'); const path = require('path'); const configparser = require('gemini-configparser'); const _ = require('lodash'); @@ -45,6 +46,8 @@ module.exports = root( parallelLimit: positiveIntegerOption(Infinity), + workers: positiveIntegerOption(os.cpus().length), + diffColor: option({ defaultValue: '#ff00ff', validate: (value) => { diff --git a/lib/gemini.js b/lib/gemini.js index 845940871..1e637f6fa 100644 --- a/lib/gemini.js +++ b/lib/gemini.js @@ -82,7 +82,7 @@ module.exports = class Gemini extends PassthroughEmitter { } update(paths, options) { - return this._exec(() => this._run(StateProcessor.createScreenUpdater(options), paths, options)); + return this._exec(() => this._run(StateProcessor.createScreenUpdater(this.config, options), paths, options)); } test(paths, options) { diff --git a/lib/state-processor/index.js b/lib/state-processor/index.js index b7d85a36d..bb960645b 100644 --- a/lib/state-processor/index.js +++ b/lib/state-processor/index.js @@ -8,7 +8,7 @@ module.exports = { return new TestStateProcessor(config); }, - createScreenUpdater: function(options) { - return new UpdateStateProcessor(options); + createScreenUpdater: function(config, options) { + return new UpdateStateProcessor(config, options); } }; diff --git a/lib/state-processor/state-processor.js b/lib/state-processor/state-processor.js index 4437e9744..96695a495 100644 --- a/lib/state-processor/state-processor.js +++ b/lib/state-processor/state-processor.js @@ -9,12 +9,16 @@ const Events = require('../constants/events'); const errorUtils = require('../errors/utils'); module.exports = class StateProcessor { - constructor(captureProcessorType) { + constructor(captureProcessorType, maxConcurrentWorkers) { this._captureProcessorType = captureProcessorType; + this._maxConcurrentWorkers = maxConcurrentWorkers; } prepare(emitter) { - this._workers = workerFarm(require.resolve('./job')); + const workerFarmConfig = { + maxConcurrentWorkers: this._maxConcurrentWorkers + }; + this._workers = workerFarm(workerFarmConfig, require.resolve('./job')); emitter.on(Events.END, () => workerFarm.end(this._workers)); } diff --git a/lib/state-processor/test-state-processor.js b/lib/state-processor/test-state-processor.js index 54b2fa193..d2956089f 100644 --- a/lib/state-processor/test-state-processor.js +++ b/lib/state-processor/test-state-processor.js @@ -8,7 +8,7 @@ const {Image} = require('gemini-core'); module.exports = class TestStateProcessor extends StateProcessor { constructor(config) { - super('tester'); + super('tester', config.system.workers); this._diffColor = config.system.diffColor; } diff --git a/lib/state-processor/update-state-processor.js b/lib/state-processor/update-state-processor.js index 4602a8b42..98993bcf5 100644 --- a/lib/state-processor/update-state-processor.js +++ b/lib/state-processor/update-state-processor.js @@ -4,12 +4,12 @@ const StateProcessor = require('./state-processor'); const Events = require('../constants/events'); module.exports = class UpdateStateProcessor extends StateProcessor { - constructor(opts) { + constructor(config, opts) { const updaterType = opts.diff && !opts.new && 'diff-updater' || !opts.diff && opts.new && 'new-updater' || 'meta-updater'; - super(updaterType); + super(updaterType, config.system.workers); } exec(state, browserSession, page, emit) { diff --git a/test/unit/state-processor/state-processor.js b/test/unit/state-processor/state-processor.js index a4a7469cf..d4f2224e9 100644 --- a/test/unit/state-processor/state-processor.js +++ b/test/unit/state-processor/state-processor.js @@ -38,7 +38,7 @@ describe('state-processor/state-processor', () => { return (args, cb) => cb(null, job(args)); } }); - const stateProcessor = new StateProcessor(opts.captureProcessorType); + const stateProcessor = new StateProcessor(opts.captureProcessorType, 4); stateProcessor.prepare(new AsyncEmitter()); return stateProcessor.exec(opts.state, browserSession, opts.page); diff --git a/test/unit/state-processor/update-state-processor.js b/test/unit/state-processor/update-state-processor.js index 7e2018edf..e0a2697ce 100644 --- a/test/unit/state-processor/update-state-processor.js +++ b/test/unit/state-processor/update-state-processor.js @@ -10,6 +10,7 @@ const util = require('../../util'); describe('state-processor/update-state-processor', () => { const sandbox = sinon.sandbox.create(); + const config = {system: {workers: 4}}; const exec_ = (opts) => { opts = _.defaultsDeep(opts || {}, { @@ -19,7 +20,7 @@ describe('state-processor/update-state-processor', () => { emit: sinon.spy() }); - return new UpdateStateProcessor({}).exec(opts.state, opts.browserSession, opts.page, opts.emit); + return new UpdateStateProcessor(config, {}).exec(opts.state, opts.browserSession, opts.page, opts.emit); }; beforeEach(() => sandbox.stub(StateProcessor.prototype, 'exec'));