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

Return exit code 0 when no features are found, or no found features match the tags filter #567

Merged
merged 4 commits into from
Aug 4, 2013
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
2 changes: 1 addition & 1 deletion core/src/main/java/cucumber/runtime/RuntimeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private void printUsage() {
}

public List<CucumberFeature> cucumberFeatures(ResourceLoader resourceLoader) {
return load(resourceLoader, featurePaths, filters);
return load(resourceLoader, featurePaths, filters, System.out);
}

public Formatter formatter(ClassLoader classLoader) {
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/cucumber/runtime/formatter/JUnitFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public void done() {
try {
//set up a transformer
rootElement.setAttribute("failures", String.valueOf(rootElement.getElementsByTagName("failure").getLength()));
if (rootElement.getElementsByTagName("testcase").getLength() == 0) {
addDummyTestCase(); // to avoid failed Jenkins jobs
}
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
Expand All @@ -108,6 +111,16 @@ public void done() {
}
}

private void addDummyTestCase() {
Element dummy = doc.createElement("testcase");
dummy.setAttribute("classname", "dummy");
dummy.setAttribute("name", "dummy");
rootElement.appendChild(dummy);
Element skipped = doc.createElement("skipped");
skipped.setAttribute("message", "No features found");
dummy.appendChild(skipped);
}

@Override
public void result(Result result) {
testCase.results.add(result);
Expand Down
26 changes: 15 additions & 11 deletions core/src/main/java/cucumber/runtime/model/CucumberFeature.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cucumber.runtime.model;

import cucumber.runtime.CucumberException;
import cucumber.runtime.FeatureBuilder;
import cucumber.runtime.Runtime;
import cucumber.runtime.io.Resource;
Expand All @@ -15,6 +14,7 @@
import gherkin.formatter.model.ScenarioOutline;
import gherkin.formatter.model.Step;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand All @@ -29,29 +29,33 @@ public class CucumberFeature {
private I18n i18n;
private CucumberScenarioOutline currentScenarioOutline;

public static List<CucumberFeature> load(ResourceLoader resourceLoader, List<String> featurePaths, final List<Object> filters, PrintStream out) {
final List<CucumberFeature> cucumberFeatures = load(resourceLoader, featurePaths, filters);
if (cucumberFeatures.isEmpty()) {
if (featurePaths.isEmpty()) {
out.println(String.format("Got no path to feature directory or feature file"));
} else if (filters.isEmpty()) {
out.println(String.format("No features found at %s", featurePaths));
} else {
out.println(String.format("None of the features at %s matched the filters: %s", featurePaths, filters));
}
}
return cucumberFeatures;
}

public static List<CucumberFeature> load(ResourceLoader resourceLoader, List<String> featurePaths, final List<Object> filters) {
final List<CucumberFeature> cucumberFeatures = new ArrayList<CucumberFeature>();
final FeatureBuilder builder = new FeatureBuilder(cucumberFeatures);
boolean resourceFound = false;
for (String featurePath : featurePaths) {
Iterable<Resource> resources = resourceLoader.resources(featurePath, ".feature");
for (Resource resource : resources) {
resourceFound = true;
builder.parse(resource, filters);
}
}
if (cucumberFeatures.isEmpty()) {
if (resourceFound) {
throw new CucumberException(String.format("None of the features at %s matched the filters: %s", featurePaths, filters));
} else {
throw new CucumberException(String.format("No features found at %s", featurePaths));
}
}
Collections.sort(cucumberFeatures, new CucumberFeatureUriComparator());
return cucumberFeatures;
}


public CucumberFeature(Feature feature, String uri) {
this.feature = feature;
this.uri = uri;
Expand Down
26 changes: 26 additions & 0 deletions core/src/test/java/cucumber/runtime/RuntimeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cucumber.api.PendingException;
import cucumber.api.Scenario;
import cucumber.runtime.io.ClasspathResourceLoader;
import cucumber.runtime.io.Resource;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.model.CucumberFeature;
import gherkin.I18n;
Expand Down Expand Up @@ -34,6 +35,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollectionOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;

public class RuntimeTest {

Expand Down Expand Up @@ -177,6 +179,16 @@ public void strict_with_errors() {
assertEquals(0x1, runtime.exitStatus());
}

@Test
public void should_pass_if_no_features_are_found() {
ResourceLoader resourceLoader = createResourceLoaderThatFindsNoFeatures();
Runtime runtime = createStrictRuntime(resourceLoader);

runtime.run();

assertEquals(0x0, runtime.exitStatus());
}

@Test
public void should_throw_cucumer_exception_if_no_backends_are_found() throws Exception {
try {
Expand Down Expand Up @@ -347,6 +359,12 @@ public void runStep(Reporter reporter, Runtime runtime) {
runtime.runStep("<uri>", step, reporter, i18n);
}

private ResourceLoader createResourceLoaderThatFindsNoFeatures() {
ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(resourceLoader.resources(anyString(), eq(".feature"))).thenReturn(Collections.<Resource>emptyList());
return resourceLoader;
}

private Runtime createStrictRuntime() {
return createRuntime("-g", "anything", "--strict");
}
Expand All @@ -355,9 +373,17 @@ private Runtime createNonStrictRuntime() {
return createRuntime("-g", "anything");
}

private Runtime createStrictRuntime(ResourceLoader resourceLoader) {
return createRuntime(resourceLoader, Thread.currentThread().getContextClassLoader(), "-g", "anything", "--strict");
}

private Runtime createRuntime(String... runtimeArgs) {
ResourceLoader resourceLoader = mock(ResourceLoader.class);
ClassLoader classLoader = mock(ClassLoader.class);
return createRuntime(resourceLoader, classLoader, runtimeArgs);
}

private Runtime createRuntime(ResourceLoader resourceLoader, ClassLoader classLoader, String... runtimeArgs) {
RuntimeOptions runtimeOptions = new RuntimeOptions(new Properties(), runtimeArgs);
Backend backend = mock(Backend.class);
Collection<Backend> backends = Arrays.asList(backend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,24 @@ public void should_handle_one_step_at_the_time_execution() throws Exception {
assertXmlEqual(expected, replaceTimeWithZeroTime(actual));
}

@Test
public void should_add_dummy_testcase_if_no_features_are_found_to_aviod_failed_jenkins_jobs() throws Exception {
final File report = File.createTempFile("cucumber-jvm-junit", ".xml");
final JUnitFormatter junitFormatter = createJUnitFormatter(report);

junitFormatter.done();
junitFormatter.close();

String actual = new Scanner(new FileInputStream(report), "UTF-8").useDelimiter("\\A").next();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
"<testsuite failures=\"0\">\n" +
" <testcase classname=\"dummy\" name=\"dummy\">\n" +
" <skipped message=\"No features found\" />\n" +
" </testcase>\n" +
"</testsuite>\n";
assertXmlEqual(expected, replaceTimeWithZeroTime(actual));
}

private File runFeaturesWithJunitFormatter(final List<String> featurePaths) throws IOException {
return runFeaturesWithJunitFormatter(featurePaths, false);
}
Expand Down
65 changes: 40 additions & 25 deletions core/src/test/java/cucumber/runtime/model/CucumberFeatureTest.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,63 @@
package cucumber.runtime.model;

import cucumber.runtime.CucumberException;
import cucumber.runtime.io.Resource;
import cucumber.runtime.io.ResourceLoader;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class CucumberFeatureTest {
@Test
public void fails_if_no_features_are_found() {
try {
ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(resourceLoader.resources("does/not/exist", ".feature")).thenReturn(Collections.<Resource>emptyList());
CucumberFeature.load(resourceLoader, asList("does/not/exist"), emptyList());
fail("Should have failed");
} catch (CucumberException e) {
assertEquals("No features found at [does/not/exist]", e.getMessage());
}
public void succeds_if_no_features_are_found() {
ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(resourceLoader.resources("does/not/exist", ".feature")).thenReturn(Collections.<Resource>emptyList());

CucumberFeature.load(resourceLoader, asList("does/not/exist"), emptyList(), new PrintStream(new ByteArrayOutputStream()));
}

@Test
public void logs_message_if_no_features_are_found() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(resourceLoader.resources("does/not/exist", ".feature")).thenReturn(Collections.<Resource>emptyList());

CucumberFeature.load(resourceLoader, asList("does/not/exist"), emptyList(), new PrintStream(baos));

assertEquals(String.format("No features found at [does/not/exist]%n"), baos.toString());
}

@Test
public void logs_message_if_features_are_found_but_filters_are_too_strict() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ResourceLoader resourceLoader = mock(ResourceLoader.class);
Resource resource = mock(Resource.class);
when(resource.getPath()).thenReturn("foo.feature");
when(resource.getInputStream()).thenReturn(new ByteArrayInputStream("Feature: foo".getBytes("UTF-8")));
when(resourceLoader.resources("features", ".feature")).thenReturn(asList(resource));

CucumberFeature.load(resourceLoader, asList("features"), asList((Object) "@nowhere"), new PrintStream(baos));

assertEquals(String.format("None of the features at [features] matched the filters: [@nowhere]%n"), baos.toString());
}

@Test
public void fails_if_features_are_found_but_filters_are_too_strict() throws IOException {
try {
ResourceLoader resourceLoader = mock(ResourceLoader.class);

Resource resource = mock(Resource.class);
when(resource.getPath()).thenReturn("foo.feature");
when(resource.getInputStream()).thenReturn(new ByteArrayInputStream("Feature: foo".getBytes("UTF-8")));

when(resourceLoader.resources("features", ".feature")).thenReturn(asList(resource));
CucumberFeature.load(resourceLoader, asList("features"), asList((Object) "@nowhere"));
fail("Should have failed");
} catch (CucumberException e) {
assertEquals("None of the features at [features] matched the filters: [@nowhere]", e.getMessage());
}
public void logs_message_if_no_feature_paths_are_given() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ResourceLoader resourceLoader = mock(ResourceLoader.class);

CucumberFeature.load(resourceLoader, Collections.<String>emptyList(), emptyList(), new PrintStream(baos));

assertEquals(String.format("Got no path to feature directory or feature file%n"), baos.toString());
}

}
7 changes: 7 additions & 0 deletions examples/java-no-features/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/.settings
/.classpath
/.project
.idea
*.iml
.DS_Store

11 changes: 11 additions & 0 deletions examples/java-no-features/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# java-no-features

If wonder what would Cucumber JVM do when there are no feature files found at all.
Just run these cukes by

mvn clean test

## Read more

- [Jenkins fails if there are no testcases](http://jenkins-ci.361315.n4.nabble.com/Jenkins-fails-if-there-are-no-testcases-td3899185.html) Jenkins CI thread
- [[JVM] Exception thrown when no tags are found](https://groups.google.com/forum/#!topic/cukes/wZo0FTQJrVA) Google group discussion
38 changes: 38 additions & 0 deletions examples/java-no-features/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>info.cukes</groupId>
<artifactId>cucumber-jvm</artifactId>
<relativePath>../../pom.xml</relativePath>
<version>1.1.4-SNAPSHOT</version>
</parent>

<artifactId>java-no-features</artifactId>
<packaging>jar</packaging>
<name>Examples: No Cucumber features</name>

<dependencies>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-jvm-deps</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cucumber.examples.java.no.features;

import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
public class RunCukesTest {
}
5 changes: 3 additions & 2 deletions junit/src/test/java/cucumber/runtime/junit/CucumberTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ public void finds_features_based_on_explicit_root_package() throws IOException,
assertEquals("Feature: FA", cucumber.getChildren().get(0).getName());
}

@Test(expected = CucumberException.class)
@Test
public void finds_no_features_when_explicit_package_has_nothnig() throws IOException, InitializationError {
new Cucumber(ExplicitFeaturePathWithNoFeatures.class);
Cucumber cucumber = new Cucumber(ExplicitFeaturePathWithNoFeatures.class);
assertEquals(0, cucumber.getChildren().size());
}

@RunWith(Cucumber.class)
Expand Down