Embedder.java

  1. package org.jbehave.core.embedder;

  2. import java.io.File;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.Properties;
  8. import java.util.concurrent.ExecutorService;
  9. import java.util.concurrent.ThreadPoolExecutor;

  10. import org.apache.commons.lang3.StringUtils;
  11. import org.apache.commons.lang3.builder.ToStringBuilder;
  12. import org.apache.commons.lang3.builder.ToStringStyle;
  13. import org.jbehave.core.ConfigurableEmbedder;
  14. import org.jbehave.core.Embeddable;
  15. import org.jbehave.core.configuration.Configuration;
  16. import org.jbehave.core.configuration.MostUsefulConfiguration;
  17. import org.jbehave.core.embedder.MetaFilter.MetaMatcher;
  18. import org.jbehave.core.embedder.StoryTimeouts.TimeoutParser;
  19. import org.jbehave.core.embedder.executors.FixedThreadExecutors;
  20. import org.jbehave.core.failures.BatchFailures;
  21. import org.jbehave.core.failures.FailingUponPendingStep;
  22. import org.jbehave.core.junit.AnnotatedEmbedderRunner;
  23. import org.jbehave.core.junit.AnnotatedEmbedderUtils;
  24. import org.jbehave.core.model.Story;
  25. import org.jbehave.core.model.StoryMaps;
  26. import org.jbehave.core.reporters.ReportsCount;
  27. import org.jbehave.core.reporters.StepdocReporter;
  28. import org.jbehave.core.reporters.StoryReporterBuilder;
  29. import org.jbehave.core.reporters.ViewGenerator;
  30. import org.jbehave.core.steps.CandidateSteps;
  31. import org.jbehave.core.steps.InjectableStepsFactory;
  32. import org.jbehave.core.steps.ProvidedStepsFactory;
  33. import org.jbehave.core.steps.StepFinder;
  34. import org.jbehave.core.steps.Stepdoc;

  35. /**
  36.  * The Embedder is a facade allowing all functionality to be embedded into other
  37.  * run contexts, such as IDEs (e.g. via JUnit support) or CLIs (via Ant or
  38.  * Maven).
  39.  */
  40. public class Embedder {

  41.     protected StoryMapper storyMapper;
  42.     protected EmbedderMonitor embedderMonitor;
  43.     protected EmbedderClassLoader classLoader;
  44.     protected EmbedderControls embedderControls;
  45.     protected EmbedderFailureStrategy embedderFailureStrategy;
  46.     protected Configuration configuration;
  47.     protected InjectableStepsFactory stepsFactory;
  48.     protected List<String> metaFilters;
  49.     protected Map<String,MetaMatcher> metaMatchers;
  50.     protected Properties systemProperties;
  51.     protected ExecutorService executorService;
  52.     protected boolean executorServiceCreated;
  53.     protected PerformableTree performableTree;
  54.     protected StoryManager storyManager;
  55.     protected TimeoutParser[] timeoutParsers;

  56.     public Embedder() {
  57.         this(new PrintStreamEmbedderMonitor());
  58.     }

  59.     public Embedder(EmbedderMonitor embedderMonitor) {
  60.         this(new StoryMapper(), new PerformableTree(), embedderMonitor);
  61.     }

  62.     public Embedder(StoryMapper storyMapper, PerformableTree performableTree, EmbedderMonitor embedderMonitor) {
  63.         this.storyMapper = storyMapper;
  64.         this.performableTree = performableTree;
  65.         this.embedderMonitor = embedderMonitor;
  66.     }

  67.     public void mapStoriesAsPaths(List<String> storyPaths) {
  68.         EmbedderControls embedderControls = embedderControls();
  69.         embedderMonitor.usingControls(embedderControls);

  70.         if (embedderControls.skip()) {
  71.             embedderMonitor.storiesSkipped(storyPaths);
  72.             return;
  73.         }

  74.         processSystemProperties();

  75.         StoryManager storyManager = storyManager();
  76.         for (String storyPath : storyPaths) {
  77.             Story story = storyManager.storyOfPath(storyPath);
  78.             embedderMonitor.mappingStory(storyPath, metaFilters());
  79.             storyMapper.map(story, new MetaFilter("", embedderMonitor));
  80.             for (String filter : metaFilters) {
  81.                 storyMapper.map(story, new MetaFilter(filter, embedderMonitor));
  82.             }
  83.         }

  84.         generateMapsView(storyMapper.getStoryMaps());

  85.     }

  86.     private void generateMapsView(StoryMaps storyMaps) {
  87.         Configuration configuration = configuration();
  88.         StoryReporterBuilder builder = configuration.storyReporterBuilder();
  89.         File outputDirectory = builder.outputDirectory();
  90.         Properties viewResources = builder.viewResources();
  91.         ViewGenerator viewGenerator = configuration.viewGenerator();
  92.         try {
  93.             embedderMonitor.generatingMapsView(outputDirectory, storyMaps, viewResources);
  94.             viewGenerator.generateMapsView(outputDirectory, storyMaps, viewResources);
  95.         } catch (RuntimeException e) {
  96.             embedderMonitor.mapsViewGenerationFailed(outputDirectory, storyMaps, viewResources, e);
  97.             throw new ViewGenerationFailed(outputDirectory, storyMaps, viewResources, e);
  98.         }
  99.     }

  100.     public void runAsEmbeddables(List<String> classNames) {
  101.         EmbedderControls embedderControls = embedderControls();
  102.         embedderMonitor.usingControls(embedderControls);

  103.         if (embedderControls.skip()) {
  104.             embedderMonitor.embeddablesSkipped(classNames);
  105.             return;
  106.         }

  107.         BatchFailures failures = new BatchFailures(embedderControls.verboseFailures());
  108.         for (Embeddable embeddable : embeddables(classNames, classLoader())) {
  109.             String name = embeddable.getClass().getName();
  110.             try {
  111.                 embedderMonitor.runningEmbeddable(name);
  112.                 embeddable.useEmbedder(this);
  113.                 embeddable.run();
  114.             } catch (Throwable e) {
  115.                 if (embedderControls.batch()) {
  116.                     // collect and postpone decision to throw exception
  117.                     failures.put(name, e);
  118.                 } else {
  119.                     if (ignoreFailure(embedderControls)) {
  120.                         embedderMonitor.embeddableFailed(name, e);
  121.                     } else {
  122.                         throw new RunningEmbeddablesFailed(name, e);
  123.                     }
  124.                 }
  125.             }
  126.         }

  127.         if (embedderControls.batch() && failures.size() > 0) {
  128.             if (ignoreFailure(embedderControls)) {
  129.                 embedderMonitor.batchFailed(failures);
  130.             } else {
  131.                 throw new RunningEmbeddablesFailed(failures);
  132.             }
  133.         }

  134.     }

  135.     private boolean ignoreFailure(EmbedderControls embedderControls) {
  136.         boolean ignore = embedderControls.ignoreFailureInStories();
  137.         if (embedderControls.generateViewAfterStories()) {
  138.             ignore = ignore && embedderControls.ignoreFailureInView();
  139.         }
  140.         return ignore;
  141.     }

  142.     private List<Embeddable> embeddables(List<String> classNames, EmbedderClassLoader classLoader) {
  143.         List<Embeddable> embeddables = new ArrayList<>();
  144.         for (String className : classNames) {
  145.             if (!classLoader.isAbstract(className)) {
  146.                 embeddables.add(classLoader.newInstance(Embeddable.class, className));
  147.             }
  148.         }
  149.         return embeddables;
  150.     }

  151.     public void runStoriesWithAnnotatedEmbedderRunner(List<String> classNames) {
  152.         EmbedderClassLoader classLoader = classLoader();
  153.         for (String className : classNames) {
  154.             embedderMonitor.runningWithAnnotatedEmbedderRunner(className);
  155.             AnnotatedEmbedderRunner runner = AnnotatedEmbedderUtils.annotatedEmbedderRunner(className, classLoader);
  156.             try {
  157.                 Object annotatedInstance = runner.createTest();
  158.                 if (annotatedInstance instanceof Embeddable) {
  159.                     ((Embeddable) annotatedInstance).run();
  160.                 } else {
  161.                     embedderMonitor.annotatedInstanceNotOfType(annotatedInstance, Embeddable.class);
  162.                 }
  163.             } catch (Throwable e) {
  164.                 throw new AnnotatedEmbedderRunFailed(runner, e);
  165.             }
  166.         }
  167.     }

  168.     public void runStoriesAsPaths(List<String> storyPaths) {

  169.         processSystemProperties();

  170.         EmbedderControls embedderControls = embedderControls();

  171.         embedderMonitor.usingControls(embedderControls);

  172.         if (embedderControls.skip()) {
  173.             embedderMonitor.storiesSkipped(storyPaths);
  174.             return;
  175.         }

  176.         try {

  177.             // set up run context
  178.             StoryManager storyManager = storyManager();
  179.             MetaFilter filter = metaFilter();
  180.             BatchFailures failures = new BatchFailures(embedderControls.verboseFailures());

  181.             // run stories
  182.             storyManager.runStoriesAsPaths(storyPaths, filter, failures);

  183.             // handle any failures
  184.             handleFailures(failures);

  185.         } finally {
  186.             // generate reports view regardless of failures in running stories
  187.             // (if configured to do so)
  188.             try {
  189.                 if (embedderControls.generateViewAfterStories()) {
  190.                     generateReportsView();
  191.                 }
  192.             } finally {
  193.                 // shutdown regardless of failures in reports view
  194.                 shutdownExecutorService();
  195.                 // reset story manager as executor service is shutdown
  196.                 storyManager = null;
  197.             }

  198.         }
  199.     }

  200.     private void handleFailures(BatchFailures failures) {
  201.         if (failures.size() > 0) {
  202.             if (embedderControls().ignoreFailureInStories()) {
  203.                 embedderMonitor.batchFailed(failures);
  204.             } else {
  205.                 embedderFailureStrategy().handleFailures(failures);
  206.             }
  207.         }
  208.     }

  209.     private void handleFailures(ReportsCount count) {
  210.         boolean failed = count.failed();
  211.         if (configuration().pendingStepStrategy() instanceof FailingUponPendingStep) {
  212.             failed = failed || count.pending();
  213.         }
  214.         if (failed) {
  215.             if (embedderControls().ignoreFailureInView()) {
  216.                 embedderMonitor.reportsViewFailures(count);
  217.             } else {
  218.                 embedderFailureStrategy().handleFailures(count);
  219.             }
  220.         }
  221.     }

  222.     public void generateReportsView() {
  223.         StoryReporterBuilder builder = configuration().storyReporterBuilder();
  224.         File outputDirectory = builder.outputDirectory();
  225.         List<String> formatNames = builder.formatNames(true);
  226.         generateReportsView(outputDirectory, formatNames, builder.viewResources());
  227.     }

  228.     public void generateReportsView(File outputDirectory, List<String> formats, Properties viewResources) {

  229.         if (embedderControls().skip()) {
  230.             embedderMonitor.reportsViewNotGenerated();
  231.             return;
  232.         }
  233.         ViewGenerator viewGenerator = configuration().viewGenerator();
  234.         try {
  235.             embedderMonitor.generatingReportsView(outputDirectory, formats, viewResources);
  236.             viewGenerator.generateReportsView(outputDirectory, formats, viewResources);
  237.         } catch (RuntimeException e) {
  238.             embedderMonitor.reportsViewGenerationFailed(outputDirectory, formats, viewResources, e);
  239.             throw new ViewGenerationFailed(outputDirectory, formats, viewResources, e);
  240.         }
  241.         ReportsCount count = viewGenerator.getReportsCount();
  242.         embedderMonitor.reportsViewGenerated(count);
  243.         handleFailures(count);

  244.     }

  245.     public void generateSurefireReport() {
  246.         StoryReporterBuilder builder = configuration().storyReporterBuilder();
  247.         if (builder.hasSurefireReporter()) {
  248.             builder.surefireReporter().generate(storyManager().performableRoot(), builder.outputDirectory());
  249.         }
  250.     }

  251.     public void reportStepdocs() {
  252.         reportStepdocs(configuration(), stepsFactory().createCandidateSteps());
  253.     }

  254.     public void reportStepdocs(Configuration configuration, List<CandidateSteps> candidateSteps) {
  255.         StepFinder finder = configuration.stepFinder();
  256.         StepdocReporter reporter = configuration.stepdocReporter();
  257.         List<Object> stepsInstances = finder.stepsInstances(candidateSteps);
  258.         reporter.stepdocs(finder.stepdocs(candidateSteps), stepsInstances);
  259.     }

  260.     public void reportStepdocsAsEmbeddables(List<String> classNames) {
  261.         EmbedderControls embedderControls = embedderControls();
  262.         if (embedderControls.skip()) {
  263.             embedderMonitor.embeddablesSkipped(classNames);
  264.             return;
  265.         }

  266.         for (Embeddable embeddable : embeddables(classNames, classLoader())) {
  267.             if (embeddable instanceof ConfigurableEmbedder) {
  268.                 ConfigurableEmbedder configurableEmbedder = (ConfigurableEmbedder) embeddable;
  269.                 Embedder configuredEmbedder = configurableEmbedder.configuredEmbedder();
  270.                 List<CandidateSteps> steps = configuredEmbedder.stepsFactory().createCandidateSteps();
  271.                 reportStepdocs(configuredEmbedder.configuration(), steps);
  272.             } else {
  273.                 embedderMonitor.embeddableNotConfigurable(embeddable.getClass().getName());
  274.             }
  275.         }
  276.     }

  277.     public void reportMatchingStepdocs(String stepAsString) {
  278.         Configuration configuration = configuration();
  279.         List<CandidateSteps> candidateSteps = stepsFactory().createCandidateSteps();
  280.         StepFinder finder = configuration.stepFinder();
  281.         StepdocReporter reporter = configuration.stepdocReporter();
  282.         List<Stepdoc> matching = finder.findMatching(stepAsString, candidateSteps);
  283.         List<Object> stepsInstances = finder.stepsInstances(candidateSteps);
  284.         reporter.stepdocsMatching(stepAsString, matching, stepsInstances);
  285.     }

  286.     public void processSystemProperties() {
  287.         Properties properties = systemProperties();
  288.         embedderMonitor.processingSystemProperties(properties);
  289.         if (!properties.isEmpty()) {
  290.             for (Object key : properties.keySet()) {
  291.                 String name = (String) key;
  292.                 String value = properties.getProperty(name);
  293.                 System.setProperty(name, value);
  294.                 embedderMonitor.systemPropertySet(name, value);
  295.             }
  296.         }
  297.     }

  298.     public EmbedderClassLoader classLoader() {
  299.         if (classLoader == null) {
  300.             this.classLoader = new EmbedderClassLoader(this.getClass().getClassLoader());
  301.         }
  302.         return classLoader;
  303.     }

  304.     public Configuration configuration() {
  305.         if (configuration == null) {
  306.             this.configuration = new MostUsefulConfiguration();
  307.         }
  308.         configureThreads(configuration, embedderControls().threads());
  309.         return configuration;
  310.     }

  311.     public InjectableStepsFactory stepsFactory() {
  312.         if (stepsFactory == null) {
  313.             stepsFactory = new ProvidedStepsFactory();
  314.         }
  315.         return stepsFactory;
  316.     }

  317.     public EmbedderControls embedderControls() {
  318.         if (embedderControls == null) {
  319.             embedderControls = new EmbedderControls();
  320.         }
  321.         return embedderControls;
  322.     }

  323.     public EmbedderMonitor embedderMonitor() {
  324.         return embedderMonitor;
  325.     }

  326.     public EmbedderFailureStrategy embedderFailureStrategy() {
  327.         if (embedderFailureStrategy == null) {
  328.             this.embedderFailureStrategy = new ThrowingRunningStoriesFailed();
  329.         }
  330.         return embedderFailureStrategy;
  331.     }

  332.     public boolean hasExecutorService() {
  333.         return executorService != null;
  334.     }

  335.     public ExecutorService executorService() {
  336.         if (executorService == null) {
  337.             executorService = createExecutorService();
  338.             executorServiceCreated = true;
  339.         }
  340.         return executorService;
  341.     }

  342.     /**
  343.      * Creates a {@link ThreadPoolExecutor} using the number of threads defined
  344.      * in the {@link EmbedderControls#threads()}
  345.      *
  346.      * @return An ExecutorService
  347.      */
  348.     private ExecutorService createExecutorService() {
  349.         return new FixedThreadExecutors().create(embedderControls());
  350.     }

  351.     /**
  352.      * Shuts down executor service, if it was created by Embedder.
  353.      * ExecutorServices provided by the
  354.      * {@link #useExecutorService(ExecutorService)} need to be managed by the
  355.      * provider.
  356.      */
  357.     protected void shutdownExecutorService() {
  358.         if (executorServiceCreated) {
  359.             executorService.shutdownNow();
  360.             executorService = null;
  361.             executorServiceCreated = false;
  362.         }
  363.     }

  364.     public StoryManager storyManager() {
  365.         if (storyManager == null) {
  366.             storyManager = createStoryManager();
  367.         }
  368.         return storyManager;
  369.     }

  370.     private StoryManager createStoryManager() {
  371.         return new StoryManager(configuration(), stepsFactory(), embedderControls(), embedderMonitor(),
  372.                 executorService(), performableTree(), timeoutParsers());
  373.     }

  374.     protected void configureThreads(Configuration configuration, int threads) {
  375.         StoryReporterBuilder reporterBuilder = configuration.storyReporterBuilder();
  376.         reporterBuilder.withMultiThreading(threads > 1);
  377.         configuration.useStoryReporterBuilder(reporterBuilder);
  378.     }

  379.     public List<String> metaFilters() {
  380.         if (metaFilters == null) {
  381.             metaFilters = new ArrayList<>();
  382.         }
  383.         return metaFilters;
  384.     }

  385.     public Map<String,MetaMatcher> metaMatchers() {
  386.         if (metaMatchers == null) {
  387.             metaMatchers = new HashMap<>();
  388.         }
  389.         return metaMatchers;
  390.     }
  391.    
  392.     public MetaFilter metaFilter() {
  393.         return new MetaFilter(StringUtils.join(metaFilters(), " "), embedderMonitor, metaMatchers());
  394.     }

  395.     public PerformableTree performableTree() {
  396.         return performableTree;
  397.     }

  398.     public Properties systemProperties() {
  399.         if (systemProperties == null) {
  400.             systemProperties = new Properties();
  401.         }
  402.         return systemProperties;
  403.     }
  404.    
  405.     public TimeoutParser[] timeoutParsers() {
  406.         if (timeoutParsers == null) {
  407.             timeoutParsers = new TimeoutParser[]{};
  408.         }
  409.         return timeoutParsers;
  410.     }

  411.     public void useClassLoader(EmbedderClassLoader classLoader) {
  412.         this.classLoader = classLoader;
  413.     }

  414.     public void useConfiguration(Configuration configuration) {
  415.         this.configuration = configuration;
  416.     }

  417.     public void useStepsFactory(InjectableStepsFactory stepsFactory) {
  418.         this.stepsFactory = stepsFactory;
  419.     }

  420.     public void useEmbedderControls(EmbedderControls embedderControls) {
  421.         this.embedderControls = embedderControls;
  422.     }

  423.     public void useEmbedderFailureStrategy(EmbedderFailureStrategy failureStategy) {
  424.         this.embedderFailureStrategy = failureStategy;
  425.     }

  426.     public void useEmbedderMonitor(EmbedderMonitor embedderMonitor) {
  427.         this.embedderMonitor = embedderMonitor;
  428.     }

  429.     public void useExecutorService(ExecutorService executorService) {
  430.         this.executorService = executorService;
  431.         embedderMonitor.usingExecutorService(executorService);
  432.     }

  433.     public void useMetaFilters(List<String> metaFilters) {
  434.         this.metaFilters = metaFilters;
  435.     }

  436.     public void useMetaMatchers(Map<String,MetaMatcher> metaMatchers) {
  437.         this.metaMatchers = metaMatchers;
  438.     }

  439.     public void usePerformableTree(PerformableTree performableTree) {
  440.         this.performableTree = performableTree;
  441.     }

  442.     public void useSystemProperties(Properties systemProperties) {
  443.         this.systemProperties = systemProperties;
  444.     }

  445.     public void useTimeoutParsers(TimeoutParser... timeoutParsers) {
  446.         this.timeoutParsers = timeoutParsers;        
  447.     }
  448.    
  449.     @Override
  450.     public String toString() {
  451.         return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  452.     }

  453.     public static interface EmbedderFailureStrategy {

  454.         void handleFailures(BatchFailures failures);

  455.         void handleFailures(ReportsCount count);

  456.     }

  457.     public static class ThrowingRunningStoriesFailed implements EmbedderFailureStrategy {

  458.         @Override
  459.         public void handleFailures(BatchFailures failures) {
  460.             throw new RunningStoriesFailed(failures);
  461.         }

  462.         @Override
  463.         public void handleFailures(ReportsCount count) {
  464.             throw new RunningStoriesFailed(count);
  465.         }

  466.     }

  467.     @SuppressWarnings("serial")
  468.     public static class AnnotatedEmbedderRunFailed extends RuntimeException {

  469.         public AnnotatedEmbedderRunFailed(AnnotatedEmbedderRunner runner, Throwable cause) {
  470.             super("Annotated embedder run failed with runner " + runner.toString(), cause);
  471.         }

  472.     }

  473.     @SuppressWarnings("serial")
  474.     public static class RunningEmbeddablesFailed extends RuntimeException {

  475.         public RunningEmbeddablesFailed(String name, Throwable failure) {
  476.             super("Failure in running embeddable: " + name, failure);
  477.         }

  478.         public RunningEmbeddablesFailed(BatchFailures failures) {
  479.             super("Failures in running embeddables: " + failures);
  480.         }

  481.     }

  482.     @SuppressWarnings("serial")
  483.     public static class RunningStoriesFailed extends RuntimeException {

  484.         public RunningStoriesFailed(ReportsCount reportsCount) {
  485.             super("Failures in running stories: " + reportsCount);
  486.         }

  487.         public RunningStoriesFailed(BatchFailures failures) {
  488.             super("Failures in running stories: " + failures);
  489.         }

  490.     }

  491.     @SuppressWarnings("serial")
  492.     public static class ViewGenerationFailed extends RuntimeException {
  493.         public ViewGenerationFailed(File outputDirectory, List<String> formats, Properties viewResources,
  494.                 RuntimeException cause) {
  495.             super("View generation failed to " + outputDirectory + " for formats " + formats + " and resources "
  496.                     + viewResources, cause);
  497.         }

  498.         public ViewGenerationFailed(File outputDirectory, StoryMaps storyMaps, Properties viewResources,
  499.                 RuntimeException cause) {
  500.             super("View generation failed to " + outputDirectory + " for story maps " + storyMaps + " for resources "
  501.                     + viewResources, cause);
  502.         }
  503.     }


  504. }