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

[JUnit Platform] Use EngineDiscoveryRequestResolver #2835

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
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
12 changes: 6 additions & 6 deletions cucumber-junit-platform-engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,14 @@ cucumber.junit-platform.naming-strategy= # long or short.
# default: short
# include parent descriptor name in test descriptor.

cucumber.junit-platform.naming-strategy.short.example-name= # number or pickle.
# default: number
# Use example number or pickle name for examples when
cucumber.junit-platform.naming-strategy.short.example-name= # number, number-and-pickle-if-parameterized or pickle.
# default: number-and-pickle-if-parameterized
# Use example number and/or pickle name for examples when
# short naming strategy is used

cucumber.junit-platform.naming-strategy.long.example-name= # number or pickle.
# default: number
# Use example number or pickle name for examples when
cucumber.junit-platform.naming-strategy.long.example-name= # number, number-and-pickle-if-parameterized, or pickle.
# default: number-and-pickle-if-parameterized
# Use example number and/or pickle name for examples when
# long naming strategy is used

cucumber.plugin= # comma separated plugin strings.
Expand Down
7 changes: 6 additions & 1 deletion cucumber-junit-platform-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<properties>
<hamcrest.version>3.0</hamcrest.version>
<junit-jupiter.version>5.11.2</junit-jupiter.version>
<junit-jupiter.version>5.12.0-SNAPSHOT</junit-jupiter.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -65,6 +65,11 @@
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-testkit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.cucumber.junit.platform.engine;

