-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import exec from 'k6/execution' | ||
|
||
// parseDuration parses the provided string as an Integer number | ||
// in millisecond precision | ||
function parseDuration(str) { | ||
let d = 0; | ||
let current = ''; | ||
let isNumber = function(c) { | ||
return c >= '0' && c <= '9' | ||
} | ||
|
||
for (let i = 0; i < str.length; i++) { | ||
if (isNumber(str[i]) || str[i] == '.') { | ||
current += str[i] | ||
} | ||
|
||
if (isNumber(str[i+1]) || str[i+1] == '.') { | ||
continue | ||
} | ||
|
||
let v = parseFloat(current, 10) | ||
let next = str[i+1] | ||
|
||
switch (next) { | ||
case 'd': | ||
d += v*24*60*60*1000 | ||
break; | ||
case 'h': | ||
d += v*60*60*1000 | ||
break; | ||
case 'm': | ||
if (i + 2 < str.length && str[i+2] == 's') { | ||
d += Math.trunc(v) | ||
i++ | ||
} else { | ||
d += v*60*1000 | ||
} | ||
break; | ||
case 's': | ||
d += v*1000 | ||
break; | ||
default: | ||
d += v | ||
} | ||
i++ | ||
current = '' | ||
} | ||
return d | ||
} | ||
|
||
// getCurrentStageIndex returns the computed index of the running stage. | ||
function getCurrentStageIndex() { | ||
let scenario = exec.test.options.scenarios[exec.scenario.name] | ||
if (scenario == null) { | ||
throw new Error(`the exec.test.options object doesn't contain the current scenario ${exec.scenario.name}`) | ||
} | ||
if (scenario.stages == null) { | ||
throw new Error(`only ramping-vus or ramping-arravial-rate supports stages, it is not possible get a stage index on other executors.`) | ||
} | ||
|
||
if (scenario.stages.length < 1) { | ||
throw new Error(`the current scenario ${scenario.name} doesn't contain any stage`) | ||
} | ||
|
||
let sum = 0; | ||
let elapsed = new Date() - exec.scenario.startTime | ||
for (let i = 0; i < scenario.stages.length; i++) { | ||
sum += parseDuration(scenario.stages[i].duration) | ||
if (elapsed < sum) { | ||
return i | ||
} | ||
} | ||
|
||
return scenario.stages.length-1 | ||
} | ||
|
||
// tagWithCurrentStageIndex adds a tag with a `stage` key | ||
// and the index of the current running stage as value. | ||
function tagWithCurrentStageIndex() { | ||
exec.vu.tags['stage'] = getCurrentStageIndex() | ||
} | ||
|
||
// tagWithCurrentStageProfile adds a tag with a `stage` key | ||
// and the profile (ramp-up, steady or ramp-down) computed | ||
// from the current running stage. | ||
function tagWithCurrentStageProfile() { | ||
//ramp-up when previous.target < current.target | ||
//ramp-down when previous.target > current.target | ||
//steady when prevuious.target = current.target | ||
|
||
let getStageProfile = function() { | ||
let currentIndex = getCurrentStageIndex() | ||
if (currentIndex < 1) { | ||
return 'ramp-up' | ||
} | ||
|
||
let stages = exec.test.options.scenarios[exec.scenario.name].stages | ||
let current = stages[currentIndex] | ||
let previous = stages[currentIndex-1] | ||
|
||
if (current.target > previous.target) { | ||
return 'ramp-up' | ||
} | ||
|
||
if (previous.target == current.target) { | ||
return 'steady' | ||
} | ||
|
||
return 'ramp-down' | ||
} | ||
|
||
// TODO: should we use another tag key? | ||
exec.vu.tags['stage'] = getStageProfile() | ||
} | ||
|
||
export { | ||
parseDuration, | ||
getCurrentStageIndex, | ||
tagWithCurrentStageIndex, | ||
tagWithCurrentStageProfile, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import { describe, expect } from 'https://jslib.k6.io/k6chaijs/4.3.4.0/index.js' | ||
import exec from 'k6/execution' | ||
import { sleep } from 'k6' | ||
|
||
import { | ||
parseDuration, | ||
getCurrentStageIndex, | ||
tagWithCurrentStageIndex, | ||
tagWithCurrentStageProfile, | ||
} from '../src/stages.js' | ||
|
||
|
||
export let options = { | ||
scenarios: { | ||
'parseDuration': { | ||
executor: 'shared-iterations', | ||
iterations: 1, | ||
exec: 'testParseDuration' | ||
}, | ||
'getCurrentStageIndex': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "1s10ms", target: 1 }, | ||
{ duration: "1s", target: 1 }, | ||
], | ||
exec: 'testGetCurrentStageIndex' | ||
}, | ||
'tagWithCurrentStageIndex': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "1s", target: 1 }, | ||
{ duration: "1s", target: 1 }, | ||
], | ||
exec: 'testTagWithCurrentStageIndex' | ||
}, | ||
'tagWithRampUpWhenOnlyOne': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "0.1s", target: 1 } | ||
], | ||
exec: 'testTagWithRampUpProfileWhenOnlyOne' | ||
}, | ||
'tagWithRampUp': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "0.1s", target: 1 }, | ||
{ duration: "0.1s", target: 2 } | ||
], | ||
exec: 'testTagWithRampUp' | ||
}, | ||
'tagWithSteady': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "0.1s", target: 1 }, | ||
{ duration: "0.1s", target: 1 } | ||
], | ||
exec: 'testTagWithSteady' | ||
}, | ||
'tagWithRampDown': { | ||
executor: 'ramping-vus', | ||
stages: [ | ||
{ duration: "0.1s", target: 2 }, | ||
{ duration: "0.1s", target: 1 } | ||
], | ||
exec: 'testTagWithRampDown' | ||
}, | ||
'unsupported-executor': { | ||
executor: 'shared-iterations', | ||
iterations: 1, | ||
exec: 'testUnsupportedExecutor' | ||
} | ||
} | ||
} | ||
|
||
export function testGetCurrentStageIndex() { | ||
describe('getCurrentStageIndex', () => { | ||
describe('returns the first stage when is in t0', () => { | ||
expect(getCurrentStageIndex()).to.be.equal(0) | ||
}) | ||
|
||
sleep(1.2) | ||
|
||
describe('returns the next stage if the iteration exceedes the expected duration', () => { | ||
expect(getCurrentStageIndex()).to.be.equal(1) | ||
}) | ||
|
||
sleep(1) | ||
|
||
describe('returns the last stage when the iteration exceedes the total expected duration', () => { | ||
expect(getCurrentStageIndex()).to.be.equal(1) | ||
}) | ||
}) | ||
} | ||
|
||
export function testTagWithCurrentStageIndex() { | ||
describe('tagWithCurrentStageIndex', () => { | ||
describe('tags with the current stage', () => { | ||
tagWithCurrentStageIndex() | ||
expect(exec.vu.tags['stage']).to.be.equal(`${exec.scenario.iterationInTest}`) | ||
}) | ||
|
||
// it forces to get only two iterations | ||
sleep(1) | ||
}) | ||
} | ||
|
||
export function testTagWithRampUpProfileWhenOnlyOne() { | ||
describe('tagWithCurrentStageProfile', () => { | ||
describe(`tags with ramp-up profile when only one stage`, () => { | ||
tagWithCurrentStageProfile() | ||
expect(exec.vu.tags['stage']).to.be.equal('ramp-up') | ||
}) | ||
}) | ||
} | ||
|
||
export function testTagWithRampUp() { | ||
describe('tagWithCurrentStageProfile', () => { | ||
describe(`tags with ramp-up profile when the current stage has a target greater than previous`, () => { | ||
if (exec.vu.iterationInScenario !== 1) { | ||
return | ||
} | ||
tagWithCurrentStageProfile() | ||
expect(exec.vu.tags['stage']).to.be.equal('ramp-up') | ||
}) | ||
sleep(0.1) | ||
}) | ||
} | ||
|
||
export function testTagWithSteady() { | ||
describe('tagWithCurrentStageProfile', () => { | ||
describe(`tags with steady profile when the current stage has the same target of the previous`, () => { | ||
if (exec.vu.iterationInScenario !== 1) { | ||
return | ||
} | ||
tagWithCurrentStageProfile() | ||
expect(exec.vu.tags['stage']).to.be.equal('steady') | ||
}) | ||
sleep(0.1) | ||
}) | ||
} | ||
|
||
export function testTagWithRampDown() { | ||
describe('tagWithCurrentStageProfile', () => { | ||
describe(`tags with ramp-down profile when the current stage has a target less than previous`, () => { | ||
if (exec.vu.iterationInScenario !== 1) { | ||
return | ||
} | ||
tagWithCurrentStageProfile() | ||
expect(exec.vu.tags['stage']).to.be.equal('ramp-down') | ||
}) | ||
sleep(0.1) | ||
}) | ||
} | ||
|
||
export function testUnsupportedExecutor() { | ||
describe('throw Error if the executor is not supported', () => { | ||
describe('getCurrentStageIndex', () => { | ||
expect(getCurrentStageIndex).to.throw() | ||
}) | ||
describe('tagWithCurrentStageIndex', () => { | ||
expect(tagWithCurrentStageIndex).to.throw() | ||
}) | ||
describe('tagWithCurrentStageProfile', () => { | ||
expect(tagWithCurrentStageProfile).to.throw() | ||
}) | ||
}) | ||
} | ||
|
||
export function testParseDuration() { | ||
describe('parseDuration', () => { | ||
let testcase = '1d5h31m20s9ms' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(((86400+18000+1860+20)*1000)+9) | ||
}) | ||
|
||
testcase = '5.2h' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(18720*1000) | ||
}) | ||
|
||
testcase = '1d5.2h31m20s9ms' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(((86400+18720+1860+20)*1000)+9) | ||
}) | ||
|
||
// ms is the maximum precision so this case is truncated | ||
testcase = '9.3ms' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(9) | ||
}) | ||
|
||
testcase = '1531209' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(1531209) | ||
}) | ||
|
||
testcase = '9.h1.s' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(32401*1000) | ||
}) | ||
|
||
testcase = '9.h1.s' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(32401*1000) | ||
}) | ||
|
||
testcase = '1h-1s' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(3601*1000) | ||
}) | ||
|
||
testcase = '-1h 1s' | ||
describe(testcase, () => { | ||
expect(parseDuration(testcase)).to.be.equal(3601*1000) | ||
}) | ||
}) | ||
} |