Skip to content

Commit 7dd3ba5

Browse files
committed
Run execution timeout
1 parent 9c593a1 commit 7dd3ba5

File tree

8 files changed

+139
-2
lines changed

8 files changed

+139
-2
lines changed

jbehave-core/src/main/java/org/jbehave/core/embedder/DelegatingEmbedderMonitor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.jbehave.core.failures.BatchFailures;
1212
import org.jbehave.core.model.Meta;
13+
import org.jbehave.core.model.RunDuration;
1314
import org.jbehave.core.model.Scenario;
1415
import org.jbehave.core.model.Story;
1516
import org.jbehave.core.model.StoryDuration;
@@ -163,6 +164,11 @@ public void systemPropertySet(String name, String value) {
163164
delegates.forEach(d -> d.systemPropertySet(name, value));
164165
}
165166

167+
@Override
168+
public void runTimeout(RunDuration runDuration) {
169+
delegates.forEach(d -> d.runTimeout(runDuration));
170+
}
171+
166172
@Override
167173
public void storyTimeout(Story story, StoryDuration storyDuration) {
168174
delegates.forEach(d -> d.storyTimeout(story, storyDuration));
@@ -187,4 +193,9 @@ public void usingControls(EmbedderControls embedderControls) {
187193
public void usingTimeout(String path, long timeout) {
188194
delegates.forEach(d -> d.usingTimeout(path, timeout));
189195
}
196+
197+
@Override
198+
public void usingRunTimeout(long timeout) {
199+
delegates.forEach(d -> d.usingRunTimeout(timeout));
200+
}
190201
}

jbehave-core/src/main/java/org/jbehave/core/embedder/EmbedderControls.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ public class EmbedderControls {
1616
private boolean verboseFailures = false;
1717
private boolean verboseFiltering = false;
1818
private String storyTimeouts = "300";
19+
private String runTimeout = "3000";
1920
private int threads = 1;
2021
private boolean failOnStoryTimeout = false;
22+
private boolean failOnRunTimeout = false;
2123

2224
public EmbedderControls() {
2325
}
@@ -54,10 +56,18 @@ public String storyTimeouts() {
5456
return storyTimeouts;
5557
}
5658

59+
public String runTimeout() {
60+
return runTimeout;
61+
}
62+
5763
public boolean failOnStoryTimeout() {
5864
return failOnStoryTimeout;
5965
}
6066

67+
public boolean failOnRunTimeout() {
68+
return failOnRunTimeout;
69+
}
70+
6171
public int threads() {
6272
return threads;
6373
}
@@ -102,11 +112,21 @@ public EmbedderControls useStoryTimeouts(String storyTimeouts) {
102112
return this;
103113
}
104114

115+
public EmbedderControls useRunTimeout(String runTimeout) {
116+
this.runTimeout = runTimeout;
117+
return this;
118+
}
119+
105120
public EmbedderControls doFailOnStoryTimeout(boolean failOnStoryTimeout) {
106121
this.failOnStoryTimeout = failOnStoryTimeout;
107122
return this;
108123
}
109124

125+
public EmbedderControls doFailOnRunTimeout(boolean failOnRunTimeout) {
126+
this.failOnRunTimeout = failOnRunTimeout;
127+
return this;
128+
}
129+
110130
public EmbedderControls useThreads(int threads) {
111131
this.threads = threads;
112132
return this;

jbehave-core/src/main/java/org/jbehave/core/embedder/EmbedderMonitor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.jbehave.core.failures.BatchFailures;
99
import org.jbehave.core.model.Meta;
10+
import org.jbehave.core.model.RunDuration;
1011
import org.jbehave.core.model.Scenario;
1112
import org.jbehave.core.model.Story;
1213
import org.jbehave.core.model.StoryDuration;
@@ -65,6 +66,8 @@ void mapsViewGenerationFailed(File outputDirectory, StoryMaps storyMaps, Propert
6566

6667
void systemPropertySet(String name, String value);
6768

69+
void runTimeout(RunDuration runDuration);
70+
6871
void storyTimeout(Story story, StoryDuration storyDuration);
6972

7073
void usingThreads(int threads);
@@ -75,4 +78,5 @@ void mapsViewGenerationFailed(File outputDirectory, StoryMaps storyMaps, Propert
7578

7679
void usingTimeout(String path, long timeout);
7780

81+
void usingRunTimeout(long timeout);
7882
}

jbehave-core/src/main/java/org/jbehave/core/embedder/NullEmbedderMonitor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.jbehave.core.failures.BatchFailures;
99
import org.jbehave.core.model.Meta;
10+
import org.jbehave.core.model.RunDuration;
1011
import org.jbehave.core.model.Scenario;
1112
import org.jbehave.core.model.Story;
1213
import org.jbehave.core.model.StoryDuration;
@@ -142,6 +143,11 @@ public void systemPropertySet(String name, String value) {
142143
// Do nothing by default
143144
}
144145

146+
@Override
147+
public void runTimeout(RunDuration runDuration) {
148+
// Do nothing by default
149+
}
150+
145151
@Override
146152
public void storyTimeout(Story story, StoryDuration storyDuration) {
147153
// Do nothing by default
@@ -166,4 +172,9 @@ public void usingControls(EmbedderControls embedderControls) {
166172
public void usingTimeout(String path, long timeout) {
167173
// Do nothing by default
168174
}
175+
176+
@Override
177+
public void usingRunTimeout(long timeout) {
178+
// Do nothing by default
179+
}
169180
}

jbehave-core/src/main/java/org/jbehave/core/embedder/PrintingEmbedderMonitor.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.jbehave.core.ConfigurableEmbedder;
1111
import org.jbehave.core.failures.BatchFailures;
1212
import org.jbehave.core.model.Meta;
13+
import org.jbehave.core.model.RunDuration;
1314
import org.jbehave.core.model.Scenario;
1415
import org.jbehave.core.model.Story;
1516
import org.jbehave.core.model.StoryDuration;
@@ -162,6 +163,12 @@ public void systemPropertySet(String name, String value) {
162163
print("System property '%s' set to '%s'", name, value);
163164
}
164165

166+
@Override
167+
public void runTimeout(RunDuration runDuration) {
168+
print("Run duration of %d seconds has exceeded timeout of %d seconds",
169+
runDuration.getTotalDurationOfAllStoriesInSecs(), runDuration.getTimeoutInSecs());
170+
}
171+
165172
@Override
166173
public void storyTimeout(Story story, StoryDuration storyDuration) {
167174
print("Story %s duration of %d seconds has exceeded timeout of %d seconds", story.getPath(),
@@ -188,6 +195,11 @@ public void usingTimeout(String path, long timeout) {
188195
print("Using timeout for story %s of %d secs.", path, timeout);
189196
}
190197

198+
@Override
199+
public void usingRunTimeout(long timeout) {
200+
print("Using run timeout of %d secs.", timeout);
201+
}
202+
191203
@Override
192204
public String toString() {
193205
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);

jbehave-core/src/main/java/org/jbehave/core/embedder/StoryManager.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.jbehave.core.embedder.PerformableTree.RunContext;
2121
import org.jbehave.core.embedder.StoryTimeouts.TimeoutParser;
2222
import org.jbehave.core.failures.BatchFailures;
23+
import org.jbehave.core.model.RunDuration;
2324
import org.jbehave.core.model.Story;
2425
import org.jbehave.core.model.StoryDuration;
2526
import org.jbehave.core.steps.InjectableStepsFactory;
@@ -42,6 +43,7 @@ public class StoryManager {
4243
private final Map<MetaFilter, List<Story>> excludedStories = new HashMap<>();
4344
private RunContext context;
4445
private StoryTimeouts timeouts;
46+
private RunDuration runDuration;
4547

4648
public StoryManager(Configuration configuration,
4749
InjectableStepsFactory stepsFactory,
@@ -169,6 +171,10 @@ public void waitUntilAllDoneOrFailed(RunContext context) {
169171
}
170172
boolean allDone = false;
171173
boolean started = false;
174+
175+
runDuration = new RunDuration(Long.parseLong(embedderControls.runTimeout()));
176+
this.embedderMonitor.usingRunTimeout(runDuration.getTimeoutInSecs());
177+
172178
while (!allDone || !started) {
173179
allDone = true;
174180
for (RunningStory runningStory : runningStories.values()) {
@@ -180,6 +186,8 @@ public void waitUntilAllDoneOrFailed(RunContext context) {
180186
allDone = false;
181187
StoryDuration duration = runningStory.getDuration();
182188
runningStory.updateDuration();
189+
runDuration.updateInProgressStoryDuration(story.getName(),
190+
runningStory.getDuration().getDurationInSecs());
183191
if (context.isCancelled(story)) {
184192
if (duration.cancelTimedOut()) {
185193
future.cancel(true);
@@ -190,12 +198,20 @@ public void waitUntilAllDoneOrFailed(RunContext context) {
190198
embedderMonitor.storyTimeout(story, duration);
191199
context.cancelStory(story, duration);
192200
if (embedderControls.failOnStoryTimeout()) {
193-
throw new StoryExecutionFailed(story.getPath(),
194-
new StoryTimedOut(duration));
201+
throw new StoryExecutionFailed(story.getPath(), new StoryTimedOut(duration));
202+
}
203+
continue;
204+
}
205+
if (runDuration.timedOut()) {
206+
embedderMonitor.runTimeout(runDuration);
207+
context.cancelStory(story, duration);
208+
if (embedderControls.failOnRunTimeout()) {
209+
throw new StoryExecutionFailed(story.getPath(), new RunTimedOut(duration));
195210
}
196211
continue;
197212
}
198213
} else {
214+
runDuration.completeStory(story.getName());
199215
try {
200216
ThrowableStory throwableStory = future.get();
201217
Throwable throwable = throwableStory.getThrowable();
@@ -343,6 +359,16 @@ public StoryTimedOut(StoryDuration storyDuration) {
343359

344360
}
345361

362+
@SuppressWarnings("serial")
363+
public static class RunTimedOut extends RuntimeException {
364+
365+
public RunTimedOut(StoryDuration runDuration) {
366+
super(runDuration.getDurationInSecs() + "s > "
367+
+ runDuration.getTimeoutInSecs() + "s");
368+
}
369+
370+
}
371+
346372
public static class ThrowableStory {
347373
private Story story;
348374
private Throwable throwable;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.jbehave.core.model;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class RunDuration {
7+
private final long timeoutInSecs;
8+
private final Map<String, Long> inProgressStories = new HashMap<>();
9+
private long totalDurationOfCompletedStoriesInSecs;
10+
11+
public RunDuration(long timeoutInSecs) {
12+
this.timeoutInSecs = timeoutInSecs;
13+
}
14+
15+
public long getTotalDurationOfCompletedStoriesInSecs() {
16+
return totalDurationOfCompletedStoriesInSecs;
17+
}
18+
19+
public long getTimeoutInSecs() {
20+
return timeoutInSecs;
21+
}
22+
23+
public boolean timedOut() {
24+
return timeoutInSecs != 0 && getTotalDurationOfAllStoriesInSecs() > timeoutInSecs;
25+
}
26+
27+
public void updateInProgressStoryDuration(String story, Long durationInSec) {
28+
inProgressStories.put(story, durationInSec);
29+
}
30+
31+
public void completeStory(String story) {
32+
this.totalDurationOfCompletedStoriesInSecs = totalDurationOfCompletedStoriesInSecs
33+
+ inProgressStories.get(story);
34+
inProgressStories.remove(story);
35+
}
36+
37+
public long getTotalDurationOfAllStoriesInSecs() {
38+
return totalDurationOfCompletedStoriesInSecs + inProgressStories.values().stream().mapToLong(d -> d).sum();
39+
}
40+
}

jbehave-maven-plugin/src/main/java/org/jbehave/mojo/AbstractEmbedderMojo.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.jbehave.core.failures.BatchFailures;
2525
import org.jbehave.core.io.StoryFinder;
2626
import org.jbehave.core.model.Meta;
27+
import org.jbehave.core.model.RunDuration;
2728
import org.jbehave.core.model.Scenario;
2829
import org.jbehave.core.model.Story;
2930
import org.jbehave.core.model.StoryDuration;
@@ -480,6 +481,13 @@ public void systemPropertySet(String name, String value) {
480481
getLog().info("System property '" + name + "' set to '" + value + "'");
481482
}
482483

484+
@Override
485+
public void runTimeout(RunDuration runDuration) {
486+
getLog().warn(
487+
"Run duration of " + runDuration.getTotalDurationOfAllStoriesInSecs()
488+
+ " seconds has exceeded timeout of " + runDuration.getTimeoutInSecs() + " seconds");
489+
}
490+
483491
@Override
484492
public void storyTimeout(Story story, StoryDuration storyDuration) {
485493
getLog().warn(
@@ -507,6 +515,11 @@ public void usingTimeout(String path, long timeout) {
507515
getLog().info("Using timeout for story " + path + " of " + timeout + " secs.");
508516
}
509517

518+
@Override
519+
public void usingRunTimeout(long timeout) {
520+
getLog().info("Using run timeout of " + timeout + " secs.");
521+
}
522+
510523
@Override
511524
public String toString() {
512525
return this.getClass().getSimpleName();

0 commit comments

Comments
 (0)