PostStoryStatisticsCollector.java

  1. package org.jbehave.core.reporters;

  2. import static java.util.Arrays.asList;

  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Properties;

  9. import org.apache.commons.lang3.builder.ToStringBuilder;
  10. import org.apache.commons.lang3.builder.ToStringStyle;
  11. import org.jbehave.core.failures.RestartingStoryFailure;
  12. import org.jbehave.core.model.GivenStories;
  13. import org.jbehave.core.model.OutcomesTable;
  14. import org.jbehave.core.model.Scenario;
  15. import org.jbehave.core.model.Story;
  16. import org.jbehave.core.model.StoryDuration;
  17. import org.jbehave.core.steps.StepCreator.PendingStep;
  18. import org.jbehave.core.steps.Timing;

  19. /**
  20.  * <p>
  21.  * Reporter that collects statistics and writes them as properties to output
  22.  * stream after each story
  23.  * </p>
  24.  */
  25. public class PostStoryStatisticsCollector extends NullStoryReporter {

  26.     private final OutputStream output;
  27.     private final Map<String, Integer> data = new HashMap<>();
  28.     private final List<String> events = asList("excluded", "pending", "scenariosExcluded",
  29.             "givenStoryScenariosExcluded", "steps", "stepsSuccessful", "stepsIgnorable", "comments", "stepsPending",
  30.             "stepsNotPerformed", "stepsFailed", "currentScenarioSteps", "currentScenarioStepsPending", "scenarios",
  31.             "scenariosSuccessful", "scenariosPending", "scenariosFailed", "givenStories", "givenStoryScenarios",
  32.             "givenStoryScenariosSuccessful", "givenStoryScenariosPending", "givenStoryScenariosFailed", "examples");

  33.     private Throwable cause;
  34.     private OutcomesTable outcomesFailed;
  35.     private int givenStories;
  36.     private boolean currentScenarioExcluded;

  37.     public PostStoryStatisticsCollector(OutputStream output) {
  38.         this.output = output;
  39.     }

  40.     @Override
  41.     public void successful(String step) {
  42.         add("steps");
  43.         add("stepsSuccessful");
  44.         add("currentScenarioSteps");
  45.     }

  46.     @Override
  47.     public void ignorable(String step) {
  48.         add("steps");
  49.         add("stepsIgnorable");
  50.         add("currentScenarioSteps");
  51.     }

  52.     @Override
  53.     public void comment(String step) {
  54.         add("steps");
  55.         add("comments");
  56.         add("currentScenarioSteps");
  57.     }

  58.     @Override
  59.     public void pending(PendingStep step) {
  60.         add("steps");
  61.         add("stepsPending");
  62.         add("currentScenarioSteps");
  63.         add("currentScenarioStepsPending");
  64.     }

  65.     @Override
  66.     public void notPerformed(String step) {
  67.         add("steps");
  68.         add("stepsNotPerformed");
  69.         add("currentScenarioSteps");
  70.     }

  71.     @Override
  72.     public void failed(String step, Throwable cause) {
  73.         this.cause = cause;

  74.         if (cause != null && !(cause.getCause() instanceof RestartingStoryFailure)) {
  75.             add("steps");
  76.             add("stepsFailed");
  77.             add("currentScenarioSteps");
  78.         }
  79.     }

  80.     @Override
  81.     public void failedOutcomes(String step, OutcomesTable table) {
  82.         this.outcomesFailed = table;
  83.         add("steps");
  84.         add("stepsFailed");
  85.         add("currentScenarioSteps");
  86.     }

  87.     @Override
  88.     public void beforeStory(Story story, boolean givenStory) {
  89.         if (givenStory) {
  90.             this.givenStories++;
  91.         }

  92.         if (!givenStory) {
  93.             resetData();
  94.         }
  95.     }

  96.     @Override
  97.     public void storyExcluded(Story story, String filter) {
  98.         resetData();
  99.         add("excluded");
  100.         writeData();
  101.     }

  102.     @Override
  103.     public void storyCancelled(Story story, StoryDuration storyDuration) {
  104.         add("cancelled");
  105.     }

  106.     @Override
  107.     public void afterStory(boolean givenStory) {
  108.         boolean write = false;
  109.         if (givenStory) {
  110.             this.givenStories--;
  111.             if (has("stepsFailed")) {
  112.                 add("scenariosFailed");
  113.                 write = true;
  114.             }
  115.         } else {
  116.             if (has("scenariosPending") || has("givenStoryScenariosPending")) {
  117.                 add("pending");
  118.             }
  119.             write = true;
  120.         }
  121.         if (write) {
  122.             writeData();
  123.         }
  124.     }

  125.     @Override
  126.     public void givenStories(GivenStories givenStories) {
  127.         add("givenStories");
  128.     }

  129.     @Override
  130.     public void givenStories(List<String> storyPaths) {
  131.         add("givenStories");
  132.     }

  133.     @Override
  134.     public void beforeScenario(Scenario scenario) {
  135.         cause = null;
  136.         outcomesFailed = null;
  137.         currentScenarioExcluded = false;
  138.         reset("currentScenarioSteps");
  139.         reset("currentScenarioStepsPending");
  140.     }

  141.     @Override
  142.     public void scenarioExcluded(Scenario scenario, String filter) {
  143.         if (givenStories > 0) {
  144.             add("givenStoryScenariosExcluded");
  145.         } else {
  146.             add("scenariosExcluded");
  147.             currentScenarioExcluded = true;
  148.         }
  149.     }

  150.     @Override
  151.     public void afterScenario(Timing timing) {
  152.         if (givenStories > 0) {
  153.             countScenarios("givenStoryScenarios");
  154.         } else {
  155.             countScenarios("scenarios");
  156.         }
  157.         if (has("currentScenarioStepsPending") || (!has("currentScenarioSteps") && !currentScenarioExcluded)) {
  158.             if (givenStories > 0) {
  159.                 add("givenStoryScenariosPending");
  160.             } else {
  161.                 add("scenariosPending");
  162.             }
  163.         }
  164.     }

  165.     private void countScenarios(String namespace) {
  166.         add(namespace);
  167.         if (!currentScenarioExcluded) {
  168.             if (cause != null || outcomesFailed != null) {
  169.                 add(namespace + "Failed");
  170.             } else {
  171.                 add(namespace + "Successful");
  172.             }
  173.         }
  174.     }

  175.     @Override
  176.     public void example(Map<String, String> tableRow, int exampleIndex) {
  177.         add("examples");
  178.     }

  179.     @Override
  180.     public void restartedStory(Story story, Throwable cause) {
  181.         resetData();
  182.     }

  183.     private void add(String event) {
  184.         Integer count = data.get(event);
  185.         if (count == null) {
  186.             count = 0;
  187.         }
  188.         count++;
  189.         data.put(event, count);
  190.     }

  191.     private boolean has(String event) {
  192.         Integer count = data.get(event);
  193.         if (count == null) {
  194.             count = 0;
  195.         }
  196.         return count > 0;
  197.     }

  198.     private void writeData() {
  199.         Properties p = new Properties();
  200.         for (String event : data.keySet()) {
  201.             if (!event.startsWith("current")) {
  202.                 p.setProperty(event, data.get(event).toString());
  203.             }
  204.         }
  205.         try {
  206.             p.store(output, this.getClass().getName());
  207.             output.close();
  208.         } catch (IOException e) {
  209.             e.printStackTrace();
  210.         }
  211.     }

  212.     private void resetData() {
  213.         data.clear();
  214.         for (String event : events) {
  215.             reset(event);
  216.         }
  217.     }

  218.     private void reset(String event) {
  219.         data.put(event, 0);
  220.     }

  221.     @Override
  222.     public String toString() {
  223.         return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(output).append(data).toString();
  224.     }
  225. }