Skip to content
Merged
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
21 changes: 9 additions & 12 deletions core/src/main/java/io/cucumber/core/cli/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.cucumber.core.cli;

import io.cucumber.core.io.MultiLoader;
import io.cucumber.core.io.ResourceLoader;
import io.cucumber.core.options.CommandlineOptionsParser;
import io.cucumber.core.options.Constants;
import io.cucumber.core.options.CucumberProperties;
Expand All @@ -13,13 +11,14 @@
/**
* Cucumber Main. Runs Cucumber as a CLI.
* <p>
* Options can be provided in order of precedence through:
* Options can be provided in by (order of precedence):
* <ol>
* <li>command line arguments</li>
* <li>{@value Constants#CUCUMBER_OPTIONS_PROPERTY_NAME} property in {@link System#getProperties()}</li>
* <li>{@value Constants#CUCUMBER_OPTIONS_PROPERTY_NAME} property in {@link System#getenv()}</li>
* <li>{@value Constants#CUCUMBER_OPTIONS_PROPERTY_NAME} property in {@value Constants#CUCUMBER_PROPERTIES_FILE_NAME}</li>
* <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 {
Expand All @@ -37,17 +36,15 @@ 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) {
ResourceLoader resourceLoader = new MultiLoader(classLoader);

RuntimeOptions propertiesFileOptions = new CucumberPropertiesParser(resourceLoader)
RuntimeOptions propertiesFileOptions = new CucumberPropertiesParser()
.parse(CucumberProperties.fromPropertiesFile())
.build();

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

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
package io.cucumber.core.options;

import io.cucumber.core.io.MultiLoader;
import io.cucumber.core.io.ResourceLoader;

import java.util.Arrays;
import java.util.List;

public final class CommandlineOptionsParser {

private final ResourceLoader resourceLoader;

public CommandlineOptionsParser() {
this(new MultiLoader(CommandlineOptionsParser.class.getClassLoader()));
}

public CommandlineOptionsParser(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}

public RuntimeOptionsBuilder parse(List<String> args) {
RerunLoader rerunLoader = new RerunLoader(resourceLoader);
RuntimeOptionsParser parser = new RuntimeOptionsParser(rerunLoader);
RuntimeOptionsParser parser = new RuntimeOptionsParser();
return parser.parse(args);
}

Expand Down
169 changes: 164 additions & 5 deletions core/src/main/java/io/cucumber/core/options/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,185 @@

public final class Constants {

/**
* Property name used to disable ansi colors in the output (not supported
* by all terminals): {@value}
* <p>
* Ansi colors are enabled by default.
*/
public static final String ANSI_COLORS_DISABLED_PROPERTY_NAME = "cucumber.ansi-colors.disabled";

/**
* File name of cucumber properties file: {@value}
*/
public static final String CUCUMBER_PROPERTIES_FILE_NAME = "cucumber.properties";

/**
* Property name used to enable dry-run: {@value}
* <p>
* When using dry run Cucumber will skip execution of glue code.
* <p>
* By default, dry-run is disabled
*/
public static final String EXECUTION_DRY_RUN_PROPERTY_NAME = "cucumber.execution.dry-run";

/**
* Property name used to enable dry-run: {@value}
* <p>
* Limits the number of scenarios to be executed to a specific amount.
* <p>
* By default scenarios are executed.
*/
public static final String EXECUTION_LIMIT_PROPERTY_NAME = "cucumber.execution.limit";

/**
* Property name used to set execution order: {@value}
* <p>
* Valid values are {@code reverse}, {@code random} or {@code random:[seed]}.
* <p>
* By default features are executed in lexical file name order
*/
public static final String EXECUTION_ORDER_PROPERTY_NAME = "cucumber.execution.order";

/**
* Property name used to determine the desired parallelism for the
* {@link Runtime} configuration: {@value}
* <p>
* No default value; must be an integer.
*/
public static final String EXECUTION_PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME = "cucumber.execution.parallel.config.fixed.parallelism";

/**
* Property name used to enable strict execution: {@value}
* <p>
* When using strict execution Cucumber will treat undefined and pending
* steps as errors.
* <p>
* By default, strict execution is disabled
*/
public static final String EXECUTION_STRICT_PROPERTY_NAME = "cucumber.execution.strict";

