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

Fix hardcoded data folder #127

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
87 changes: 87 additions & 0 deletions src/main/java/io/cucumber/core/cli/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.cucumber.core.cli;

import org.apiguardian.api.API;

import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.core.options.CommandlineOptionsParser;
import io.cucumber.core.options.Constants;
import io.cucumber.core.options.CucumberProperties;
import io.cucumber.core.options.CucumberPropertiesParser;
import io.cucumber.core.options.RuntimeOptions;
import io.cucumber.core.runtime.Runtime;

/**
* Cucumber Main. Runs Cucumber as a CLI.
* <p>
* Options can be provided in by (order of precedence):
* <ol>
* <li>Command line arguments</li>
* <li>Properties from {@link System#getProperties()}</li>
* <li>Properties from in {@link System#getenv()}</li>
* <li>Properties from {@value Constants#CUCUMBER_PROPERTIES_FILE_NAME}</li>
* </ol>
* For available properties see {@link Constants}.
*/
@API(status = API.Status.STABLE)
public class Main {

private static final Logger log = LoggerFactory.getLogger(Main.class);

private static RuntimeOptions RUNTIME_OPTIONS = null;

public static void main(String... argv) {
byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader());
System.exit(exitStatus);
}

/**
* Launches the Cucumber-JVM command line.
*
* @param argv runtime options. See details in the {@code cucumber.api.cli.Usage.txt} resource.
* @param classLoader classloader used to load the runtime
* @return 0 if execution was successful, 1 if it was not (test failures)
*/
public static byte run(String[] argv, ClassLoader classLoader) {
RuntimeOptions propertiesFileOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromPropertiesFile())
.build();

RuntimeOptions environmentOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromEnvironment())
.build(propertiesFileOptions);

RuntimeOptions systemOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromSystemProperties())
.build(environmentOptions);

RuntimeOptions runtimeOptions = new CommandlineOptionsParser()
.parse(argv)
.addDefaultGlueIfAbsent()
.addDefaultFeaturePathIfAbsent()
.addDefaultFormatterIfAbsent()
.addDefaultSummaryPrinterIfAbsent()
.build(systemOptions);

RUNTIME_OPTIONS = runtimeOptions;

if (!runtimeOptions.isStrict()) {
log.warn(() -> "By default Cucumber is running in --non-strict mode.\n" +
"This default will change to --strict and --non-strict will be removed.\n" +
"You can use --strict to suppress this warning"
);
}

final Runtime runtime = Runtime.builder()
.withRuntimeOptions(runtimeOptions)
.withClassLoader(() -> classLoader)
.build();

runtime.run();
return runtime.exitStatus();
}

public static RuntimeOptions getRuntimeOptions() {
return RUNTIME_OPTIONS;
}
}
233 changes: 233 additions & 0 deletions src/main/java/io/cucumber/junit/Cucumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package io.cucumber.junit;

import static java.util.stream.Collectors.toList;

import org.apiguardian.api.API;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runners.model.Statement;

import java.time.Clock;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;

import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.feature.FeatureParser;
import io.cucumber.core.filter.Filters;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.core.options.Constants;
import io.cucumber.core.options.CucumberOptionsAnnotationParser;
import io.cucumber.core.options.CucumberProperties;
import io.cucumber.core.options.CucumberPropertiesParser;
import io.cucumber.core.options.RuntimeOptions;
import io.cucumber.core.plugin.PluginFactory;
import io.cucumber.core.plugin.Plugins;
import io.cucumber.core.resource.ClassLoaders;
import io.cucumber.core.runtime.BackendServiceLoader;
import io.cucumber.core.runtime.BackendSupplier;
import io.cucumber.core.runtime.FeaturePathFeatureSupplier;
import io.cucumber.core.runtime.ObjectFactoryServiceLoader;
import io.cucumber.core.runtime.ObjectFactorySupplier;
import io.cucumber.core.runtime.ScanningTypeRegistryConfigurerSupplier;
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
import io.cucumber.core.runtime.TypeRegistryConfigurerSupplier;
import io.cucumber.plugin.event.TestRunFinished;
import io.cucumber.plugin.event.TestRunStarted;
import io.cucumber.plugin.event.TestSourceRead;

