5
5
import cucumber .api .event .TestRunFinished ;
6
6
import cucumber .api .event .TestRunStarted ;
7
7
import cucumber .runner .EventBus ;
8
+ import cucumber .runner .ThreadLocalRunnerSupplier ;
8
9
import cucumber .runner .TimeService ;
10
+ import cucumber .runner .TimeServiceEventBus ;
9
11
import cucumber .runtime .BackendModuleBackendSupplier ;
10
12
import cucumber .runtime .BackendSupplier ;
11
- import cucumber .runner .TimeServiceEventBus ;
12
13
import cucumber .runtime .ClassFinder ;
13
- import cucumber .runtime .RuntimeOptions ;
14
14
import cucumber .runtime .FeaturePathFeatureSupplier ;
15
+ import cucumber .runtime .RuntimeOptions ;
16
+ import cucumber .runtime .RuntimeOptionsFactory ;
15
17
import cucumber .runtime .filter .Filters ;
16
- import cucumber .runtime .formatter .Plugins ;
17
18
import cucumber .runtime .formatter .PluginFactory ;
18
- import cucumber .runtime .model .FeatureLoader ;
19
- import cucumber .runner .ThreadLocalRunnerSupplier ;
20
- import cucumber .runtime .RuntimeOptionsFactory ;
19
+ import cucumber .runtime .formatter .Plugins ;
21
20
import cucumber .runtime .io .MultiLoader ;
22
21
import cucumber .runtime .io .ResourceLoader ;
23
22
import cucumber .runtime .io .ResourceLoaderClassFinder ;
24
23
import cucumber .runtime .junit .Assertions ;
25
24
import cucumber .runtime .junit .FeatureRunner ;
26
25
import cucumber .runtime .junit .JUnitOptions ;
27
26
import cucumber .runtime .model .CucumberFeature ;
27
+ import cucumber .runtime .model .FeatureLoader ;
28
28
import org .junit .AfterClass ;
29
29
import org .junit .BeforeClass ;
30
30
import org .junit .ClassRule ;
47
47
* @CucumberOptions(plugin = "pretty")
48
48
* public class RunCukesTest {
49
49
* }
50
- Fail * </pre></blockquote>
50
+ * </pre></blockquote>
51
51
* <p>
52
52
* By default Cucumber will look for {@code .feature} and glue files on the classpath, using the same resource
53
53
* path as the annotated class. For example, if the annotated class is {@code com.example.RunCucumber} then
63
63
* @see CucumberOptions
64
64
*/
65
65
public class Cucumber extends ParentRunner <FeatureRunner > {
66
- private final List <FeatureRunner > children = new ArrayList <FeatureRunner >();
66
+ private final List <FeatureRunner > children = new ArrayList <>();
67
67
private final EventBus bus ;
68
68
private final ThreadLocalRunnerSupplier runnerSupplier ;
69
- private final Filters filters ;
70
- private final JUnitOptions junitOptions ;
69
+ private final List < CucumberFeature > features ;
70
+ private final Plugins plugins ;
71
71
72
72
/**
73
73
* Constructor called by JUnit.
@@ -77,35 +77,36 @@ public class Cucumber extends ParentRunner<FeatureRunner> {
77
77
*/
78
78
public Cucumber (Class clazz ) throws InitializationError {
79
79
super (clazz );
80
- ClassLoader classLoader = clazz .getClassLoader ();
81
80
Assertions .assertNoCucumberAnnotatedMethods (clazz );
82
81
82
+ // Parse the options early to provide fast feedback about invalid options
83
83
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory (clazz );
84
84
RuntimeOptions runtimeOptions = runtimeOptionsFactory .create ();
85
+ JUnitOptions junitOptions = new JUnitOptions (runtimeOptions .isStrict (), runtimeOptions .getJunitOptions ());
85
86
87
+ ClassLoader classLoader = clazz .getClassLoader ();
86
88
ResourceLoader resourceLoader = new MultiLoader (classLoader );
89
+ ClassFinder classFinder = new ResourceLoaderClassFinder (resourceLoader , classLoader );
90
+
91
+ // Parse the features early. Don't proceed when there are lexer errors
87
92
FeatureLoader featureLoader = new FeatureLoader (resourceLoader );
88
93
FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier (featureLoader , runtimeOptions );
89
- // Parse the features early. Don't proceed when there are lexer errors
90
- final List <CucumberFeature > features = featureSupplier .get ();
94
+ this .features = featureSupplier .get ();
91
95
92
- ClassFinder classFinder = new ResourceLoaderClassFinder (resourceLoader , classLoader );
93
- BackendSupplier backendSupplier = new BackendModuleBackendSupplier (resourceLoader , classFinder , runtimeOptions );
96
+ // Create plugins after feature parsing to avoid the creation of empty files on lexer errors.
94
97
this .bus = new TimeServiceEventBus (TimeService .SYSTEM );
95
- Plugins plugins = new Plugins (classLoader , new PluginFactory (), bus , runtimeOptions );
98
+ this .plugins = new Plugins (classLoader , new PluginFactory (), bus , runtimeOptions );
99
+
100
+
101
+ BackendSupplier backendSupplier = new BackendModuleBackendSupplier (resourceLoader , classFinder , runtimeOptions );
96
102
this .runnerSupplier = new ThreadLocalRunnerSupplier (runtimeOptions , bus , backendSupplier );
97
- this .filters = new Filters (runtimeOptions );
98
- this .junitOptions = new JUnitOptions (runtimeOptions .isStrict (), runtimeOptions .getJunitOptions ());
99
- final StepDefinitionReporter stepDefinitionReporter = plugins .stepDefinitionReporter ();
100
-
101
- // Start the run before reading the features.
102
- // Allows the test source read events to be broadcast properly
103
- bus .send (new TestRunStarted (bus .getTime ()));
104
- for (CucumberFeature feature : features ) {
105
- feature .sendTestSourceRead (bus );
103
+ Filters filters = new Filters (runtimeOptions );
104
+ for (CucumberFeature cucumberFeature : features ) {
105
+ FeatureRunner featureRunner = new FeatureRunner (cucumberFeature , filters , runnerSupplier , junitOptions );
106
+ if (!featureRunner .isEmpty ()) {
107
+ children .add (featureRunner );
108
+ }
106
109
}
107
- runnerSupplier .get ().reportStepDefinitions (stepDefinitionReporter );
108
- addChildren (features );
109
110
}
110
111
111
112
@ Override
@@ -125,22 +126,27 @@ protected void runChild(FeatureRunner child, RunNotifier notifier) {
125
126
126
127
@ Override
127
128
protected Statement childrenInvoker (RunNotifier notifier ) {
128
- final Statement features = super .childrenInvoker (notifier );
129
- return new Statement () {
130
- @ Override
131
- public void evaluate () throws Throwable {
132
- features .evaluate ();
133
- bus .send (new TestRunFinished (bus .getTime ()));
134
- }
135
- };
129
+ Statement runFeatures = super .childrenInvoker (notifier );
130
+ return new RunCucumber (runFeatures );
136
131
}
137
132
138
- private void addChildren (List <CucumberFeature > cucumberFeatures ) throws InitializationError {
139
- for (CucumberFeature cucumberFeature : cucumberFeatures ) {
140
- FeatureRunner featureRunner = new FeatureRunner (cucumberFeature , filters , runnerSupplier , junitOptions );
141
- if (!featureRunner .isEmpty ()) {
142
- children .add (featureRunner );
133
+ class RunCucumber extends Statement {
134
+ private final Statement runFeatures ;
135
+
136
+ RunCucumber (Statement runFeatures ) {
137
+ this .runFeatures = runFeatures ;
138
+ }
139
+
140
+ @ Override
141
+ public void evaluate () throws Throwable {
142
+ bus .send (new TestRunStarted (bus .getTime ()));
143
+ for (CucumberFeature feature : features ) {
144
+ feature .sendTestSourceRead (bus );
143
145
}
146
+ StepDefinitionReporter stepDefinitionReporter = plugins .stepDefinitionReporter ();
147
+ runnerSupplier .get ().reportStepDefinitions (stepDefinitionReporter );
148
+ runFeatures .evaluate ();
149
+ bus .send (new TestRunFinished (bus .getTime ()));
144
150
}
145
151
}
146
152
}
0 commit comments