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