Skip to content

Commit

Permalink
[Core] Read rerun files from directories (#2710)
Browse files Browse the repository at this point in the history
Fixes: #2708

Co-authored-by: M.P. Korstanje <[email protected]>
  • Loading branch information
dbfwhitney and mpkorstanje authored Apr 13, 2023
1 parent 5f929d3 commit 654cafc
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 131 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- [JUnit Platform Engine] Add constant for fixed.max-pool-size property ([#2713](https://github.com/cucumber/cucumber-jvm/pull/2713) M.P. Korstanje)
- [Core] Support directories containing exclusively rerun files using the `@path/to/rerun` syntax ([#2710](https://github.com/cucumber/cucumber-jvm/pull/2710) Daniel Whitney, M.P. Korstanje)
- [Core] Improved event bus performance using UUID generator selectable through SPI ([#2703](https://github.com/cucumber/cucumber-jvm/pull/2703) Julien Kronegg)

### Fixed
Expand Down
5 changes: 3 additions & 2 deletions cucumber-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ cucumber.execution.wip= # true or false. default: false.
# Fails if there any passing scenarios
# CLI only.
cucumber.features= # comma separated paths to feature files.
# example: path/to/example.feature, path/to/other.feature
cucumber.features= # comma separated list of feature paths.
# format: [ PATH[.feature[:LINE]*] | URI[.feature[:LINE]*] | @PATH ]
# example: path/to/features, classpath:com/example/features, path/to/example.feature:42, @path/to/rerun.txt
cucumber.filter.name= # a regular expression
# only scenarios with matching names are executed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
* Identifies either a directory containing feature files, a specific feature or
* specific scenarios and examples (pickles) in a feature.
* <p>
* The syntax of a a feature with lines defined as either a {@link FeaturePath}
* or a {@link FeatureIdentifier} followed by a sequence of line numbers each
* The syntax of a feature with lines defined as either a {@link FeaturePath} or
* a {@link FeatureIdentifier} followed by a sequence of line numbers each
* preceded by a colon.
*/
public class FeatureWithLines implements Serializable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.cucumber.core.options;

import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
Expand All @@ -17,8 +16,6 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -59,7 +56,6 @@
import static io.cucumber.core.cli.CommandlineOptions.WIP;
import static io.cucumber.core.cli.CommandlineOptions.WIP_SHORT;
import static io.cucumber.core.options.ObjectFactoryParser.parseObjectFactory;
import static io.cucumber.core.options.OptionsFileParser.parseFeatureWithLinesFile;
import static io.cucumber.core.options.UuidGeneratorParser.parseUuidGenerator;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
Expand Down Expand Up @@ -178,13 +174,9 @@ private RuntimeOptionsBuilder parse(List<String> args) {
exitCode = 1;
return parsedOptions;
} else if (!arg.isEmpty()) {
if (arg.startsWith("@")) {
Path rerunFile = Paths.get(arg.substring(1));
parsedOptions.addRerun(parseFeatureWithLinesFile(rerunFile));
} else {
FeatureWithLines featureWithLines = FeatureWithLines.parse(arg);
parsedOptions.addFeature(featureWithLines);
}
FeatureWithLinesOrRerunPath parsed = FeatureWithLinesOrRerunPath.parse(arg);
parsed.getFeaturesToRerun().ifPresent(parsedOptions::addRerun);
parsed.getFeatureWithLines().ifPresent(parsedOptions::addFeature);
}
}

Expand Down
32 changes: 18 additions & 14 deletions cucumber-core/src/main/java/io/cucumber/core/options/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,26 @@ public final class Constants {
public static final String WIP_PROPERTY_NAME = "cucumber.execution.wip";

/**
* Property name used to set feature location: {@value}
* Property name used to select features: {@value}
* <p>
* A comma separated list of:
* A comma separated list of feature paths. A feature path is constructed as
* {@code [ PATH[.feature[:LINE]*] | URI[.feature[:LINE]*] | @PATH ] }
* <p>
* Examples:
* <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>
* <li>{@code src/test/resources/features} -- All features in the
* {@code src/test/resources/features} directory</li>
* <li>{@code classpath:com/example/application} -- All features in the
* {@code com.example.application} package</li>
* <li>{@code in-memory:/features} -- All features in the {@code /features}
* directory on an in memory file system supported by
* {@link java.nio.file.FileSystems}</li>
* <li>{@code src/test/resources/features/example.feature:42} -- The
* scenario or example at line 42 in the example feature file</li>
* <li>{@code @target/rerun} -- All the scenarios in the files in the rerun
* directory</li>
* <li>{@code @target/rerun/RunCucumber.txt} -- All the scenarios in
* RunCucumber.txt file</li>
* </ul>
*
* @see io.cucumber.core.feature.FeatureWithLines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
import io.cucumber.tagexpressions.TagExpressionException;
import io.cucumber.tagexpressions.TagExpressionParser;

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

import static io.cucumber.core.options.OptionsFileParser.parseFeatureWithLinesFile;
import static io.cucumber.core.resource.ClasspathSupport.CLASSPATH_SCHEME_PREFIX;
import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -133,13 +130,9 @@ private void addGlue(CucumberOptions options, RuntimeOptionsBuilder args) {
private void addFeatures(CucumberOptions options, RuntimeOptionsBuilder args) {
if (options != null && options.features().length != 0) {
for (String feature : options.features()) {
if (feature.startsWith("@")) {
Path rerunFile = Paths.get(feature.substring(1));
args.addRerun(parseFeatureWithLinesFile(rerunFile));
} else {
FeatureWithLines featureWithLines = FeatureWithLines.parse(feature);
args.addFeature(featureWithLines);
}
FeatureWithLinesOrRerunPath parsed = FeatureWithLinesOrRerunPath.parse(feature);
parsed.getFeaturesToRerun().ifPresent(args::addRerun);
parsed.getFeatureWithLines().ifPresent(args::addFeature);
}
featuresSpecified = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
package io.cucumber.core.options;

import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static io.cucumber.core.options.Constants.ANSI_COLORS_DISABLED_PROPERTY_NAME;
import static io.cucumber.core.options.Constants.EXECUTION_DRY_RUN_PROPERTY_NAME;
Expand All @@ -34,7 +30,6 @@
import static io.cucumber.core.options.Constants.SNIPPET_TYPE_PROPERTY_NAME;
import static io.cucumber.core.options.Constants.UUID_GENERATOR_PROPERTY_NAME;
import static io.cucumber.core.options.Constants.WIP_PROPERTY_NAME;
import static io.cucumber.core.options.OptionsFileParser.parseFeatureWithLinesFile;
import static java.util.Arrays.stream;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -72,16 +67,11 @@ public RuntimeOptionsBuilder parse(CucumberPropertiesProvider properties) {

parseAll(properties,
FEATURES_PROPERTY_NAME,
splitAndThenFlatMap(CucumberPropertiesParser::parseFeatureFile),
builder::addFeature);

parseAll(properties,
// For historical reasons rerun files are also provided through the
// feature property. They are differentiated by prefixing the uri
// name with an `@` symbol.
FEATURES_PROPERTY_NAME,
splitAndThenFlatMap(CucumberPropertiesParser::parseRerunFile),
builder::addRerun);
splitAndMap(FeatureWithLinesOrRerunPath::parse),
parsed -> {
parsed.getFeaturesToRerun().ifPresent(builder::addRerun);
parsed.getFeatureWithLines().ifPresent(builder::addFeature);
});

parse(properties,
FILTER_NAME_PROPERTY_NAME,
Expand Down Expand Up @@ -178,21 +168,6 @@ private <T> void parseAll(
}
}

private static <T> Function<String, Collection<T>> splitAndThenFlatMap(Function<String, Stream<T>> parse) {
return combined -> stream(combined.split(","))
.map(String::trim)
.filter(part -> !part.isEmpty())
.flatMap(parse)
.collect(toList());
}

private static Stream<FeatureWithLines> parseFeatureFile(String property) {
if (property.startsWith("@")) {
return Stream.empty();
}
return Stream.of(FeatureWithLines.parse(property));
}

private static <T> Function<String, Collection<T>> splitAndMap(Function<String, T> parse) {
return combined -> stream(combined.split(","))
.map(String::trim)
Expand All @@ -201,12 +176,4 @@ private static <T> Function<String, Collection<T>> splitAndMap(Function<String,
.collect(toList());
}

private static Stream<Collection<FeatureWithLines>> parseRerunFile(String property) {
if (property.startsWith("@")) {
Path rerunFile = Paths.get(property.substring(1));
return Stream.of(parseFeatureWithLinesFile(rerunFile));
}
return Stream.empty();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.cucumber.core.options;

import io.cucumber.core.feature.FeatureWithLines;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Optional;

/**
* Identifies either:
* <li>
* <ul>
* a single rerun file,
* </ul>
* <ul>
* a directory of containing exclusively rerun files,
* </ul>
* <ul>
* a directory containing feature files,
* </ul>
* <ul>
* a specific feature,
* </ul>
* <ul>
* or specific scenarios and examples (pickles) in a feature
* </ul>
* </li>
* <p>
* The syntax is either a {@link FeatureWithLines} or an {@code @} followed by a
* {@link RerunPath}.
*/
class FeatureWithLinesOrRerunPath {

private final FeatureWithLines featureWithLines;
private final Collection<FeatureWithLines> featuresWithLinesToRerun;

FeatureWithLinesOrRerunPath(
FeatureWithLines featureWithLines, Collection<FeatureWithLines> featuresWithLinesToRerun
) {
this.featureWithLines = featureWithLines;
this.featuresWithLinesToRerun = featuresWithLinesToRerun;
}

static FeatureWithLinesOrRerunPath parse(String arg) {
if (arg.startsWith("@")) {
Path rerunFileOrDirectory = Paths.get(arg.substring(1));
return new FeatureWithLinesOrRerunPath(null, RerunPath.parse(rerunFileOrDirectory));
} else {
return new FeatureWithLinesOrRerunPath(FeatureWithLines.parse(arg), null);
}
}

Optional<Collection<FeatureWithLines>> getFeaturesToRerun() {
return Optional.ofNullable(featuresWithLinesToRerun);
}

Optional<FeatureWithLines> getFeatureWithLines() {
return Optional.ofNullable(featureWithLines);
}

}

This file was deleted.

Loading

0 comments on commit 654cafc

Please sign in to comment.