Skip to content

Commit 186d51c

Browse files
authored
Merge pull request #1162 from cucumber/add-test-run-started-event
[Core] Add TestRunStarted event, let Stats handle the exit code
2 parents 2c9a79d + d8559dc commit 186d51c

File tree

6 files changed

+132
-164
lines changed

6 files changed

+132
-164
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package cucumber.api.event;
2+
3+
public final class TestRunStarted extends TimeStampedEvent {
4+
5+
public TestRunStarted(Long timeStamp) {
6+
super(timeStamp);
7+
}
8+
}

core/src/main/java/cucumber/runtime/Runtime.java

+5-99
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package cucumber.runtime;
22

3-
import cucumber.api.Pending;
4-
import cucumber.api.Result;
53
import cucumber.api.StepDefinitionReporter;
64
import cucumber.api.SummaryPrinter;
7-
import cucumber.api.event.EventHandler;
8-
import cucumber.api.event.TestCaseFinished;
95
import cucumber.api.event.TestRunFinished;
106
import cucumber.api.event.TestStepFinished;
117
import cucumber.runner.EventBus;
@@ -21,7 +17,6 @@
2117
import java.io.IOException;
2218
import java.io.PrintStream;
2319
import java.util.ArrayList;
24-
import java.util.Arrays;
2520
import java.util.Collection;
2621
import java.util.List;
2722
import java.util.Map;
@@ -32,50 +27,17 @@
3227
*/
3328
public class Runtime {
3429

35-
private static final String[] ASSUMPTION_VIOLATED_EXCEPTIONS = {
36-
"org.junit.AssumptionViolatedException",
37-
"org.junit.internal.AssumptionViolatedException"
38-
};
39-
40-
static {
41-
Arrays.sort(ASSUMPTION_VIOLATED_EXCEPTIONS);
42-
}
43-
44-
private static final byte ERRORS = 0x1;
45-
46-
private final Stats stats;
47-
UndefinedStepsTracker undefinedStepsTracker = new UndefinedStepsTracker(); // package private to be avaiable for tests.
30+
final Stats stats; // package private to be avaiable for tests.
31+
private final UndefinedStepsTracker undefinedStepsTracker = new UndefinedStepsTracker();
4832

4933
private final RuntimeOptions runtimeOptions;
5034

51-
private final List<Throwable> errors = new ArrayList<Throwable>();
5235
private final ResourceLoader resourceLoader;
5336
private final ClassLoader classLoader;
5437
private final Runner runner;
5538
private final List<PicklePredicate> filters;
5639
private final EventBus bus;
5740
private final Compiler compiler = new Compiler();
58-
private final EventHandler<TestStepFinished> stepFinishedHandler = new EventHandler<TestStepFinished>() {
59-
@Override
60-
public void receive(TestStepFinished event) {
61-
Result result = event.result;
62-
if (result.getError() != null) {
63-
addError(result.getError());
64-
}
65-
if (event.testStep.isHook()) {
66-
addHookToCounterAndResult(result);
67-
} else {
68-
addStepToCounterAndResult(result);
69-
}
70-
}
71-
};
72-
private final EventHandler<TestCaseFinished> testCaseFinishedHandler = new EventHandler<TestCaseFinished>() {
73-
@Override
74-
public void receive(TestCaseFinished event) {
75-
stats.addScenario(event.result.getStatus(), event.testCase.getScenarioDesignation());
76-
}
77-
};
78-
7941
public Runtime(ResourceLoader resourceLoader, ClassFinder classFinder, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
8042
this(resourceLoader, classLoader, loadBackends(resourceLoader, classFinder), runtimeOptions);
8143
}
@@ -115,8 +77,7 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collectio
11577
this.filters.add(new LinePredicate(lineFilters));
11678
}
11779

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

129-
public void addError(Throwable error) {
130-
errors.add(error);
131-
}
132-
13390
/**
13491
* This is the main entry point. Used from CLI, but not from JUnit.
13592
*/
@@ -191,40 +148,11 @@ void printStats(PrintStream out) {
191148
}
192149

193150
public List<Throwable> getErrors() {
194-
return errors;
151+
return stats.getErrors();
195152
}
196153

