Coverage Report - org.jbehave.core.embedder.StoryManager
 
Classes in this File Line Coverage Branch Coverage Complexity
StoryManager
83%
65/78
67%
19/28
1.767
StoryManager$1
N/A
N/A
1.767
StoryManager$EnqueuedStory
100%
21/21
100%
2/2
1.767
StoryManager$RunningStory
46%
6/13
0%
0/4
1.767
StoryManager$StoryExecutionFailed
100%
2/2
N/A
1.767
StoryManager$StoryOutcome
0%
0/8
N/A
1.767
StoryManager$ThrowableStory
83%
5/6
N/A
1.767
 
 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  
 import java.util.concurrent.Callable;
 8  
 import java.util.concurrent.ExecutionException;
 9  
 import java.util.concurrent.ExecutorService;
 10  
 import java.util.concurrent.Future;
 11  
 
 12  
 import org.jbehave.core.configuration.Configuration;
 13  
 import org.jbehave.core.embedder.StoryRunner.State;
 14  
 import org.jbehave.core.failures.BatchFailures;
 15  
 import org.jbehave.core.model.Story;
 16  
 import org.jbehave.core.model.StoryDuration;
 17  
 import org.jbehave.core.steps.InjectableStepsFactory;
 18  
 
 19  
 /**
 20  
  * Manages the execution and outcomes of running stories. While each story is
 21  
  * run by the {@link StoryRunner}, the manager is responsible for the concurrent
 22  
  * submission and monitoring of their execution via the {@link ExecutorService}.
 23  
  */
 24  
 public class StoryManager {
 25  
 
 26  
     private final Configuration configuration;
 27  
     private final EmbedderControls embedderControls;
 28  
     private final EmbedderMonitor embedderMonitor;
 29  
     private final ExecutorService executorService;
 30  
     private final InjectableStepsFactory stepsFactory;
 31  
     private final StoryRunner storyRunner;
 32  11
     private final Map<String, RunningStory> runningStories = new HashMap<String, RunningStory>();
 33  11
     private final Map<MetaFilter, List<Story>> excludedStories = new HashMap<MetaFilter, List<Story>>();
 34  
 
 35  
     public StoryManager(Configuration configuration, EmbedderControls embedderControls,
 36  
             EmbedderMonitor embedderMonitor, ExecutorService executorService, InjectableStepsFactory stepsFactory,
 37  11
             StoryRunner storyRunner) {
 38  11
         this.configuration = configuration;
 39  11
         this.embedderControls = embedderControls;
 40  11
         this.embedderMonitor = embedderMonitor;
 41  11
         this.executorService = executorService;
 42  11
         this.stepsFactory = stepsFactory;
 43  11
         this.storyRunner = storyRunner;
 44  11
     }
 45  
 
 46  
     public Story storyOfPath(String storyPath) {
 47  19
         return storyRunner.storyOfPath(configuration, storyPath);
 48  
     }
 49  
 
 50  
     public Story storyOfText(String storyAsText, String storyId) {
 51  0
         return storyRunner.storyOfText(configuration, storyAsText, storyId);
 52  
     }
 53  
 
 54  
     public void clear() {
 55  0
         runningStories.clear();
 56  0
     }
 57  
 
 58  
     public List<StoryOutcome> outcomes() {
 59  0
         List<StoryOutcome> outcomes = new ArrayList<StoryOutcome>();
 60  0
         for (RunningStory story : runningStories.values()) {
 61  0
             outcomes.add(new StoryOutcome(story));
 62  
         }
 63  0
         return outcomes;
 64  
     }
 65  
 
 66  
     public Map<String, RunningStory> runningStoriesAsPaths(List<String> storyPaths, MetaFilter filter,
 67  
             State beforeStories) {
 68  11
         for (String storyPath : storyPaths) {
 69  19
             filterRunning(filter, beforeStories, storyPath, storyOfPath(storyPath));
 70  
         }
 71  11
         return runningStories;
 72  
     }
 73  
 
 74  
     public Map<String, RunningStory> runningStories(List<Story> stories, MetaFilter filter, State beforeStories) {
 75  0
         for (Story story : stories) {
 76  0
             filterRunning(filter, beforeStories, story.getPath(), story);
 77  
         }
 78  0
         return runningStories;
 79  
     }
 80  
 
 81  
     private void filterRunning(MetaFilter filter, State beforeStories, String storyPath, Story story) {
 82  19
         FilteredStory filteredStory = new FilteredStory(filter, story, configuration.storyControls());
 83  19
         if (filteredStory.allowed()) {
 84  19
             runningStories.put(storyPath, runningStory(storyPath, story, filter, beforeStories));
 85  
         } else {
 86  0
             notAllowedBy(filter).add(story);
 87  
         }
 88  19
     }
 89  
 
 90  
     public List<Story> notAllowedBy(MetaFilter filter) {
 91  11
         List<Story> stories = excludedStories.get(filter);
 92  11
         if (stories == null) {
 93  11
             stories = new ArrayList<Story>();
 94  11
             excludedStories.put(filter, stories);
 95  
         }
 96  11
         return stories;
 97  
     }
 98  
 
 99  
     public RunningStory runningStory(String storyPath, Story story, MetaFilter filter, State beforeStories) {
 100  19
         return submit(new EnqueuedStory(storyRunner, configuration, stepsFactory, embedderControls, embedderMonitor,
 101  
                 storyPath, story, filter, beforeStories));
 102  
     }
 103  
 
 104  
     public void waitUntilAllDoneOrFailed(BatchFailures failures) {
 105  11
         long start = System.currentTimeMillis();
 106  11
         boolean allDone = false;
 107  93
         while (!allDone) {
 108  82
             allDone = true;
 109  82
             for (RunningStory runningStory : runningStories.values()) {
 110  88
                 Future<ThrowableStory> future = runningStory.getFuture();
 111  88
                 if (!future.isDone()) {
 112  71
                     allDone = false;
 113  71
                     long durationInSecs = storyDurationInSecs(start);
 114  71
                     long timeoutInSecs = embedderControls.storyTimeoutInSecs();
 115  71
                     if (durationInSecs > timeoutInSecs) {
 116  3
                         Story story = runningStory.getStory();
 117  3
                         StoryDuration storyDuration = new StoryDuration(durationInSecs, timeoutInSecs);
 118  3
                         embedderMonitor.storyTimeout(story, storyDuration);
 119  3
                         storyRunner.cancelStory(story, storyDuration);
 120  3
                         future.cancel(true);
 121  3
                     }
 122  
                     break;
 123  
                 } else {
 124  17
                     Story story = runningStory.getStory();
 125  
                     try {
 126  17
                         ThrowableStory throwableStory = future.get();
 127  14
                         Throwable throwable = throwableStory.getThrowable();
 128  14
                         if (throwable != null) {
 129  2
                             failures.put(story.getPath(), throwable);
 130  2
                             if (!embedderControls.ignoreFailureInStories()) {
 131  2
                                 break;
 132  
                             }
 133  
                         }
 134  3
                     } catch (Throwable e) {
 135  3
                         failures.put(story.getPath(), e);
 136  3
                         if (!embedderControls.ignoreFailureInStories()) {
 137  3
                             break;
 138  
                         }
 139  12
                     }
 140  
                 }
 141  12
             }
 142  82
             tickTock();
 143  
         }
 144  
         // cancel any outstanding execution which is not done before returning
 145  11
         for (RunningStory runningStory : runningStories.values()) {
 146  19
             Future<ThrowableStory> future = runningStory.getFuture();
 147  19
             if (!future.isDone()) {
 148  0
                 future.cancel(true);
 149  
             }
 150  19
         }
 151  
 
 152  11
     }
 153  
 
 154  
     private void tickTock() {
 155  
         try {
 156  82
             Thread.sleep(100);
 157  0
         } catch (InterruptedException e) {
 158  82
         }
 159  82
     }
 160  
 
 161  
     private long storyDurationInSecs(long start) {
 162  71
         return (System.currentTimeMillis() - start) / 1000;
 163  
     }
 164  
 
 165  
     private synchronized RunningStory submit(EnqueuedStory enqueuedStory) {
 166  19
         return new RunningStory(enqueuedStory.getStory(), executorService.submit(enqueuedStory));
 167  
     }
 168  
 
 169  38
     private static class EnqueuedStory implements Callable<ThrowableStory> {
 170  
         private final StoryRunner storyRunner;
 171  
         private final Configuration configuration;
 172  
         private final InjectableStepsFactory stepsFactory;
 173  
         private final EmbedderControls embedderControls;
 174  
         private final EmbedderMonitor embedderMonitor;
 175  
         private final String storyPath;
 176  
         private final Story story;
 177  
         private final MetaFilter filter;
 178  
         private final State beforeStories;
 179  
 
 180  
         private EnqueuedStory(StoryRunner storyRunner, Configuration configuration,
 181  
                 InjectableStepsFactory stepsFactory, EmbedderControls embedderControls,
 182  19
                 EmbedderMonitor embedderMonitor, String storyPath, Story story, MetaFilter filter, State beforeStories) {
 183  19
             this.storyRunner = storyRunner;
 184  19
             this.configuration = configuration;
 185  19
             this.stepsFactory = stepsFactory;
 186  19
             this.embedderControls = embedderControls;
 187  19
             this.embedderMonitor = embedderMonitor;
 188  19
             this.storyPath = storyPath;
 189  19
             this.story = story;
 190  19
             this.filter = filter;
 191  19
             this.beforeStories = beforeStories;
 192  19
         }
 193  
 
 194  
         public ThrowableStory call() throws Exception {
 195  
             try {
 196  19
                 embedderMonitor.runningStory(storyPath);
 197  19
                 storyRunner.run(configuration, stepsFactory, story, filter, beforeStories);
 198  10
             } catch (Throwable e) {
 199  10
                 if (embedderControls.ignoreFailureInStories()) {
 200  4
                     embedderMonitor.storyFailed(storyPath, e);
 201  
                 } else {
 202  6
                     return new ThrowableStory(story, new StoryExecutionFailed(storyPath, e));
 203  
                 }
 204  8
             }
 205  12
             return new ThrowableStory(story, null);
 206  
         }
 207  
 
 208  
         public Story getStory() {
 209  19
             return story;
 210  
         }
 211  
 
 212  
     }
 213  
 
 214  
     @SuppressWarnings("serial")
 215  
     public static class StoryExecutionFailed extends RuntimeException {
 216  
 
 217  
         public StoryExecutionFailed(String storyPath, Throwable failure) {
 218  6
             super(storyPath, failure);
 219  6
         }
 220  
 
 221  
     }
 222  
 
 223  
     public static class ThrowableStory {
 224  
         private Story story;
 225  
         private Throwable throwable;
 226  
 
 227  18
         public ThrowableStory(Story story, Throwable throwable) {
 228  18
             this.story = story;
 229  18
             this.throwable = throwable;
 230  18
         }
 231  
 
 232  
         public Story getStory() {
 233  0
             return story;
 234  
         }
 235  
 
 236  
         public Throwable getThrowable() {
 237  14
             return throwable;
 238  
         }
 239  
     }
 240  
 
 241  
     public static class RunningStory {
 242  
         private Story story;
 243  
         private Future<ThrowableStory> future;
 244  
 
 245  19
         public RunningStory(Story story, Future<ThrowableStory> future) {
 246  19
             this.story = story;
 247  19
             this.future = future;
 248  19
         }
 249  
 
 250  
         public Future<ThrowableStory> getFuture() {
 251  107
             return future;
 252  
         }
 253  
 
 254  
         public Story getStory() {
 255  20
             return story;
 256  
         }
 257  
 
 258  
         public boolean isDone() {
 259  0
             return future.isDone();
 260  
         }
 261  
 
 262  
         public boolean isFailed() {
 263  0
             if (isDone()) {
 264  
                 try {
 265  0
                     return future.get().getThrowable() != null;
 266  0
                 } catch (InterruptedException e) {
 267  0
                 } catch (ExecutionException e) {
 268  0
                 }
 269  
             }
 270  0
             return false;
 271  
         }
 272  
     }
 273  
 
 274  
     public static class StoryOutcome {
 275  
         private String path;
 276  
         private Boolean done;
 277  
         private Boolean failed;
 278  
 
 279  0
         public StoryOutcome(RunningStory story) {
 280  0
             this.path = story.getStory().getPath();
 281  0
             this.done = story.isDone();
 282  0
             this.failed = story.isFailed();
 283  0
         }
 284  
 
 285  
         public String getPath() {
 286  0
             return path;
 287  
         }
 288  
 
 289  
         public Boolean isDone() {
 290  0
             return done;
 291  
         }
 292  
 
 293  
         public Boolean isFailed() {
 294  0
             return failed;
 295  
         }
 296  
 
 297  
     }
 298  
 
 299  
 }