Skip to content

Commit

Permalink
Implemented method of restarting a story when the RestartingStoryFailure
Browse files Browse the repository at this point in the history
exception is thrown.
  • Loading branch information
bbarke committed Nov 3, 2014
1 parent efc58b1 commit c6c7020
Show file tree
Hide file tree
Showing 18 changed files with 129 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jbehave.core.failures.PendingStepFound;
import org.jbehave.core.failures.PendingStepStrategy;
import org.jbehave.core.failures.RestartingScenarioFailure;
import org.jbehave.core.failures.RestartingStoryFailure;
import org.jbehave.core.failures.UUIDExceptionWrapper;
import org.jbehave.core.model.ExamplesTable;
import org.jbehave.core.model.GivenStories;
Expand Down Expand Up @@ -49,6 +50,7 @@ public class StoryRunner {
private ThreadLocal<FailureStrategy> failureStrategy = new ThreadLocal<FailureStrategy>();
private ThreadLocal<PendingStepStrategy> pendingStepStrategy = new ThreadLocal<PendingStepStrategy>();
private ThreadLocal<UUIDExceptionWrapper> storyFailure = new ThreadLocal<UUIDExceptionWrapper>();
private ThreadLocal<UUIDExceptionWrapper> beforeAfterStoryFailure = new ThreadLocal<UUIDExceptionWrapper>();
private ThreadLocal<StoryReporter> reporter = new ThreadLocal<StoryReporter>();
private ThreadLocal<String> reporterStoryPath = new ThreadLocal<String>();
private ThreadLocal<State> storiesState = new ThreadLocal<State>();
Expand Down Expand Up @@ -214,19 +216,50 @@ public Story storyOfText(Configuration configuration, String storyAsText, String
public void cancelStory(Story story, StoryDuration storyDuration) {
cancelledStories.put(story, storyDuration);
}

/**
* Helper method to determine if the cause of a story failure contains {@link RestartingStoryFailure}
* @param cause - the stacktrace to check for {@link RestartingStoryFailure}
* @return true if found, false otherwise
*/
public boolean hasRestartingStoryException(Throwable cause)
{
while(cause != null) {
if (cause instanceof RestartingStoryFailure) {
return true;
}
cause = cause.getCause();
}
return false;
}

private void run(RunContext context, Story story, Map<String, String> storyParameters) throws Throwable {
try {

boolean restartingStory = false;

try {
runCancellable(context, story, storyParameters);
} catch (Throwable e) {
if (cancelledStories.containsKey(story)) {
reporter.get().storyCancelled(story, cancelledStories.get(story));
reporter.get().afterScenario();
reporter.get().afterStory(context.givenStory);
}
throw e;
} finally {
if (!context.givenStory() && reporter.get() instanceof ConcurrentStoryReporter) {


if (cancelledStories.containsKey(story)) {
reporter.get().storyCancelled(story, cancelledStories.get(story));
reporter.get().afterScenario();
reporter.get().afterStory(context.givenStory);
}

// Restart entire story if determined it needs it
if (hasRestartingStoryException(e) || hasRestartingStoryException(beforeAfterStoryFailure.get())) {
//this is not getting logged when running in multi-threaded mode
reporter.get().restartedStory(story, e);
restartingStory = true;
run(context, story, storyParameters);
} else {
throw e;
}

}finally {
if (!context.givenStory() && reporter.get() instanceof ConcurrentStoryReporter && !restartingStory) {
((ConcurrentStoryReporter) reporter.get()).invokeDelayed();
}
}
Expand Down Expand Up @@ -373,6 +406,7 @@ private void resetStoryFailure(RunContext context) {
}
currentStrategy.set(context.configuration().failureStrategy());
storyFailure.set(null);
beforeAfterStoryFailure.set(null);
}

private void runGivenStories(GivenStories givenStories, Map<String, String> parameters, RunContext context) throws Throwable {
Expand Down Expand Up @@ -569,6 +603,12 @@ public SomethingHappened(UUIDExceptionWrapper scenarioFailure) {
public State run(Step step) {
StepResult result = step.doNotPerform(scenarioFailure);
result.describeTo(reporter.get());

if (result.getFailure() != null) {
//persist failures to see if we need to restart the story
beforeAfterStoryFailure.set(result.getFailure());
}

return this;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.jbehave.core.failures;

/**
* Runtime exception thrown to indicate that the story should be restarted.
*/
@SuppressWarnings("serial")
public class RestartingStoryFailure extends RuntimeException {

public RestartingStoryFailure(String message) {
super(message);
}

public RestartingStoryFailure(String message, Throwable cause) {
super(message, cause);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class ConcurrentStoryReporter implements StoryReporter {
private static Method dryRun;
private static Method pendingMethods;
private static Method restarted;
private static Method restartedStory;

static {
try {
Expand Down Expand Up @@ -76,6 +77,7 @@ public class ConcurrentStoryReporter implements StoryReporter {
dryRun = StoryReporter.class.getMethod("dryRun");
pendingMethods = StoryReporter.class.getMethod("pendingMethods", List.class);
restarted = StoryReporter.class.getMethod("restarted", String.class, Throwable.class);
restartedStory = StoryReporter.class.getMethod("restartedStory", Story.class, Throwable.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -300,7 +302,7 @@ public void pendingMethods(List<String> methods) {
}

}

public void restarted(String step, Throwable cause) {
crossReferencing.restarted(step, cause);
if (multiThreading) {
Expand All @@ -309,6 +311,15 @@ public void restarted(String step, Throwable cause) {
delegate.restarted(step, cause);
}
}

public void restartedStory(Story story, Throwable cause){
crossReferencing.restartedStory(story, cause);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(restartedStory, story, cause));
} else {
delegate.restartedStory(story, cause);
}
}

public void storyCancelled(Story story, StoryDuration storyDuration) {
crossReferencing.storyCancelled(story, storyDuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ public void restarted(String step, Throwable cause) {
reporter.restarted(step, cause);
}
}

public void restartedStory(Story story, Throwable cause) {
for (StoryReporter reporter : delegates) {
reporter.restartedStory(story, cause);
}

}

public void storyCancelled(Story story, StoryDuration storyDuration) {
for (StoryReporter reporter : delegates) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private static Properties defaultHtmlPatterns() {
patterns.setProperty("notPerformed", "<div class=\"step notPerformed\">{0} <span class=\"keyword notPerformed\">({1})</span></div>\n");
patterns.setProperty("failed", "<div class=\"step failed\">{0} <span class=\"keyword failed\">({1})</span><br/><span class=\"message failed\">{2}</span></div>\n");
patterns.setProperty("restarted", "<div class=\"step restarted\">{0} <span class=\"message restarted\">{1}</span></div>\n");
patterns.setProperty("restartedStory", "<div class=\"story restarted\">{0} <span class=\"message restarted\">{1}</span></div>\n");
patterns.setProperty("outcomesTableStart", "<div class=\"outcomes\"><table>\n");
patterns.setProperty("outcomesTableHeadStart", "<thead>\n<tr>\n");
patterns.setProperty("outcomesTableHeadCell", "<th>{0}</th>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ public void pendingMethods(List<String> methods) {

public void restarted(String step, Throwable cause) {
}

public void restartedStory(Story story, Throwable cause) {
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ public void pendingMethods(List<String> methods) {

public void restarted(String step, Throwable cause) {
}

public void restartedStory(Story story, Throwable cause) {
}

private void add(String event) {
Integer count = data.get(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ public void pendingMethods(List<String> methods) {
public void restarted(String step, Throwable cause) {
print(format("restarted", "{0} {1}\n", step, cause.getMessage()));
}

public void restartedStory(Story story, Throwable cause) {
print(format("restartedStory", "{0} {1}\n", story.getPath(), cause.getMessage()));
}

/**
* Formats event output by key, usually equal to the method name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public void report(){

public void restarted(String step, Throwable cause) {
}

public void restartedStory(Story story, Throwable cause) {
}

public void storyCancelled(Story story, StoryDuration storyDuration) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ public void pendingMethods(List<String> methods) {
public void restarted(String step, Throwable cause) {
delegate.restarted(step, cause);
}

public void restartedStory(Story story, Throwable cause) {
delegate.restartedStory(story, cause);
}

public void storyCancelled(Story story, StoryDuration storyDuration) {
delegate.storyCancelled(story, storyDuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public interface StoryReporter {
void failedOutcomes(String step, OutcomesTable table);

void restarted(String step, Throwable cause);

void restartedStory(Story story, Throwable cause);

void dryRun();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ public void pendingMethods(List<String> methods) {
public void restarted(String step, Throwable cause) {
this.outputScenario.addStep(new OutputRestart(step, cause.getMessage()));
}

public void restartedStory(Story story, Throwable cause) {
this.outputScenario.addStep(new OutputRestart(story.getName(), cause.getMessage()));
}

public void storyCancelled(Story story, StoryDuration storyDuration) {
this.outputStory.cancelled = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ private static Properties defaultPatterns() {
patterns.setProperty("notPerformed", "{0} ({1})\n");
patterns.setProperty("failed", "{0} ({1})\n({2})\n");
patterns.setProperty("restarted", "{0} ({1})\n");
patterns.setProperty("restartedStory", "{0} ({1})\n");
patterns.setProperty("outcomesTableStart", "");
patterns.setProperty("outcomesTableHeadStart", "|");
patterns.setProperty("outcomesTableHeadCell", "{0}|");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private static Properties defaultHtmlPatterns() {
patterns.setProperty("notPerformed", "<step outcome=\"notPerformed\" keyword=\"{1}\">{0}</step>\n");
patterns.setProperty("failed", "<step outcome=\"failed\" keyword=\"{1}\">{0}<failure>{2}</failure></step>\n");
patterns.setProperty("restarted", "<step outcome=\"restarted\">{0}<reason>{1}</reason></step>\n");
patterns.setProperty("restartedStory", "<story outcome=\"restartedStory\">{0}<reason>{1}</reason></story>\n");
patterns.setProperty("outcomesTableStart", "<outcomes>\n");
patterns.setProperty("outcomesTableHeadStart", "<fields>");
patterns.setProperty("outcomesTableHeadCell", "<field>{0}</field>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public void shouldReportEventsToTxtOutput() {
+ "When I write special chars in parameter <>&\"\n"
+ "When I write two parameters ,,, and &&&\n"
+ "Then I should... - try again (hi)\n"
+ "/path/to/story (Restarted Story)\n"
+ "STORY CANCELLED (DURATION 2 s)\n"
+ "Then I should have a balance of $30 (PENDING)\n"
+ "Then I should have $20 (NOT PERFORMED)\n"
Expand Down Expand Up @@ -183,6 +184,7 @@ public PrintStream createPrintStream() {
+ "<div class=\"step successful\">When I write special chars in parameter <span class=\"step parameter\">&lt;&gt;&amp;&quot;</span></div>\n"
+ "<div class=\"step successful\">When I write two parameters <span class=\"step parameter\">,,,</span> and <span class=\"step parameter\">&amp;&amp;&amp;</span></div>\n"
+ "<div class=\"step restarted\">Then I should... - try again <span class=\"message restarted\">hi</span></div>\n"
+ "<div class=\"story restarted\">/path/to/story <span class=\"message restarted\">Restarted Story</span></div>\n"
+ "<div class=\"cancelled\">STORY CANCELLED (DURATION 2 s)</div>\n"
+ "<div class=\"step pending\">Then I should have a balance of $30 <span class=\"keyword pending\">(PENDING)</span></div>\n"
+ "<div class=\"step notPerformed\">Then I should have $20 <span class=\"keyword notPerformed\">(NOT PERFORMED)</span></div>\n"
Expand Down Expand Up @@ -321,6 +323,7 @@ public PrintStream createPrintStream() {
+ "<div class=\"step successful\">When I write special chars in parameter <span class=\"step parameter\">&lt;&gt;&amp;&quot;</span></div>\n"
+ "<div class=\"step successful\">When I write two parameters <span class=\"step parameter\">,,,</span> and <span class=\"step parameter\">&amp;&amp;&amp;</span></div>\n"
+ "<div class=\"step restarted\">Then I should... - try again <span class=\"message restarted\">hi</span></div>\n"
+ "<div class=\"story restarted\">/path/to/story <span class=\"message restarted\">Restarted Story</span></div>\n"
+ "<div class=\"cancelled\">STORY CANCELLED (DURATION 2 s)</div>\n"
+ "<div class=\"step pending\">Then I should have a balance of $30 <span class=\"keyword pending\">(PENDING)</span></div>\n"
+ "<div class=\"step notPerformed\">Then I should have $20 <span class=\"keyword notPerformed\">(NOT PERFORMED)</span></div>\n"
Expand Down Expand Up @@ -425,6 +428,7 @@ public PrintStream createPrintStream() {
+ "<step outcome=\"successful\">When I write special chars in parameter <parameter>&lt;&gt;&amp;&quot;</parameter></step>\n"
+ "<step outcome=\"successful\">When I write two parameters <parameter>,,,</parameter> and <parameter>&amp;&amp;&amp;</parameter></step>\n"
+ "<step outcome=\"restarted\">Then I should... - try again<reason>hi</reason></step>\n"
+ "<story outcome=\"restartedStory\">/path/to/story<reason>Restarted Story</reason></story>\n"
+ "<cancelled keyword=\"STORY CANCELLED\" durationKeyword=\"DURATION\" durationInSecs=\"2\"/>\n"
+ "<step outcome=\"pending\" keyword=\"PENDING\">Then I should have a balance of $30</step>\n"
+ "<step outcome=\"notPerformed\" keyword=\"NOT PERFORMED\">Then I should have $20</step>\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.hamcrest.core.IsEqual;
import org.jbehave.core.failures.RestartingScenarioFailure;
import org.jbehave.core.failures.RestartingStoryFailure;
import org.jbehave.core.failures.UUIDExceptionWrapper;
import org.jbehave.core.i18n.LocalizedKeywords;
import org.jbehave.core.model.Description;
Expand Down Expand Up @@ -53,6 +54,7 @@ static void narrateAnInterestingStory(StoryReporter reporter,
+ " and "
+ StepCreator.PARAMETER_VALUE_START+"&&&"+StepCreator.PARAMETER_VALUE_END);
reporter.restarted("Then I should... - try again", new RestartingScenarioFailure("hi"));
reporter.restartedStory(story, new RestartingStoryFailure("Restarted Story"));
reporter.storyCancelled(story, new StoryDuration(1).setDurationInSecs(2));
if (withFailure) {
reporter.failed("Then I should have a balance of $30", new Exception("Expected <30> got <25>"));
Expand Down
10 changes: 6 additions & 4 deletions jbehave-core/src/test/resources/story.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@

<div class="step hi">Then I should... - try again </div>

<div class="step Restarted Story">/path/to/story </div>

<div class="step failed">Then I should have a balance of $30 <span class="keyword failed">(FAILED)</span></div>
<pre class="failure">java.lang.Exception: Expected &lt;30&gt; got &lt;25&gt;
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:58)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:60)
at org.jbehave.core.reporters.TemplateableOutputBehaviour.shouldReportEventsToHtmlOutput(TemplateableOutputBehaviour.java:24)
(reflection-invoke)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
Expand All @@ -50,7 +52,7 @@
<div class="step failed">Then I don't return loan <span class="keyword failed">(FAILED)</span></div>
<pre class="failure">org.jbehave.core.model.OutcomesTable$OutcomesFailed
at org.jbehave.core.model.OutcomesTable.verify(OutcomesTable.java:55)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:69)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:71)
at org.jbehave.core.reporters.TemplateableOutputBehaviour.shouldReportEventsToHtmlOutput(TemplateableOutputBehaviour.java:24)
(reflection-invoke)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
Expand All @@ -76,7 +78,7 @@

<pre class="failure">org.jbehave.core.model.OutcomesTable$OutcomesFailed
at org.jbehave.core.model.OutcomesTable.verify(OutcomesTable.java:55)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:69)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:71)
at org.jbehave.core.reporters.TemplateableOutputBehaviour.shouldReportEventsToHtmlOutput(TemplateableOutputBehaviour.java:24)
(reflection-invoke)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
Expand Down Expand Up @@ -116,7 +118,7 @@ <h3 class="example">Example: {money=$50, to=Paul}</h3>

<div class="step failed">Then I should have a balance of $30 <span class="keyword failed">(FAILED)</span></div>
<pre class="failure">java.lang.Exception: Expected &lt;30&gt; got &lt;25&gt;
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:84)
at org.jbehave.core.reporters.StoryNarrator.narrateAnInterestingStory(StoryNarrator.java:86)
at org.jbehave.core.reporters.TemplateableOutputBehaviour.shouldReportEventsToHtmlOutput(TemplateableOutputBehaviour.java:24)
(reflection-invoke)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
Expand Down

0 comments on commit c6c7020

Please sign in to comment.