From 982c785509f518d81f5d39cd7c98bfccbe44e257 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Thu, 5 Jul 2018 12:05:20 +0300 Subject: [PATCH 1/3] 1. Add the option to create request that run once before test 2. Fix vulnerabilities Dina Yakovlev 3. remove entries from aggregate data --- .gitignore | 3 +- core/lib/runner.js | 253 +++++++++++--------- core/lib/schemas/artillery_test_script.json | 12 + core/lib/stats2.js | 7 +- package-lock.json | 77 +++++- package.json | 4 +- test/core/scripts/before_test.json | 49 ++++ test/core/test_capture.js | 30 +++ test/core/test_reuse.js | 10 +- 9 files changed, 322 insertions(+), 123 deletions(-) create mode 100644 test/core/scripts/before_test.json diff --git a/.gitignore b/.gitignore index cea62edbac..025bad81e6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ lib-cov artillery_report* coverage/* .vagrant/ -.vscode \ No newline at end of file +.vscode +.history \ No newline at end of file diff --git a/core/lib/runner.js b/core/lib/runner.js index 95825999c7..c7267281f5 100644 --- a/core/lib/runner.js +++ b/core/lib/runner.js @@ -25,6 +25,8 @@ const Engines = { socketio: {} }; +let contextVars; + JSCK.Draft4 = JSCK.draft4; const schema = new JSCK.Draft4(require('./schemas/artillery_test_script.json')); @@ -120,6 +122,10 @@ function runner(script, payload, options, callback) { } ); + if (script.before) { + handleBeforeRequests(script, runnableScript, runnerEngines, ee); + } + // // load plugins: // @@ -244,71 +250,72 @@ function runner(script, payload, options, callback) { } function run(script, ee, options, runState) { - let intermediate = Stats.create(); - let aggregate = []; + let intermediate = Stats.create(); + let aggregate = []; - let phaser = createPhaser(script.config.phases); - phaser.on('arrival', function() { - runScenario(script, intermediate, runState); - }); - phaser.on('phaseStarted', function(spec) { - ee.emit('phaseStarted', spec); - }); - phaser.on('phaseCompleted', function(spec) { - ee.emit('phaseCompleted', spec); - }); - phaser.on('done', function() { - debug('All phases launched'); + let phaser = createPhaser(script.config.phases); + phaser.on('arrival', function() { + runScenario(script, intermediate, runState); + }); + phaser.on('phaseStarted', function(spec) { + ee.emit('phaseStarted', spec); + }); + phaser.on('phaseCompleted', function(spec) { + ee.emit('phaseCompleted', spec); + }); + phaser.on('done', function() { + debug('All phases launched'); - const doneYet = setInterval(function checkIfDone() { - if (runState.pendingScenarios === 0) { - if (runState.pendingRequests !== 0) { - debug('DONE. Pending requests: %s', runState.pendingRequests); - } + const doneYet = setInterval(function checkIfDone() { + if (runState.pendingScenarios === 0) { + if (runState.pendingRequests !== 0) { + debug('DONE. Pending requests: %s', runState.pendingRequests); + } - clearInterval(doneYet); - clearInterval(periodicStatsTimer); + clearInterval(doneYet); + clearInterval(periodicStatsTimer); - sendStats(); + sendStats(); - intermediate.free(); + intermediate.free(); - let aggregateReport = Stats.combine(aggregate).report(); - return ee.emit('done', aggregateReport); - } else { - debug('Pending requests: %s', runState.pendingRequests); - debug('Pending scenarios: %s', runState.pendingScenarios); - } - }, 500); - }); + let aggregateReport = Stats.combine(aggregate).report(); + return ee.emit('done', aggregateReport); + } else { + debug('Pending requests: %s', runState.pendingRequests); + debug('Pending scenarios: %s', runState.pendingScenarios); + } + }, 500); + }); - const periodicStatsTimer = setInterval(sendStats, options.periodicStats * 1000); + const periodicStatsTimer = setInterval(sendStats, options.periodicStats * 1000); - function sendStats() { - aggregate.push(intermediate.clone()); - intermediate._concurrency = runState.pendingScenarios; - intermediate._pendingRequests = runState.pendingRequests; - ee.emit('stats', intermediate.clone()); - intermediate.reset(); - } + function sendStats() { + intermediate._concurrency = runState.pendingScenarios; + intermediate._pendingRequests = runState.pendingRequests; + ee.emit('stats', intermediate.clone()); + delete intermediate._entries; + aggregate.push(intermediate.clone()); + intermediate.reset(); + } - phaser.run(); + phaser.run(); } function runScenario(script, intermediate, runState) { - const start = process.hrtime(); - - // - // Compile scenarios if needed - // - if (!runState.compiledScenarios) { - _.each(script.scenarios, function(scenario) { - if (!scenario.weight) { - scenario.weight = 1; - } - }); + const start = process.hrtime(); + + // + // Compile scenarios if needed + // + if (!runState.compiledScenarios) { + _.each(script.scenarios, function(scenario) { + if (!scenario.weight) { + scenario.weight = 1; + } + }); - runState.picker = wl(script.scenarios); + runState.picker = wl(script.scenarios); runState.scenarioEvents = new EventEmitter(); runState.scenarioEvents.on('counter', function(name, value) { @@ -330,74 +337,83 @@ function runScenario(script, intermediate, runState) { runState.scenarioEvents.on('request', function() { intermediate.newRequest(); - runState.pendingRequests++; - }); - runState.scenarioEvents.on('match', function() { - intermediate.addMatch(); - }); - runState.scenarioEvents.on('response', function(delta, code, uid) { - intermediate.completedRequest(); - intermediate.addLatency(delta); - intermediate.addCode(code); + runState.pendingRequests++; + }); + runState.scenarioEvents.on('match', function() { + intermediate.addMatch(); + }); + runState.scenarioEvents.on('response', function(delta, code, uid) { + intermediate.completedRequest(); + intermediate.addLatency(delta); + intermediate.addCode(code); - let entry = [Date.now(), uid, delta, code]; - intermediate.addEntry(entry); + let entry = [Date.now(), uid, delta, code]; - runState.pendingRequests--; - }); + intermediate.addEntry(entry); - runState.compiledScenarios = _.map( - script.scenarios, - function(scenarioSpec) { - const name = scenarioSpec.engine || 'http'; - const engine = runState.engines.find((e) => e.__name === name); - return engine.createScenario(scenarioSpec, runState.scenarioEvents); - } - ); - } + runState.pendingRequests--; + }); + + runState.compiledScenarios = _.map( + script.scenarios, + function(scenarioSpec) { + const name = scenarioSpec.engine || 'http'; + const engine = runState.engines.find((e) => e.__name === name); + return engine.createScenario(scenarioSpec, runState.scenarioEvents); + } + ); + } - let i = runState.picker()[0]; + let i = runState.picker()[0]; - debug('picking scenario %s (%s) weight = %s', + debug('picking scenario %s (%s) weight = %s', i, script.scenarios[i].name, script.scenarios[i].weight); - intermediate.newScenario(script.scenarios[i].name || i); - - const scenarioStartedAt = process.hrtime(); - const scenarioContext = createContext(script); - const finish = process.hrtime(start); - const runScenarioDelta = (finish[0] * 1e9) + finish[1]; - debugPerf('runScenarioDelta: %s', Math.round(runScenarioDelta / 1e6 * 100) / 100); - runState.compiledScenarios[i](scenarioContext, function(err, context) { - runState.pendingScenarios--; - if (err) { - debug(err); - } else { - const scenarioFinishedAt = process.hrtime(scenarioStartedAt); - const delta = (scenarioFinishedAt[0] * 1e9) + scenarioFinishedAt[1]; - intermediate.addScenarioLatency(delta); - intermediate.completedScenario(); - } - }); + intermediate.newScenario(script.scenarios[i].name || i); + + const scenarioStartedAt = process.hrtime(); + const scenarioContext = createContext(script); + const finish = process.hrtime(start); + const runScenarioDelta = (finish[0] * 1e9) + finish[1]; + debugPerf('runScenarioDelta: %s', Math.round(runScenarioDelta / 1e6 * 100) / 100); + runState.compiledScenarios[i](scenarioContext, function(err, context) { + runState.pendingScenarios--; + if (err) { + debug(err); + } else { + const scenarioFinishedAt = process.hrtime(scenarioStartedAt); + const delta = (scenarioFinishedAt[0] * 1e9) + scenarioFinishedAt[1]; + intermediate.addScenarioLatency(delta); + intermediate.completedScenario(); + } + }); } /** - * Create initial context for a scenario. - */ +* Create initial context for a scenario. +*/ function createContext(script) { - const INITIAL_CONTEXT = { - vars: { - target: script.config.target, - $environment: script._environment - }, - funcs: { - $randomNumber: $randomNumber, - $randomString: $randomString - } + let initialContext = {}; + if (contextVars) { + initialContext = { + vars: contextVars + }; + } else { + initialContext = { + vars: { + target: script.config.target, + $environment: script._environment + } + }; + } + initialContext.funcs = { + $randomNumber: $randomNumber, + $randomString: $randomString }; - let result = _.cloneDeep(INITIAL_CONTEXT); + + let result = _.cloneDeep(initialContext); // // variables from payloads @@ -434,9 +450,30 @@ function createContext(script) { // Generator functions for template strings: // function $randomNumber(min, max) { - return _.random(min, max); + return _.random(min, max); } function $randomString(length) { - return Math.random().toString(36).substr(2, length); + return Math.random().toString(36).substr(2, length); +} + +function handleBeforeRequests(script, runnableScript, runnerEngines, testEvents) { + let ee = new EventEmitter(); + ee.on('request', function() { + testEvents.emit('beforeTestRequest'); + }); + ee.on('error', function(error) { + testEvents.emit('beforeTestError', error); + }); + let name = runnableScript.before.engine || 'http'; + let engine = runnerEngines.find((e) => e.__name === name); + let beforeTestScenario = engine.createScenario(runnableScript.before, ee); + let beforeTestContext = createContext(script); + beforeTestScenario(beforeTestContext, function(err, context) { + if (err) { + debug(err); + } else { + contextVars = context.vars; + } + }); } diff --git a/core/lib/schemas/artillery_test_script.json b/core/lib/schemas/artillery_test_script.json index ef0264f881..85a98c1472 100644 --- a/core/lib/schemas/artillery_test_script.json +++ b/core/lib/schemas/artillery_test_script.json @@ -31,6 +31,18 @@ } } }, + "before": { + "type": "object", + "properties": { + "flow": { + "type": "array", + "items": { + "type": "object" + } + } + }, + "required": ["flow"] + }, "scenarios": { "type": "array" } diff --git a/core/lib/stats2.js b/core/lib/stats2.js index aaf168999e..f23381206f 100644 --- a/core/lib/stats2.js +++ b/core/lib/stats2.js @@ -26,9 +26,6 @@ function create() { function combine(statsObjects) { let result = create(); L.each(statsObjects, function(stats) { - L.each(stats._entries, function(entry) { - result._entries.push(entry); - }); L.each(stats._latencies, function(latency) { result._latencies.push(latency); }); @@ -169,9 +166,7 @@ Stats.prototype.report = function() { result.scenariosCompleted = this._completedScenarios; result.requestsCompleted = this._completedRequests; - let latencies = L.map(this._entries, (e) => { - return e[2]; - }); + let latencies = this._latencies; result.latency = { min: round(L.min(latencies) / 1e6, 1), diff --git a/package-lock.json b/package-lock.json index 160859f1b2..2223deb812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -818,6 +818,21 @@ "type-is": "~1.6.15" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -1400,9 +1415,9 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" }, @@ -1865,12 +1880,27 @@ "xml-escape": "~1.0.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "espree": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/espree/-/espree-2.2.5.tgz", "integrity": "sha1-32kbkxCIlAKuspzAZnCMVmkLhUs=", "dev": true }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "strip-json-comments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", @@ -1987,6 +2017,21 @@ "vary": "~1.1.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -2073,6 +2118,23 @@ "parseurl": "~1.3.2", "statuses": "~1.4.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "for-each": { @@ -3954,6 +4016,15 @@ "statuses": "~1.4.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index 768faf3d22..840c0d71cb 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,13 @@ "cheerio": "^1.0.0-rc.2", "commander": "2.9.0", "csv-parse": "^0.1.4", - "debug": "^2.2.0", + "debug": "^3.1.0", "deep-equal": "^1.0.1", "esprima": "^4.0.0", "filtrex": "^0.5.4", "js-yaml": "^3.10.0", "jsck": "^0.3.2", - "lodash": "^4.17.2", + "lodash": "^4.17.10", "moment": "^2.22.1", "nanotimer": "^0.3.15", "opn": "^5.3.0", diff --git a/test/core/scripts/before_test.json b/test/core/scripts/before_test.json new file mode 100644 index 0000000000..40b9ce1b46 --- /dev/null +++ b/test/core/scripts/before_test.json @@ -0,0 +1,49 @@ +{ + "config": { + "target": "http://127.0.0.1:3003", + "phases": [ + { "duration": 10, "arrivalRate": 1 } + ], + "payload": { + "fields": ["species", "name"] + }, + "ensure": { + "p95": 300 + }, + "variables": { + "jsonPathExpr": ["$.id"] + } + }, + "before": { + "flow": [ + {"post": + { + "url": "/pets", + "json": {"name": "Guiness", "species": "Dog"}, + "capture": [{ + "json": "{{{ jsonPathExpr }}}", + "as": "id" + }, { + "json": "$.doesnotexist", + "transform": "this.doesnotexist.toUpperCase()", + "as": "doesnotexist" + }, { + "regexp": ".+", + "as": "id2" + }] + } + } + ] + }, + "scenarios": [ + { + "name": "Get the same pet in every scenario, as the pet was created once before the test started. ", + "flow": [ + {"get": { + "url": "/pets/{{ id }}", + "match": {"json": "$.name", "value": "Guiness"} + }} + ] + } + ] +} diff --git a/test/core/test_capture.js b/test/core/test_capture.js index be2b13897b..a11e6d030a 100644 --- a/test/core/test_capture.js +++ b/test/core/test_capture.js @@ -60,6 +60,36 @@ test('Capture - JSON', (t) => { }); }); +test('Capture before test - JSON', (t) => { + const fn = path.resolve(__dirname, './scripts/before_test.json'); + const script = require(fn); + const data = fs.readFileSync(path.join(__dirname, 'pets.csv')); + csv(data, function(err, parsedData) { + if (err) { + t.fail(err); + } + let beforeRequest = 0; + + runner(script, parsedData, {}).then(function(ee) { + ee.on('beforeTestRequest', function(){ + beforeRequest++; + }); + ee.on('done', function(report) { + let c200 = report.codes[200]; + let expectedAmountRequests = script.config.phases[0].duration * script.config.phases[0].arrivalRate; + t.assert(c200 === expectedAmountRequests, + 'There should be ' + expectedAmountRequests + ' requests'); + t.assert(report.matches === expectedAmountRequests, 'All requests should have the same match'); + t.assert(beforeRequest === 1, + 'There should be only one request before test starts'); + t.end(); + }); + + ee.run(); + }); + }); +}); + test('Capture - XML', (t) => { if (!xmlCapture) { console.log('artillery-xml-capture does not seem to be installed, skipping XML capture test.'); diff --git a/test/core/test_reuse.js b/test/core/test_reuse.js index 5f246cd219..3e4a276564 100644 --- a/test/core/test_reuse.js +++ b/test/core/test_reuse.js @@ -26,6 +26,9 @@ test('reuse', function(t) { } } expected *= weightedFlowLengths; + ee.on('beforeTestRequest', function(){ + t.assert('should preform before requests in the \'before requests\' test', script === 'before requests'); + }); ee.on('stats', function(stats) { intermediate.push(stats.report()); }); @@ -47,15 +50,16 @@ test('reuse', function(t) { report.latencies.length === expected ); if (first) { - let last = report.latencies.length - 1; + let lastIntermediate = intermediate.length - 1; + let last = intermediate[lastIntermediate].latencies.length - 1; first = false; - lastLatency = report.latencies[last][0]; + lastLatency = intermediate[lastIntermediate].latencies[last]; ee.run(); } else { t.assert( 'first latency of second aggregate should be after ' + 'the last latency of the first aggregate', - lastLatency <= report.latencies[0][0] + lastLatency <= intermediate[0].latencies[0] ); t.end(); } From ba3823de580c24c7b750474d0e084666363dc795 Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Thu, 5 Jul 2018 21:23:01 +0300 Subject: [PATCH 2/3] before test --- core/lib/runner.js | 264 +++++++++++++++++++++++---------------------- 1 file changed, 136 insertions(+), 128 deletions(-) diff --git a/core/lib/runner.js b/core/lib/runner.js index c7267281f5..87aa3fd5fd 100644 --- a/core/lib/runner.js +++ b/core/lib/runner.js @@ -42,7 +42,7 @@ function validate(script) { return validation; } -function runner(script, payload, options, callback) { +async function runner(script, payload, options, callback) { let opts = _.assign({ periodicStats: script.config.statsInterval || 10, mode: script.config.mode || 'uniform' @@ -122,131 +122,131 @@ function runner(script, payload, options, callback) { } ); - if (script.before) { - handleBeforeRequests(script, runnableScript, runnerEngines, ee); - } - // - // load plugins: - // - let runnerPlugins = []; - let requirePaths = []; + return handleBeforeRequests(script, runnableScript, runnerEngines, ee) + .then(function(){ + // + // load plugins: + // + let runnerPlugins = []; + let requirePaths = []; - let pro = null; - if (tryResolve('artillery-pro')) { - pro = require('artillery-pro'); - requirePaths = requirePaths.concat(pro.getPluginPath()); - } else { - debug('Artillery Pro is not installed.'); - } - - requirePaths.push(''); + let pro = null; + if (tryResolve('artillery-pro')) { + pro = require('artillery-pro'); + requirePaths = requirePaths.concat(pro.getPluginPath()); + } else { + debug('Artillery Pro is not installed.'); + } - if (process.env.ARTILLERY_PLUGIN_PATH) { - requirePaths = requirePaths.concat(process.env.ARTILLERY_PLUGIN_PATH.split(':')); - } + requirePaths.push(''); - debug('require paths: ', requirePaths); + if (process.env.ARTILLERY_PLUGIN_PATH) { + requirePaths = requirePaths.concat(process.env.ARTILLERY_PLUGIN_PATH.split(':')); + } - runnableScript.config.plugins = runnableScript.config.plugins || {}; + debug('require paths: ', requirePaths); - if (process.env.ARTILLERY_PLUGINS) { - let additionalPlugins = {}; - try { - additionalPlugins = JSON.parse(process.env.ARTILLERY_PLUGINS); - } catch (ignoreErr) { - debug(ignoreErr); - } - runnableScript.config.plugins = Object.assign( - runnableScript.config.plugins, - additionalPlugins); - } + runnableScript.config.plugins = runnableScript.config.plugins || {}; - _.each(runnableScript.config.plugins, function tryToLoadPlugin(pluginConfig, pluginName) { - let pluginConfigScope = pluginConfig.scope || runnableScript.config.pluginsScope; - let pluginPrefix = pluginConfigScope ? pluginConfigScope : 'artillery-plugin-'; - let requireString = pluginPrefix + pluginName; - let Plugin, plugin; - - requirePaths.forEach(function(rp) { - try { - Plugin = require(path.join(rp, requireString)); - if (typeof Plugin === 'function') { - // Plugin interface v1 - plugin = new Plugin(runnableScript.config, ee); - plugin.__name = pluginName; - } else if (typeof Plugin === 'object' && typeof Plugin.Plugin === 'function') { - // Plugin interface 2+ - plugin = new Plugin.Plugin(runnableScript, ee, options); - plugin.__name = pluginName; + if (process.env.ARTILLERY_PLUGINS) { + let additionalPlugins = {}; + try { + additionalPlugins = JSON.parse(process.env.ARTILLERY_PLUGINS); + } catch (ignoreErr) { + debug(ignoreErr); + } + runnableScript.config.plugins = Object.assign( + runnableScript.config.plugins, + additionalPlugins); } - } catch (err) { - debug(err); - } - }); - if (!Plugin || !plugin) { - console.log( - 'WARNING: plugin %s specified but module %s could not be loaded', - pluginName, - requireString); - warnings.plugins[pluginName] = { - message: 'Could not load' - }; - } else { - debug('Plugin %s loaded from %s', pluginName, requireString); - runnerPlugins.push(plugin); - } - }); - - const promise = new Promise(function(resolve, reject) { - ee.run = function() { - let runState = { - pendingScenarios: 0, - pendingRequests: 0, - compiledScenarios: null, - scenarioEvents: null, - picker: undefined, - plugins: runnerPlugins, - engines: runnerEngines - }; - debug('run() with: %j', runnableScript); - run(runnableScript, ee, opts, runState); - }; - - ee.stop = function (done) { - // allow plugins to cleanup - A.eachSeries( - runnerPlugins, - function(plugin, next) { - if (plugin.cleanup) { - plugin.cleanup(function(err) { - if (err) { + _.each(runnableScript.config.plugins, function tryToLoadPlugin(pluginConfig, pluginName) { + let pluginConfigScope = pluginConfig.scope || runnableScript.config.pluginsScope; + let pluginPrefix = pluginConfigScope ? pluginConfigScope : 'artillery-plugin-'; + let requireString = pluginPrefix + pluginName; + let Plugin, plugin; + + requirePaths.forEach(function(rp) { + try { + Plugin = require(path.join(rp, requireString)); + if (typeof Plugin === 'function') { + // Plugin interface v1 + plugin = new Plugin(runnableScript.config, ee); + plugin.__name = pluginName; + } else if (typeof Plugin === 'object' && typeof Plugin.Plugin === 'function') { + // Plugin interface 2+ + plugin = new Plugin.Plugin(runnableScript, ee, options); + plugin.__name = pluginName; + } + } catch (err) { debug(err); - } - return next(); + } }); - } else { - return next(); - } - }, - function(err) { - return done(err); + + if (!Plugin || !plugin) { + console.log( + 'WARNING: plugin %s specified but module %s could not be loaded', + pluginName, + requireString); + warnings.plugins[pluginName] = { + message: 'Could not load' + }; + } else { + debug('Plugin %s loaded from %s', pluginName, requireString); + runnerPlugins.push(plugin); + } }); - }; - // FIXME: Warnings should be returned from this function instead along with - // the event emitter. That will be a breaking change. - ee.warnings = warnings; + const promise = new Promise(function(resolve, reject) { + ee.run = function() { + let runState = { + pendingScenarios: 0, + pendingRequests: 0, + compiledScenarios: null, + scenarioEvents: null, + picker: undefined, + plugins: runnerPlugins, + engines: runnerEngines + }; + debug('run() with: %j', runnableScript); + run(runnableScript, ee, opts, runState); + }; + + ee.stop = function (done) { + // allow plugins to cleanup + A.eachSeries( + runnerPlugins, + function(plugin, next) { + if (plugin.cleanup) { + plugin.cleanup(function(err) { + if (err) { + debug(err); + } + return next(); + }); + } else { + return next(); + } + }, + function(err) { + return done(err); + }); + }; - resolve(ee); - }); + // FIXME: Warnings should be returned from this function instead along with + // the event emitter. That will be a breaking change. + ee.warnings = warnings; - if (callback && typeof callback === 'function') { - promise.then(callback.bind(null, null), callback); - } + resolve(ee); + }); + + if (callback && typeof callback === 'function') { + promise.then(callback.bind(null, null), callback); + } - return promise; + return promise; + }); } function run(script, ee, options, runState) { @@ -458,22 +458,30 @@ function $randomString(length) { } function handleBeforeRequests(script, runnableScript, runnerEngines, testEvents) { - let ee = new EventEmitter(); - ee.on('request', function() { - testEvents.emit('beforeTestRequest'); - }); - ee.on('error', function(error) { - testEvents.emit('beforeTestError', error); - }); - let name = runnableScript.before.engine || 'http'; - let engine = runnerEngines.find((e) => e.__name === name); - let beforeTestScenario = engine.createScenario(runnableScript.before, ee); - let beforeTestContext = createContext(script); - beforeTestScenario(beforeTestContext, function(err, context) { - if (err) { - debug(err); - } else { - contextVars = context.vars; - } - }); + return new Promise(function(resolve, reject){ + if (script.before) { + let ee = new EventEmitter(); + ee.on('request', function() { + testEvents.emit('beforeTestRequest'); + }); + ee.on('error', function(error) { + testEvents.emit('beforeTestError', error); + }); + let name = runnableScript.before.engine || 'http'; + let engine = runnerEngines.find((e) => e.__name === name); + let beforeTestScenario = engine.createScenario(runnableScript.before, ee); + let beforeTestContext = createContext(script); + beforeTestScenario(beforeTestContext, function(err, context) { + if (err) { + debug(err); + return reject(err); + } else { + contextVars = context.vars; + return resolve(); + } + }); + } else { + return resolve(); + } + }); } From 73758c6aa525aea8dfe8ad0c0302439a8783263b Mon Sep 17 00:00:00 2001 From: Dina Yakovlev Date: Thu, 5 Jul 2018 21:26:47 +0300 Subject: [PATCH 3/3] before test --- core/lib/runner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/runner.js b/core/lib/runner.js index 87aa3fd5fd..75af684e66 100644 --- a/core/lib/runner.js +++ b/core/lib/runner.js @@ -42,7 +42,7 @@ function validate(script) { return validation; } -async function runner(script, payload, options, callback) { +function runner(script, payload, options, callback) { let opts = _.assign({ periodicStats: script.config.statsInterval || 10, mode: script.config.mode || 'uniform'