Skip to content

Commit

Permalink
migrate to androidx, set targetSdk to 28, extract UniqueTestNameProvi…
Browse files Browse the repository at this point in the history
…der from duplicated code, remove AndroidObjectFactory
  • Loading branch information
lsuski committed Feb 21, 2019
1 parent 3156074 commit f49f594
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 413 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -18,7 +18,7 @@ apply plugin: 'io.codearte.nexus-staging'


ext {
targetSdkVersion = 27
targetSdkVersion = 28
buildToolsVersion = '28.0.3'
minSdkVersion = '14'

Expand Down
2 changes: 1 addition & 1 deletion cucumber-android/build.gradle
Expand Up @@ -14,7 +14,7 @@ configurations.all {
dependencies {
api "io.cucumber:cucumber-java:$cucumber_javaVersion"
api 'junit:junit:4.12'
api 'com.android.support.test:runner:1.0.2'
api "androidx.test:runner:1.1.1"
testImplementation "org.robolectric:robolectric:4.1"
testImplementation "org.powermock:powermock-api-mockito2:2.0.0"
testImplementation "org.powermock:powermock-module-junit4:2.0.0"
Expand Down
@@ -1,11 +1,7 @@
package cucumber.api.android;

import android.os.Bundle;
import android.support.test.runner.AndroidJUnitRunner;

import java.io.File;

import cucumber.api.CucumberOptions;
import androidx.test.runner.AndroidJUnitRunner;
import cucumber.runtime.android.CucumberJUnitRunnerBuilder;

/**
Expand All @@ -19,39 +15,15 @@ public class CucumberAndroidJUnitRunner extends AndroidJUnitRunner {

@Override
public void onCreate(final Bundle bundle) {
bundle.putString("plugin", getPluginConfigurationString()); // we programmatically create the plugin configuration
bundle.putString(ARGUMENT_ORCHESTRATOR_RUNNER_BUILDER, CucumberJUnitRunnerBuilder.class.getName());
//there is no need to scan all classes - we can fake this execution to be for single class
//because we delegate test execution to CucumberJUnitRunner
bundle.putString("class", CucumberJUnitRunnerBuilder.class.getName());
arguments = bundle;

super.onCreate(bundle);
}

/**
* Since we want to checkout the external storage directory programmatically, we create the plugin configuration
* here, instead of the {@link CucumberOptions} annotation.
*
* @return the plugin string for the configuration, which contains XML, HTML and JSON paths
*/
private String getPluginConfigurationString() {
final String cucumber = "cucumber";
final String separator = "--";
return
"junit:" + getAbsoluteFilesPath() + "/" + cucumber + ".xml" + separator +
"html:" + getAbsoluteFilesPath() + "/" + cucumber + ".html" + separator +
"json:" + getAbsoluteFilesPath() + "/" + cucumber + ".json";
}

/**
* The path which is used for the report files.
*
* @return the absolute path for the report files
*/
private String getAbsoluteFilesPath() {
//sdcard/Android/data/cucumber.cukeulator
File directory = getTargetContext().getExternalFilesDir(null);
return new File(directory,"reports").getAbsolutePath();
}

public Bundle getArguments() {
return arguments;
}
Expand Down

This file was deleted.

Expand Up @@ -192,11 +192,10 @@ private BackendSupplier createBackends() {
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));
return singletonList(new JavaBackend(delegateObjectFactory, classFinder, typeRegistry));
}
};

Expand Down
@@ -1,38 +1,29 @@
package cucumber.runtime.android;

import androidx.test.platform.app.InstrumentationRegistry;
import cucumber.api.android.CucumberAndroidJUnitRunner;
import cucumber.runtime.formatter.UniqueTestNameProvider;
import gherkin.events.PickleEvent;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.notification.RunNotifier;

import android.support.test.InstrumentationRegistry;
import android.util.Pair;

import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import cucumber.api.android.CucumberAndroidJUnitRunner;
import cucumber.runtime.Utils;
import gherkin.events.PickleEvent;

