Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Add TestRunStarted event, let Stats handle the exit code #1162

Merged
merged 1 commit into from
Jul 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/src/main/java/cucumber/api/event/TestRunStarted.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cucumber.api.event;

public final class TestRunStarted extends TimeStampedEvent {

public TestRunStarted(Long timeStamp) {
super(timeStamp);
}
}
104 changes: 5 additions & 99 deletions core/src/main/java/cucumber/runtime/Runtime.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package cucumber.runtime;

import cucumber.api.Pending;
import cucumber.api.Result;
import cucumber.api.StepDefinitionReporter;
import cucumber.api.SummaryPrinter;
import cucumber.api.event.EventHandler;
import cucumber.api.event.TestCaseFinished;
import cucumber.api.event.TestRunFinished;
import cucumber.api.event.TestStepFinished;
import cucumber.runner.EventBus;
Expand All @@ -21,7 +17,6 @@
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
Expand All @@ -32,50 +27,17 @@
*/
public class Runtime {

private static final String[] ASSUMPTION_VIOLATED_EXCEPTIONS = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good riddance!

"org.junit.AssumptionViolatedException",
"org.junit.internal.AssumptionViolatedException"
};

static {
Arrays.sort(ASSUMPTION_VIOLATED_EXCEPTIONS);
}

private static final byte ERRORS = 0x1;

private final Stats stats;
UndefinedStepsTracker undefinedStepsTracker = new UndefinedStepsTracker(); // package private to be avaiable for tests.
final Stats stats; // package private to be avaiable for tests.
private final UndefinedStepsTracker undefinedStepsTracker = new UndefinedStepsTracker();

private final RuntimeOptions runtimeOptions;

private final List<Throwable> errors = new ArrayList<Throwable>();
private final ResourceLoader resourceLoader;
private final ClassLoader classLoader;
private final Runner runner;
private final List<PicklePredicate> filters;
private final EventBus bus;
private final Compiler compiler = new Compiler();
private final EventHandler<TestStepFinished> stepFinishedHandler = new EventHandler<TestStepFinished>() {
@Override
public void receive(TestStepFinished event) {
Result result = event.result;
if (result.getError() != null) {
addError(result.getError());
}
if (event.testStep.isHook()) {
addHookToCounterAndResult(result);
} else {
addStepToCounterAndResult(result);
}
}
};
private final EventHandler<TestCaseFinished> testCaseFinishedHandler = new EventHandler<TestCaseFinished>() {
@Override
public void receive(TestCaseFinished event) {
stats.addScenario(event.result.getStatus(), event.testCase.getScenarioDesignation());
}
};

public Runtime(ResourceLoader resourceLoader, ClassFinder classFinder, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
this(resourceLoader, classLoader, loadBackends(resourceLoader, classFinder), runtimeOptions);
}
Expand Down Expand Up @@ -115,8 +77,7 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collectio
this.filters.add(new LinePredicate(lineFilters));
}

bus.registerHandlerFor(TestStepFinished.class, stepFinishedHandler);
bus.registerHandlerFor(TestCaseFinished.class, testCaseFinishedHandler);
stats.setEventPublisher(bus);
undefinedStepsTracker.setEventPublisher(bus);
runtimeOptions.setEventBus(bus);
}
Expand All @@ -126,10 +87,6 @@ private static Collection<? extends Backend> loadBackends(ResourceLoader resourc
return reflections.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
}

public void addError(Throwable error) {
errors.add(error);
}

/**
* This is the main entry point. Used from CLI, but not from JUnit.
*/
Expand Down Expand Up @@ -191,40 +148,11 @@ void printStats(PrintStream out) {
}

public List<Throwable> getErrors() {
return errors;
return stats.getErrors();
}

public byte exitStatus() {
byte result = 0x0;
if (hasErrors() || hasUndefinedOrPendingStepsAndIsStrict()) {
result |= ERRORS;
}
return result;
}

private boolean hasUndefinedOrPendingStepsAndIsStrict() {
return runtimeOptions.isStrict() && hasUndefinedOrPendingSteps();
}

private boolean hasUndefinedOrPendingSteps() {
return hasUndefinedSteps() || hasPendingSteps();
}

private boolean hasUndefinedSteps() {
return undefinedStepsTracker.hasUndefinedSteps();
}

private boolean hasPendingSteps() {
return !errors.isEmpty() && !hasErrors();
}

private boolean hasErrors() {
for (Throwable error : errors) {
if (!isPending(error) && !isAssumptionViolated(error)) {
return true;
}
}
return false;
return stats.exitStatus(runtimeOptions.isStrict());
}