/**
* Property name used to enable wip execution: {@value}
* <p>
* When using wip execution Cucumber will fail if there are any passing
* scenarios.
* <p>
* By default, wip execution is disabled
*/
public static final String WIP_PROPERTY_NAME = "cucumber.execution.wip";

/**
* Property name used to set feature location: {@value}
* <p>
* A comma separated list of:
* <ul>
* <li>{@code path/to/dir} - Load the files with the extension ".feature" for the
* directory {@code path} and its sub directories.
* <li>{@code path/name.feature} - Load the feature file {@code path/name.feature}
* from the file system.</li>
* <li>{@code classpath:path/name.feature} - Load the feature file
* {@code path/name.feature} from the classpath.</li>
* <li>{@code path/name.feature:3:9} - Load the scenarios on line 3 and line 9 in
* the file {@code path/name.feature}.</li>
* <li>{@code @path/file} - Load {@code path/file} from the file system and parse
* feature paths.</li>
* <li>{@code @classpath:path/file} - Load {@code path/file} from the classpath and
* parse feature paths.</li>
* </ul>
*
* @see io.cucumber.core.feature.FeatureWithLines
*/
public static final String FEATURES_PROPERTY_NAME = "cucumber.features";

/**
* Property name to set a file from which feature locations will be read: {@value}
* <p>
* Feature paths must be separated by a new line. The feature path is
* a uri or path e.g.: {@code path/to/feature/dir}.
*
* @see io.cucumber.core.feature.FeatureWithLines
* @see #FEATURES_PROPERTY_NAME
*/
public static final String RERUN_FILE_PROPERTY_NAME = "cucumber.rerun-file";

/**
* Property name used to set name filter: {@value}
* <p>
* Filters features based on the provided regex pattern.
* <p>
* By default no features are filtered
*/
public static final String FILTER_NAME_PROPERTY_NAME = "cucumber.filter.name";
/**
* Property name used to set tag filter: {@value}
* <p>
* Filters features based on the provided tag expression e.g:
* {@code @Integration and not @Ignored}.
* <p>
* By default no features are filtered
*/
public static final String FILTER_TAGS_PROPERTY_NAME = "cucumber.filter.tags";

/**
* Property name to set the glue path: {@value}
* <p>
* A comma separated list of a classpath uri or package name e.g.:
* {@code com.example.app.steps}.
*
* @see io.cucumber.core.feature.GluePath
*/
public static final String GLUE_PROPERTY_NAME = "cucumber.glue";

/**
* Property name used to select a specific object factory implementation:
* {@value}
*
* @see io.cucumber.core.backend.ObjectFactoryServiceLoader
*/
public static final String OBJECT_FACTORY_PROPERTY_NAME = "cucumber.object-factory";

/**
* Property name used to pass command line options: {@value}
* <p>
* When available it is recommended to use a property based alternative.
*
* @see RuntimeOptionsParser
*/
public static final String CUCUMBER_OPTIONS_PROPERTY_NAME = "cucumber.options";
public static final String OPTIONS_PROPERTY_NAME = "cucumber.options";

/**
* Property name used to select a specific object factory implementation: {@value}
*
* @see io.cucumber.core.backend.ObjectFactoryServiceLoader
* Property name to enable plugins: {@value}
* <p>
* A comma separated list of {@code [PLUGIN[:PATH_OR_URL]]} e.g:
* {@code json:target/cucumber.json}.
* <p>
* Built-in formatter PLUGIN types:
* <ul>
* <li>html</li>
* <li>pretty</li>
* <li>progress</li>
* <li>summary</li>
* <li>json</li>
* <li>usage</li>
* <li>rerun</li>
* <li>junit</li>
* <li>testng</li>
* </ul>
* <p>
* {@code PLUGIN} can also be a fully qualified class name, allowing registration
* of 3rd party plugins.
*/
public static final String CUCUMBER_OBJECT_FACTORY_PROPERTY_NAME = "cucumber.object-factory";
public static final String PLUGIN_PROPERTY_NAME = "cucumber.plugin";

