forked from cucumber/cucumber-jvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c370c6d
commit 9c93be9
Showing
9 changed files
with
567 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
217 changes: 217 additions & 0 deletions
217
core/src/main/java/cucumber/formatter/UsageFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<String, List<Long>> usageMap = new HashMap<String, List<Long>>(); | ||
final Map<String, UsageStatisticStrategy> statisticStrategies = new HashMap<String, UsageStatisticStrategy>(); | ||
|
||
private final List<Step> steps = new ArrayList<Step>(); | ||
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<String> legalEvents, String uri, int line) | ||
{ | ||
} | ||
|
||
@Override | ||
public void done() | ||
{ | ||
for (Map.Entry<String, List<Long>> usageEntry : usageMap.entrySet()) | ||
{ | ||
printAggregatedResult(usageEntry); | ||
|
||
String key = usageEntry.getKey(); | ||
for (Long duration : usageEntry.getValue()) | ||
{ | ||
printSingleDurationEntry(key, duration); | ||
} | ||
} | ||
} | ||
|
||
private void printAggregatedResult(Map.Entry<String, List<Long>> usageEntry) | ||
{ | ||
String stepName = usageEntry.getKey(); | ||
for (Map.Entry<String, UsageStatisticStrategy> 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<Long> durationEntries = usageMap.get(stepNameWithArgs); | ||
if (durationEntries == null) | ||
{ | ||
durationEntries = new ArrayList<Long>(); | ||
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); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
core/src/main/java/cucumber/formatter/usage/AverageUsageStatisticStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Long> durationEntries) | ||
{ | ||
if (verifyNoNulls(durationEntries)) | ||
{ | ||
return 0L; | ||
} | ||
|
||
long sum = 0; | ||
for (Long duration : durationEntries) | ||
{ | ||
sum += duration; | ||
} | ||
return sum / durationEntries.size(); | ||
} | ||
|
||
private boolean verifyNoNulls(List<Long> durationEntries) | ||
{ | ||
return durationEntries == null || durationEntries.isEmpty() || durationEntries.contains(null); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
core/src/main/java/cucumber/formatter/usage/MedianUsageStatisticStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Long> 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<Long> durationEntries) | ||
{ | ||
return durationEntries == null || durationEntries.isEmpty() || durationEntries.contains(null); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
core/src/main/java/cucumber/formatter/usage/UsageStatisticStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Long> durationEntries); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.