public List<String> getSnippets() {
Expand All @@ -235,28 +163,6 @@ public Glue getGlue() {
return runner.getGlue();
}

public static boolean isPending(Throwable t) {
if (t == null) {
return false;
}
return t.getClass().isAnnotationPresent(Pending.class);
}

public static boolean isAssumptionViolated(Throwable t) {
if (t == null) {
return false;
}
return Arrays.binarySearch(ASSUMPTION_VIOLATED_EXCEPTIONS, t.getClass().getName()) >= 0;
}

private void addStepToCounterAndResult(Result result) {
stats.addStep(result);
}

private void addHookToCounterAndResult(Result result) {
stats.addHookTime(result.getDuration());
}

public EventBus getEventBus() {
return bus;
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/cucumber/runtime/RuntimeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cucumber.api.SnippetType;
import cucumber.api.StepDefinitionReporter;
import cucumber.api.SummaryPrinter;
import cucumber.api.event.TestRunStarted;
import cucumber.api.formatter.ColorAware;
import cucumber.api.formatter.Formatter;
import cucumber.api.formatter.StrictAware;
Expand Down Expand Up @@ -308,6 +309,7 @@ private void addKeywordRow(List<List<String>> table, String key, List<String> ke
public List<CucumberFeature> cucumberFeatures(ResourceLoader resourceLoader, EventBus bus) {
List<CucumberFeature> features = load(resourceLoader, featurePaths, System.out);
getPlugins(); // to create the formatter objects
bus.send(new TestRunStarted(bus.getTime()));
for (CucumberFeature feature : features) {
feature.sendTestSourceRead(bus);
}
Expand Down
90 changes: 75 additions & 15 deletions core/src/main/java/cucumber/runtime/Stats.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package cucumber.runtime;

import cucumber.api.Result;
import cucumber.api.event.EventHandler;
import cucumber.api.event.EventListener;
import cucumber.api.event.EventPublisher;
import cucumber.api.event.TestCaseFinished;
import cucumber.api.event.TestRunFinished;
import cucumber.api.event.TestRunStarted;
import cucumber.api.event.TestStepFinished;
import cucumber.runtime.formatter.AnsiFormats;
import cucumber.runtime.formatter.Format;
import cucumber.runtime.formatter.Formats;
Expand All @@ -13,18 +20,51 @@
import java.util.List;
import java.util.Locale;

class Stats {
class Stats implements EventListener {
public static final long ONE_SECOND = 1000000000;
public static final long ONE_MINUTE = 60 * ONE_SECOND;
private static final byte ERRORS = 0x1;
private SubCounts scenarioSubCounts = new SubCounts();
private SubCounts stepSubCounts = new SubCounts();
private long startTime = 0;
private long totalDuration = 0;
private Formats formats;
private Locale locale;
private List<String> failedScenarios = new ArrayList<String>();
private final List<String> failedScenarios = new ArrayList<String>();
private List<String> ambiguousScenarios = new ArrayList<String>();
private List<String> pendingScenarios = new ArrayList<String>();
private List<String> undefinedScenarios = new ArrayList<String>();
private final List<String> pendingScenarios = new ArrayList<String>();
private final List<String> undefinedScenarios = new ArrayList<String>();
private final List<Throwable> errors = new ArrayList<Throwable>();
private final EventHandler<TestRunStarted> testRunStartedHandler = new EventHandler<TestRunStarted>() {
@Override
public void receive(TestRunStarted event) {
setStartTime(event.getTimeStamp());
}
};
private final EventHandler<TestStepFinished> stepFinishedHandler = new EventHandler<TestStepFinished>() {
@Override
public void receive(TestStepFinished event) {
Result result = event.result;
if (result.getError() != null) {
addError(result.getError());
}
if (!event.testStep.isHook()) {
addStep(result.getStatus());
}
}
};
private final EventHandler<TestCaseFinished> testCaseFinishedHandler = new EventHandler<TestCaseFinished>() {
@Override
public void receive(TestCaseFinished event) {
addScenario(event.result.getStatus(), event.testCase.getScenarioDesignation());
}
};
private final EventHandler<TestRunFinished> testRunFinishedHandler = new EventHandler<TestRunFinished>() {
@Override
public void receive(TestRunFinished event) {
setFinishTime(event.getTimeStamp());
}
};

public Stats(boolean monochrome) {
this(monochrome, Locale.getDefault());
Expand All @@ -39,6 +79,27 @@ public Stats(boolean monochrome, Locale locale) {
}
}


@Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestRunStarted.class, testRunStartedHandler);
publisher.registerHandlerFor(TestStepFinished.class, stepFinishedHandler);
publisher.registerHandlerFor(TestCaseFinished.class, testCaseFinishedHandler);
publisher.registerHandlerFor(TestRunFinished.class, testRunFinishedHandler);
}

public List<Throwable> getErrors() {
return errors;
}

public byte exitStatus(boolean isStrict) {
byte result = 0x0;
if (!failedScenarios.isEmpty() || (isStrict && (!pendingScenarios.isEmpty() || !undefinedScenarios.isEmpty()))) {
result |= ERRORS;
}
return result;
}

public void printStats(PrintStream out, boolean isStrict) {
printNonZeroResultScenarios(out, isStrict);
if (stepSubCounts.getTotal() == 0) {
Expand Down Expand Up @@ -119,21 +180,20 @@ private void printScenarios(PrintStream out, List<String> scenarios, Result.Type
}
}

public void addStep(Result result) {
addResultToSubCount(stepSubCounts, result.getStatus());
addTime(result.getDuration());
void addStep(Result.Type resultStatus) {
addResultToSubCount(stepSubCounts, resultStatus);
}

public void addScenario(Result.Type resultStatus) {
addResultToSubCount(scenarioSubCounts, resultStatus);
private void addError(Throwable error) {
errors.add(error);
}

public void addHookTime(Long duration) {
addTime(duration);
void setStartTime(Long startTime) {
this.startTime = startTime;
}

private void addTime(Long duration) {
totalDuration += duration != null ? duration : 0;
void setFinishTime(Long finishTime) {
this.totalDuration = finishTime - startTime;
}

private void addResultToSubCount(SubCounts subCounts, Result.Type resultStatus) {
Expand All @@ -158,7 +218,7 @@ private void addResultToSubCount(SubCounts subCounts, Result.Type resultStatus)
}
}

public void addScenario(Result.Type resultStatus, String scenarioDesignation) {
void addScenario(Result.Type resultStatus, String scenarioDesignation) {
addResultToSubCount(scenarioSubCounts, resultStatus);
switch (resultStatus) {
case FAILED:
Expand All @@ -178,7 +238,7 @@ public void addScenario(Result.Type resultStatus, String scenarioDesignation) {
}
}

class SubCounts {
static class SubCounts {
public int passed = 0;
public int failed = 0;
public int ambiguous = 0;
Expand Down
Loading