/**
* Cucumber JUnit Runner.
* <p>
* A class annotated with {@code @RunWith(Cucumber.class)} will run feature files as junit tests.
* In general, the runner class should be empty without any fields or methods.
* For example:
* <blockquote><pre>
* &#64;RunWith(Cucumber.class)
* &#64;CucumberOptions(plugin = "pretty")
* public class RunCucumberTest {
* }
* </pre></blockquote>
* <p>
* By default Cucumber will look for {@code .feature} and glue files on the classpath, using the same resource
* path as the annotated class. For example, if the annotated class is {@code com.example.RunCucumber} then
* features and glue are assumed to be located in {@code com.example}.
* <p>
* Options can be provided in by (order of precedence):
* <ol>
* <li>Properties from {@link System#getProperties()} ()}</li>
* <li>Properties from in {@link System#getenv()}</li>
* <li>Annotating the runner class with {@link CucumberOptions}</li>
* <li>Properties from {@value Constants#CUCUMBER_PROPERTIES_FILE_NAME}</li>
* </ol>
* For available properties see {@link Constants}.
* <p>
* Cucumber also supports JUnits {@link ClassRule}, {@link BeforeClass} and {@link AfterClass} annotations.
* These will be executed before and after all scenarios. Using these is not recommended as it limits the portability
* between different runners; they may not execute correctly when using the commandline, IntelliJ IDEA or
* Cucumber-Eclipse. Instead it is recommended to use Cucumbers `Before` and `After` hooks.
*
* @see CucumberOptions
*/
@API(status = API.Status.STABLE)
public final class Cucumber extends ParentRunner<ParentRunner<?>> {

private static final Logger log = LoggerFactory.getLogger(Cucumber.class);

private final List<ParentRunner<?>> children;
private final EventBus bus;
private final List<Feature> features;
private final Plugins plugins;

private boolean multiThreadingAssumed = false;

private static RuntimeOptions RUNTIME_OPTIONS = null;

/**
* Constructor called by JUnit.
*
* @param clazz the class with the @RunWith annotation.
* @throws org.junit.runners.model.InitializationError if there is another problem
*/
public Cucumber(Class<?> clazz) throws InitializationError {
super(clazz);
Assertions.assertNoCucumberAnnotatedMethods(clazz);

// Parse the options early to provide fast feedback about invalid options
RuntimeOptions propertiesFileOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromPropertiesFile())
.build();

RuntimeOptions annotationOptions = new CucumberOptionsAnnotationParser()
.withOptionsProvider(new JUnitCucumberOptionsProvider())
.parse(clazz)
.build(propertiesFileOptions);

RuntimeOptions environmentOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromEnvironment())
.build(annotationOptions);

RuntimeOptions runtimeOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromSystemProperties())
.addDefaultSummaryPrinterIfAbsent()
.build(environmentOptions);


if (!runtimeOptions.isStrict()) {
log.warn(() -> "By default Cucumber is running in --non-strict mode.\n" +
"This default will change to --strict and --non-strict will be removed.\n" +
"You can use --strict or @CucumberOptions(strict = true) to suppress this warning"
);
}

// Next parse the junit options
JUnitOptions junitPropertiesFileOptions = new JUnitOptionsParser()
.parse(CucumberProperties.fromPropertiesFile())
.build();

JUnitOptions junitAnnotationOptions = new JUnitOptionsParser()
.parse(clazz)
.build(junitPropertiesFileOptions);

JUnitOptions junitEnvironmentOptions = new JUnitOptionsParser()
.parse(CucumberProperties.fromEnvironment())
.build(junitAnnotationOptions);

JUnitOptions junitOptions = new JUnitOptionsParser()
.parse(CucumberProperties.fromSystemProperties())
.setStrict(runtimeOptions.isStrict())
.build(junitEnvironmentOptions);

RUNTIME_OPTIONS = runtimeOptions;

this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);

// Parse the features early. Don't proceed when there are lexer errors
FeatureParser parser = new FeatureParser(bus::generateId);
Supplier<ClassLoader> classLoader = ClassLoaders::getDefaultClassLoader;
FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser);
this.features = featureSupplier.get();

// Create plugins after feature parsing to avoid the creation of empty files on lexer errors.
this.plugins = new Plugins(new PluginFactory(), runtimeOptions);

ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions);
ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader);
BackendSupplier backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier);
TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions);
ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactorySupplier, typeRegistryConfigurerSupplier);
Predicate<Pickle> filters = new Filters(runtimeOptions);
this.children = features.stream()
.map(feature -> FeatureRunner.create(feature, filters, runnerSupplier, junitOptions))
.filter(runner -> !runner.isEmpty())
.collect(toList());
}

