Coverage Report - org.jbehave.core.embedder.StoryRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
StoryRunner
90%
168/185
81%
75/92
2.231
StoryRunner$1
N/A
N/A
2.231
StoryRunner$FilterContext
100%
14/14
83%
5/6
2.231
StoryRunner$FineSoFar
100%
14/14
70%
7/10
2.231
StoryRunner$RunContext
100%
31/31
N/A
2.231
StoryRunner$SomethingHappened
100%
6/6
N/A
2.231
StoryRunner$State
N/A
N/A
2.231
 
 1  
 package org.jbehave.core.embedder;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.HashMap;
 5  
 import java.util.List;
 6  
 import java.util.Map;
 7  
 
 8  
 import org.jbehave.core.annotations.ScenarioType;
 9  
 import org.jbehave.core.configuration.Configuration;
 10  
 import org.jbehave.core.failures.FailureStrategy;
 11  
 import org.jbehave.core.failures.PendingStepFound;
 12  
 import org.jbehave.core.failures.PendingStepStrategy;
 13  
 import org.jbehave.core.failures.RestartingScenarioFailure;
 14  
 import org.jbehave.core.failures.UUIDExceptionWrapper;
 15  
 import org.jbehave.core.model.ExamplesTable;
 16  
 import org.jbehave.core.model.GivenStories;
 17  
 import org.jbehave.core.model.GivenStory;
 18  
 import org.jbehave.core.model.Meta;
 19  
 import org.jbehave.core.model.Scenario;
 20  
 import org.jbehave.core.model.Story;
 21  
 import org.jbehave.core.reporters.ConcurrentStoryReporter;
 22  
 import org.jbehave.core.reporters.StoryReporter;
 23  
 import org.jbehave.core.steps.CandidateSteps;
 24  
 import org.jbehave.core.steps.InjectableStepsFactory;
 25  
 import org.jbehave.core.steps.PendingStepMethodGenerator;
 26  
 import org.jbehave.core.steps.ProvidedStepsFactory;
 27  
 import org.jbehave.core.steps.Step;
 28  
 import org.jbehave.core.steps.StepCollector.Stage;
 29  
 import org.jbehave.core.steps.StepCreator.PendingStep;
 30  
 import org.jbehave.core.steps.StepResult;
 31  
 
 32  
 import static org.codehaus.plexus.util.StringUtils.capitalizeFirstLetter;
 33  
 
 34  
 /**
 35  
  * Runs a {@link Story}, given a {@link Configuration} and a list of
 36  
  * {@link CandidateSteps}, describing the results to the {@link StoryReporter}.
 37  
  * 
 38  
  * @author Elizabeth Keogh
 39  
  * @author Mauro Talevi
 40  
  * @author Paul Hammant
 41  
  */
 42  212
 public class StoryRunner {
 43  
 
 44  76
     private ThreadLocal<FailureStrategy> currentStrategy = new ThreadLocal<FailureStrategy>();
 45  76
     private ThreadLocal<FailureStrategy> failureStrategy = new ThreadLocal<FailureStrategy>();
 46  76
     private ThreadLocal<PendingStepStrategy> pendingStepStrategy = new ThreadLocal<PendingStepStrategy>();
 47  76
     private ThreadLocal<UUIDExceptionWrapper> storyFailure = new ThreadLocal<UUIDExceptionWrapper>();
 48  76
     private ThreadLocal<StoryReporter> reporter = new ThreadLocal<StoryReporter>();
 49  76
     private ThreadLocal<String> reporterStoryPath = new ThreadLocal<String>();
 50  76
     private ThreadLocal<State> storiesState = new ThreadLocal<State>();
 51  
 
 52  
     /**
 53  
      * Run steps before or after a collection of stories. Steps are execute only
 54  
      * <b>once</b> per collection of stories.
 55  
      * 
 56  
      * @param configuration the Configuration used to find the steps to run
 57  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 58  
      *            steps methods
 59  
      * @param stage the Stage
 60  
      * @return The State after running the steps
 61  
      */
 62  
     public State runBeforeOrAfterStories(Configuration configuration, List<CandidateSteps> candidateSteps, Stage stage) {
 63  5
         String storyPath = capitalizeFirstLetter(stage.name().toLowerCase()) + "Stories";
 64  5
         reporter.set(configuration.storyReporter(storyPath));
 65  5
         reporter.get().beforeStory(new Story(storyPath), false);
 66  5
         RunContext context = new RunContext(configuration, candidateSteps, storyPath, MetaFilter.EMPTY);
 67  5
         if (stage == Stage.BEFORE) {
 68  3
             resetStoryFailure(context);
 69  
         }
 70  5
         if (stage == Stage.AFTER && storiesState.get() != null) {
 71  2
             context.stateIs(storiesState.get());
 72  
         }
 73  5
         runStepsWhileKeepingState(context,
 74  
                 configuration.stepCollector().collectBeforeOrAfterStoriesSteps(context.candidateSteps(), stage));
 75  5
         reporter.get().afterStory(false);
 76  5
         storiesState.set(context.state());
 77  
         // handle any after stories failure according to strategy
 78  5
         if (stage == Stage.AFTER) {
 79  
             try {
 80  2
                 handleStoryFailureByStrategy();
 81  1
             } catch (Throwable e) {
 82  1
                 return new SomethingHappened(storyFailure.get());
 83  
             } finally {
 84  2
                 if (reporter.get() instanceof ConcurrentStoryReporter) {
 85  0
                     ((ConcurrentStoryReporter) reporter.get()).invokeDelayed();
 86  
                 }
 87  
             }
 88  
         }
 89  4
         return context.state();
 90  
     }
 91  
 
 92  
     /**
 93  
      * Runs a Story with the given configuration and steps.
 94  
      * 
 95  
      * @param configuration the Configuration used to run story
 96  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 97  
      *            steps methods
 98  
      * @param story the Story to run
 99  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 100  
      *             be re-thrown.
 101  
      */
 102  
     public void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story) throws Throwable {
 103  15
         run(configuration, candidateSteps, story, MetaFilter.EMPTY);
 104  14
     }
 105  
 
 106  
     /**
 107  
      * Runs a Story with the given configuration and steps, applying the given
 108  
      * meta filter.
 109  
      * 
 110  
      * @param configuration the Configuration used to run story
 111  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 112  
      *            steps methods
 113  
      * @param story the Story to run
 114  
      * @param filter the Filter to apply to the story Meta
 115  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 116  
      *             be re-thrown.
 117  
      */
 118  
     public void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story, MetaFilter filter)
 119  
             throws Throwable {
 120  19
         run(configuration, candidateSteps, story, filter, null);
 121  18
     }
 122  
 
 123  
     /**
 124  
      * Runs a Story with the given configuration and steps, applying the given
 125  
      * meta filter, and staring from given state.
 126  
      * 
 127  
      * @param configuration the Configuration used to run story
 128  
      * @param candidateSteps the List of CandidateSteps containing the candidate
 129  
      *            steps methods
 130  
      * @param story the Story to run
 131  
      * @param filter the Filter to apply to the story Meta
 132  
      * @param beforeStories the State before running any of the stories, if not
 133  
      *            <code>null</code>
 134  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 135  
      *             be re-thrown.
 136  
      */
 137  
     public void run(Configuration configuration, List<CandidateSteps> candidateSteps, Story story, MetaFilter filter,
 138  
             State beforeStories) throws Throwable {
 139  19
         run(configuration, new ProvidedStepsFactory(candidateSteps), story, filter, beforeStories);
 140  18
     }
 141  
 
 142  
     /**
 143  
      * Runs a Story with the given steps factory, applying the given meta
 144  
      * filter, and staring from given state.
 145  
      * 
 146  
      * @param configuration the Configuration used to run story
 147  
      * @param stepsFactory the InjectableStepsFactory used to created the
 148  
      *            candidate steps methods
 149  
      * @param story the Story to run
 150  
      * @param filter the Filter to apply to the story Meta
 151  
      * @param beforeStories the State before running any of the stories, if not
 152  
      *            <code>null</code>
 153  
      * 
 154  
      * @throws Throwable if failures occurred and FailureStrategy dictates it to
 155  
      *             be re-thrown.
 156  
      */
 157  
     public void run(Configuration configuration, InjectableStepsFactory stepsFactory, Story story, MetaFilter filter,
 158  
             State beforeStories) throws Throwable {
 159  19
         RunContext context = new RunContext(configuration, stepsFactory, story.getPath(), filter);
 160  19
         if (beforeStories != null) {
 161  0
             context.stateIs(beforeStories);
 162  
         }
 163  19
         Map<String, String> storyParameters = new HashMap<String, String>();
 164  19
         run(context, story, storyParameters);
 165  18
     }
 166  
 
 167  
     /**
 168  
      * Returns the parsed story from the given path
 169  
      * 
 170  
      * @param configuration the Configuration used to run story
 171  
      * @param storyPath the story path
 172  
      * @return The parsed Story
 173  
      */
 174  
     public Story storyOfPath(Configuration configuration, String storyPath) {
 175  2
         String storyAsText = configuration.storyLoader().loadStoryAsText(storyPath);
 176  2
         return configuration.storyParser().parseStory(storyAsText, storyPath);
 177  
     }
 178  
 
 179  
     private void run(RunContext context, Story story, Map<String, String> storyParameters) throws Throwable {
 180  
         try {
 181  21
             runIt(context, story, storyParameters);
 182  0
         } catch (InterruptedException interruptedException) {
 183  0
             reporter.get().cancelled();
 184  0
             throw interruptedException;
 185  20
         }
 186  20
     }
 187  
 
 188  
     private void runIt(RunContext context, Story story, Map<String, String> storyParameters) throws Throwable {
 189  21
         if (!context.givenStory) {
 190  19
             reporter.set(reporterFor(context, story));
 191  
         }
 192  21
         pendingStepStrategy.set(context.configuration().pendingStepStrategy());
 193  21
         failureStrategy.set(context.configuration().failureStrategy());
 194  
 
 195  
         try {
 196  21
             resetStoryFailure(context);
 197  
 
 198  21
             if (context.dryRun()) {
 199  2
                 reporter.get().dryRun();
 200  
             }
 201  
 
 202  21
             if (context.configuration().storyControls().resetStateBeforeStory()) {
 203  20
                 context.resetState();
 204  
             }
 205  
 
 206  
             // run before story steps, if any
 207  21
             reporter.get().beforeStory(story, context.givenStory());
 208  
 
 209  21
             boolean storyAllowed = true;
 210  
 
 211  21
             FilterContext filterContext = context.filter(story);
 212  21
             Meta storyMeta = story.getMeta();
 213  21
             if (!filterContext.allowed()) {
 214  2
                 reporter.get().storyNotAllowed(story, context.metaFilterAsString());
 215  2
                 storyAllowed = false;
 216  
             }
 217  
 
 218  21
             if (storyAllowed) {
 219  
 
 220  19
                 reporter.get().narrative(story.getNarrative());
 221  
 
 222  19
                 runBeforeOrAfterStorySteps(context, story, Stage.BEFORE);
 223  
 
 224  
                 // determine if before and after scenario steps should be run
 225  19
                 boolean runBeforeAndAfterScenarioSteps = shouldRunBeforeOrAfterScenarioSteps(context);
 226  
 
 227  19
                 for (Scenario scenario : story.getScenarios()) {
 228  
                     // scenario also inherits meta from story
 229  25
                     boolean scenarioAllowed = true;
 230  25
                     if (failureOccurred(context) && context.configuration().storyControls().skipScenariosAfterFailure()) {
 231  1
                         continue;
 232  
                     }
 233  24
                     reporter.get().beforeScenario(scenario.getTitle());
 234  24
                     reporter.get().scenarioMeta(scenario.getMeta());
 235  
 
 236  24
                     if ( !filterContext.allowed(scenario) ) {
 237  2
                         reporter.get().scenarioNotAllowed(scenario, context.metaFilterAsString());
 238  2
                         scenarioAllowed = false;
 239  
                     }
 240  
 
 241  24
                     if (scenarioAllowed) {
 242  22
                         if (context.configuration().storyControls().resetStateBeforeScenario()) {
 243  19
                             context.resetState();
 244  
                         }
 245  22
                         Meta storyAndScenarioMeta = scenario.getMeta().inheritFrom(storyMeta);
 246  
                         // run before scenario steps, if allowed
 247  22
                         if (runBeforeAndAfterScenarioSteps) {
 248  21
                             runBeforeOrAfterScenarioSteps(context, scenario, storyAndScenarioMeta, Stage.BEFORE,
 249  
                                     ScenarioType.NORMAL);
 250  
                         }
 251  
 
 252  
                         // run given stories, if any
 253  22
                         runGivenStories(scenario, context);
 254  22
                         if (isParameterisedByExamples(scenario)) {
 255  
                             // run parametrised scenarios by examples
 256  1
                             runParametrisedScenariosByExamples(context, scenario, storyAndScenarioMeta);
 257  
                         } else { // run as plain old scenario
 258  21
                             addMetaParameters(storyParameters, storyAndScenarioMeta);
 259  21
                             runScenarioSteps(context, scenario, storyParameters);
 260  
                         }
 261  
 
 262  
                         // run after scenario steps, if allowed
 263  22
                         if (runBeforeAndAfterScenarioSteps) {
 264  21
                             runBeforeOrAfterScenarioSteps(context, scenario, storyAndScenarioMeta, Stage.AFTER,
 265  
                                     ScenarioType.NORMAL);
 266  
                         }
 267  
 
 268  
                     }
 269  
 
 270  24
                     reporter.get().afterScenario();
 271  24
                 }
 272  
 
 273  
                 // run after story steps, if any
 274  19
                 runBeforeOrAfterStorySteps(context, story, Stage.AFTER);
 275  
 
 276  
             }
 277  
 
 278  21
             reporter.get().afterStory(context.givenStory());
 279  
 
 280  
             // handle any failure according to strategy
 281  21
             if (!context.givenStory()) {
 282  19
                 handleStoryFailureByStrategy();
 283  
             }
 284  
         } finally {
 285  21
             if (!context.givenStory() && reporter.get() instanceof ConcurrentStoryReporter) {
 286  19
                 ((ConcurrentStoryReporter) reporter.get()).invokeDelayed();
 287  
             }
 288  
         }
 289  20
     }
 290  
 
 291  
     private void addMetaParameters(Map<String, String> storyParameters, Meta meta) {
 292  21
         for (String name : meta.getPropertyNames()) {
 293  0
             storyParameters.put(name, meta.getProperty(name));
 294  
         }
 295  21
     }
 296  
 
 297  
     private boolean shouldRunBeforeOrAfterScenarioSteps(RunContext context) {
 298  19
         Configuration configuration = context.configuration();
 299  19
         if (!configuration.storyControls().skipBeforeAndAfterScenarioStepsIfGivenStory()) {
 300  17
             return true;
 301  
         }
 302  
 
 303  2
         return !context.givenStory();
 304  
     }
 305  
 
 306  
     private boolean failureOccurred(RunContext context) {
 307  25
         return context.failureOccurred();
 308  
     }
 309  
 
 310  
     private StoryReporter reporterFor(RunContext context, Story story) {
 311  19
         Configuration configuration = context.configuration();
 312  19
         if (context.givenStory()) {
 313  0
             return configuration.storyReporter(reporterStoryPath.get());
 314  
         } else {
 315  
             // store parent story path for reporting
 316  19
             reporterStoryPath.set(story.getPath());
 317  19
             return configuration.storyReporter(reporterStoryPath.get());
 318  
         }
 319  
     }
 320  
 
 321  
     private void handleStoryFailureByStrategy() throws Throwable {
 322  21
         Throwable throwable = storyFailure.get();
 323  21
         if (throwable != null) {
 324  11
             currentStrategy.get().handleFailure(throwable);
 325  
         }
 326  19
     }
 327  
 
 328  
     private void resetStoryFailure(RunContext context) {
 329  24
         if (context.givenStory()) {
 330  
             // do not reset failure for given stories
 331  2
             return;
 332  
         }
 333  22
         currentStrategy.set(context.configuration().failureStrategy());
 334  22
         storyFailure.set(null);
 335  22
     }
 336  
 
 337  
     private void runGivenStories(Scenario scenario, RunContext context) throws Throwable {
 338  22
         GivenStories givenStories = scenario.getGivenStories();
 339  22
         if (givenStories.getPaths().size() > 0) {
 340  2
             reporter.get().givenStories(givenStories);
 341  2
             for (GivenStory givenStory : givenStories.getStories()) {
 342  2
                 RunContext childContext = context.childContextFor(givenStory);
 343  
                 // run given story, using any parameters if provided
 344  2
                 Story story = storyOfPath(context.configuration(), childContext.path());
 345  2
                 run(childContext, story, givenStory.getParameters());
 346  2
             }
 347  
         }
 348  22
     }
 349  
 
 350  
     private boolean isParameterisedByExamples(Scenario scenario) {
 351  22
         return scenario.getExamplesTable().getRowCount() > 0 && !scenario.getGivenStories().requireParameters();
 352  
     }
 353  
 
 354  
     private void runParametrisedScenariosByExamples(RunContext context, Scenario scenario, Meta storyAndScenarioMeta) {
 355  1
         ExamplesTable table = scenario.getExamplesTable();
 356  1
         reporter.get().beforeExamples(scenario.getSteps(), table);
 357  1
         for (Map<String, String> scenarioParameters : table.getRows()) {
 358  1
             reporter.get().example(scenarioParameters);
 359  1
             if (context.configuration().storyControls().resetStateBeforeScenario()) {
 360  1
                 context.resetState();
 361  
             }
 362  1
             runBeforeOrAfterScenarioSteps(context, scenario, storyAndScenarioMeta, Stage.BEFORE, ScenarioType.EXAMPLE);
 363  1
             runScenarioSteps(context, scenario, scenarioParameters);
 364  1
             runBeforeOrAfterScenarioSteps(context, scenario, storyAndScenarioMeta, Stage.AFTER, ScenarioType.EXAMPLE);
 365  
         }
 366  1
         reporter.get().afterExamples();
 367  1
     }
 368  
 
 369  
     private void runBeforeOrAfterStorySteps(RunContext context, Story story, Stage stage) {
 370  38
         runStepsWhileKeepingState(context, context.collectBeforeOrAfterStorySteps(story, stage));
 371  38
     }
 372  
 
 373  
     private void runBeforeOrAfterScenarioSteps(RunContext context, Scenario scenario, Meta storyAndScenarioMeta,
 374  
             Stage stage, ScenarioType type) {
 375  44
         runStepsWhileKeepingState(context, context.collectBeforeOrAfterScenarioSteps(storyAndScenarioMeta, stage, type));
 376  44
     }
 377  
 
 378  
     private void runScenarioSteps(RunContext context, Scenario scenario, Map<String, String> scenarioParameters) {
 379  22
         boolean restart = true;
 380  45
         while (restart) {
 381  23
             restart = false;
 382  23
             List<Step> steps = context.collectScenarioSteps(scenario, scenarioParameters);
 383  
             try {
 384  23
                 runStepsWhileKeepingState(context, steps);
 385  1
             } catch (RestartingScenarioFailure e) {
 386  1
                 restart = true;
 387  1
                 continue;
 388  22
             }
 389  22
             generatePendingStepMethods(context, steps);
 390  22
         }
 391  22
     }
 392  
 
 393  
     private void generatePendingStepMethods(RunContext context, List<Step> steps) {
 394  22
         List<PendingStep> pendingSteps = new ArrayList<PendingStep>();
 395  22
         for (Step step : steps) {
 396  33
             if (step instanceof PendingStep) {
 397  0
                 pendingSteps.add((PendingStep) step);
 398  
             }
 399  
         }
 400  22
         if (!pendingSteps.isEmpty()) {
 401  0
             PendingStepMethodGenerator generator = new PendingStepMethodGenerator(context.configuration().keywords());
 402  0
             List<String> methods = new ArrayList<String>();
 403  0
             for (PendingStep pendingStep : pendingSteps) {
 404  0
                 if (!pendingStep.annotated()) {
 405  0
                     methods.add(generator.generateMethod(pendingStep));
 406  
                 }
 407  
             }
 408  0
             reporter.get().pendingMethods(methods);
 409  
         }
 410  22
     }
 411  
 
 412  
     private void runStepsWhileKeepingState(RunContext context, List<Step> steps) {
 413  110
         if (steps == null || steps.size() == 0) {
 414  78
             return;
 415  
         }
 416  32
         State state = context.state();
 417  32
         for (Step step : steps) {
 418  
             try {
 419  44
                 state = state.run(step);
 420  1
             } catch (RestartingScenarioFailure e) {
 421  1
                 reporter.get().restarted(step.toString(), e);
 422  1
                 throw e;
 423  43
             }
 424  
         }
 425  31
         context.stateIs(state);
 426  31
     }
 427  
 
 428  
     public interface State {
 429  
         State run(Step step);
 430  
     }
 431  
 
 432  132
     private final class FineSoFar implements State {
 433  
 
 434  
         public State run(Step step) {
 435  37
             UUIDExceptionWrapper storyFailureIfItHappened = storyFailure.get();
 436  37
             StepResult result = step.perform(storyFailureIfItHappened);
 437  36
             result.describeTo(reporter.get());
 438  36
             UUIDExceptionWrapper stepFailure = result.getFailure();
 439  36
             if (stepFailure == null) {
 440  22
                 return this;
 441  
             }
 442  
 
 443  14
             storyFailure.set(mostImportantOf(storyFailureIfItHappened, stepFailure));
 444  14
             currentStrategy.set(strategyFor(storyFailure.get()));
 445  14
             return new SomethingHappened(stepFailure);
 446  
         }
 447  
 
 448  
         private UUIDExceptionWrapper mostImportantOf(UUIDExceptionWrapper failure1, UUIDExceptionWrapper failure2) {
 449  14
             return failure1 == null ? failure2
 450  
                     : failure1.getCause() instanceof PendingStepFound ? (failure2 == null ? failure1 : failure2)
 451  
                             : failure1;
 452  
         }
 453  
 
 454  
         private FailureStrategy strategyFor(Throwable failure) {
 455  14
             if (failure instanceof PendingStepFound) {
 456  6
                 return pendingStepStrategy.get();
 457  
             } else {
 458  8
                 return failureStrategy.get();
 459  
             }
 460  
         }
 461  
     }
 462  
 
 463  
     private final class SomethingHappened implements State {
 464  
         UUIDExceptionWrapper scenarioFailure;
 465  
 
 466  15
         public SomethingHappened(UUIDExceptionWrapper scenarioFailure) {
 467  15
             this.scenarioFailure = scenarioFailure;
 468  15
         }
 469  
 
 470  
         public State run(Step step) {
 471  7
             StepResult result = step.doNotPerform(scenarioFailure);
 472  7
             result.describeTo(reporter.get());
 473  7
             return this;
 474  
         }
 475  
     }
 476  
 
 477  
     @Override
 478  
     public String toString() {
 479  1
         return this.getClass().getSimpleName();
 480  
     }
 481  
 
 482  
     /**
 483  
      * The context for running a story.
 484  
      */
 485  21
     private class RunContext {
 486  
         private final Configuration configuration;
 487  
         private final List<CandidateSteps> candidateSteps;
 488  
         private final String path;
 489  
         private final MetaFilter filter;
 490  
         private final boolean givenStory;
 491  
         private State state;
 492  
 
 493  
         public RunContext(Configuration configuration, InjectableStepsFactory stepsFactory, String path,
 494  
                 MetaFilter filter) {
 495  19
             this(configuration, stepsFactory.createCandidateSteps(), path, filter);
 496  19
         }
 497  
 
 498  
         public RunContext(Configuration configuration, List<CandidateSteps> steps, String path, MetaFilter filter) {
 499  24
             this(configuration, steps, path, filter, false);
 500  24
         }
 501  
 
 502  
         private RunContext(Configuration configuration, List<CandidateSteps> steps, String path, MetaFilter filter,
 503  26
                 boolean givenStory) {
 504  26
             this.configuration = configuration;
 505  26
             this.candidateSteps = steps;
 506  26
             this.path = path;
 507  26
             this.filter = filter;
 508  26
             this.givenStory = givenStory;
 509  26
             resetState();
 510  26
         }
 511  
 
 512  
         public boolean dryRun() {
 513  21
             return configuration.storyControls().dryRun();
 514  
         }
 515  
 
 516  
         public Configuration configuration() {
 517  153
             return configuration;
 518  
         }
 519  
 
 520  
         public List<CandidateSteps> candidateSteps() {
 521  5
             return candidateSteps;
 522  
         }
 523  
 
 524  
         public boolean givenStory() {
 525  129
             return givenStory;
 526  
         }
 527  
 
 528  
         public String path() {
 529  2
             return path;
 530  
         }
 531  
 
 532  
         public FilterContext filter(Story story) {
 533  21
             return new FilterContext(filter, story, configuration.storyControls());
 534  
         }
 535  
 
 536  
         public String metaFilterAsString() {
 537  4
             return filter.asString();
 538  
         }
 539  
 
 540  
         public List<Step> collectBeforeOrAfterStorySteps(Story story, Stage stage) {
 541  38
             return configuration.stepCollector().collectBeforeOrAfterStorySteps(candidateSteps, story, stage,
 542  
                     givenStory);
 543  
         }
 544  
 
 545  
         public List<Step> collectBeforeOrAfterScenarioSteps(Meta storyAndScenarioMeta, Stage stage, ScenarioType type) {
 546  44
             return configuration.stepCollector().collectBeforeOrAfterScenarioSteps(candidateSteps,
 547  
                     storyAndScenarioMeta, stage, type);
 548  
         }
 549  
 
 550  
         public List<Step> collectScenarioSteps(Scenario scenario, Map<String, String> parameters) {
 551  23
             return configuration.stepCollector().collectScenarioSteps(candidateSteps, scenario, parameters);
 552  
         }
 553  
 
 554  
         public RunContext childContextFor(GivenStory givenStory) {
 555  2
             String actualPath = configuration.pathCalculator().calculate(path, givenStory.getPath());
 556  2
             return new RunContext(configuration, candidateSteps, actualPath, filter, true);
 557  
         }
 558  
 
 559  
         public State state() {
 560  41
             return state;
 561  
         }
 562  
 
 563  
         public void stateIs(State state) {
 564  33
             this.state = state;
 565  33
         }
 566  
 
 567  
         public boolean failureOccurred() {
 568  25
             return failed(state);
 569  
         }
 570  
 
 571  
         public void resetState() {
 572  66
             this.state = new FineSoFar();
 573  66
         }
 574  
 
 575  
     }
 576  
 
 577  76
     public class FilterContext {
 578  
 
 579  
         private boolean storyAllowed;
 580  
         private Map<Scenario, Boolean> scenariosAllowed;
 581  
 
 582  21
         public FilterContext(MetaFilter filter, Story story, StoryControls storyControls) {
 583  21
             String storyMetaPrefix = storyControls.storyMetaPrefix();
 584  21
             String scenarioMetaPrefix = storyControls.scenarioMetaPrefix();
 585  21
             Meta storyMeta = story.getMeta().inheritFrom(story.asMeta(storyMetaPrefix));
 586  21
             storyAllowed = filter.allow(storyMeta);
 587  21
             scenariosAllowed = new HashMap<Scenario, Boolean>();
 588  21
             for (Scenario scenario : story.getScenarios()) {
 589  27
                 Meta scenarioMeta = scenario.getMeta().inheritFrom(scenario.asMeta(scenarioMetaPrefix).inheritFrom(storyMeta));
 590  27
                 boolean scenarioAllowed = filter.allow(scenarioMeta);
 591  27
                 scenariosAllowed.put(scenario, scenarioAllowed);
 592  27
             }
 593  21
         }
 594  
 
 595  
         public boolean allowed() {
 596  21
             return storyAllowed || scenariosAllowed.values().contains(true);
 597  
         }
 598  
 
 599  
         public boolean allowed(Scenario scenario){
 600  24
             return scenariosAllowed.get(scenario);
 601  
         }
 602  
     }
 603  
 
 604  
     public boolean failed(State state) {
 605  25
         return !state.getClass().equals(FineSoFar.class);
 606  
     }
 607  
 
 608  
     public Throwable failure(State state) {
 609  0
         if (failed(state)) {
 610  0
             return ((SomethingHappened) state).scenarioFailure.getCause();
 611  
         }
 612  0
         return null;
 613  
     }
 614  
 }