Coverage Report - org.jbehave.core.embedder.StoryRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
StoryRunner
100%
87/87
95%
42/44
2.364
StoryRunner$1
N/A
N/A
2.364
StoryRunner$FineSoFar
100%
13/13
70%
7/10
2.364
StoryRunner$SomethingHappened
100%
4/4
N/A
2.364
StoryRunner$State
N/A
N/A
2.364
 
 1  
 package org.jbehave.core.embedder;
 2  
 
 3  
 import java.util.HashMap;
 4  
 import java.util.List;
 5  
 import java.util.Map;
 6  
 
 7  
 import org.jbehave.core.configuration.Configuration;
 8  
 import org.jbehave.core.failures.FailureStrategy;
 9  
 import org.jbehave.core.failures.PendingStepFound;
 10  
 import org.jbehave.core.failures.PendingStepStrategy;
 11  
 import org.jbehave.core.failures.SilentlyAbsorbingFailure;
 12  
 import org.jbehave.core.model.ExamplesTable;
 13  
 import org.jbehave.core.model.GivenStories;
 14  
 import org.jbehave.core.model.GivenStory;
 15  
 import org.jbehave.core.model.Scenario;
 16  
 import org.jbehave.core.model.Story;
 17  
 import org.jbehave.core.reporters.StoryReporter;
 18  
 import org.jbehave.core.steps.CandidateSteps;
 19  
 import org.jbehave.core.steps.Step;
 20  
 import org.jbehave.core.steps.StepCollector;
 21  
 import org.jbehave.core.steps.StepCollector.Stage;
 22  
 import org.jbehave.core.steps.StepResult;
 23  
 
 24  
 /**
 25  
  * Runs a {@link Story}, given a {@link Configuration} and a list of
 26  
  * {@link CandidateSteps}, describing the results to the {@link StoryReporter}.
 27  
  * 
 28  
  * @author Elizabeth Keogh
 29  
  * @author Mauro Talevi
 30  
  * @author Paul Hammant
 31  
  */
 32  157
 public class StoryRunner {
 33  
 
 34  64
     private State state = new FineSoFar();
 35  
     private FailureStrategy currentStrategy;
 36  
     private FailureStrategy failureStrategy;
 37  
     private PendingStepStrategy pendingStepStrategy;
 38  
     private Throwable storyFailure;
 39  
     private StepCollector stepCollector;
 40  
     private StoryReporter reporter;
 41  
     private String reporterStoryPath;
 42  
 
 43  
     /**
 44  
      * Run steps before or after a collection of stories. Steps are execute only
 45  
      * <b>once</b> per collection of stories.
 46  
      * 
 47  
      * @param configuration the Configuration used to find the steps to run
 48  
      * @param candidateSteps List of CandidateSteps containing the candidate
 49  
      *            steps methods
 50  
      * @param stage the Stage
 51  
      */
 52  
     public void runBeforeOrAfterStories(Configuration configuration, List<CandidateSteps> candidateSteps, Stage stage) {
 53  2
         runStepsWhileKeepingState(configuration.stepCollector().collectBeforeOrAfterStoriesSteps(candidateSteps, stage));
 54  2
     }
 55  
     
 56  
     
 57  
     /**
 58  
      * Runs a Story with the given configuration and steps.
 59  
      * 
 60  
      * @param configuration the Configuration used to run story
 61  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 62  
      *            steps methods
 63  
      * @param story the Story to run
 64  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 65  
      *             be re-thrown.
 66  
      */
 67  
     public void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story)
 68  
             throws Throwable {
 69  12
         run(configuration, candidateSteps, story, MetaFilter.EMPTY, false, new HashMap<String, String>());
 70  11
     }
 71  
 
 72  
     /**
 73  
      * Runs a Story with the given configuration and steps, applying the given meta filter.
 74  
      * 
 75  
      * @param configuration the Configuration used to run story
 76  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 77  
      *            steps methods
 78  
      * @param story the Story to run
 79  
      * @param filter the Filter to apply to the story Meta
 80  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 81  
      *             be re-thrown.
 82  
      */
 83  
     public void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story, MetaFilter filter)
 84  
             throws Throwable {
 85  2
         run(configuration, candidateSteps, story, filter, false, new HashMap<String, String>());
 86  2
     }
 87  
     
 88  
     /**
 89  
      * Returns the parsed story from the given path
 90  
      * 
 91  
      * @param configuration the Configuration used to run story
 92  
      * @param storyPath the story path 
 93  
      * @return The parsed Story
 94  
      */
 95  
     public Story storyOfPath(Configuration configuration, String storyPath) {
 96  2
         String storyAsText = configuration.storyLoader().loadStoryAsText(storyPath);
 97  2
         return configuration.storyParser().parseStory(storyAsText, storyPath);
 98  
     }
 99  
 
 100  
     private void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story, MetaFilter filter,
 101  
             boolean givenStory, Map<String, String> storyParameters) throws Throwable {
 102  16
         stepCollector = configuration.stepCollector();
 103  16
         reporter = reporterFor(configuration, story, givenStory);
 104  16
         pendingStepStrategy = configuration.pendingStepStrategy();
 105  16
         failureStrategy = configuration.failureStrategy();
 106  
 
 107  
         
 108  16
         if (!filter.allow(story.getMeta())) {
 109  1
             reporter.storyNotAllowed(story, filter.asString());
 110  1
             return;
 111  
         }
 112  
 
 113  15
         resetFailureState(givenStory);
 114  
 
 115  15
         if (configuration.storyControls().dryRun() ){
 116  2
             reporter.dryRun();
 117  
         }
 118  
 
 119  
         // run before story steps, if any
 120  15
         reporter.beforeStory(story, givenStory);
 121  15
         runBeforeOrAfterStorySteps(candidateSteps, story, givenStory, StepCollector.Stage.BEFORE);
 122  
         
 123  
         // determine if before and after scenario steps should be run
 124  15
         boolean runBeforeAndAfterScenarioSteps = shouldRunBeforeOrAfterScenarioSteps(configuration, givenStory);
 125  
         
 126  15
         for (Scenario scenario : story.getScenarios()) {
 127  
             // scenario also inherits meta from story
 128  20
             if (!filter.allow(scenario.getMeta().inheritFrom(story.getMeta()))) {
 129  1
                 reporter.scenarioNotAllowed(scenario, filter.asString());
 130  1
                 continue;
 131  
             }
 132  19
             if ( failureOccurred() && configuration.storyControls().skipScenariosAfterFailure() ){
 133  1
                 continue;
 134  
             }
 135  18
             reporter.beforeScenario(scenario.getTitle());
 136  18
             reporter.scenarioMeta(scenario.getMeta());
 137  
 
 138  
             // run before scenario steps, if allowed
 139  18
             if (runBeforeAndAfterScenarioSteps) {
 140  17
                 runBeforeOrAfterScenarioSteps(candidateSteps, scenario, Stage.BEFORE);
 141  
             }
 142  
 
 143  
             // run given stories, if any
 144  18
             runGivenStories(configuration, candidateSteps, scenario, filter);
 145  18
             if (isParametrisedByExamples(scenario)) {
 146  
                 // run parametrised scenarios by examples
 147  1
                 runParametrisedScenariosByExamples(candidateSteps, scenario);
 148  
             } else { // run as plain old scenario
 149  17
                 runScenarioSteps(candidateSteps, scenario, storyParameters);
 150  
             }
 151  
 
 152  
             // run after scenario steps, if allowed
 153  18
             if (runBeforeAndAfterScenarioSteps) {
 154  17
                 runBeforeOrAfterScenarioSteps(candidateSteps, scenario, Stage.AFTER);
 155  
             }
 156  
 
 157  18
             reporter.afterScenario();
 158  
         }
 159  
 
 160  
         // run after story steps, if any
 161  15
         runBeforeOrAfterStorySteps(candidateSteps, story, givenStory, StepCollector.Stage.AFTER);        
 162  15
         reporter.afterStory(givenStory);
 163  
         
 164  
         // handle any failure according to strategy
 165  15
         currentStrategy.handleFailure(storyFailure);
 166  14
     }
 167  
 
 168  
     private boolean shouldRunBeforeOrAfterScenarioSteps(Configuration configuration, boolean givenStory) {
 169  15
         if (!configuration.storyControls().skipBeforeAndAfterScenarioStepsIfGivenStory()){
 170  13
             return true;
 171  
         }
 172  
         
 173  2
         return !givenStory;
 174  
     }
 175  
 
 176  
     private boolean failureOccurred() {
 177  19
         return storyFailure != null;
 178  
     }
 179  
 
 180  
     private StoryReporter reporterFor(Configuration configuration, Story story, boolean givenStory) {
 181  16
         if (givenStory) {
 182  2
             return configuration.storyReporter(reporterStoryPath);
 183  
         } else {
 184  
             // store parent story path for reporting
 185  14
             reporterStoryPath = story.getPath();
 186  14
             return configuration.storyReporter(reporterStoryPath);
 187  
         }
 188  
     }
 189  
 
 190  
     private void resetFailureState(boolean givenStory) {
 191  15
         if (givenStory) {
 192  
             // do not reset failure state for given stories
 193  2
             return;
 194  
         }
 195  13
         currentStrategy = new SilentlyAbsorbingFailure();
 196  13
         storyFailure = null;
 197  13
     }
 198  
 
 199  
     private void runGivenStories(Configuration configuration, List<CandidateSteps> candidateSteps, Scenario scenario,
 200  
             MetaFilter filter) throws Throwable {
 201  18
         GivenStories givenStories = scenario.getGivenStories();
 202  18
         if (givenStories.getPaths().size() > 0) {
 203  2
             reporter.givenStories(givenStories);
 204  2
             for (GivenStory givenStory : givenStories.getStories()) {
 205  
                 // run given story, using any parameters if provided
 206  2
                 Story story = storyOfPath(configuration, givenStory.getPath());
 207  2
                 run(configuration, candidateSteps, story, filter, true, givenStory.getParameters());
 208  2
             }
 209  
         }
 210  18
     }
 211  
 
 212  
     private boolean isParametrisedByExamples(Scenario scenario) {
 213  18
         return scenario.getExamplesTable().getRowCount() > 0 && !scenario.getGivenStories().requireParameters();
 214  
     }
 215  
 
 216  
     private void runParametrisedScenariosByExamples(List<CandidateSteps> candidateSteps, Scenario scenario) {
 217  1
         ExamplesTable table = scenario.getExamplesTable();
 218  1
         reporter.beforeExamples(scenario.getSteps(), table);
 219  1
         for (Map<String, String> scenarioParameters : table.getRows()) {
 220  1
             reporter.example(scenarioParameters);
 221  1
             runScenarioSteps(candidateSteps, scenario, scenarioParameters);
 222  
         }
 223  1
         reporter.afterExamples();
 224  1
     }
 225  
 
 226  
     private void runBeforeOrAfterStorySteps(List<CandidateSteps> candidateSteps, Story story, boolean givenStory, Stage stage) {
 227  30
         runStepsWhileKeepingState(stepCollector.collectBeforeOrAfterStorySteps(candidateSteps, story, stage, givenStory));
 228  30
     }
 229  
 
 230  
     private void runBeforeOrAfterScenarioSteps(List<CandidateSteps> candidateSteps, Scenario scenario, Stage stage) {
 231  34
         runStepsWhileKeepingState(stepCollector.collectBeforeOrAfterScenarioSteps(candidateSteps, stage));
 232  34
     }
 233  
 
 234  
     private void runScenarioSteps(List<CandidateSteps> candidateSteps, Scenario scenario, Map<String, String> scenarioParameters) {
 235  18
         runStepsWhileKeepingState(stepCollector.collectScenarioSteps(candidateSteps, scenario, scenarioParameters));
 236  18
     }
 237  
 
 238  
     private void runStepsWhileKeepingState(List<Step> steps) {
 239  84
         if (steps == null || steps.size() == 0){
 240  60
             return;
 241  
         }
 242  24
         state = new FineSoFar();
 243  24
         for (Step step : steps) {
 244  33
             state.run(step);
 245  
         }
 246  24
     }
 247  
 
 248  
     private interface State {
 249  
         void run(Step step);
 250  
     }
 251  
 
 252  176
     private final class FineSoFar implements State {
 253  
 
 254  
         public void run(Step step) {
 255  
 
 256  28
             StepResult result = step.perform();
 257  28
             result.describeTo(reporter);
 258  28
             Throwable scenarioFailure = result.getFailure();
 259  28
             if (scenarioFailure != null) {
 260  10
                 state = new SomethingHappened();
 261  10
                 storyFailure = mostImportantOf(storyFailure, scenarioFailure);
 262  10
                 currentStrategy = strategyFor(storyFailure);
 263  
             }
 264  28
         }
 265  
 
 266  
         private Throwable mostImportantOf(Throwable failure1, Throwable failure2) {
 267  10
             return failure1 == null ? failure2 : failure1 instanceof PendingStepFound ? (failure2 == null ? failure1
 268  
                     : failure2) : failure1;
 269  
         }
 270  
 
 271  
         private FailureStrategy strategyFor(Throwable failure) {
 272  10
             if (failure instanceof PendingStepFound) {
 273  4
                 return pendingStepStrategy;
 274  
             } else {
 275  6
                 return failureStrategy;
 276  
             }
 277  
         }
 278  
     }
 279  
 
 280  20
     private class SomethingHappened implements State {
 281  
         public void run(Step step) {
 282  5
             StepResult result = step.doNotPerform();
 283  5
             result.describeTo(reporter);
 284  5
         }
 285  
     }
 286  
 
 287  
     @Override
 288  
     public String toString() {
 289  1
         return this.getClass().getSimpleName();
 290  
     }
 291  
 
 292  
 }