public class CucumberJUnitRunner extends Runner implements Filterable {

private final CucumberAndroidJUnitRunner instrumentationRunner;

private final CucumberExecutor cucumberExecutor;

private final List<PickleEvent> pickleEvents;

public CucumberJUnitRunner(Class testClass) {
instrumentationRunner = (CucumberAndroidJUnitRunner) InstrumentationRegistry
private final UniqueTestNameProvider<PickleEvent> uniqueTestNameProvider = new UniqueTestNameProvider<>();

public CucumberJUnitRunner(@SuppressWarnings("unused") Class testClass) {
CucumberAndroidJUnitRunner instrumentationRunner = (CucumberAndroidJUnitRunner) InstrumentationRegistry
.getInstrumentation();
cucumberExecutor = new CucumberExecutor(new Arguments(instrumentationRunner.getArguments()),
instrumentationRunner);
Expand All @@ -50,15 +41,16 @@ public Description getDescription() {
}

private Description makeDescriptionFromPickle(PickleEvent pickleEvent) {
Pair<String, String> testName = calculateUniqueTestName(pickleEvent);
return Description.createTestDescription(testName.first, testName.second, testName.second);
String testName = uniqueTestNameProvider.calculateUniqueTestName(pickleEvent, pickleEvent.pickle.getName(), pickleEvent.uri);
return Description.createTestDescription(pickleEvent.uri, testName, testName);
}

@Override
public void run(final RunNotifier notifier) {
cucumberExecutor.execute();
}

@Override
public int testCount() {
return pickleEvents.size();
}
Expand All @@ -77,54 +69,4 @@ public void filter(final Filter filter) throws NoTestsRemainException {
}
}

/**
* The stored unique test name for a test case.
* We use an identity hash-map since we want to distinct all test case objects.
* Thus, the key is a unique test case object.<br/>
* The mapped value is the unique test name, which maybe differs from test case original
* non-unique name.
*/
private final Map<PickleEvent, String>
uniqueTestNameForTestCase = new IdentityHashMap<>();

/**
* The stored unique test names grouped by feature.<br/>
* The key contains the feature file.<br/>
* The mapped value is a set of unique test names for the feature.
*/
private final Map<String, Set<String>> uniqueTestNamesForFeature = new HashMap<>();

/**
* Creates a unique test name for the given test case by filling the internal maps
* {@link #uniqueTestNameForTestCase} and {@link #uniqueTestNamesForFeature}.<br/>
* If the test case name is unique, it will be used, otherwise, a index will be added " 2", "
* 3", " 4", ...
*
* @param pickleEvent the test case
* @return a unique test name
*/
private Pair<String, String> calculateUniqueTestName(PickleEvent pickleEvent) {
String existingName = uniqueTestNameForTestCase.get(pickleEvent);
if (existingName != null) {
// Nothing to do: there is already a test name for the passed test case object
return new Pair<>(pickleEvent.uri, existingName);
}
final String feature = pickleEvent.uri;
String uniqueTestCaseName = pickleEvent.pickle.getName();
if (!uniqueTestNamesForFeature.containsKey(feature)) {
// First test case of the feature
uniqueTestNamesForFeature.put(feature, new HashSet<String>());
}
final Set<String> uniqueTestNamesSetForFeature = uniqueTestNamesForFeature.get(feature);
// If "name" already exists, the next one is "name_2" or "name with spaces 2"
int i = 2;
while (uniqueTestNamesSetForFeature.contains(uniqueTestCaseName)) {
uniqueTestCaseName = Utils
.getUniqueTestNameForScenarioExample(pickleEvent.pickle.getName(), i);
i++;
}
uniqueTestNamesSetForFeature.add(uniqueTestCaseName);
uniqueTestNameForTestCase.put(pickleEvent, uniqueTestCaseName);
return new Pair<>(pickleEvent.uri, uniqueTestNameForTestCase.get(pickleEvent));
}
}
Expand Up @@ -5,8 +5,8 @@

public class CucumberJUnitRunnerBuilder extends RunnerBuilder {
@Override
public Runner runnerForClass(final Class<?> testClass) {
if (testClass.equals(this.getClass())) {
public Runner runnerForClass(Class<?> testClass) {
if (testClass.equals(getClass())) {
return new CucumberJUnitRunner(testClass);
}

Expand Down
Expand Up @@ -12,15 +12,9 @@
import cucumber.api.event.TestSourceRead;
import cucumber.api.event.TestStepFinished;
import cucumber.runtime.UndefinedStepsTracker;
import cucumber.runtime.Utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;

/**
* Reports the test results to the instrumentation through {@link Instrumentation#sendStatus(int, Bundle)} calls.
Expand Down Expand Up @@ -147,6 +141,7 @@ public void receive(TestCaseFinished event) {

private final UndefinedStepsTracker undefinedStepsTracker;

private final UniqueTestNameProvider<TestCase> uniqueTestNameProvider = new UniqueTestNameProvider<>();

/**
* Creates a new instance for the given parameters
Expand Down Expand Up @@ -181,7 +176,7 @@ void startTestCase(final TestCase testCase) {
}
currentTestCaseNumber++;
// Since the names of test cases are not guaranteed to be unique, we must check for unique names
currentTestCaseName = calculateUniqueTestName(testCase);
currentTestCaseName = uniqueTestNameProvider.calculateUniqueTestName(testCase, testCase.getName(), testCase.getUri());
resetSeverestResult();
final Bundle testStart = createBundle(currentFeatureName, currentTestCaseName);
instrumentation.sendStatus(StatusCodes.START, testStart);
Expand Down Expand Up @@ -290,50 +285,5 @@ private static String getStackTrace(final Throwable throwable) {
return stringWriter.getBuffer().toString();
}

/**
* The stored unique test name for a test case.
* We use an identity hash-map since we want to distinct all test case objects.
* Thus, the key is a unique test case object.<br/>
* The mapped value is the unique test name, which maybe differs from test case original non-unique name.
*/
private final Map<TestCase, String> uniqueTestNameForTestCase = new IdentityHashMap<TestCase, String>();

/**
* The stored unique test names grouped by feature.<br/>
* The key contains the feature file.<br/>
* The mapped value is a set of unique test names for the feature.
*/
private final Map<String, Set<String>> uniqueTestNamesForFeature = new HashMap<String, Set<String>>();

/**
* Creates a unique test name for the given test case by filling the internal maps
* {@link #uniqueTestNameForTestCase} and {@link #uniqueTestNamesForFeature}.<br/>
* If the test case name is unique, it will be used, otherwise, a index will be added " 2", " 3", " 4", ...
* @param testCase the test case
* @return a unique test name
*/
private String calculateUniqueTestName(TestCase testCase) {
String existingName = uniqueTestNameForTestCase.get(testCase);
if (existingName != null) {
// Nothing to do: there is already a test name for the passed test case object
return existingName;
}
final String feature = testCase.getUri();
String uniqueTestCaseName = testCase.getName();
if (!uniqueTestNamesForFeature.containsKey(feature)) {
// First test case of the feature
uniqueTestNamesForFeature.put(feature, new HashSet<String>());
}
final Set<String> uniqueTestNamesSetForFeature = uniqueTestNamesForFeature.get(feature);
// If "name" already exists, the next one is "name_2" or "name with spaces 2"
int i = 2;
while (uniqueTestNamesSetForFeature.contains(uniqueTestCaseName)) {
uniqueTestCaseName = Utils.getUniqueTestNameForScenarioExample(testCase.getName(), i);
i++;
}
uniqueTestNamesSetForFeature.add(uniqueTestCaseName);
uniqueTestNameForTestCase.put(testCase, uniqueTestCaseName);
return uniqueTestNameForTestCase.get(testCase);
}

}

0 comments on commit f49f594

Please sign in to comment.