Skip to content

Commit

Permalink
Merge pull request #871 from pshrm/develop
Browse files Browse the repository at this point in the history
Jnuit4 parallel test runner
  • Loading branch information
ptrthomas authored Aug 20, 2019
2 parents 0da4f41 + 7bdc7c8 commit cb905bd
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
String[] features() default {};

String[] tags() default {};


int threads() default 1;
}
10 changes: 9 additions & 1 deletion karate-core/src/main/java/com/intuit/karate/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@ public Builder path(String... paths) {
return this;
}

public Builder path(List<String> paths) {
this.paths.addAll(paths);
return this;
}
public Builder tags(String... tags) {
this.tags.addAll(Arrays.asList(tags));
return this;
}
public Builder tags(List<String> tags) {
this.tags.addAll(tags);
return this;
}

public Builder forClass(Class clazz) {
this.optionsClass = clazz;
Expand Down Expand Up @@ -108,7 +116,7 @@ List<Resource> resources() {
return resources;
}

Results parallel(int threadCount) {
public Results parallel(int threadCount) {
this.threadCount = threadCount;
return Runner.parallel(this);
}
Expand Down
17 changes: 15 additions & 2 deletions karate-core/src/main/java/com/intuit/karate/RunnerOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ public class RunnerOptions {
@CommandLine.Option(names = {"-n", "--name"}, description = "name of scenario to run")
String name;

@CommandLine.Option(names = {"-T", "--threads"}, description = "number of threads when running tests")
int threads = 1;

@CommandLine.Parameters(description = "one or more tests (features) or search-paths to run")
List<String> features;

Expand All @@ -81,6 +84,10 @@ public List<String> getFeatures() {
return features;
}

public int getThreads(){
return threads;
}

public static RunnerOptions parseStringArgs(String[] args) {
RunnerOptions options = CommandLine.populateCommand(new RunnerOptions(), args);
List<String> featuresTemp = new ArrayList();
Expand Down Expand Up @@ -114,6 +121,7 @@ public static RunnerOptions parseCommandLine(String line) {
public static RunnerOptions fromAnnotationAndSystemProperties(Class<?> clazz) {
List<String> tags = null;
List<String> features = null;
int threads = 1;
KarateOptions ko = clazz.getAnnotation(KarateOptions.class);
if (ko == null) {
CucumberOptions co = clazz.getAnnotation(CucumberOptions.class);
Expand All @@ -122,13 +130,14 @@ public static RunnerOptions fromAnnotationAndSystemProperties(Class<?> clazz) {
features = Arrays.asList(co.features());
}
} else {
threads = ko.threads();
tags = Arrays.asList(ko.tags());
features = Arrays.asList(ko.features());
}
return fromAnnotationAndSystemProperties(features, tags, clazz);
return fromAnnotationAndSystemProperties(features, tags, threads, clazz);
}

public static RunnerOptions fromAnnotationAndSystemProperties(List<String> features, List<String> tags, Class clazz) {
public static RunnerOptions fromAnnotationAndSystemProperties(List<String> features, List<String> tags, int threads, Class clazz) {
if (clazz != null && (features == null || features.isEmpty())) {
String relative = FileUtils.toRelativeClassPath(clazz);
features = Collections.singletonList(relative);
Expand All @@ -144,6 +153,7 @@ public static RunnerOptions fromAnnotationAndSystemProperties(List<String> featu
options = new RunnerOptions();
options.tags = tags;
options.features = features;
options.threads = threads;
} else {
logger.info("found system property 'karate.options': {}", line);
options = parseCommandLine(line);
Expand All @@ -153,6 +163,9 @@ public static RunnerOptions fromAnnotationAndSystemProperties(List<String> featu
if (options.features == null) {
options.features = features;
}
if (options.threads == 0){
options.threads = threads;
}
}
return options;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,61 +23,42 @@
*/
package com.intuit.karate.junit4;

import com.intuit.karate.CallContext;
import com.intuit.karate.core.FeatureContext;
import com.intuit.karate.core.ExecutionContext;
import com.intuit.karate.core.ExecutionHook;
import com.intuit.karate.core.Feature;
import com.intuit.karate.core.FeatureExecutionUnit;
import com.intuit.karate.core.FeatureResult;
import com.intuit.karate.core.PerfEvent;
import com.intuit.karate.core.Scenario;
import com.intuit.karate.core.ScenarioContext;
import com.intuit.karate.core.ScenarioExecutionUnit;
import com.intuit.karate.core.ScenarioResult;
import com.intuit.karate.core.*;
import com.intuit.karate.http.HttpRequestBuilder;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

import static org.junit.runner.Description.createTestDescription;

/**
*
* @author pthomas3
*/
public class FeatureInfo implements ExecutionHook {
public class JunitHook implements ExecutionHook {

public final Feature feature;
public final ExecutionContext exec;
public final Description description;
public final FeatureExecutionUnit unit;

private RunNotifier notifier;

public JunitHook(Class clazz) {
description = Description.createSuiteDescription(clazz);
}

public void setNotifier(RunNotifier notifier) {
this.notifier = notifier;
}

private static String getFeatureName(Feature feature) {
return "[" + feature.getResource().getFileNameWithoutExtension() + "]";
return feature.getResource().getFileNameWithoutExtension();
}

public static Description getScenarioDescription(Scenario scenario) {
String featureName = getFeatureName(scenario.getFeature());
return Description.createTestDescription(featureName, scenario.getDisplayMeta() + ' ' + scenario.getName());
return createTestDescription("Feature: " + featureName, "Scenario: " + scenario.getDisplayMeta() + ' ' + scenario.getName());
}

public FeatureInfo(Feature feature, String tagSelector) {
this.feature = feature;
description = Description.createSuiteDescription(getFeatureName(feature), feature.getResource().getPackageQualifiedName());
FeatureContext featureContext = new FeatureContext(null, feature, tagSelector);
CallContext callContext = new CallContext(null, true, this);
exec = new ExecutionContext(System.currentTimeMillis(), featureContext, callContext, null, null, null);
unit = new FeatureExecutionUnit(exec);
unit.init();
for (ScenarioExecutionUnit u : unit.getScenarioExecutionUnits()) {
Description scenarioDescription = getScenarioDescription(u.scenario);
description.addChild(scenarioDescription);
}
public Description getDescription() {
return description;
}

@Override
Expand All @@ -93,7 +74,7 @@ public boolean beforeScenario(Scenario scenario, ScenarioContext context) {
@Override
public void afterScenario(ScenarioResult result, ScenarioContext context) {
// if dynamic scenario outline background or a call
if (notifier == null || context.callDepth > 0) {
if (notifier == null || context.callDepth > 0) {
return;
}
Description scenarioDescription = getScenarioDescription(result.getScenario());
Expand All @@ -103,7 +84,7 @@ public void afterScenario(ScenarioResult result, ScenarioContext context) {
// apparently this method should be always called
// even if fireTestFailure was called
notifier.fireTestFinished(scenarioDescription);
}
}

@Override
public boolean beforeFeature(Feature feature) {
Expand All @@ -112,9 +93,9 @@ public boolean beforeFeature(Feature feature) {

@Override
public void afterFeature(FeatureResult result) {
}

}

@Override
public String getPerfEventName(HttpRequestBuilder req, ScenarioContext context) {
return null;
Expand Down
69 changes: 26 additions & 43 deletions karate-junit4/src/main/java/com/intuit/karate/junit4/Karate.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,10 @@
*/
package com.intuit.karate.junit4;

import com.intuit.karate.Resource;
import com.intuit.karate.FileUtils;
import com.intuit.karate.Runner.Builder;
import com.intuit.karate.RunnerOptions;
import com.intuit.karate.core.Engine;
import com.intuit.karate.core.Feature;
import com.intuit.karate.core.FeatureParser;
import com.intuit.karate.core.FeatureResult;
import com.intuit.karate.core.ScenarioResult;
import com.intuit.karate.core.Tags;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.FrameworkMethod;
Expand All @@ -49,39 +35,42 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
*
* @author pthomas3
*/
public class Karate extends ParentRunner<Feature> {
public class Karate extends ParentRunner<JunitHook> {

private static final Logger logger = LoggerFactory.getLogger(Karate.class);

private final List<Feature> children;
private final Map<String, FeatureInfo> featureMap;
private final String tagSelector;
private final List<JunitHook> children;

private Builder builder;

public Karate(Class<?> clazz) throws InitializationError, IOException {
private int threads;

public Karate(Class<?> clazz) throws InitializationError {
super(clazz);
List<FrameworkMethod> testMethods = getTestClass().getAnnotatedMethods(Test.class);
if (!testMethods.isEmpty()) {
logger.warn("WARNING: there are methods annotated with '@Test', they will NOT be run when using '@RunWith(Karate.class)'");
}

JunitHook junitHook = new JunitHook(clazz);
RunnerOptions options = RunnerOptions.fromAnnotationAndSystemProperties(clazz);
List<Resource> resources = FileUtils.scanForFeatureFiles(options.getFeatures(), clazz.getClassLoader());
children = new ArrayList(resources.size());
featureMap = new HashMap(resources.size());
for (Resource resource : resources) {
Feature feature = FeatureParser.parse(resource);
feature.setCallName(options.getName());
feature.setCallLine(resource.getLine());
children.add(feature);
}
tagSelector = Tags.fromKarateOptionsTags(options.getTags());
builder = new Builder().hook(junitHook).path(options.getFeatures());
if (options.getTags() != null)
builder = builder.tags(options.getTags());
threads = options.getThreads();
children = new ArrayList<>();
children.add(junitHook);
}

@Override
public List<Feature> getChildren() {
public List<JunitHook> getChildren() {
return children;
}

Expand All @@ -103,7 +92,7 @@ protected Statement withBeforeClasses(Statement statement) {
}

@Override
protected Description describeChild(Feature feature) {
protected Description describeChild(JunitHook junitHook) {
if (!beforeClassDone) {
try {
Statement statement = withBeforeClasses(NO_OP);
Expand All @@ -113,19 +102,13 @@ protected Description describeChild(Feature feature) {
throw new RuntimeException(e);
}
}
FeatureInfo info = new FeatureInfo(feature, tagSelector);
featureMap.put(feature.getRelativePath(), info);
return info.description;
return junitHook.getDescription();
}

@Override
protected void runChild(Feature feature, RunNotifier notifier) {
FeatureInfo info = featureMap.get(feature.getRelativePath());
info.setNotifier(notifier);
info.unit.run();
FeatureResult result = info.exec.result;
result.printStats(null);
Engine.saveResultHtml(FileUtils.getBuildDir() + File.separator + "surefire-reports", result, null);
protected void runChild(JunitHook feature, RunNotifier notifier) {
feature.setNotifier(notifier);
builder.parallel(threads);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.intuit.karate.junit4;


import com.intuit.karate.KarateOptions;
import org.junit.runner.RunWith;

/**
*
* @author pthomas3
*/
@KarateOptions(tags = "~@ignore", threads = 5)
@RunWith(Karate.class)
public class KarateJunitParallelTest {

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Karate tags(String... tags) {

@Override
public Iterator<DynamicNode> iterator() {
RunnerOptions options = RunnerOptions.fromAnnotationAndSystemProperties(paths, tags, clazz);
RunnerOptions options = RunnerOptions.fromAnnotationAndSystemProperties(paths, tags, 1, clazz);
List<Resource> resources = FileUtils.scanForFeatureFiles(options.getFeatures(), clazz);
List<Feature> features = new ArrayList(resources.size());
for (Resource resource : resources) {
Expand Down

0 comments on commit cb905bd

Please sign in to comment.