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

Add new hooks for global level (BeforeTests and AfterTests) that run onl... #672

Closed
wants to merge 2 commits into from
Closed
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
17 changes: 16 additions & 1 deletion core/src/main/java/cucumber/runtime/Glue.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import gherkin.I18n;
import gherkin.formatter.model.Step;

import java.io.IOException;
import java.net.URL;
import java.util.List;

Expand All @@ -15,14 +14,30 @@ public interface Glue {

void addStepDefinition(StepDefinition stepDefinition) throws DuplicateStepDefinitionException;

void addBeforeAllHook(HookDefinition hookDefinition);

void addBeforeFeatureHook(HookDefinition hookDefinition);

void addBeforeHook(HookDefinition hookDefinition);

void addAfterHook(HookDefinition hookDefinition);

void addAfterFeatureHook(HookDefinition hookDefinition);

void addAfterAllHook(HookDefinition hookDefinition);

List<HookDefinition> getBeforeAllHooks();

List<HookDefinition> getBeforeFeatureHooks();

List<HookDefinition> getBeforeHooks();

List<HookDefinition> getAfterHooks();

List<HookDefinition> getAfterFeatureHooks();

List<HookDefinition> getAfterAllHooks();

StepDefinitionMatch stepDefinitionMatch(String featurePath, Step step, I18n i18n);

void writeStepdefsJson(ResourceLoader resourceLoader, List<String> featurePaths, URL dotCucumber);
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/cucumber/runtime/HookComparator.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class HookComparator implements Comparator<HookDefinition> {
private final boolean ascending;

public HookComparator(boolean ascending) {
private HookComparator(boolean ascending) {
this.ascending = ascending;
}

Expand All @@ -14,4 +14,8 @@ public int compare(HookDefinition hook1, HookDefinition hook2) {
int comparison = hook1.getOrder() - hook2.getOrder();
return ascending ? comparison : -comparison;
}

// stateless objects - no need for other instances created
public static final HookComparator ASCENDING = new HookComparator(true);
public static final HookComparator DESCENDING = new HookComparator(false);
}
40 changes: 34 additions & 6 deletions core/src/main/java/cucumber/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.HashSet;

/**
* This is the main entry point for running Cucumber features.
Expand Down Expand Up @@ -103,19 +104,24 @@ public void addError(Throwable error) {
* This is the main entry point. Used from CLI, but not from JUnit.
*/
public void run() throws IOException {
Reporter reporter = runtimeOptions.reporter(classLoader);
runBeforeAllHooks(reporter);
for (CucumberFeature cucumberFeature : runtimeOptions.cucumberFeatures(resourceLoader)) {
run(cucumberFeature);
Set<Tag> tags = new HashSet<Tag>(cucumberFeature.getGherkinFeature().getTags());
runBeforeFeatureHooks(reporter, tags);
run(cucumberFeature, reporter);
runAfterFeatureHooks(reporter, tags);
}
Formatter formatter = runtimeOptions.formatter(classLoader);
runAfterAllHooks(reporter);

Formatter formatter = runtimeOptions.formatter(classLoader);
formatter.done();
formatter.close();
printSummary();
}

private void run(CucumberFeature cucumberFeature) {
private void run(CucumberFeature cucumberFeature, Reporter reporter) {
Formatter formatter = runtimeOptions.formatter(classLoader);
Reporter reporter = runtimeOptions.reporter(classLoader);
cucumberFeature.run(formatter, reporter, this);
}

Expand Down Expand Up @@ -203,6 +209,22 @@ public void runAfterHooks(Reporter reporter, Set<Tag> tags) {
runHooks(glue.getAfterHooks(), reporter, tags, false);
}

public void runBeforeFeatureHooks(Reporter reporter, Set<Tag> tags) {
runHooks(glue.getBeforeFeatureHooks(), reporter, tags, true);
}

public void runAfterFeatureHooks(Reporter reporter, Set<Tag> tags) {
runHooks(glue.getAfterFeatureHooks(), reporter, tags, false);
}

public void runBeforeAllHooks(Reporter reporter) {
runHooks(glue.getBeforeAllHooks(), reporter, Collections.<Tag>emptySet(), true);
}

public void runAfterAllHooks(Reporter reporter) {
runHooks(glue.getAfterAllHooks(), reporter, Collections.<Tag>emptySet(), false);
}

private void runHooks(List<HookDefinition> hooks, Reporter reporter, Set<Tag> tags, boolean isBefore) {
if (!runtimeOptions.isDryRun()) {
for (HookDefinition hook : hooks) {
Expand Down Expand Up @@ -317,12 +339,18 @@ public static boolean isPending(Throwable t) {
}

private void addStepToCounterAndResult(Result result) {
scenarioResult.add(result);
// global hooks (@BeforeAll, @AfterAll, @BeforeFeature and @AfterFeature) are not part af a scenario
// so in those cases will be no scenarioResult
if (scenarioResult != null)
scenarioResult.add(result);
stats.addStep(result);
}

private void addHookToCounterAndResult(Result result) {
scenarioResult.add(result);
// global hooks (@BeforeAll, @AfterAll, @BeforeFeature and @AfterFeature) are not part af a scenario
// so in those cases will be no scenarioResult
if (scenarioResult != null)
scenarioResult.add(result);
stats.addHookTime(result.getDuration());
}
}
60 changes: 55 additions & 5 deletions core/src/main/java/cucumber/runtime/RuntimeGlue.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static cucumber.runtime.HookComparator.ASCENDING;
import static cucumber.runtime.HookComparator.DESCENDING;
import static cucumber.runtime.model.CucumberFeature.load;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort;

public class RuntimeGlue implements Glue {
private static final List<Object> NO_FILTERS = emptyList();

private final Map<String, StepDefinition> stepDefinitionsByPattern = new TreeMap<String, StepDefinition>();
private final List<HookDefinition> beforeAllHooks = new ArrayList<HookDefinition>();
private final List<HookDefinition> beforeFeatureHooks = new ArrayList<HookDefinition>();
private final List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>();
private final List<HookDefinition> afterHooks = new ArrayList<HookDefinition>();
private final List<HookDefinition> afterFeatureHooks = new ArrayList<HookDefinition>();
private final List<HookDefinition> afterAllHooks = new ArrayList<HookDefinition>();

private final UndefinedStepsTracker tracker;
private final LocalizedXStreams localizedXStreams;
Expand All @@ -49,16 +55,50 @@ public void addStepDefinition(StepDefinition stepDefinition) {
stepDefinitionsByPattern.put(stepDefinition.getPattern(), stepDefinition);
}

@Override
public void addBeforeAllHook(HookDefinition hookDefinition) {
addNewHookAndSort(beforeAllHooks, hookDefinition, true);
}

@Override
public void addBeforeFeatureHook(HookDefinition hookDefinition) {
addNewHookAndSort(beforeFeatureHooks, hookDefinition, true);
}

@Override
public void addBeforeHook(HookDefinition hookDefinition) {
beforeHooks.add(hookDefinition);
Collections.sort(beforeHooks, new HookComparator(true));
addNewHookAndSort(beforeHooks, hookDefinition, true);
}

@Override
public void addAfterHook(HookDefinition hookDefinition) {
afterHooks.add(hookDefinition);
Collections.sort(afterHooks, new HookComparator(false));
addNewHookAndSort(afterHooks, hookDefinition, false);
}

@Override
public void addAfterFeatureHook(HookDefinition hookDefinition) {
addNewHookAndSort(afterFeatureHooks, hookDefinition, false);
}

@Override
public void addAfterAllHook(HookDefinition hookDefinition) {
addNewHookAndSort(afterAllHooks, hookDefinition, false);
}

private void addNewHookAndSort(List<HookDefinition> hooks, HookDefinition hookToAdd, boolean ascending) {
hooks.add(hookToAdd);
if (ascending) sort(hooks, ASCENDING);
else sort(hooks, DESCENDING);
}

@Override
public List<HookDefinition> getBeforeAllHooks() {
return beforeAllHooks;
}

@Override
public List<HookDefinition> getBeforeFeatureHooks() {
return beforeFeatureHooks;
}

@Override
Expand All @@ -71,6 +111,16 @@ public List<HookDefinition> getAfterHooks() {
return afterHooks;
}

@Override
public List<HookDefinition> getAfterFeatureHooks() {
return afterFeatureHooks;
}

@Override
public List<HookDefinition> getAfterAllHooks() {
return afterAllHooks;
}

@Override
public StepDefinitionMatch stepDefinitionMatch(String featurePath, Step step, I18n i18n) {
List<StepDefinitionMatch> matches = stepDefinitionMatches(featurePath, step);
Expand Down
22 changes: 22 additions & 0 deletions java/src/main/java/cucumber/api/java/AfterAll.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cucumber.api.java;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterAll {

/**
* @return max amount of milliseconds this is allowed to run for. 0 (default) means no restriction.
*/
long timeout() default 0;

/**
* The order in which this hook should run. Higher numbers are run first.
* The default order is 10000.
*/
int order() default Integer.MAX_VALUE;
}
26 changes: 26 additions & 0 deletions java/src/main/java/cucumber/api/java/AfterFeature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cucumber.api.java;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterFeature {
/**
* @return a tag expression
*/
String[] value() default {};

/**
* @return max amount of milliseconds this is allowed to run for. 0 (default) means no restriction.
*/
long timeout() default 0;

/**
* The order in which this hook should run. Higher numbers are run first.
* The default order is 10000.
*/
int order() default 10000;
}
21 changes: 21 additions & 0 deletions java/src/main/java/cucumber/api/java/BeforeAll.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cucumber.api.java;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeAll {
/**
* @return max amount of milliseconds this is allowed to run for. 0 (default) means no restriction.
*/
long timeout() default 0;

/**
* The order in which this hook should run. Lower numbers are run first.
* The default order is 10000.
*/
int order() default Integer.MIN_VALUE;
}
26 changes: 26 additions & 0 deletions java/src/main/java/cucumber/api/java/BeforeFeature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cucumber.api.java;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeFeature {
/**
* @return a tag expression
*/
String[] value() default {};

/**
* @return max amount of milliseconds this is allowed to run for. 0 (default) means no restriction.
*/
long timeout() default 0;

/**
* The order in which this hook should run. Lower numbers are run first.
* The default order is 10000.
*/
int order() default 10000;
}
33 changes: 33 additions & 0 deletions java/src/main/java/cucumber/runtime/java/HookType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cucumber.runtime.java;

import cucumber.api.java.*;

import java.lang.annotation.Annotation;

enum HookType {
BEFORE_ALL(BeforeAll.class),
AFTER_ALL(AfterAll.class),

BEFORE_FEATURE(BeforeFeature.class),
AFTER_FEATURE(AfterFeature.class),

BEFORE(Before.class),
AFTER(After.class);

private Class<? extends Annotation> type;

private HookType(Class<? extends Annotation> type) {
this.type = type;
}

private Class<? extends Annotation> getType() {
return type;
}

public static HookType fromAnnotation(Annotation annotation) {
for (HookType hookType : values())
if (hookType.type.equals(annotation.annotationType())) return hookType;

return null;
}
}
Loading