Skip to content

Commit

Permalink
Allow to configure a maximum time for threads when using iterations
Browse files Browse the repository at this point in the history
  • Loading branch information
rabelenda-abstracta committed Dec 4, 2023
1 parent 5ed5aac commit 03f2385
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private void checkRampNotAfterIterations() {
}

private boolean isLastStageHoldingForIterations() {
return !stages.isEmpty() && getLastStage().duration() == null;
return !stages.isEmpty() && getLastStage().iterations() != null;
}

private Stage getLastStage() {
Expand Down Expand Up @@ -295,6 +295,24 @@ private void checkIterationsPreConditions() {
}
}

/**
* Specifies to stop thread group if iterations take more than specified duration.
* <p>
* <b>Note:</b> This method should only be used after specifying iterations.
*
* @param duration specifies a maximum duration for thread execution when threads are iterating.
* @return the thread group for further configuration or usage.
* @since 1.24
*/
public DslDefaultThreadGroup upTo(Duration duration) {
if (stages.isEmpty() || getLastStage().iterations() == null) {
throw new IllegalStateException("Configuring an upTo duration is only supported after "
+ "configuring a hold for iterations.");
}
getLastStage().duration(duration);
return this;
}

/**
* simply combines {@link #rampTo(int, Duration)} and {@link #holdFor(Duration)} which are usually
* used in combination.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ public AbstractThreadGroup buildThreadGroup() {
if (ZERO.equals(firstStage.threadCount())) {
delay = firstStage.duration();
} else {
rampUpPeriod = firstStage.duration();
threads = firstStage.threadCount();
iterations = firstStage.iterations();
if (firstStage.iterations() == null) {
rampUpPeriod = firstStage.duration();
} else {
duration = firstStage.duration();
}
}
iterations = firstStage.iterations();
if (stages.size() > 1) {
Stage secondStage = stages.get(1);
threads = secondStage.threadCount();
Expand Down Expand Up @@ -110,12 +114,14 @@ private ThreadGroup buildSimpleThreadGroupFrom(Object threads, Object iterations
setIntProperty(ret, ThreadGroup.RAMP_TIME, rampUpPeriod == null ? Duration.ZERO : rampUpPeriod);
LoopController loopController = new LoopController();
ret.setSamplerController(loopController);
if (duration != null) {
if (iterations == null) {
loopController.setLoops(-1);
setLongProperty(ret, ThreadGroup.DURATION, duration);
} else {
setIntProperty(loopController, LoopController.LOOPS, iterations);
}
if (duration != null) {
setLongProperty(ret, ThreadGroup.DURATION, duration);
}
if (delay != null) {
setLongProperty(ret, ThreadGroup.DELAY, delay);
}
Expand Down Expand Up @@ -165,34 +171,35 @@ public MethodCall buildMethodCall(MethodCallContext context) {
MethodParam duration = testElement.durationParam(ThreadGroup.DURATION);
MethodParam delay = testElement.durationParam(ThreadGroup.DELAY);
MethodParam iterations = testElement.intParam(
ThreadGroup.MAIN_CONTROLLER + "/" + LoopController.LOOPS);
ThreadGroup.MAIN_CONTROLLER + "/" + LoopController.LOOPS, -1);
if (threads instanceof IntParam && duration instanceof DurationParam
&& iterations instanceof IntParam && isDefaultOrZeroDuration(rampTime)
&& isDefaultOrZeroDuration(delay)) {
&& isDefaultOrZeroDuration(delay) && (isDefaultOrZeroDuration(duration)
|| iterations.isDefault())) {
return buildMethodCall(name, threads,
isDefaultOrZeroDuration(duration) ? iterations : duration,
new ChildrenParam<>(ThreadGroupChild[].class));
} else {
if (!(threads instanceof IntParam) || !(rampTime instanceof DurationParam)
|| !(duration instanceof DurationParam)) {
threads = new StringParam(threads.getExpression());
rampTime = new StringParam(rampTime.getExpression());
duration = new StringParam(duration.getExpression());
}
MethodCall ret = buildMethodCall(name);
if (!delay.isDefault()) {
if (!isDefaultOrZeroDuration(delay)) {
ret.chain("holdFor", delay);
}
if (!isDefaultOrZeroDuration(duration)) {
duration = buildDurationParam(duration, rampTime, ret);
if (!(threads instanceof IntParam) || !(rampTime instanceof DurationParam)
|| !(duration instanceof DurationParam)) {
threads = new StringParam(threads.getExpression());
rampTime = new StringParam(rampTime.getExpression());
duration = new StringParam(duration.getExpression());
if (!iterations.isDefault() || isDefaultOrZeroDuration(duration)) {
ret.chain("rampTo", threads, rampTime)
.chain("holdIterating", !iterations.isDefault() ? iterations : new IntParam(-1));
if (!isDefaultOrZeroDuration(duration)) {
duration = buildDurationParam(duration, rampTime, ret);
ret.chain("upTo", duration);
}
ret.chain("rampToAndHold", threads, rampTime, duration);
} else {
if (!(threads instanceof IntParam) || !(rampTime instanceof DurationParam)) {
threads = new StringParam(threads.getExpression());
rampTime = new StringParam(rampTime.getExpression());
}
ret.chain("rampTo", threads, rampTime)
.chain("holdIterating", iterations);
duration = buildDurationParam(duration, rampTime, ret);
ret.chain("rampToAndHold", threads, rampTime, duration);
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Stage {
private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");

private final Object threadCount;
private final Object duration;
private Object duration;
private final Object iterations;

public Stage(Object threadCount, Object duration, Object iterations) {
Expand All @@ -25,6 +25,10 @@ public Object threadCount() {
return threadCount;
}

public void duration(Duration val) {
this.duration = val;
}

public Object duration() {
return duration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class DslDefaultThreadGroupTest {
private static final int THREAD_COUNT = 3;

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithThreadsAndIterations() {
public void shouldBuildSimpleThreadGroupWhenThreadsAndIterations() {
assertThat(new DslDefaultThreadGroup(null, THREAD_COUNT, ITERATIONS,
Collections.emptyList()).buildThreadGroup())
.isEqualTo(buildSimpleThreadGroup(THREAD_COUNT, ITERATIONS, 0, 0, 0));
Expand All @@ -56,11 +56,13 @@ private TestElement buildSimpleThreadGroupFromObjects(Object threads, Object ite
LoopController loopController = new LoopController();
ret.setSamplerController(loopController);
Long zero = 0L;
if (durationSecs != null && !zero.equals(durationSecs)) {
if (iterations != null) {
setProperty(loopController, LoopController.LOOPS, iterations);
} else {
loopController.setLoops(-1);
}
if (durationSecs != null && !zero.equals(durationSecs)) {
setProperty(ret, ThreadGroup.DURATION, durationSecs);
} else {
setProperty(loopController, LoopController.LOOPS, iterations);
}
if (delaySecs != null && !zero.equals(delaySecs)) {
setProperty(ret, ThreadGroup.DELAY, delaySecs);
Expand Down Expand Up @@ -112,7 +114,7 @@ private static Map<String, Object> propsFrom(TestElement elem) {
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithHoldRampAndIterations() {
public void shouldBuildSimpleThreadGroupWhenHoldRampAndIterations() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.holdFor(Duration.ofSeconds(DURATION1_SECONDS))
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION2_SECONDS))
Expand All @@ -123,7 +125,7 @@ public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithHoldRampAndItera
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithRampAndIterations() {
public void shouldBuildSimpleThreadGroupWhenRampAndIterations() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION1_SECONDS))
.holdIterating(ITERATIONS)
Expand All @@ -132,7 +134,7 @@ public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithRampAndIteration
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithHoldAndRampWithZeroDurationAndIterations() {
public void shouldBuildSimpleThreadGroupWhenHoldAndRampWithZeroDurationAndIterations() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.holdFor(Duration.ofSeconds(DURATION1_SECONDS))
.rampTo(THREAD_COUNT, Duration.ZERO)
Expand All @@ -142,7 +144,7 @@ public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithHoldAndRampWithZ
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithRampWithZeroDurationAndIterations() {
public void shouldBuildSimpleThreadGroupWhenRampWithZeroDurationAndIterations() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.rampTo(THREAD_COUNT, Duration.ZERO)
.holdIterating(ITERATIONS)
Expand All @@ -151,31 +153,22 @@ public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithRampWithZeroDura
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithHoldAndRamp() {
public void shouldBuildSimpleThreadGroupWhenHoldAndRamp() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.holdFor(Duration.ofSeconds(DURATION1_SECONDS))
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION2_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION2_SECONDS, DURATION2_SECONDS,
buildSimpleThreadGroup(THREAD_COUNT, -1, DURATION2_SECONDS, DURATION2_SECONDS,
DURATION1_SECONDS));
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithRamp() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION1_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION1_SECONDS, DURATION1_SECONDS, 0));
}

@Test
public void shouldBuildSimpleThreadGroupWhenBuildTestElementWithThreadsAndDuration() {
public void shouldBuildSimpleThreadGroupWhenThreadsAndDuration() {
assertThatThreadGroup(
new DslDefaultThreadGroup(null, THREAD_COUNT, Duration.ofSeconds(DURATION1_SECONDS),
Collections.emptyList()).buildThreadGroup())
.isEqualTo(buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION1_SECONDS, 0, 0));
.isEqualTo(buildSimpleThreadGroup(THREAD_COUNT, -1, DURATION1_SECONDS, 0, 0));
}

@Test
Expand All @@ -185,7 +178,7 @@ public void shouldBuildSimpleThreadGroupWhenHoldAndRampAndHoldDuration() {
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION2_SECONDS))
.holdFor(Duration.ofSeconds(DURATION3_SECONDS))
.buildThreadGroup())
.isEqualTo(buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION2_SECONDS + DURATION3_SECONDS,
.isEqualTo(buildSimpleThreadGroup(THREAD_COUNT, -1, DURATION2_SECONDS + DURATION3_SECONDS,
DURATION2_SECONDS, DURATION1_SECONDS));
}

Expand All @@ -196,7 +189,7 @@ public void shouldBuildSimpleThreadGroupWhenRampAndHoldDuration() {
.holdFor(Duration.ofSeconds(DURATION2_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION1_SECONDS + DURATION2_SECONDS,
buildSimpleThreadGroup(THREAD_COUNT, -1, DURATION1_SECONDS + DURATION2_SECONDS,
DURATION1_SECONDS, 0));
}

Expand All @@ -206,7 +199,7 @@ public void shouldBuildSimpleThreadGroupWhenRamp() {
.rampTo(THREAD_COUNT, Duration.ofSeconds(DURATION1_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(THREAD_COUNT, 0, DURATION1_SECONDS, DURATION1_SECONDS, 0));
buildSimpleThreadGroup(THREAD_COUNT, -1, DURATION1_SECONDS, DURATION1_SECONDS, 0));
}

@Test
Expand All @@ -216,6 +209,31 @@ public void shouldBuildSimpleThreadGroupWhenNoStages() {
.isEqualTo(buildSimpleThreadGroup(1, 1, 0, 0, 0));
}

@Test
public void shouldBuildSimpleThreadGroupWhenRampAndIterationsAndDuration() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.rampTo(1, Duration.ofSeconds(DURATION1_SECONDS))
.holdIterating(1)
.upTo(Duration.ofSeconds(DURATION2_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(1, 1, DURATION1_SECONDS + DURATION2_SECONDS, DURATION1_SECONDS,
0));
}

@Test
public void shouldBuildSimpleThreadGroupWhenDelayAndRampAndIterationsAndDuration() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
.holdFor(Duration.ofSeconds(DURATION3_SECONDS))
.rampTo(1, Duration.ofSeconds(DURATION1_SECONDS))
.holdIterating(1)
.upTo(Duration.ofSeconds(DURATION2_SECONDS))
.buildThreadGroup())
.isEqualTo(
buildSimpleThreadGroup(1, 1, DURATION1_SECONDS + DURATION2_SECONDS, DURATION1_SECONDS,
DURATION3_SECONDS));
}

@Test
public void shouldThrowIllegalStateExceptionWhenIterationAfterIteration() {
assertThrows(IllegalStateException.class, () -> new DslDefaultThreadGroup(null)
Expand Down Expand Up @@ -246,6 +264,20 @@ public void shouldThrowIllegalArgumentExceptionWhenRampWithNegativeThreads() {
.rampTo(-1, Duration.ZERO));
}

@Test
public void shouldThrowIllegalStateExceptionWhenUpToAfterRamp() {
assertThrows(IllegalStateException.class, () -> new DslDefaultThreadGroup(null)
.rampTo(THREAD_COUNT, Duration.ZERO)
.upTo(Duration.ZERO));
}

@Test
public void shouldThrowIllegalStateExceptionWhenUpToAfterHoldFor() {
assertThrows(IllegalStateException.class, () -> new DslDefaultThreadGroup(null)
.rampToAndHold(THREAD_COUNT, Duration.ZERO, Duration.ZERO)
.upTo(Duration.ZERO));
}

@Test
public void shouldBuildUltimateThreadGroupWhenRampUpAndDown() {
assertThatThreadGroup(new DslDefaultThreadGroup(null)
Expand Down Expand Up @@ -479,7 +511,7 @@ public DslTestPlan simpleThreadGroupWithDelayAndDuration() {
);
}

public DslTestPlan simpleThreadGroupWithRampDelayAndIterations() {
public DslTestPlan simpleThreadGroupWithDelayAndRampAndIterations() {
return testPlan(
threadGroup()
.holdFor(Duration.ofSeconds(1))
Expand All @@ -492,7 +524,7 @@ public DslTestPlan simpleThreadGroupWithRampDelayAndIterations() {
);
}

public DslTestPlan simpleThreadGroupWithRampDelayAndDuration() {
public DslTestPlan simpleThreadGroupWithDelayAndRampAndDuration() {
return testPlan(
threadGroup()
.holdFor(Duration.ofSeconds(1))
Expand All @@ -504,7 +536,7 @@ public DslTestPlan simpleThreadGroupWithRampDelayAndDuration() {
);
}

public DslTestPlan simpleNamedThreadGroupWithRampAndDelay() {
public DslTestPlan simpleNamedThreadGroupWithDelayAndRamp() {
return testPlan(
threadGroup("myThreads")
.holdFor(Duration.ofSeconds(1))
Expand All @@ -529,6 +561,19 @@ public DslTestPlan simpleThreadGroupWithRampAndInfiniteIterations() {
);
}

public DslTestPlan simplThreadGroupWithIterationsAndUpTo() {
return testPlan(
threadGroup()
.rampTo(1, Duration.ZERO)
.holdIterating(1)
.upTo(Duration.ofSeconds(2))
.children(
httpSampler("http://localhost"),
httpSampler("http://myhost")
)
);
}

/*
this test uses as ramp duration difference of threads to avoid rounding issues in conversions
of schedules.
Expand Down

0 comments on commit 03f2385

Please sign in to comment.