197154
public byte exitStatus() {
198-
byte result = 0x0;
199-
if (hasErrors() || hasUndefinedOrPendingStepsAndIsStrict()) {
200-
result |= ERRORS;
201-
}
202-
return result;
203-
}
204-
205-
private boolean hasUndefinedOrPendingStepsAndIsStrict() {
206-
return runtimeOptions.isStrict() && hasUndefinedOrPendingSteps();
207-
}
208-
209-
private boolean hasUndefinedOrPendingSteps() {
210-
return hasUndefinedSteps() || hasPendingSteps();
211-
}
212-
213-
private boolean hasUndefinedSteps() {
214-
return undefinedStepsTracker.hasUndefinedSteps();
215-
}
216-
217-
private boolean hasPendingSteps() {
218-
return !errors.isEmpty() && !hasErrors();
219-
}
220-
221-
private boolean hasErrors() {
222-
for (Throwable error : errors) {
223-
if (!isPending(error) && !isAssumptionViolated(error)) {
224-
return true;
225-
}
226-
}
227-
return false;
155+
return stats.exitStatus(runtimeOptions.isStrict());
228156
}
229157

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

238-
public static boolean isPending(Throwable t) {
239-
if (t == null) {
240-
return false;
241-
}
242-
return t.getClass().isAnnotationPresent(Pending.class);
243-
}
244-
245-
public static boolean isAssumptionViolated(Throwable t) {
246-
if (t == null) {
247-
return false;
248-
}
249-
return Arrays.binarySearch(ASSUMPTION_VIOLATED_EXCEPTIONS, t.getClass().getName()) >= 0;
250-
}
251-
252-
private void addStepToCounterAndResult(Result result) {
253-
stats.addStep(result);
254-
}
255-
256-
private void addHookToCounterAndResult(Result result) {
257-
stats.addHookTime(result.getDuration());
258-
}
259-
260166
public EventBus getEventBus() {
261167
return bus;
262168
}

