Skip to content

Commit

Permalink
Add the option to create request that run once before test starts
Browse files Browse the repository at this point in the history
  • Loading branch information
users-search-assignment committed Jul 5, 2018
1 parent 6777418 commit ffebdd7
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 127 deletions.
9 changes: 5 additions & 4 deletions core/lib/engine_http.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,17 +436,18 @@ HttpEngine.prototype.step = function step(requestSpec, ee, opts) {

request(requestParams, maybeCallback)
.on('request', function(req) {
debugRequests("request start: %s", req.path);
debugRequests('request start: %s', req.path);
ee.emit('request');

const startedAt = process.hrtime();

req.on('response', function updateLatency(res) {
let code = res.statusCode;
let path = res.req.method + ' ' + res.req.path;
const endedAt = process.hrtime(startedAt);
let delta = (endedAt[0] * 1e9) + endedAt[1];
debugRequests("request end: %s", req.path);
ee.emit('response', delta, code, context._uid);
debugRequests('request end: %s', req.path);
ee.emit('response', delta, code, context._uid, path);
});
}).on('end', function() {
context._successCount++;
Expand All @@ -458,7 +459,7 @@ HttpEngine.prototype.step = function step(requestSpec, ee, opts) {
debug(err);

// Run onError hooks and end the scenario
runOnErrorHooks(onErrorHandlers, config.processor, err, requestParams, context, ee, function(asyncErr) {
runOnErrorHooks(onErrorHandlers, config.processor, err, requestParams, context, ee, function() {
let errCode = err.code || err.message;
ee.emit('error', errCode);
return callback(err, context);
Expand Down
280 changes: 160 additions & 120 deletions core/lib/runner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

Expand All @@ -20,24 +20,26 @@ const engineUtil = require('./engine_util');
const wl = require('./weighted-pick');

const Engines = {
http: {},
ws: {},
socketio: {}
http: {},
ws: {},
socketio: {}
};

let contextVars;

JSCK.Draft4 = JSCK.draft4;

const schema = new JSCK.Draft4(require('./schemas/artillery_test_script.json'));

module.exports = {
runner: runner,
validate: validate,
stats: Stats
runner: runner,
validate: validate,
stats: Stats
};

function validate(script) {
let validation = schema.validate(script);
return validation;
let validation = schema.validate(script);
return validation;
}

function runner(script, payload, options, callback) {
Expand Down Expand Up @@ -120,6 +122,10 @@ function runner(script, payload, options, callback) {
}
);

if (script.before) {
handleBeforeRequests(script, runnableScript, runnerEngines, ee);
}

//
// load plugins:
//
Expand Down Expand Up @@ -244,72 +250,76 @@ function runner(script, payload, options, callback) {
}

function run(script, ee, options, runState) {
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');

const doneYet = setInterval(function checkIfDone() {
if (runState.pendingScenarios === 0) {
if (runState.pendingRequests !== 0) {
debug('DONE. Pending requests: %s', runState.pendingRequests);
let intermediate = Stats.create();
let aggregate = [];

let phaser = createPhaser(script.config.phases);
phaser.on('arrival', function() {
if (runState.pendingRequests >= (process.env.CONCURRENCY_LIMIT || 250)) {
intermediate._scenariosAvoided++;
} else {
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');

clearInterval(doneYet);
clearInterval(periodicStatsTimer);
const doneYet = setInterval(function checkIfDone() {
if (runState.pendingScenarios === 0) {
if (runState.pendingRequests !== 0) {
debug('DONE. Pending requests: %s', runState.pendingRequests);
}

sendStats();
clearInterval(doneYet);
clearInterval(periodicStatsTimer);

intermediate.free();
sendStats();

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);
});
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);
});

const periodicStatsTimer = setInterval(sendStats, options.periodicStats * 1000);
const periodicStatsTimer = setInterval(sendStats, options.periodicStats * 1000);

function sendStats() {
intermediate._concurrency = runState.pendingScenarios;
intermediate._pendingRequests = runState.pendingRequests;
ee.emit('stats', intermediate.clone());
delete intermediate._entries;
aggregate.push(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) {
Expand All @@ -331,74 +341,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
Expand Down Expand Up @@ -435,9 +454,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;
}
});
}
Loading

0 comments on commit ffebdd7

Please sign in to comment.