/**
* Property name to control naming convention for generated snippets: {@value}
* <p>
* Valid values are {@code underscore} or {@code camelcase}.
* <p>
* By defaults are generated using the under score naming convention.
*/
public static final String SNIPPET_TYPE_PROPERTY_NAME = "cucumber.snippet-type";

private Constants() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,38 @@

import io.cucumber.core.backend.ObjectFactory;
import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.FeaturePath;
import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.io.Classpath;
import io.cucumber.core.io.MultiLoader;
import io.cucumber.core.io.ResourceLoader;
import io.cucumber.core.snippets.SnippetType;

import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;

public final class CucumberOptionsAnnotationParser {
private final RerunLoader rerunLoader;
private boolean featuresSpecified = false;
private boolean overridingGlueSpecified = false;
private OptionsProvider optionsProvider;

public CucumberOptionsAnnotationParser() {
this(new MultiLoader(CucumberOptionsAnnotationParser.class.getClassLoader()));
private static String packagePath(Class clazz) {
String packageName = packageName(clazz);

if (packageName.isEmpty()) {
return Classpath.CLASSPATH_SCHEME_PREFIX + "/";
}

return Classpath.CLASSPATH_SCHEME_PREFIX + packageName.replace('.', '/');
}

public CucumberOptionsAnnotationParser(ResourceLoader resourceLoader) {
this.rerunLoader = new RerunLoader(resourceLoader);
private static String packageName(Class clazz) {
String className = clazz.getName();
return className.substring(0, Math.max(0, className.lastIndexOf('.')));
}

public CucumberOptionsAnnotationParser withOptionsProvider(OptionsProvider optionsProvider){
public CucumberOptionsAnnotationParser withOptionsProvider(OptionsProvider optionsProvider) {
this.optionsProvider = optionsProvider;
return this;
}
Expand Down Expand Up @@ -98,8 +102,8 @@ private void addFeatures(CucumberOptions options, RuntimeOptionsBuilder args) {
for (String feature : options.features()) {
if (feature.startsWith("@")) {
args.setIsRerun(true);
URI rerunFile = FeaturePath.parse(feature.substring(1));
for (FeatureWithLines featureWithLines : rerunLoader.load(rerunFile)) {
Path rerunFile = Paths.get(feature.substring(1));
for (FeatureWithLines featureWithLines : OptionsFileParser.parseFeatureWithLinesFile(rerunFile)) {
args.addFeature(featureWithLines);
}
} else {
Expand Down Expand Up @@ -159,21 +163,6 @@ private void addObjectFactory(CucumberOptions options, RuntimeOptionsBuilder arg
}
}

private static String packagePath(Class clazz) {
String packageName = packageName(clazz);

if (packageName.isEmpty()) {
return Classpath.CLASSPATH_SCHEME_PREFIX + "/";
}

return Classpath.CLASSPATH_SCHEME_PREFIX + packageName.replace('.', '/');
}

private static String packageName(Class clazz) {
String className = clazz.getName();
return className.substring(0, Math.max(0, className.lastIndexOf('.')));
}

private boolean runningInEnvironmentWithoutAnsiSupport() {
boolean intelliJidea = System.getProperty("idea.launcher.bin.path") != null;
// TODO: What does Eclipse use?
Expand Down Expand Up @@ -210,7 +199,7 @@ public interface CucumberOptions {
String[] name();

SnippetType snippets();

Class<? extends ObjectFactory> objectFactory();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@

import static io.cucumber.core.options.Constants.CUCUMBER_PROPERTIES_FILE_NAME;

/**
* Store properties.
* <p>
* Cucumber can read properties from file, environment or system properties.
* <p>
* Cucumber properties are formatted using kebab-case. E.g.
* {@code cucumber.snippet-type}. To facilitate environments that do no support
* kebab case properties can also be formatted (in order of preference) using
* upper snake case e.g. {@code CUCUMBER_SNIPPET_TYPE} or lower snake case e.g.
* {@code cucumber_snippet_type}.
*/
public final class CucumberProperties {

private static final Logger log = LoggerFactory.getLogger(CucumberProperties.class);
Expand Down
Loading