@Override
protected List<ParentRunner<?>> getChildren() {
return children;
}

@Override
protected Description describeChild(ParentRunner<?> child) {
return child.getDescription();
}

@Override
protected void runChild(ParentRunner<?> child, RunNotifier notifier) {
child.run(notifier);
}

@Override
protected Statement childrenInvoker(RunNotifier notifier) {
Statement runFeatures = super.childrenInvoker(notifier);
return new RunCucumber(runFeatures);
}

@Override
public void setScheduler(RunnerScheduler scheduler) {
super.setScheduler(scheduler);
multiThreadingAssumed = true;
}

class RunCucumber extends Statement {
private final Statement runFeatures;

RunCucumber(Statement runFeatures) {
this.runFeatures = runFeatures;
}

@Override
public void evaluate() throws Throwable {
if (multiThreadingAssumed) {
plugins.setSerialEventBusOnEventListenerPlugins(bus);
} else {
plugins.setEventBusOnEventListenerPlugins(bus);
}

bus.send(new TestRunStarted(bus.getInstant()));
for (Feature feature : features) {
bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource()));
}
runFeatures.evaluate();
bus.send(new TestRunFinished(bus.getInstant()));
}

}

public static RuntimeOptions getRuntimeOptions() {
return RUNTIME_OPTIONS;
}
}
27 changes: 27 additions & 0 deletions src/main/java/uk/gov/hmcts/befta/TestAutomationConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package uk.gov.hmcts.befta;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

import io.cucumber.core.cli.Main;
import io.cucumber.core.options.RuntimeOptions;
import io.cucumber.junit.Cucumber;
import uk.gov.hmcts.befta.auth.UserTokenProviderConfig;
import uk.gov.hmcts.befta.util.BeftaUtils;
import uk.gov.hmcts.befta.util.EnvironmentVariableUtils;

public class TestAutomationConfig {
Expand Down Expand Up @@ -66,6 +74,25 @@ public double getTestDataLoadSkipPeriod() {
return testDataLoadSkipPeriod;
}

public static RuntimeOptions getCucumberRuntimeOptions() {
String command = System.getProperty("sun.java.command");
BeftaUtils.defaultLog("Running tests with command: " + command);
RuntimeOptions options = Main.getRuntimeOptions();
if (options == null) {
options = Cucumber.getRuntimeOptions();
}
return options;
}

public static Set<String> getTestDataPackages() {
RuntimeOptions options = getCucumberRuntimeOptions();
if (options == null)
return Collections.emptySet();
return options.getFeaturePaths().stream()
.map(u -> u == null ? null : u.toString().replace("classpath:", ""))
.collect(Collectors.toSet());
}

}


Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package uk.gov.hmcts.befta.factory;

import java.util.Arrays;

import uk.gov.hmcts.befta.data.HttpTestDataSource;
import uk.gov.hmcts.befta.data.JsonStoreHttpTestDataSource;
import uk.gov.hmcts.befta.util.BeftaUtils;

public class HttpTestDataSourceFactory {

private HttpTestDataSourceFactory() {
}

public static HttpTestDataSource createHttpTestDataSource(String[] resourcePackages) {
BeftaUtils.defaultLog("Loading test data resources at: " + Arrays.asList(resourcePackages));
return new JsonStoreHttpTestDataSource(resourcePackages);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.cucumber.java.Scenario;
import io.restassured.specification.RequestSpecification;
import uk.gov.hmcts.befta.BeftaMain;
import uk.gov.hmcts.befta.TestAutomationConfig;
import uk.gov.hmcts.befta.data.HttpTestData;
import uk.gov.hmcts.befta.data.HttpTestDataSource;
import uk.gov.hmcts.befta.data.ResponseData;
Expand All @@ -19,7 +20,8 @@

public class BackEndFunctionalTestScenarioContext {

private static final String[] TEST_DATA_RESOURCE_PACKAGES = { "features" };
private static final String[] TEST_DATA_RESOURCE_PACKAGES = TestAutomationConfig.getTestDataPackages()
.toArray(new String[0]);

static final HttpTestDataSource DATA_SOURCE = HttpTestDataSourceFactory.createHttpTestDataSource(TEST_DATA_RESOURCE_PACKAGES);

Expand Down