core/src/main/java/cucumber/runtime/RuntimeOptions.java

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import cucumber.api.SnippetType;
44
import cucumber.api.StepDefinitionReporter;
55
import cucumber.api.SummaryPrinter;
6+
import cucumber.api.event.TestRunStarted;
67
import cucumber.api.formatter.ColorAware;
78
import cucumber.api.formatter.Formatter;
89
import cucumber.api.formatter.StrictAware;
@@ -308,6 +309,7 @@ private void addKeywordRow(List<List<String>> table, String key, List<String> ke
308309
public List<CucumberFeature> cucumberFeatures(ResourceLoader resourceLoader, EventBus bus) {
309310
List<CucumberFeature> features = load(resourceLoader, featurePaths, System.out);
310311
getPlugins(); // to create the formatter objects
312+
bus.send(new TestRunStarted(bus.getTime()));
311313
for (CucumberFeature feature : features) {
312314
feature.sendTestSourceRead(bus);
313315
}

core/src/main/java/cucumber/runtime/Stats.java

+75-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package cucumber.runtime;
22

33
import cucumber.api.Result;
4+
import cucumber.api.event.EventHandler;
5+
import cucumber.api.event.EventListener;
6+
import cucumber.api.event.EventPublisher;
7+
import cucumber.api.event.TestCaseFinished;
8+
import cucumber.api.event.TestRunFinished;
9+
import cucumber.api.event.TestRunStarted;
10+
import cucumber.api.event.TestStepFinished;
411
import cucumber.runtime.formatter.AnsiFormats;
512
import cucumber.runtime.formatter.Format;
613
import cucumber.runtime.formatter.Formats;
@@ -13,18 +20,51 @@
1320
import java.util.List;
1421
import java.util.Locale;
1522

16-
class Stats {
23+
class Stats implements EventListener {
1724
public static final long ONE_SECOND = 1000000000;
1825
public static final long ONE_MINUTE = 60 * ONE_SECOND;
26+
private static final byte ERRORS = 0x1;
1927
private SubCounts scenarioSubCounts = new SubCounts();
2028
private SubCounts stepSubCounts = new SubCounts();
29+
private long startTime = 0;
2130
private long totalDuration = 0;
2231
private Formats formats;
2332
private Locale locale;
24-
private List<String> failedScenarios = new ArrayList<String>();
33+
private final List<String> failedScenarios = new ArrayList<String>();
2534
private List<String> ambiguousScenarios = new ArrayList<String>();
26-
private List<String> pendingScenarios = new ArrayList<String>();
27-
private List<String> undefinedScenarios = new ArrayList<String>();
35+
private final List<String> pendingScenarios = new ArrayList<String>();
36+
private final List<String> undefinedScenarios = new ArrayList<String>();
37+
private final List<Throwable> errors = new ArrayList<Throwable>();
38+
private final EventHandler<TestRunStarted> testRunStartedHandler = new EventHandler<TestRunStarted>() {
39+
@Override
40+
public void receive(TestRunStarted event) {
41+
setStartTime(event.getTimeStamp());
42+
}
43+
};
44+
private final EventHandler<TestStepFinished> stepFinishedHandler = new EventHandler<TestStepFinished>() {
45+
@Override
46+
public void receive(TestStepFinished event) {
47+
Result result = event.result;
48+
if (result.getError() != null) {
49+
addError(result.getError());
50+
}
51+
if (!event.testStep.isHook()) {
52+
addStep(result.getStatus());
53+
}
54+
}
55+
};
56+
private final EventHandler<TestCaseFinished> testCaseFinishedHandler = new EventHandler<TestCaseFinished>() {
57+
@Override
58+
public void receive(TestCaseFinished event) {
59+
addScenario(event.result.getStatus(), event.testCase.getScenarioDesignation());
60+
}
61+
};
62+
private final EventHandler<TestRunFinished> testRunFinishedHandler = new EventHandler<TestRunFinished>() {
63+
@Override
64+
public void receive(TestRunFinished event) {
65+
setFinishTime(event.getTimeStamp());
66+
}
67+
};
2868

2969
public Stats(boolean monochrome) {
3070
this(monochrome, Locale.getDefault());
@@ -39,6 +79,27 @@ public Stats(boolean monochrome, Locale locale) {
3979
}
4080
}
4181

82+
83+
@Override
84+
public void setEventPublisher(EventPublisher publisher) {
85+
publisher.registerHandlerFor(TestRunStarted.class, testRunStartedHandler);
86+
publisher.registerHandlerFor(TestStepFinished.class, stepFinishedHandler);
87+
publisher.registerHandlerFor(TestCaseFinished.class, testCaseFinishedHandler);
88+
publisher.registerHandlerFor(TestRunFinished.class, testRunFinishedHandler);
89+
}
90+
91+
public List<Throwable> getErrors() {
92+
return errors;
93+
}
94+
95+
public byte exitStatus(boolean isStrict) {
96+
byte result = 0x0;
97+
if (!failedScenarios.isEmpty() || (isStrict && (!pendingScenarios.isEmpty() || !undefinedScenarios.isEmpty()))) {
98+
result |= ERRORS;
99+
}
100+
return result;
101+
}
102+
42103
public void printStats(PrintStream out, boolean isStrict) {
43104
printNonZeroResultScenarios(out, isStrict);
44105
if (stepSubCounts.getTotal() == 0) {
@@ -119,21 +180,20 @@ private void printScenarios(PrintStream out, List<String> scenarios, Result.Type
119180
}
120181
}
121182

122-
public void addStep(Result result) {
123-
addResultToSubCount(stepSubCounts, result.getStatus());
124-
addTime(result.getDuration());
183+
void addStep(Result.Type resultStatus) {
184+
addResultToSubCount(stepSubCounts, resultStatus);
125185
}
126186

127-
public void addScenario(Result.Type resultStatus) {
128-
addResultToSubCount(scenarioSubCounts, resultStatus);
187+
private void addError(Throwable error) {
188+
errors.add(error);
129189
}
130190

131-
public void addHookTime(Long duration) {
132-
addTime(duration);
191+
void setStartTime(Long startTime) {
192+
this.startTime = startTime;
133193
}
134194

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

139199
private void addResultToSubCount(SubCounts subCounts, Result.Type resultStatus) {
@@ -158,7 +218,7 @@ private void addResultToSubCount(SubCounts subCounts, Result.Type resultStatus)
158218
}
159219
}
160220

161-
public void addScenario(Result.Type resultStatus, String scenarioDesignation) {
221+
void addScenario(Result.Type resultStatus, String scenarioDesignation) {
162222
addResultToSubCount(scenarioSubCounts, resultStatus);
163223
switch (resultStatus) {
164224
case FAILED:
@@ -178,7 +238,7 @@ public void addScenario(Result.Type resultStatus, String scenarioDesignation) {
178238
}
179239
}
180240

181-
class SubCounts {
241+
static class SubCounts {
182242
public int passed = 0;
183243
public int failed = 0;
184244
public int ambiguous = 0;

0 commit comments

Comments
 (0)