From 9c93be902c837ab5e5bcdb37e87f83509c67ab70 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Sun, 19 Feb 2012 18:58:35 +0100 Subject: [PATCH 1/8] Issue #207 UsageFormatter --- .../cucumber/formatter/FormatterFactory.java | 14 ++ .../cucumber/formatter/UsageFormatter.java | 217 ++++++++++++++++++ .../usage/AverageUsageStatisticStrategy.java | 30 +++ .../usage/MedianUsageStatisticStrategy.java | 34 +++ .../usage/UsageStatisticStrategy.java | 15 ++ .../formatter/FormatterFactoryTest.java | 19 +- .../formatter/UsageFormatterTest.java | 145 ++++++++++++ .../AverageUsageStatisticStrategyTest.java | 43 ++++ .../MedianUsageStatisticStrategyTest.java | 51 ++++ 9 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/cucumber/formatter/UsageFormatter.java create mode 100644 core/src/main/java/cucumber/formatter/usage/AverageUsageStatisticStrategy.java create mode 100644 core/src/main/java/cucumber/formatter/usage/MedianUsageStatisticStrategy.java create mode 100644 core/src/main/java/cucumber/formatter/usage/UsageStatisticStrategy.java create mode 100644 core/src/test/java/cucumber/formatter/UsageFormatterTest.java create mode 100644 core/src/test/java/cucumber/formatter/usage/AverageUsageStatisticStrategyTest.java create mode 100644 core/src/test/java/cucumber/formatter/usage/MedianUsageStatisticStrategyTest.java diff --git a/core/src/main/java/cucumber/formatter/FormatterFactory.java b/core/src/main/java/cucumber/formatter/FormatterFactory.java index 231e44a97f..725a8062a7 100644 --- a/core/src/main/java/cucumber/formatter/FormatterFactory.java +++ b/core/src/main/java/cucumber/formatter/FormatterFactory.java @@ -1,5 +1,7 @@ package cucumber.formatter; +import cucumber.formatter.usage.AverageUsageStatisticStrategy; +import cucumber.formatter.usage.MedianUsageStatisticStrategy; import cucumber.runtime.CucumberException; import gherkin.formatter.Formatter; import gherkin.formatter.JSONFormatter; @@ -8,6 +10,7 @@ import java.io.File; import java.io.FileWriter; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; @@ -21,6 +24,7 @@ public class FormatterFactory { put("json", JSONFormatter.class.getName()); put("json-pretty", JSONPrettyFormatter.class.getName()); put("pretty", PrettyFormatter.class.getName()); + put("usage", UsageFormatter.class.getName()); }}; public FormatterFactory(ClassLoader classLoader) { @@ -51,6 +55,8 @@ private Formatter createFormatterFromClassName(String className, Object out) { return formatterClass.getConstructor(ctorArgClass, Boolean.TYPE, Boolean.TYPE).newInstance(out, false, true); } else if (ProgressFormatter.class.isAssignableFrom(formatterClass)) { return formatterClass.getConstructor(ctorArgClass, Boolean.TYPE).newInstance(out, false); + } else if (UsageFormatter.class.isAssignableFrom(formatterClass)) { + return createUsageFormatter(out, ctorArgClass, formatterClass); } else { return formatterClass.getConstructor(ctorArgClass).newInstance(out); } @@ -66,4 +72,12 @@ private Class getFormatterClass(String className) { throw new CucumberException("Formatter class not found: " + className, e); } } + + private UsageFormatter createUsageFormatter(Object out, Class ctorArgClass, Class formatterClass) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException + { + UsageFormatter formatter = (UsageFormatter)formatterClass.getConstructor(ctorArgClass).newInstance(out); + formatter.addUsageStatisticStrategy("average", new AverageUsageStatisticStrategy()); + formatter.addUsageStatisticStrategy("median", new MedianUsageStatisticStrategy()); + return formatter; + } } diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java new file mode 100644 index 0000000000..2bdb6ff6d4 --- /dev/null +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -0,0 +1,217 @@ +package cucumber.formatter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import cucumber.formatter.usage.UsageStatisticStrategy; +import gherkin.formatter.Format; +import gherkin.formatter.Formatter; +import gherkin.formatter.MonochromeFormats; +import gherkin.formatter.NiceAppendable; +import gherkin.formatter.Reporter; +import gherkin.formatter.StepPrinter; +import gherkin.formatter.model.Background; +import gherkin.formatter.model.Examples; +import gherkin.formatter.model.Feature; +import gherkin.formatter.model.Match; +import gherkin.formatter.model.Result; +import gherkin.formatter.model.Scenario; +import gherkin.formatter.model.ScenarioOutline; +import gherkin.formatter.model.Step; + +/** + * Formatter to measure performance of steps. Aggregated results for all steps can be computed + * by adding {@link UsageStatisticStrategy} to the usageFormatter + */ +public class UsageFormatter implements Formatter, Reporter +{ + private final MonochromeFormats monochromeFormat = new MonochromeFormats(); + private final StepPrinter stepPrinter = new StepPrinter(); + + final Map> usageMap = new HashMap>(); + final Map statisticStrategies = new HashMap(); + + private final List steps = new ArrayList(); + private final NiceAppendable out; + + private Match match; + + /** + * Constructor + * @param out {@link Appendable} to print the result + */ + public UsageFormatter(Appendable out) + { + this.out = new NiceAppendable(out); + } + + @Override + public void uri(String uri) + { + } + + @Override + public void feature(Feature feature) + { + } + + @Override + public void background(Background background) + { + } + + @Override + public void scenario(Scenario scenario) + { + } + + @Override + public void scenarioOutline(ScenarioOutline scenarioOutline) + { + } + + @Override + public void examples(Examples examples) + { + } + + @Override + public void step(Step step) + { + steps.add(step); + } + + @Override + public void eof() + { + } + + @Override + public void syntaxError(String state, String event, List legalEvents, String uri, int line) + { + } + + @Override + public void done() + { + for (Map.Entry> usageEntry : usageMap.entrySet()) + { + printAggregatedResult(usageEntry); + + String key = usageEntry.getKey(); + for (Long duration : usageEntry.getValue()) + { + printSingleDurationEntry(key, duration); + } + } + } + + private void printAggregatedResult(Map.Entry> usageEntry) + { + String stepName = usageEntry.getKey(); + for (Map.Entry calculatorEntry : statisticStrategies.entrySet()) + { + String calculatorName = calculatorEntry.getKey(); + UsageStatisticStrategy statisticStrategy = calculatorEntry.getValue(); + + Long calculationResult = statisticStrategy.calculate(usageEntry.getValue()); + out.println(String.format("%s (%s) : %s", formatDuration(calculationResult), calculatorName, stepName)); + } + } + + private void printSingleDurationEntry(String key, Long duration) + { + String formattedDuration = formatDuration(duration); + out.println("\t" + formattedDuration + " : " + key); + } + + private String formatDuration(Long duration) + { + long seconds = TimeUnit.MICROSECONDS.toSeconds(duration); + long microSeconds = duration - TimeUnit.SECONDS.toMicros(seconds); + return String.format("%d.%06d", seconds, microSeconds); + } + + @Override + public void close() + { + out.close(); + } + + @Override + public void result(Result result) + { + if (steps.size() > 0) + { + Step step = steps.remove(0); + String stepNameWithArgs = formatStepNameWithArgs(result, step); + addUsageEntry(result, stepNameWithArgs); + } + } + + private String formatStepNameWithArgs(Result result, Step step) + { + StringBuffer buffer = new StringBuffer(); + Format format = getFormat(result.getStatus()); + Format argFormat = getArgFormat(result.getStatus()); + stepPrinter.writeStep(new NiceAppendable(buffer), format, argFormat, step.getName(), match.getArguments()); + + return buffer.toString(); + } + + private void addUsageEntry(Result result, String stepNameWithArgs) + { + List durationEntries = usageMap.get(stepNameWithArgs); + if (durationEntries == null) + { + durationEntries = new ArrayList(); + usageMap.put(stepNameWithArgs, durationEntries); + } + durationEntries.add(durationInMillis(result)); + } + + private Long durationInMillis(Result result) + { + long duration; + if (result.getDuration() == null) + { + duration = 0; + } else + { + duration = result.getDuration() / 1000; + } + return duration; + } + + @Override + public void match(Match match) + { + this.match = match; + } + + @Override + public void embedding(String mimeType, byte[] data) + { + } + + private Format getFormat(String key) { + return monochromeFormat.get(key); + } + + private Format getArgFormat(String key) { + return monochromeFormat.get(key + "_arg"); + } + + /** + * Add a {@link UsageStatisticStrategy} to the formatter + * @param key the key, will be displayed in the output + * @param strategy the strategy + */ + public void addUsageStatisticStrategy(String key, UsageStatisticStrategy strategy) + { + statisticStrategies.put(key, strategy); + } +} diff --git a/core/src/main/java/cucumber/formatter/usage/AverageUsageStatisticStrategy.java b/core/src/main/java/cucumber/formatter/usage/AverageUsageStatisticStrategy.java new file mode 100644 index 0000000000..b0b032db09 --- /dev/null +++ b/core/src/main/java/cucumber/formatter/usage/AverageUsageStatisticStrategy.java @@ -0,0 +1,30 @@ +package cucumber.formatter.usage; + +import java.util.List; + +/** + * Calculate the average of a list of duration entries + */ +public class AverageUsageStatisticStrategy implements UsageStatisticStrategy +{ + @Override + public Long calculate(List durationEntries) + { + if (verifyNoNulls(durationEntries)) + { + return 0L; + } + + long sum = 0; + for (Long duration : durationEntries) + { + sum += duration; + } + return sum / durationEntries.size(); + } + + private boolean verifyNoNulls(List durationEntries) + { + return durationEntries == null || durationEntries.isEmpty() || durationEntries.contains(null); + } +} diff --git a/core/src/main/java/cucumber/formatter/usage/MedianUsageStatisticStrategy.java b/core/src/main/java/cucumber/formatter/usage/MedianUsageStatisticStrategy.java new file mode 100644 index 0000000000..98573f3353 --- /dev/null +++ b/core/src/main/java/cucumber/formatter/usage/MedianUsageStatisticStrategy.java @@ -0,0 +1,34 @@ +package cucumber.formatter.usage; + +import java.util.Collections; +import java.util.List; + +/** + * Calculate the median of a list of duration entries + */ +public class MedianUsageStatisticStrategy implements UsageStatisticStrategy +{ + @Override + public Long calculate(List durationEntries) + { + if (verifyNoNulls(durationEntries)) + { + return 0L; + } + Collections.sort(durationEntries); + int middle = durationEntries.size() / 2; + if (durationEntries.size() % 2 == 1) + { + return durationEntries.get(middle); + } + else + { + return (durationEntries.get(middle - 1) + durationEntries.get(middle)) / 2; + } + } + + private boolean verifyNoNulls(List durationEntries) + { + return durationEntries == null || durationEntries.isEmpty() || durationEntries.contains(null); + } +} diff --git a/core/src/main/java/cucumber/formatter/usage/UsageStatisticStrategy.java b/core/src/main/java/cucumber/formatter/usage/UsageStatisticStrategy.java new file mode 100644 index 0000000000..c4a612ed14 --- /dev/null +++ b/core/src/main/java/cucumber/formatter/usage/UsageStatisticStrategy.java @@ -0,0 +1,15 @@ +package cucumber.formatter.usage; + +import java.util.List; + +/** + * Calculate a statistical value to be displayed in the usage-file + */ +public interface UsageStatisticStrategy +{ + /** + * @param durationEntries list of execution times of steps as nanoseconds + * @return a statistical value (e.g. median, average, ..) + */ + Long calculate(List durationEntries); +} diff --git a/core/src/test/java/cucumber/formatter/FormatterFactoryTest.java b/core/src/test/java/cucumber/formatter/FormatterFactoryTest.java index 838ca4bbb2..3875aed9a9 100644 --- a/core/src/test/java/cucumber/formatter/FormatterFactoryTest.java +++ b/core/src/test/java/cucumber/formatter/FormatterFactoryTest.java @@ -1,5 +1,8 @@ package cucumber.formatter; +import cucumber.formatter.usage.AverageUsageStatisticStrategy; +import cucumber.formatter.usage.MedianUsageStatisticStrategy; +import cucumber.formatter.usage.UsageStatisticStrategy; import gherkin.formatter.Formatter; import gherkin.formatter.JSONFormatter; import gherkin.formatter.JSONPrettyFormatter; @@ -8,10 +11,13 @@ import java.io.File; import java.io.StringWriter; +import java.util.Map; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; public class FormatterFactoryTest { @@ -42,6 +48,18 @@ public void shouldInstantiateHtmlFormatter() { assertThat(formatterFactory.createFormatter("html", new File(System.getProperty("user.dir"))), is(HTMLFormatter.class)); } + + @Test + public void shouldInstantiateUsageFormatter() { + Formatter formatter = formatterFactory.createFormatter("usage", mock(Appendable.class)); + + assertThat(formatter, is(UsageFormatter.class)); + Map statisticStrategies = ((UsageFormatter) formatter).statisticStrategies; + assertEquals(statisticStrategies.size(), 2); + assertThat(statisticStrategies.get("average"), is(AverageUsageStatisticStrategy.class)); + assertThat(statisticStrategies.get("median"), is(MedianUsageStatisticStrategy.class)); + } + @Test public void shouldInstantiateCustomFormatterFromClassNameWithAppender() { StringWriter writer = new StringWriter(); @@ -57,5 +75,4 @@ public void shouldInstantiateCustomFormatterFromClassNameWithDirFile() { assertThat(formatter, is(TestFormatter.class)); assertSame(dir, ((TestFormatter) formatter).dir); } - } diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java new file mode 100644 index 0000000000..427cb6af43 --- /dev/null +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -0,0 +1,145 @@ +package cucumber.formatter; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import cucumber.formatter.usage.UsageStatisticStrategy; +import gherkin.formatter.model.Match; +import gherkin.formatter.model.Result; +import gherkin.formatter.model.Step; + +public class UsageFormatterTest +{ + @Test + public void close() throws IOException + { + Appendable out = mock(Appendable.class, withSettings().extraInterfaces(Closeable.class)); + UsageFormatter usageFormatter = new UsageFormatter(out); + usageFormatter.close(); + verify((Closeable) out).close(); + } + + @Test + public void resultWithoutSteps() + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + Result result = mock(Result.class); + usageFormatter.result(result); + verifyZeroInteractions(out); + } + + @Test + public void resultWithOneStep() + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + + Step step = mock(Step.class); + when(step.getName()).thenReturn("step"); + usageFormatter.step(step); + + Match match = mock(Match.class); + usageFormatter.match(match); + + Result result = mock(Result.class); + when(result.getDuration()).thenReturn(12345L); + usageFormatter.result(result); + + Map> usageMap = usageFormatter.usageMap; + assertEquals(usageMap.size(), 1); + List durationEntries = usageMap.get("step"); + assertEquals(durationEntries.size(), 1); + assertEquals(durationEntries.get(0), Long.valueOf(12)); + } + + @Test + public void resultWithZeroDuration() + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + + Step step = mock(Step.class); + when(step.getName()).thenReturn("step"); + usageFormatter.step(step); + + Match match = mock(Match.class); + usageFormatter.match(match); + + Result result = mock(Result.class); + when(result.getDuration()).thenReturn(0L); + usageFormatter.result(result); + + Map> usageMap = usageFormatter.usageMap; + assertEquals(usageMap.size(), 1); + List durationEntries = usageMap.get("step"); + assertEquals(durationEntries.size(), 1); + assertEquals(durationEntries.get(0), Long.valueOf(0)); + } + + @Test + public void resultWithNullDuration() + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + + Step step = mock(Step.class); + when(step.getName()).thenReturn("step"); + usageFormatter.step(step); + + Match match = mock(Match.class); + usageFormatter.match(match); + + Result result = mock(Result.class); + when(result.getDuration()).thenReturn(null); + usageFormatter.result(result); + + Map> usageMap = usageFormatter.usageMap; + assertEquals(usageMap.size(), 1); + List durationEntries = usageMap.get("step"); + assertEquals(durationEntries.size(), 1); + assertEquals(durationEntries.get(0), Long.valueOf(0)); + } + + @Test + public void doneWithoutUsageStatisticStrategies() throws IOException + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); + + usageFormatter.done(); + + verify(out).append("\t12.345678 : aStep"); + } + + @Test + public void doneWithUsageStatisticStrategies() throws IOException + { + Appendable out = mock(Appendable.class); + UsageFormatter usageFormatter = new UsageFormatter(out); + + UsageStatisticStrategy usageStatisticStrategy = mock(UsageStatisticStrategy.class); + when(usageStatisticStrategy.calculate(Arrays.asList(12345678L))).thenReturn(23456L); + usageFormatter.addUsageStatisticStrategy("average", usageStatisticStrategy); + + usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); + + usageFormatter.done(); + + verify(out).append("0.023456 (average) : aStep"); + verify(out).append("\t12.345678 : aStep"); + } +} diff --git a/core/src/test/java/cucumber/formatter/usage/AverageUsageStatisticStrategyTest.java b/core/src/test/java/cucumber/formatter/usage/AverageUsageStatisticStrategyTest.java new file mode 100644 index 0000000000..03fec9d05f --- /dev/null +++ b/core/src/test/java/cucumber/formatter/usage/AverageUsageStatisticStrategyTest.java @@ -0,0 +1,43 @@ +package cucumber.formatter.usage; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Test; + +public class AverageUsageStatisticStrategyTest +{ + @Test + public void calculate() throws Exception + { + AverageUsageStatisticStrategy averageUsageStatisticStrategy = new AverageUsageStatisticStrategy(); + Long result = averageUsageStatisticStrategy.calculate(Arrays.asList(1L, 2L, 3L)); + assertEquals(result, Long.valueOf(2)); + } + + @Test + public void calculateNull() throws Exception + { + AverageUsageStatisticStrategy averageUsageStatisticStrategy = new AverageUsageStatisticStrategy(); + Long result = averageUsageStatisticStrategy.calculate(null); + assertEquals(result, Long.valueOf(0)); + } + + @Test + public void calculateEmptylist() throws Exception + { + AverageUsageStatisticStrategy averageUsageStatisticStrategy = new AverageUsageStatisticStrategy(); + Long result = averageUsageStatisticStrategy.calculate(Collections.emptyList()); + assertEquals(result, Long.valueOf(0)); + } + + @Test + public void calculateListWithNulls() throws Exception + { + AverageUsageStatisticStrategy averageUsageStatisticStrategy = new AverageUsageStatisticStrategy(); + Long result = averageUsageStatisticStrategy.calculate(Arrays.asList(3L, null)); + assertEquals(result, Long.valueOf(0)); + } +} diff --git a/core/src/test/java/cucumber/formatter/usage/MedianUsageStatisticStrategyTest.java b/core/src/test/java/cucumber/formatter/usage/MedianUsageStatisticStrategyTest.java new file mode 100644 index 0000000000..110813ae69 --- /dev/null +++ b/core/src/test/java/cucumber/formatter/usage/MedianUsageStatisticStrategyTest.java @@ -0,0 +1,51 @@ +package cucumber.formatter.usage; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Test; + +public class MedianUsageStatisticStrategyTest +{ + @Test + public void calculateOddEntries() throws Exception + { + MedianUsageStatisticStrategy medianUsageStatisticStrategy = new MedianUsageStatisticStrategy(); + Long result = medianUsageStatisticStrategy.calculate(Arrays.asList(1L, 2L, 3L)); + assertEquals(result, Long.valueOf(2)); + } + + @Test + public void calculateEvenEntries() throws Exception + { + MedianUsageStatisticStrategy medianUsageStatisticStrategy = new MedianUsageStatisticStrategy(); + Long result = medianUsageStatisticStrategy.calculate(Arrays.asList(1L, 3L, 10L, 5L)); + assertEquals(result, Long.valueOf(4)); + } + + @Test + public void calculateNull() throws Exception + { + MedianUsageStatisticStrategy medianUsageStatisticStrategy = new MedianUsageStatisticStrategy(); + Long result = medianUsageStatisticStrategy.calculate(null); + assertEquals(result, Long.valueOf(0)); + } + + @Test + public void calculateEmptylist() throws Exception + { + MedianUsageStatisticStrategy medianUsageStatisticStrategy = new MedianUsageStatisticStrategy(); + Long result = medianUsageStatisticStrategy.calculate(Collections.emptyList()); + assertEquals(result, Long.valueOf(0)); + } + + @Test + public void calculateListWithNulls() throws Exception + { + MedianUsageStatisticStrategy medianUsageStatisticStrategy = new MedianUsageStatisticStrategy(); + Long result = medianUsageStatisticStrategy.calculate(Arrays.asList(1L, null, 3L)); + assertEquals(result, Long.valueOf(0)); + } +} From e6d6e4bb74e29029e0e536eb43611c6276405dc4 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Sun, 19 Feb 2012 19:12:58 +0100 Subject: [PATCH 2/8] Added Keyword for Output --- .../main/java/cucumber/formatter/UsageFormatter.java | 1 + .../java/cucumber/formatter/UsageFormatterTest.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index 2bdb6ff6d4..be5c23c8d4 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -155,6 +155,7 @@ public void result(Result result) private String formatStepNameWithArgs(Result result, Step step) { StringBuffer buffer = new StringBuffer(); + buffer.append(step.getKeyword()).append(" "); Format format = getFormat(result.getStatus()); Format argFormat = getArgFormat(result.getStatus()); stepPrinter.writeStep(new NiceAppendable(buffer), format, argFormat, step.getName(), match.getArguments()); diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index 427cb6af43..1699f2f558 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -49,6 +49,7 @@ public void resultWithOneStep() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); + when(step.getKeyword()).thenReturn("when"); usageFormatter.step(step); Match match = mock(Match.class); @@ -56,11 +57,12 @@ public void resultWithOneStep() Result result = mock(Result.class); when(result.getDuration()).thenReturn(12345L); + usageFormatter.result(result); Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("step"); + List durationEntries = usageMap.get("when step"); assertEquals(durationEntries.size(), 1); assertEquals(durationEntries.get(0), Long.valueOf(12)); } @@ -73,6 +75,7 @@ public void resultWithZeroDuration() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); + when(step.getKeyword()).thenReturn("when"); usageFormatter.step(step); Match match = mock(Match.class); @@ -80,11 +83,12 @@ public void resultWithZeroDuration() Result result = mock(Result.class); when(result.getDuration()).thenReturn(0L); + usageFormatter.result(result); Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("step"); + List durationEntries = usageMap.get("when step"); assertEquals(durationEntries.size(), 1); assertEquals(durationEntries.get(0), Long.valueOf(0)); } @@ -97,6 +101,7 @@ public void resultWithNullDuration() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); + when(step.getKeyword()).thenReturn("then"); usageFormatter.step(step); Match match = mock(Match.class); @@ -108,7 +113,7 @@ public void resultWithNullDuration() Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("step"); + List durationEntries = usageMap.get("then step"); assertEquals(durationEntries.size(), 1); assertEquals(durationEntries.get(0), Long.valueOf(0)); } From 3a7ca5f5177040be012998f2cd5fa0fd4d9d5e31 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Mon, 20 Feb 2012 13:19:52 +0100 Subject: [PATCH 3/8] Issue #270 output in json --- .../cucumber/formatter/UsageFormatter.java | 73 ++++++++++++++----- .../formatter/UsageFormatterTest.java | 17 +++-- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index be5c23c8d4..c8f531ab83 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -7,6 +7,8 @@ import java.util.concurrent.TimeUnit; import cucumber.formatter.usage.UsageStatisticStrategy; +import gherkin.deps.com.google.gson.Gson; +import gherkin.deps.com.google.gson.GsonBuilder; import gherkin.formatter.Format; import gherkin.formatter.Formatter; import gherkin.formatter.MonochromeFormats; @@ -97,35 +99,47 @@ public void syntaxError(String state, String event, List legalEvents, St @Override public void done() { + List stepContainers = new ArrayList(); + for (Map.Entry> usageEntry : usageMap.entrySet()) { - printAggregatedResult(usageEntry); + StepContainer stepContainer = new StepContainer(); + stepContainers.add(stepContainer); - String key = usageEntry.getKey(); - for (Long duration : usageEntry.getValue()) - { - printSingleDurationEntry(key, duration); - } + stepContainer.stepName = usageEntry.getKey(); + stepContainer.durations = formatDurationEntries(usageEntry.getValue()); + + stepContainer.aggregatedResults = createAggregatedResults(usageEntry); } + + out.append(gson().toJson(stepContainers)); } - private void printAggregatedResult(Map.Entry> usageEntry) + private List formatDurationEntries(List durationEntries) { - String stepName = usageEntry.getKey(); - for (Map.Entry calculatorEntry : statisticStrategies.entrySet()) + ArrayList formattedDuration = new ArrayList(); + for(Long duration : durationEntries) { - String calculatorName = calculatorEntry.getKey(); - UsageStatisticStrategy statisticStrategy = calculatorEntry.getValue(); - - Long calculationResult = statisticStrategy.calculate(usageEntry.getValue()); - out.println(String.format("%s (%s) : %s", formatDuration(calculationResult), calculatorName, stepName)); + formattedDuration.add(formatDuration(duration)); } + return formattedDuration; } - private void printSingleDurationEntry(String key, Long duration) + private List createAggregatedResults(Map.Entry> usageEntry) { - String formattedDuration = formatDuration(duration); - out.println("\t" + formattedDuration + " : " + key); + ArrayList aggregatedResults = new ArrayList(); + for (Map.Entry calculatorEntry : statisticStrategies.entrySet()) + { + AggregatedResult aggregatedResult = new AggregatedResult(); + aggregatedResults.add(aggregatedResult); + + UsageStatisticStrategy statisticStrategy = calculatorEntry.getValue(); + Long calculationResult = statisticStrategy.calculate(usageEntry.getValue()); + + aggregatedResult.strategy = calculatorEntry.getKey(); + aggregatedResult.value = formatDuration(calculationResult); + } + return aggregatedResults; } private String formatDuration(Long duration) @@ -135,6 +149,10 @@ private String formatDuration(Long duration) return String.format("%d.%06d", seconds, microSeconds); } + private Gson gson() { + return new GsonBuilder().setPrettyPrinting().create(); + } + @Override public void close() { @@ -144,7 +162,7 @@ public void close() @Override public void result(Result result) { - if (steps.size() > 0) + if (!steps.isEmpty()) { Step step = steps.remove(0); String stepNameWithArgs = formatStepNameWithArgs(result, step); @@ -215,4 +233,23 @@ public void addUsageStatisticStrategy(String key, UsageStatisticStrategy strateg { statisticStrategies.put(key, strategy); } + + /** + * Contains for usage-entries of steps + */ + private static class StepContainer { + public String stepName; + public List aggregatedResults = new ArrayList(); + public List durations = new ArrayList(); + } + + /** + * Container for aggregated results, computed by a specific strategy (e.g. average, median, ..) + */ + private static class AggregatedResult { + public String strategy; + public String value; + } + + } diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index 1699f2f558..0058071998 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -1,6 +1,7 @@ package cucumber.formatter; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -121,30 +122,30 @@ public void resultWithNullDuration() @Test public void doneWithoutUsageStatisticStrategies() throws IOException { - Appendable out = mock(Appendable.class); + StringBuffer out = new StringBuffer(); UsageFormatter usageFormatter = new UsageFormatter(out); usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); usageFormatter.done(); - - verify(out).append("\t12.345678 : aStep"); + + assertTrue(out.toString().contains("12.345678")); } @Test public void doneWithUsageStatisticStrategies() throws IOException { - Appendable out = mock(Appendable.class); + StringBuffer out = new StringBuffer(); UsageFormatter usageFormatter = new UsageFormatter(out); UsageStatisticStrategy usageStatisticStrategy = mock(UsageStatisticStrategy.class); when(usageStatisticStrategy.calculate(Arrays.asList(12345678L))).thenReturn(23456L); usageFormatter.addUsageStatisticStrategy("average", usageStatisticStrategy); - + usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); usageFormatter.done(); - - verify(out).append("0.023456 (average) : aStep"); - verify(out).append("\t12.345678 : aStep"); + + assertTrue(out.toString().contains("0.023456")); + assertTrue(out.toString().contains("12.345678")); } } From 3d93be79c66a67ccc8ea2d65f021783af2916755 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Mon, 20 Feb 2012 16:07:30 +0100 Subject: [PATCH 4/8] removed unnecessary space (keyword already contains a space) --- core/src/main/java/cucumber/formatter/UsageFormatter.java | 2 +- .../test/java/cucumber/formatter/UsageFormatterTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index c8f531ab83..651e32fab6 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -173,7 +173,7 @@ public void result(Result result) private String formatStepNameWithArgs(Result result, Step step) { StringBuffer buffer = new StringBuffer(); - buffer.append(step.getKeyword()).append(" "); + buffer.append(step.getKeyword()); Format format = getFormat(result.getStatus()); Format argFormat = getArgFormat(result.getStatus()); stepPrinter.writeStep(new NiceAppendable(buffer), format, argFormat, step.getName(), match.getArguments()); diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index 0058071998..9ea3148af9 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -50,7 +50,7 @@ public void resultWithOneStep() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("when"); + when(step.getKeyword()).thenReturn("when "); usageFormatter.step(step); Match match = mock(Match.class); @@ -76,7 +76,7 @@ public void resultWithZeroDuration() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("when"); + when(step.getKeyword()).thenReturn("when "); usageFormatter.step(step); Match match = mock(Match.class); @@ -102,7 +102,7 @@ public void resultWithNullDuration() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("then"); + when(step.getKeyword()).thenReturn("then "); usageFormatter.step(step); Match match = mock(Match.class); From 2e564576cd36ed54f3acadbe6f0c6eec41c465c4 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Thu, 23 Feb 2012 13:47:39 +0100 Subject: [PATCH 5/8] * added steplocation to report * added stepDefinition (pattern) * grouped steps under stepdefinitions --- .../cucumber/formatter/UsageFormatter.java | 173 ++++++++++++------ .../formatter/UsageFormatterTest.java | 72 +++++--- 2 files changed, 168 insertions(+), 77 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index 651e32fab6..e72e4b370f 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -1,12 +1,13 @@ package cucumber.formatter; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import cucumber.formatter.usage.UsageStatisticStrategy; +import cucumber.runtime.StepDefinitionMatch; import gherkin.deps.com.google.gson.Gson; import gherkin.deps.com.google.gson.GsonBuilder; import gherkin.formatter.Format; @@ -33,7 +34,7 @@ public class UsageFormatter implements Formatter, Reporter private final MonochromeFormats monochromeFormat = new MonochromeFormats(); private final StepPrinter stepPrinter = new StepPrinter(); - final Map> usageMap = new HashMap>(); + final Map> usageMap = new HashMap>(); final Map statisticStrategies = new HashMap(); private final List steps = new ArrayList(); @@ -99,54 +100,66 @@ public void syntaxError(String state, String event, List legalEvents, St @Override public void done() { - List stepContainers = new ArrayList(); - - for (Map.Entry> usageEntry : usageMap.entrySet()) + List stepDefContainers = new ArrayList(); + for (Map.Entry> usageEntry : usageMap.entrySet()) { - StepContainer stepContainer = new StepContainer(); - stepContainers.add(stepContainer); - - stepContainer.stepName = usageEntry.getKey(); - stepContainer.durations = formatDurationEntries(usageEntry.getValue()); + StepDefContainer stepDefContainer = new StepDefContainer(); + stepDefContainers.add(stepDefContainer); - stepContainer.aggregatedResults = createAggregatedResults(usageEntry); + stepDefContainer.source = usageEntry.getKey(); + stepDefContainer.steps = createStepContainer(usageEntry.getValue()); } - out.append(gson().toJson(stepContainers)); + out.append(gson().toJson(stepDefContainers)); } - private List formatDurationEntries(List durationEntries) + private List createStepContainer(List stepContainers) { - ArrayList formattedDuration = new ArrayList(); - for(Long duration : durationEntries) + for (StepContainer stepContainer : stepContainers) { - formattedDuration.add(formatDuration(duration)); + stepContainer.aggregatedResults = createAggregatedResults(stepContainer); + formatDurationAsSeconds(stepContainer.durations); } - return formattedDuration; + return stepContainers; } - private List createAggregatedResults(Map.Entry> usageEntry) + private void formatDurationAsSeconds(List durations) { - ArrayList aggregatedResults = new ArrayList(); + for (StepDuration duration : durations) + { + duration.duration = toSeconds(duration.duration.longValue()); + } + } + + private Map createAggregatedResults(StepContainer stepContainer) + { + Map aggregatedResults = new HashMap(); for (Map.Entry calculatorEntry : statisticStrategies.entrySet()) { - AggregatedResult aggregatedResult = new AggregatedResult(); - aggregatedResults.add(aggregatedResult); - UsageStatisticStrategy statisticStrategy = calculatorEntry.getValue(); - Long calculationResult = statisticStrategy.calculate(usageEntry.getValue()); + List rawDurations = getRawDurations(stepContainer.durations); + Long calculationResult = statisticStrategy.calculate(rawDurations); - aggregatedResult.strategy = calculatorEntry.getKey(); - aggregatedResult.value = formatDuration(calculationResult); + String strategy = calculatorEntry.getKey(); + aggregatedResults.put(strategy, toSeconds(calculationResult)); } return aggregatedResults; } - private String formatDuration(Long duration) + private BigDecimal toSeconds(Long nanoSeconds) { - long seconds = TimeUnit.MICROSECONDS.toSeconds(duration); - long microSeconds = duration - TimeUnit.SECONDS.toMicros(seconds); - return String.format("%d.%06d", seconds, microSeconds); + return BigDecimal.valueOf(nanoSeconds).divide(BigDecimal.valueOf(1000000000)); + } + + private List getRawDurations(List stepDurations) + { + List rawDurations = new ArrayList(); + + for(StepDuration stepDuration : stepDurations) + { + rawDurations.add(stepDuration.duration.longValue()); + } + return rawDurations; } private Gson gson() { @@ -162,18 +175,28 @@ public void close() @Override public void result(Result result) { - if (!steps.isEmpty()) + String stepDefinition = getStepDefinition(); + + if (stepDefinition != null && !steps.isEmpty()) { Step step = steps.remove(0); String stepNameWithArgs = formatStepNameWithArgs(result, step); - addUsageEntry(result, stepNameWithArgs); + addUsageEntry(result, stepDefinition, stepNameWithArgs); } } + private String getStepDefinition() + { + if (match instanceof StepDefinitionMatch) + { + return ((StepDefinitionMatch) match).getPattern(); + } + return null; + } + private String formatStepNameWithArgs(Result result, Step step) { StringBuffer buffer = new StringBuffer(); - buffer.append(step.getKeyword()); Format format = getFormat(result.getStatus()); Format argFormat = getArgFormat(result.getStatus()); stepPrinter.writeStep(new NiceAppendable(buffer), format, argFormat, step.getName(), match.getArguments()); @@ -181,28 +204,59 @@ private String formatStepNameWithArgs(Result result, Step step) return buffer.toString(); } - private void addUsageEntry(Result result, String stepNameWithArgs) + private void addUsageEntry(Result result, String stepDefinition, String stepNameWithArgs) + { + List stepContainers = usageMap.get(stepDefinition); + if (stepContainers == null) + { + stepContainers = new ArrayList(); + usageMap.put(stepDefinition, stepContainers); + } + StepContainer stepContainer = findOrCreateStepContainer(stepNameWithArgs, stepContainers); + + String stepLocation = getStepLocation(); + Long duration = result.getDuration(); + StepDuration stepDuration = createStepDuration(duration, stepLocation); + stepContainer.durations.add(stepDuration); + } + + private String getStepLocation() { - List durationEntries = usageMap.get(stepNameWithArgs); - if (durationEntries == null) + if(match instanceof StepDefinitionMatch) { - durationEntries = new ArrayList(); - usageMap.put(stepNameWithArgs, durationEntries); + StackTraceElement stepLocation = ((StepDefinitionMatch) match).getStepLocation(); + return stepLocation.getFileName() + ":" + stepLocation.getLineNumber(); } - durationEntries.add(durationInMillis(result)); + return null; } - private Long durationInMillis(Result result) + private StepDuration createStepDuration(Long duration, String location) { - long duration; - if (result.getDuration() == null) + StepDuration stepDuration = new StepDuration(); + if (duration == null) { - duration = 0; + stepDuration.duration = BigDecimal.ZERO; } else { - duration = result.getDuration() / 1000; + stepDuration.duration = BigDecimal.valueOf(duration); + } + stepDuration.location = location; + return stepDuration; + } + + private StepContainer findOrCreateStepContainer(String stepNameWithArgs, List stepContainers) + { + for (StepContainer container : stepContainers) + { + if (stepNameWithArgs.equals(container.name)) + { + return container; + } } - return duration; + StepContainer stepContainer = new StepContainer(); + stepContainer.name = stepNameWithArgs; + stepContainers.add(stepContainer); + return stepContainer; } @Override @@ -235,21 +289,32 @@ public void addUsageStatisticStrategy(String key, UsageStatisticStrategy strateg } /** - * Contains for usage-entries of steps + * Container of Step Definitions (patterns) */ - private static class StepContainer { - public String stepName; - public List aggregatedResults = new ArrayList(); - public List durations = new ArrayList(); + static class StepDefContainer + { + /** + * The StepDefinition (pattern) + */ + public String source; + + /** + * A list of Steps + */ + public List steps; } /** - * Container for aggregated results, computed by a specific strategy (e.g. average, median, ..) + * Contains for usage-entries of steps */ - private static class AggregatedResult { - public String strategy; - public String value; + static class StepContainer { + public String name; + public Map aggregatedResults = new HashMap(); + public List durations = new ArrayList(); } - + static class StepDuration { + public BigDecimal duration; + public String location; + } } diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index 9ea3148af9..c27dc0ccb9 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -10,14 +10,16 @@ import java.io.Closeable; import java.io.IOException; +import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.Test; +import org.mockito.Mockito; import cucumber.formatter.usage.UsageStatisticStrategy; -import gherkin.formatter.model.Match; +import cucumber.runtime.StepDefinitionMatch; import gherkin.formatter.model.Result; import gherkin.formatter.model.Step; @@ -50,10 +52,9 @@ public void resultWithOneStep() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("when "); usageFormatter.step(step); - Match match = mock(Match.class); + StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); Result result = mock(Result.class); @@ -61,11 +62,21 @@ public void resultWithOneStep() usageFormatter.result(result); - Map> usageMap = usageFormatter.usageMap; + Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("when step"); + List durationEntries = usageMap.get("stepDef"); assertEquals(durationEntries.size(), 1); - assertEquals(durationEntries.get(0), Long.valueOf(12)); + assertEquals(durationEntries.get(0).name, "step"); + assertEquals(durationEntries.get(0).durations.size(), 1); + assertEquals(durationEntries.get(0).durations.get(0).duration, BigDecimal.valueOf(12345)); + } + + private StepDefinitionMatch mockStepDefinitionMatch() + { + StepDefinitionMatch match = mock(StepDefinitionMatch.class, Mockito.RETURNS_MOCKS); + when(match.getPattern()).thenReturn("stepDef"); + when(match.getStepLocation()).thenReturn(new StackTraceElement("x", "y", "z", 3)); + return match; } @Test @@ -74,12 +85,11 @@ public void resultWithZeroDuration() Appendable out = mock(Appendable.class); UsageFormatter usageFormatter = new UsageFormatter(out); - Step step = mock(Step.class); + Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("when "); usageFormatter.step(step); - Match match = mock(Match.class); + StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); Result result = mock(Result.class); @@ -87,11 +97,13 @@ public void resultWithZeroDuration() usageFormatter.result(result); - Map> usageMap = usageFormatter.usageMap; + Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("when step"); + List durationEntries = usageMap.get("stepDef"); assertEquals(durationEntries.size(), 1); - assertEquals(durationEntries.get(0), Long.valueOf(0)); + assertEquals(durationEntries.get(0).name, "step"); + assertEquals(durationEntries.get(0).durations.size(), 1); + assertEquals(durationEntries.get(0).durations.get(0).duration, BigDecimal.ZERO); } @Test @@ -102,21 +114,22 @@ public void resultWithNullDuration() Step step = mock(Step.class); when(step.getName()).thenReturn("step"); - when(step.getKeyword()).thenReturn("then "); usageFormatter.step(step); - Match match = mock(Match.class); + StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); Result result = mock(Result.class); when(result.getDuration()).thenReturn(null); usageFormatter.result(result); - Map> usageMap = usageFormatter.usageMap; + Map> usageMap = usageFormatter.usageMap; assertEquals(usageMap.size(), 1); - List durationEntries = usageMap.get("then step"); + List durationEntries = usageMap.get("stepDef"); assertEquals(durationEntries.size(), 1); - assertEquals(durationEntries.get(0), Long.valueOf(0)); + assertEquals(durationEntries.get(0).name, "step"); + assertEquals(durationEntries.get(0).durations.size(), 1); + assertEquals(durationEntries.get(0).durations.get(0).duration, BigDecimal.ZERO); } @Test @@ -124,11 +137,18 @@ public void doneWithoutUsageStatisticStrategies() throws IOException { StringBuffer out = new StringBuffer(); UsageFormatter usageFormatter = new UsageFormatter(out); - usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); + + UsageFormatter.StepContainer stepContainer = new UsageFormatter.StepContainer(); + UsageFormatter.StepDuration stepDuration = new UsageFormatter.StepDuration(); + stepDuration.duration = BigDecimal.valueOf(12345678L); + stepDuration.location = "location.feature"; + stepContainer.durations = Arrays.asList(stepDuration); + + usageFormatter.usageMap.put("aStep", Arrays.asList(stepContainer)); usageFormatter.done(); - assertTrue(out.toString().contains("12.345678")); + assertTrue(out.toString().contains("0.012345678")); } @Test @@ -137,15 +157,21 @@ public void doneWithUsageStatisticStrategies() throws IOException StringBuffer out = new StringBuffer(); UsageFormatter usageFormatter = new UsageFormatter(out); + UsageFormatter.StepContainer stepContainer = new UsageFormatter.StepContainer(); + UsageFormatter.StepDuration stepDuration = new UsageFormatter.StepDuration(); + stepDuration.duration = BigDecimal.valueOf(12345678L); + stepDuration.location = "location.feature"; + stepContainer.durations = Arrays.asList(stepDuration); + + usageFormatter.usageMap.put("aStep", Arrays.asList(stepContainer)); + UsageStatisticStrategy usageStatisticStrategy = mock(UsageStatisticStrategy.class); when(usageStatisticStrategy.calculate(Arrays.asList(12345678L))).thenReturn(23456L); usageFormatter.addUsageStatisticStrategy("average", usageStatisticStrategy); - usageFormatter.usageMap.put("aStep", Arrays.asList(12345678L)); - usageFormatter.done(); - assertTrue(out.toString().contains("0.023456")); - assertTrue(out.toString().contains("12.345678")); + assertTrue(out.toString().contains("0.000023456")); + assertTrue(out.toString().contains("0.012345678")); } } From 35038117bf2e874e08f86ae106f4b7ff57fc82fb Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Thu, 23 Feb 2012 18:45:08 +0100 Subject: [PATCH 6/8] removed unnecessary StepPrinter --- .../cucumber/formatter/UsageFormatter.java | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index e72e4b370f..37cc54c0db 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -31,9 +31,6 @@ */ public class UsageFormatter implements Formatter, Reporter { - private final MonochromeFormats monochromeFormat = new MonochromeFormats(); - private final StepPrinter stepPrinter = new StepPrinter(); - final Map> usageMap = new HashMap>(); final Map statisticStrategies = new HashMap(); @@ -180,7 +177,7 @@ public void result(Result result) if (stepDefinition != null && !steps.isEmpty()) { Step step = steps.remove(0); - String stepNameWithArgs = formatStepNameWithArgs(result, step); + String stepNameWithArgs = step.getName(); addUsageEntry(result, stepDefinition, stepNameWithArgs); } } @@ -194,16 +191,6 @@ private String getStepDefinition() return null; } - private String formatStepNameWithArgs(Result result, Step step) - { - StringBuffer buffer = new StringBuffer(); - Format format = getFormat(result.getStatus()); - Format argFormat = getArgFormat(result.getStatus()); - stepPrinter.writeStep(new NiceAppendable(buffer), format, argFormat, step.getName(), match.getArguments()); - - return buffer.toString(); - } - private void addUsageEntry(Result result, String stepDefinition, String stepNameWithArgs) { List stepContainers = usageMap.get(stepDefinition); @@ -270,14 +257,6 @@ public void embedding(String mimeType, byte[] data) { } - private Format getFormat(String key) { - return monochromeFormat.get(key); - } - - private Format getArgFormat(String key) { - return monochromeFormat.get(key + "_arg"); - } - /** * Add a {@link UsageStatisticStrategy} to the formatter * @param key the key, will be displayed in the output From 75303775745263e4fb87bff08efc0fb94b18c058 Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Thu, 1 Mar 2012 13:48:40 +0100 Subject: [PATCH 7/8] retrieve stepname from match instead of a stepStack --- .../cucumber/formatter/UsageFormatter.java | 37 +++++++++++-------- .../cucumber/runtime/StepDefinitionMatch.java | 5 +++ .../formatter/UsageFormatterTest.java | 16 +------- .../runtime/StepDefinitionMatchTest.java | 15 ++++++++ 4 files changed, 43 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index 37cc54c0db..bfc3f07149 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -1,5 +1,6 @@ package cucumber.formatter; +import java.io.InputStream; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; @@ -10,12 +11,9 @@ import cucumber.runtime.StepDefinitionMatch; import gherkin.deps.com.google.gson.Gson; import gherkin.deps.com.google.gson.GsonBuilder; -import gherkin.formatter.Format; import gherkin.formatter.Formatter; -import gherkin.formatter.MonochromeFormats; import gherkin.formatter.NiceAppendable; import gherkin.formatter.Reporter; -import gherkin.formatter.StepPrinter; import gherkin.formatter.model.Background; import gherkin.formatter.model.Examples; import gherkin.formatter.model.Feature; @@ -34,7 +32,6 @@ public class UsageFormatter implements Formatter, Reporter final Map> usageMap = new HashMap>(); final Map statisticStrategies = new HashMap(); - private final List steps = new ArrayList(); private final NiceAppendable out; private Match match; @@ -78,10 +75,19 @@ public void examples(Examples examples) { } + @Override + public void embedding(String mimeType, InputStream data) + { + } + + @Override + public void write(String text) + { + } + @Override public void step(Step step) { - steps.add(step); } @Override @@ -173,13 +179,17 @@ public void close() public void result(Result result) { String stepDefinition = getStepDefinition(); + String stepName = getStepName(); + addUsageEntry(result, stepDefinition, stepName); + } - if (stepDefinition != null && !steps.isEmpty()) + private String getStepName() + { + if (match instanceof StepDefinitionMatch) { - Step step = steps.remove(0); - String stepNameWithArgs = step.getName(); - addUsageEntry(result, stepDefinition, stepNameWithArgs); + return ((StepDefinitionMatch) match).getStepName(); } + return null; } private String getStepDefinition() @@ -200,7 +210,7 @@ private void addUsageEntry(Result result, String stepDefinition, String stepName usageMap.put(stepDefinition, stepContainers); } StepContainer stepContainer = findOrCreateStepContainer(stepNameWithArgs, stepContainers); - + String stepLocation = getStepLocation(); Long duration = result.getDuration(); StepDuration stepDuration = createStepDuration(duration, stepLocation); @@ -252,11 +262,6 @@ public void match(Match match) this.match = match; } - @Override - public void embedding(String mimeType, byte[] data) - { - } - /** * Add a {@link UsageStatisticStrategy} to the formatter * @param key the key, will be displayed in the output @@ -276,7 +281,7 @@ static class StepDefContainer * The StepDefinition (pattern) */ public String source; - + /** * A list of Steps */ diff --git a/core/src/main/java/cucumber/runtime/StepDefinitionMatch.java b/core/src/main/java/cucumber/runtime/StepDefinitionMatch.java index 323fa76c68..83af7853b7 100644 --- a/core/src/main/java/cucumber/runtime/StepDefinitionMatch.java +++ b/core/src/main/java/cucumber/runtime/StepDefinitionMatch.java @@ -177,4 +177,9 @@ public String getPattern() { public StackTraceElement getStepLocation() { return step.getStackTraceElement(uri); } + + public String getStepName() + { + return step.getName(); + } } diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index c27dc0ccb9..eea02be1cd 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -21,7 +21,6 @@ import cucumber.formatter.usage.UsageStatisticStrategy; import cucumber.runtime.StepDefinitionMatch; import gherkin.formatter.model.Result; -import gherkin.formatter.model.Step; public class UsageFormatterTest { @@ -45,15 +44,11 @@ public void resultWithoutSteps() } @Test - public void resultWithOneStep() + public void resultWithStep() { Appendable out = mock(Appendable.class); UsageFormatter usageFormatter = new UsageFormatter(out); - Step step = mock(Step.class); - when(step.getName()).thenReturn("step"); - usageFormatter.step(step); - StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); @@ -76,6 +71,7 @@ private StepDefinitionMatch mockStepDefinitionMatch() StepDefinitionMatch match = mock(StepDefinitionMatch.class, Mockito.RETURNS_MOCKS); when(match.getPattern()).thenReturn("stepDef"); when(match.getStepLocation()).thenReturn(new StackTraceElement("x", "y", "z", 3)); + when(match.getStepName()).thenReturn("step"); return match; } @@ -85,10 +81,6 @@ public void resultWithZeroDuration() Appendable out = mock(Appendable.class); UsageFormatter usageFormatter = new UsageFormatter(out); - Step step = mock(Step.class); - when(step.getName()).thenReturn("step"); - usageFormatter.step(step); - StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); @@ -112,10 +104,6 @@ public void resultWithNullDuration() Appendable out = mock(Appendable.class); UsageFormatter usageFormatter = new UsageFormatter(out); - Step step = mock(Step.class); - when(step.getName()).thenReturn("step"); - usageFormatter.step(step); - StepDefinitionMatch match = mockStepDefinitionMatch(); usageFormatter.match(match); diff --git a/core/src/test/java/cucumber/runtime/StepDefinitionMatchTest.java b/core/src/test/java/cucumber/runtime/StepDefinitionMatchTest.java index 3be49ef9b1..44dfd9dac3 100644 --- a/core/src/test/java/cucumber/runtime/StepDefinitionMatchTest.java +++ b/core/src/test/java/cucumber/runtime/StepDefinitionMatchTest.java @@ -13,6 +13,7 @@ import java.util.List; import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -70,4 +71,18 @@ public void can_have_doc_string_as_last_argument_among_many() throws Throwable { stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{5, "HELLO"}); } + + @Test + public void retrieve_step_name() + { + String theName = "name"; + + Step step = mock(Step.class); + when(step.getName()).thenReturn(theName); + + StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(null, mock(StepDefinition.class), null, step, null); + + String stepName = stepDefinitionMatch.getStepName(); + assertEquals(theName, stepName); + } } From a86542842f34f26e4dc9b77b4562eedcb7786c3a Mon Sep 17 00:00:00 2001 From: Klaus Bayrhammer Date: Thu, 22 Mar 2012 10:50:01 +0100 Subject: [PATCH 8/8] ignore failed and skipped steps --- core/src/main/java/cucumber/formatter/UsageFormatter.java | 5 ++++- .../test/java/cucumber/formatter/UsageFormatterTest.java | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/cucumber/formatter/UsageFormatter.java b/core/src/main/java/cucumber/formatter/UsageFormatter.java index bfc3f07149..00193ba715 100644 --- a/core/src/main/java/cucumber/formatter/UsageFormatter.java +++ b/core/src/main/java/cucumber/formatter/UsageFormatter.java @@ -180,7 +180,10 @@ public void result(Result result) { String stepDefinition = getStepDefinition(); String stepName = getStepName(); - addUsageEntry(result, stepDefinition, stepName); + if (result.getStatus().equals(Result.PASSED)) + { + addUsageEntry(result, stepDefinition, stepName); + } } private String getStepName() diff --git a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java index eea02be1cd..f271f9932d 100644 --- a/core/src/test/java/cucumber/formatter/UsageFormatterTest.java +++ b/core/src/test/java/cucumber/formatter/UsageFormatterTest.java @@ -34,11 +34,13 @@ public void close() throws IOException } @Test - public void resultWithoutSteps() + public void resultWithoutSkippedSteps() { Appendable out = mock(Appendable.class); UsageFormatter usageFormatter = new UsageFormatter(out); Result result = mock(Result.class); + when(result.getStatus()).thenReturn(Result.SKIPPED.getStatus()); + usageFormatter.result(result); verifyZeroInteractions(out); } @@ -54,6 +56,7 @@ public void resultWithStep() Result result = mock(Result.class); when(result.getDuration()).thenReturn(12345L); + when(result.getStatus()).thenReturn(Result.PASSED); usageFormatter.result(result); @@ -86,6 +89,7 @@ public void resultWithZeroDuration() Result result = mock(Result.class); when(result.getDuration()).thenReturn(0L); + when(result.getStatus()).thenReturn(Result.PASSED); usageFormatter.result(result); @@ -109,6 +113,8 @@ public void resultWithNullDuration() Result result = mock(Result.class); when(result.getDuration()).thenReturn(null); + when(result.getStatus()).thenReturn(Result.PASSED); + usageFormatter.result(result); Map> usageMap = usageFormatter.usageMap;