Skip to content

[Core] Refactor Runtime #1367

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

Merged
merged 53 commits into from
Jun 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
4a8937b
Extract backend supplier class
mpkorstanje May 21, 2018
1acf5cd
Restore api
mpkorstanje May 21, 2018
3fcb0f4
Inline constructor
mpkorstanje May 21, 2018
decdc6f
Inline constructor
mpkorstanje May 21, 2018
2849aa4
Inline constructor
mpkorstanje May 21, 2018
d5b73f0
Inline constructor
mpkorstanje May 21, 2018
cf7d65f
Extract glue supplier
mpkorstanje May 21, 2018
a9c7575
Inline method
mpkorstanje May 21, 2018
6142371
Rename and move RuntimeGlueSupplier to upper level
mpkorstanje May 21, 2018
b13927f
Fix compile errors
mpkorstanje May 21, 2018
1cf94fe
Fix compile tests
mpkorstanje May 21, 2018
6353c94
Extract a Runner provider
mpkorstanje May 21, 2018
57a9ae4
Lift up RunnerSupplier creation to constructors
mpkorstanje May 21, 2018
6a26cc2
Remove unused constructor parameters
mpkorstanje May 21, 2018
47c9b8a
Inline constructor
mpkorstanje May 21, 2018
15b32b1
Inline constructor
mpkorstanje May 21, 2018
0d0d3e5
Use local bus instead of Runtime.getBus
mpkorstanje May 21, 2018
f289b6a
Move RunnerSupplier to top level
mpkorstanje May 21, 2018
1e416b1
Nitpicking
mpkorstanje May 21, 2018
6245f4e
Remove now unused event bus getter
mpkorstanje May 21, 2018
f8923ba
Move TestRunStartEvent to same level as TestRunFinished
mpkorstanje May 21, 2018
fc51069
Extract FeatureCompiler
mpkorstanje May 22, 2018
ebc0b19
Remove runtime from runner test
mpkorstanje May 22, 2018
2241333
Remove runtime from java step definition test
mpkorstanje May 22, 2018
47b623f
Remove runtime from hook test
mpkorstanje May 22, 2018
47104cf
Fix compile error
mpkorstanje May 22, 2018
11cdf72
Remove getGlue from Runtime
mpkorstanje May 22, 2018
a17b215
Fix android
mpkorstanje May 22, 2018
a01604f
Initialize plugins before sending start event.
mpkorstanje May 22, 2018
10a6ce3
Extract getting filters from RuntimeOptions
mlvandijk May 19, 2018
26f728f
Wrap match method
mlvandijk May 19, 2018
04d6e55
Remove unused method
mlvandijk May 21, 2018
9ea80ab
Partial refactoring
mlvandijk May 21, 2018
465c87d
Partial refactoring and fix tests
mlvandijk May 22, 2018
d3e2528
Cleanup unused code
mlvandijk May 22, 2018
7e7ddac
Fix merge conflicts
mlvandijk May 22, 2018
524e089
Merge branch 'refactor-runtime-constructors' into mvd-filters
mpkorstanje May 22, 2018
c383a7c
Merge branch 'master' into refactor-runtime-constructors
mpkorstanje May 26, 2018
b88c6ea
Extract FeatureLoader
mpkorstanje May 26, 2018
2dfd90c
Clean up unused
mpkorstanje May 26, 2018
1b9e52b
Use DI to inject filters and features
mpkorstanje May 26, 2018
d7c2ca9
Remove Runtime from TestNG and JUnit
mpkorstanje May 26, 2018
722c76b
Seperate Plugins from RuntimeOptions
mpkorstanje May 26, 2018
fc45db7
Remove getters from Runtime
mpkorstanje May 26, 2018
3325947
Group into filter and formatter packages
mpkorstanje May 27, 2018
4c4c463
Clean up warnings
mpkorstanje May 27, 2018
73abc62
Add public constructor to NullSummaryPrinter
mpkorstanje May 27, 2018
2a389a8
Add JUnit tests for parallel
mpkorstanje May 29, 2018
165550b
Move semantic version check to separate profile
mpkorstanje Jun 2, 2018
59eef86
Merge branch 'master' into refactor-runtime-constructors
mpkorstanje Jun 2, 2018
a92d0f1
Add tests for Runner and Backend suppliers
mpkorstanje Jun 5, 2018
3ba1ee7
Replace generic supplier interface with specific purpose interfaces
mpkorstanje Jun 5, 2018
c489820
Merge branch 'master' into refactor-runtime-constructors
mpkorstanje Jun 5, 2018
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
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ jobs:
include:
- stage: test
jdk: oraclejdk8
script: mvn -q verify -Pcheck-semantic-version -DskipTests=true
env: CHECK_SEMANTIC_VERSION=true
- jdk: oraclejdk8
script: mvn -q install
after_success:
- mvn clean cobertura:cobertura coveralls:report -P coveralls.io
Expand Down
100 changes: 58 additions & 42 deletions android/src/main/java/cucumber/runtime/android/CucumberExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
import cucumber.api.TypeRegistryConfigurer;
import cucumber.api.CucumberOptions;
import cucumber.api.StepDefinitionReporter;
import cucumber.api.event.TestRunStarted;
import cucumber.runner.EventBus;
import cucumber.runner.Runner;
import cucumber.runner.TimeService;
import cucumber.runtime.BackendSupplier;
import cucumber.runtime.FeaturePathFeatureSupplier;
import cucumber.runtime.filter.Filters;
import cucumber.runtime.formatter.Plugins;
import cucumber.runtime.filter.RerunFilters;
import cucumber.runtime.formatter.PluginFactory;
import cucumber.runtime.model.FeatureLoader;
import cucumber.runtime.ThreadLocalRunnerSupplier;
import cucumber.runtime.RuntimeGlueSupplier;
import io.cucumber.stepexpression.TypeRegistry;
import cucumber.api.event.TestRunFinished;
import cucumber.api.java.ObjectFactory;
Expand All @@ -15,10 +28,9 @@
import cucumber.runtime.DefaultTypeRegistryConfiguration;
import cucumber.runtime.Env;
import cucumber.runtime.Reflections;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.RuntimeOptionsFactory;
import cucumber.runtime.Stats;
import cucumber.runtime.formatter.Stats;
import cucumber.runtime.UndefinedStepsTracker;
import cucumber.runtime.formatter.AndroidInstrumentationReporter;
import cucumber.runtime.formatter.AndroidLogcatReporter;
Expand Down Expand Up @@ -56,11 +68,6 @@ public final class CucumberExecutor {
*/
private final Instrumentation instrumentation;

/**
* The {@link java.lang.ClassLoader} for all test relevant classes.
*/
private final ClassLoader classLoader;

/**
* The {@link cucumber.runtime.ClassFinder} to find all to be loaded classes.
*/
Expand All @@ -71,15 +78,10 @@ public final class CucumberExecutor {
*/
private final RuntimeOptions runtimeOptions;

/**
* The {@link cucumber.runtime.Runtime} to run with.
*/
private final Runtime runtime;

/**
* The actual {@link PickleEvent}s to run stored in {@link PickleStruct}s.
*/
private final List<PickleEvent> pickleEvents;
private final EventBus bus;
private final Plugins plugins;
private final Runner runner;

/**
* Creates a new instance for the given parameters.
Expand All @@ -91,44 +93,52 @@ public final class CucumberExecutor {
public CucumberExecutor(final Arguments arguments, final Instrumentation instrumentation) {

trySetCucumberOptionsToSystemProperties(arguments);

final Context context = instrumentation.getContext();
this.instrumentation = instrumentation;
this.classLoader = context.getClassLoader();
ClassLoader classLoader = context.getClassLoader();
this.classFinder = createDexClassFinder(context);
this.runtimeOptions = createRuntimeOptions(context).noSummaryPrinter();

ResourceLoader resourceLoader = new AndroidResourceLoader(context);
this.runtime = new Runtime(resourceLoader, classLoader, createBackends(), runtimeOptions);

this.bus = new EventBus(TimeService.SYSTEM);
this.plugins = new Plugins(classLoader, new PluginFactory(), bus, runtimeOptions);
RuntimeGlueSupplier glueSupplier = new RuntimeGlueSupplier();
this.runner = new ThreadLocalRunnerSupplier(runtimeOptions, bus, createBackends(), glueSupplier).get();
FeatureLoader featureLoader = new FeatureLoader(resourceLoader);
FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(featureLoader, runtimeOptions);
RerunFilters rerunFilters = new RerunFilters(runtimeOptions, featureLoader);
Filters filters = new Filters(runtimeOptions, rerunFilters);
UndefinedStepsTracker undefinedStepsTracker = new UndefinedStepsTracker();
undefinedStepsTracker.setEventPublisher(runtime.getEventBus());
undefinedStepsTracker.setEventPublisher(bus);
Stats stats = new Stats();
stats.setEventPublisher(runtime.getEventBus());
stats.setEventPublisher(bus);

AndroidInstrumentationReporter instrumentationReporter = new AndroidInstrumentationReporter(undefinedStepsTracker, instrumentation);
runtimeOptions.addPlugin(instrumentationReporter);
runtimeOptions.addPlugin(new AndroidLogcatReporter(stats, undefinedStepsTracker, TAG));

List<CucumberFeature> cucumberFeatures = runtimeOptions.cucumberFeatures(resourceLoader, runtime.getEventBus());
this.pickleEvents = FeatureCompiler.compile(cucumberFeatures, this.runtime);
plugins.addPlugin(instrumentationReporter);
plugins.addPlugin(new AndroidLogcatReporter(stats, undefinedStepsTracker, TAG));

// Start the run before reading the features.
// Allows the test source read events to be broadcast properly
List<CucumberFeature> features = featureSupplier.get();
bus.send(new TestRunStarted(bus.getTime()));
for (CucumberFeature feature : features) {
feature.sendTestSourceRead(bus);
}
this.pickleEvents = FeatureCompiler.compile(features, filters);
instrumentationReporter.setNumberOfTests(getNumberOfConcreteScenarios());
}

/**
* Runs the cucumber scenarios with the specified arguments.
*/
public void execute() {

// TODO: This is duplicated in info.cucumber.Runtime.

final StepDefinitionReporter stepDefinitionReporter = runtimeOptions.stepDefinitionReporter(classLoader);
runtime.reportStepDefinitions(stepDefinitionReporter);

final StepDefinitionReporter stepDefinitionReporter = plugins.stepDefinitionReporter();
runner.reportStepDefinitions(stepDefinitionReporter);
for (final PickleEvent pickleEvent : pickleEvents) {
runtime.getRunner().runPickle(pickleEvent);
runner.runPickle(pickleEvent);
}

runtime.getEventBus().send(new TestRunFinished(runtime.getEventBus().getTime()));
bus.send(new TestRunFinished(bus.getTime()));
}

/**
Expand Down Expand Up @@ -171,13 +181,19 @@ private RuntimeOptions createRuntimeOptions(final Context context) {
throw new CucumberException("No CucumberOptions annotation");
}

private Collection<? extends Backend> createBackends() {
final Reflections reflections = new Reflections(classFinder);
final ObjectFactory delegateObjectFactory = ObjectFactoryLoader.loadObjectFactory(classFinder, Env.INSTANCE.get(ObjectFactory.class.getName()));
final AndroidObjectFactory objectFactory = new AndroidObjectFactory(delegateObjectFactory, instrumentation);
final TypeRegistryConfigurer typeRegistryConfigurer = reflections.instantiateExactlyOneSubclass(TypeRegistryConfigurer.class, MultiLoader.packageName(runtimeOptions.getGlue()), new Class[0], new Object[0], new DefaultTypeRegistryConfiguration());
final TypeRegistry typeRegistry = new TypeRegistry(typeRegistryConfigurer.locale());
typeRegistryConfigurer.configureTypeRegistry(typeRegistry);
return singletonList(new JavaBackend(objectFactory, classFinder, typeRegistry));
private BackendSupplier createBackends() {
return new BackendSupplier() {
@Override
public Collection<? extends Backend> get() {
final Reflections reflections = new Reflections(classFinder);
final ObjectFactory delegateObjectFactory = ObjectFactoryLoader.loadObjectFactory(classFinder, Env.INSTANCE.get(ObjectFactory.class.getName()));
final AndroidObjectFactory objectFactory = new AndroidObjectFactory(delegateObjectFactory, instrumentation);
final TypeRegistryConfigurer typeRegistryConfigurer = reflections.instantiateExactlyOneSubclass(TypeRegistryConfigurer.class, MultiLoader.packageName(runtimeOptions.getGlue()), new Class[0], new Object[0], new DefaultTypeRegistryConfiguration());
final TypeRegistry typeRegistry = new TypeRegistry(typeRegistryConfigurer.locale());
typeRegistryConfigurer.configureTypeRegistry(typeRegistry);
return singletonList(new JavaBackend(objectFactory, classFinder, typeRegistry));
}
};

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cucumber.runtime.android;

import cucumber.runtime.Runtime;
import cucumber.runtime.filter.Filters;
import cucumber.runtime.model.CucumberFeature;
import gherkin.events.PickleEvent;

Expand All @@ -18,11 +18,12 @@ final class FeatureCompiler {
* @param cucumberFeatures the list of {@link CucumberFeature} to compile
* @return the compiled pickles in {@link PickleEvent}s
*/
static List<PickleEvent> compile(final List<CucumberFeature> cucumberFeatures, final Runtime runtime) {
static List<PickleEvent> compile(final List<CucumberFeature> cucumberFeatures, final Filters filters) {
List<PickleEvent> pickles = new ArrayList<PickleEvent>();
cucumber.runtime.FeatureCompiler compiler = new cucumber.runtime.FeatureCompiler();
for (final CucumberFeature feature : cucumberFeatures) {
for (final PickleEvent pickleEvent : runtime.compileFeature(feature)) {
if (runtime.matchesFilters(pickleEvent)) {
for (final PickleEvent pickleEvent : compiler.compileFeature(feature)) {
if (filters.matchesFilters(pickleEvent)) {
pickles.add(pickleEvent);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import cucumber.api.event.TestRunFinished;
import cucumber.api.event.TestStepStarted;
import cucumber.api.formatter.Formatter;
import cucumber.runtime.Stats;
import cucumber.runtime.UndefinedStepsTracker;

/**
Expand Down
30 changes: 25 additions & 5 deletions core/src/main/java/cucumber/api/cli/Main.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package cucumber.api.cli;

import cucumber.runner.EventBus;
import cucumber.runner.TimeService;
import cucumber.runtime.BackendModuleBackendSupplier;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.FeaturePathFeatureSupplier;
import cucumber.runtime.FeatureSupplier;
import cucumber.runtime.GlueSupplier;
import cucumber.runtime.RunnerSupplier;
import cucumber.runtime.filter.Filters;
import cucumber.runtime.formatter.Plugins;
import cucumber.runtime.filter.RerunFilters;
import cucumber.runtime.ThreadLocalRunnerSupplier;
import cucumber.runtime.RuntimeGlueSupplier;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.formatter.PluginFactory;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderClassFinder;

import java.io.IOException;
import java.util.ArrayList;
import cucumber.runtime.model.FeatureLoader;

import static java.util.Arrays.asList;

Expand All @@ -27,11 +38,20 @@ public static void main(String[] argv) {
* @return 0 if execution was successful, 1 if it was not (test failures)
*/
public static byte run(String[] argv, ClassLoader classLoader) {
RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
RuntimeOptions runtimeOptions = new RuntimeOptions(asList(argv));

ResourceLoader resourceLoader = new MultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
BackendModuleBackendSupplier backendSupplier = new BackendModuleBackendSupplier(resourceLoader, classFinder, runtimeOptions);
EventBus bus = new EventBus(TimeService.SYSTEM);
Plugins plugins = new Plugins(classLoader, new PluginFactory(), bus, runtimeOptions);
GlueSupplier glueSupplier = new RuntimeGlueSupplier();
RunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, glueSupplier);
FeatureLoader featureLoader = new FeatureLoader(resourceLoader);
FeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(featureLoader, runtimeOptions);
RerunFilters rerunFilters = new RerunFilters(runtimeOptions, featureLoader);
Filters filters = new Filters(runtimeOptions, rerunFilters);
Runtime runtime = new Runtime(plugins, runtimeOptions, bus, filters, runnerSupplier, featureSupplier);
runtime.run();
return runtime.exitStatus();
}
Expand Down
44 changes: 43 additions & 1 deletion core/src/main/java/cucumber/runner/EventBus.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import cucumber.api.event.Event;
import cucumber.api.event.EventHandler;
import cucumber.api.event.EventPublisher;
import cucumber.api.event.TestCaseFinished;
import cucumber.api.event.TestCaseStarted;

import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -17,11 +19,16 @@ public EventBus(TimeService stopWatch) {
this.stopWatch = stopWatch;
}

public EventBus createBatchedEventBus() {
return new BatchEventBus(this);
}

public Long getTime() {
return stopWatch.time();
}

public void send(Event event) {

public synchronized void send(Event event) {
if (handlers.containsKey(event.getClass())) {
for (EventHandler handler : handlers.get(event.getClass())) {
//noinspection unchecked: protected by registerHandlerFor
Expand All @@ -30,6 +37,12 @@ public void send(Event event) {
}
}

synchronized void sendAll(List<Event> events) {
for (Event event : events) {
send(event);
}
}

@Override
public <T extends Event> void registerHandlerFor(Class<T> eventType, EventHandler<T> handler) {
if (handlers.containsKey(eventType)) {
Expand All @@ -40,4 +53,33 @@ public <T extends Event> void registerHandlerFor(Class<T> eventType, EventHandle
handlers.put(eventType, list);
}
}

public <T extends Event> void removeHandlerFor(Class<T> eventType, EventHandler<T> handler) {
if (handlers.containsKey(eventType)) {
handlers.get(eventType).remove(handler);
}
}


private class BatchEventBus extends EventBus {

private final EventBus parent;
private final List<Event> queue = new ArrayList<Event>();

BatchEventBus(EventBus parent) {
super(parent.stopWatch);
this.parent = parent;
}

@Override
public void send(Event event) {
super.send(event);
queue.add(event);
if(event instanceof TestCaseFinished){
parent.sendAll(queue);
queue.clear();
}
}
}

}
6 changes: 4 additions & 2 deletions core/src/main/java/cucumber/runner/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public Runner(Glue glue, EventBus bus, Collection<? extends Backend> backends, R

}

public EventBus getBus() {
return bus;
}

public void runPickle(PickleEvent pickle) {
buildBackendWorlds(); // Java8 step definitions will be added to the glue here
TestCase testCase = createTestCaseForPickle(pickle);
Expand Down Expand Up @@ -127,8 +131,6 @@ private List<HookTestStep> getBeforeStepHooks(List<PickleTag> tags) {
}

private void buildBackendWorlds() {
runtimeOptions.getPlugins(); // To make sure that the plugins are instantiated after
// the features have been parsed but before the pickles start to execute.
for (Backend backend : backends) {
backend.buildWorld();
}
Expand Down
Loading