import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.FeatureParser;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.resource.Resource;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -21,4 +25,32 @@ class CachingFeatureParser {
Optional<Feature> parseResource(Resource resource) {
return cache.computeIfAbsent(resource.getUri(), uri -> delegate.parseResource(resource));
}

Optional<Feature> parseResource(org.junit.platform.commons.support.Resource resource) {
return cache.computeIfAbsent(resource.getUri(), uri -> delegate.parseResource(new ResourceAdapter(resource)));
}

private static class ResourceAdapter implements Resource {
private final org.junit.platform.commons.support.Resource resource;

public ResourceAdapter(org.junit.platform.commons.support.Resource resource) {
this.resource = resource;
}

@Override
public URI getUri() {
String name = resource.getName();
try {
return new URI("classpath", name, null);
} catch (URISyntaxException e) {
String message = String.format("Could not create classpath uri for resource '%s'", name);
throw new CucumberException(message, e);
}
}

@Override
public InputStream getInputStream() throws IOException {
return resource.getInputStream();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
* <p>
*
* @deprecated Please use the JUnit Platform Suite to run Cucumber in
* combination with Surefire or Gradle. E.g: <code><pre>{@code
* combination with Surefire or Gradle. E.g:
*
* <pre>{@code
*package com.example;
*
*import org.junit.platform.suite.api.ConfigurationParameter;
Expand All @@ -33,15 +35,15 @@
*
*import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
*
*&#64;Suite
*&#64;SelectPackages("com.example")
*&#64;ConfigurationParameter(
* key = GLUE_PROPERTY_NAME,
* value = "com.example"
*)
*public class RunCucumberTest {
*}
*}</pre></code>
* @Suite
* @SelectPackages("com.example")
* @ConfigurationParameter(
* key = GLUE_PROPERTY_NAME,
* value = "com.example")
* public class RunCucumberTest {
* }
* }</pre>
*
* @see CucumberTestEngine
*/
@API(status = Status.DEPRECATED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,29 @@
import io.cucumber.core.plugin.NoPublishFormatter;
import io.cucumber.core.plugin.PublishFormatter;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.junit.platform.engine.CucumberDiscoverySelectors.FeatureWithLinesSelector;
import io.cucumber.tagexpressions.Expression;
import io.cucumber.tagexpressions.TagExpressionParser;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.config.PrefixedConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static io.cucumber.core.resource.ClasspathSupport.CLASSPATH_SCHEME_PREFIX;
import static io.cucumber.junit.platform.engine.Constants.ANSI_COLORS_DISABLED_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.EXECUTION_DRY_RUN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.EXECUTION_EXCLUSIVE_RESOURCES_PREFIX;
import static io.cucumber.junit.platform.engine.Constants.EXECUTION_MODE_FEATURE_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.FEATURES_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.FILTER_NAME_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.FILTER_TAGS_PROPERTY_NAME;
Expand All @@ -41,17 +47,18 @@
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PUBLISH_TOKEN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.SNIPPET_TYPE_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.UUID_GENERATOR_PROPERTY_NAME;
import static java.util.Objects.requireNonNull;

class CucumberEngineOptions implements
class CucumberConfiguration implements
io.cucumber.core.plugin.Options,
io.cucumber.core.runner.Options,
io.cucumber.core.backend.Options,
io.cucumber.core.eventbus.Options {

private final ConfigurationParameters configurationParameters;

CucumberEngineOptions(ConfigurationParameters configurationParameters) {
this.configurationParameters = configurationParameters;
CucumberConfiguration(ConfigurationParameters configurationParameters) {
this.configurationParameters = requireNonNull(configurationParameters);
}

@Override
Expand Down Expand Up @@ -177,14 +184,27 @@ NamingStrategy namingStrategy() {
.create(configurationParameters);
}

List<FeatureWithLines> featuresWithLines() {
Set<FeatureWithLinesSelector> featuresWithLines() {
return configurationParameters.get(FEATURES_PROPERTY_NAME,
s -> Arrays.stream(s.split(","))
.map(String::trim)
.map(FeatureWithLines::parse)
.sorted(Comparator.comparing(FeatureWithLines::uri))
.distinct()
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
.map(FeatureWithLinesSelector::from)
.collect(Collectors.toSet()))
.orElse(Collections.emptySet());
}

ExecutionMode getExecutionModeFeature() {
return configurationParameters.get(EXECUTION_MODE_FEATURE_PROPERTY_NAME,
value -> ExecutionMode.valueOf(value.toUpperCase(Locale.US)))
.orElse(ExecutionMode.CONCURRENT);
}

ExclusiveResourceConfiguration getExclusiveResourceConfiguration(String tag) {
requireNonNull(tag);
return new ExclusiveResourceConfiguration(new PrefixedConfigurationParameters(
configurationParameters,
EXECUTION_EXCLUSIVE_RESOURCES_PREFIX + tag));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.cucumber.junit.platform.engine;

import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.plugin.event.Node;
import org.junit.platform.engine.DiscoverySelector;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.discovery.FilePosition;

import java.net.URI;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;

class CucumberDiscoverySelectors {

static final class FeatureWithLinesSelector implements DiscoverySelector {
private final URI uri;
private final Set<FilePosition> filePositions;

private FeatureWithLinesSelector(URI uri, Set<FilePosition> filePositions) {
this.uri = requireNonNull(uri);
this.filePositions = requireNonNull(filePositions);
}

static FeatureWithLinesSelector from(FeatureWithLines featureWithLines) {
Set<FilePosition> lines = featureWithLines.lines().stream()
.map(FilePosition::from)
.collect(Collectors.toSet());
return new FeatureWithLinesSelector(featureWithLines.uri(), lines);
}

static Set<FeatureWithLinesSelector> from(UniqueId uniqueId) {
return uniqueId.getSegments()
.stream()
.filter(FeatureOrigin::isFeatureSegment)
.map(featureSegment -> {
URI uri = URI.create(featureSegment.getValue());
Set<FilePosition> filePosition = getFilePosition(uniqueId.getLastSegment());
return new FeatureWithLinesSelector(uri, filePosition);
})
.collect(Collectors.toSet());
}

static FeatureWithLinesSelector from(URI uri) {
Set<FilePosition> positions = FilePosition.fromQuery(uri.getQuery())
.map(Collections::singleton)
.orElseGet(Collections::emptySet);
return new FeatureWithLinesSelector(stripQuery(uri), positions);
}

private static URI stripQuery(URI uri) {
if (uri.getQuery() == null) {
return uri;
}
String uriString = uri.toString();
return URI.create(uriString.substring(0, uriString.indexOf('?')));
}

private static Set<FilePosition> getFilePosition(UniqueId.Segment segment) {
if (FeatureOrigin.isFeatureSegment(segment)) {
return Collections.emptySet();
}

int line = Integer.parseInt(segment.getValue());
return Collections.singleton(FilePosition.from(line));
}

URI getUri() {
return uri;
}

Optional<Set<FilePosition>> getFilePositions() {
return filePositions.isEmpty() ? Optional.empty() : Optional.of(filePositions);
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
FeatureWithLinesSelector that = (FeatureWithLinesSelector) o;
return uri.equals(that.uri) && filePositions.equals(that.filePositions);
}

@Override
public int hashCode() {
return Objects.hash(uri, filePositions);
}
}

static class FeatureElementSelector implements DiscoverySelector {

private final Feature feature;
private final Node element;

private FeatureElementSelector(Feature feature) {
this(feature, feature);
}

private FeatureElementSelector(Feature feature, Node element) {
this.feature = requireNonNull(feature);
this.element = requireNonNull(element);
}

static FeatureElementSelector selectFeature(Feature feature) {
return new FeatureElementSelector(feature);
}

static FeatureElementSelector selectElement(Feature feature, Node element) {
return new FeatureElementSelector(feature, element);
}

static Optional<FeatureElementSelector> selectElementAt(Feature feature, FilePosition filePosition) {
return feature.findPathTo(candidate -> candidate.getLocation().getLine() == filePosition.getLine())
.map(nodes -> nodes.get(nodes.size() - 1))
.map(node -> new FeatureElementSelector(feature, node));
}

static Set<FeatureElementSelector> selectElementsOf(Feature feature, Node selected) {
if (selected instanceof Node.Container) {
Node.Container<?> container = (Node.Container<?>) selected;
return container.elements().stream()
.map(element -> new FeatureElementSelector(feature, element))
.collect(toSet());
}
return Collections.emptySet();
}

Feature getFeature() {
return feature;
}

Node getElement() {
return element;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
FeatureElementSelector that = (FeatureElementSelector) o;
return feature.equals(that.feature) && element.equals(that.element);
}

@Override
public int hashCode() {
return Objects.hash(feature, element);
}
}
}
Loading