Skip to content

Commit

Permalink
cukexit wip the parallel runner works ! ref #444
Browse files Browse the repository at this point in the history
the demos are working including reports TODO called steps
support for the legacy cucumber-options, only tags and features
new scenario-hooks strategy
todo time reporting and hiding skipped features
but it does look like the bulk of the work is over
  • Loading branch information
ptrthomas committed Aug 19, 2018
1 parent 28f1fce commit 05e9c11
Show file tree
Hide file tree
Showing 33 changed files with 568 additions and 456 deletions.
11 changes: 9 additions & 2 deletions karate-core/src/main/java/com/intuit/karate/CallContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package com.intuit.karate;

import com.intuit.karate.core.ScenarioHook;
import com.intuit.karate.cucumber.ScenarioInfo;
import com.intuit.karate.cucumber.StepInterceptor;
import java.util.List;
Expand All @@ -45,6 +46,7 @@ public class CallContext {
public final Consumer<Runnable> asyncSystem;
public final Runnable asyncNext;
public final StepInterceptor stepInterceptor;
public final ScenarioHook scenarioHook;

private List<String> tags;
private Map<String, List<String>> tagValues;
Expand Down Expand Up @@ -79,12 +81,16 @@ public boolean isCalled() {
}

public CallContext(Map<String, Object> callArg, boolean evalKarateConfig) {
this(null, 0, callArg, -1, false, evalKarateConfig, null, null, null, null);
this(null, 0, callArg, -1, false, evalKarateConfig, null, null, null, null, null);
}

public CallContext(ScenarioHook scenarioHook) {
this(null, 0, null, -1, false, true, null, null, null, null, scenarioHook);
}

public CallContext(ScriptContext parentContext, int callDepth, Map<String, Object> callArg, int loopIndex,
boolean reuseParentContext, boolean evalKarateConfig, String httpClientClass,
Consumer<Runnable> asyncSystem, Runnable asyncNext, StepInterceptor stepInterceptor) {
Consumer<Runnable> asyncSystem, Runnable asyncNext, StepInterceptor stepInterceptor, ScenarioHook scenarioHook) {
this.parentContext = parentContext;
this.callDepth = callDepth;
this.callArg = callArg;
Expand All @@ -95,6 +101,7 @@ public CallContext(ScriptContext parentContext, int callDepth, Map<String, Objec
this.asyncSystem = asyncSystem;
this.asyncNext = asyncNext;
this.stepInterceptor = stepInterceptor;
this.scenarioHook = scenarioHook;
}

}
46 changes: 34 additions & 12 deletions karate-core/src/main/java/com/intuit/karate/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ public static ScriptValue readFile(String text, ScriptContext context) {
String contents = readFileAsString(text, prefix, context);
return new ScriptValue(contents, text);
} else if (isFeatureFile(text)) {
String featurePath;
String featurePath; // TODO switch case and optimize
if (prefix == PathPrefix.CLASSPATH) {
featurePath = "classpath:" + text;
featurePath = CLASSPATH_COLON + text;
} else if (prefix == PathPrefix.NONE) {
featurePath = context.env.featureDir.getPath() + "/" + text;
} else { // FILE
Expand Down Expand Up @@ -236,6 +236,7 @@ public static ClassLoader createClassLoader(String... paths) {
}

public static String toPackageQualifiedName(String path) {
path = removePrefix(path);
String packagePath = path.replace("/", "."); // assumed to be already in non-windows form
if (packagePath.endsWith(".feature")) {
packagePath = packagePath.substring(0, packagePath.length() - 8);
Expand Down Expand Up @@ -423,7 +424,7 @@ public static void renameFileIfZeroBytes(String fileName) {
}
}

private static Path getRootPathFor(String pathString) {
private static Path getPathFor(String pathString) {
pathString = pathString == null ? "." : pathString.trim();
try {
URI uri;
Expand Down Expand Up @@ -453,39 +454,60 @@ private static Path getRootPathFor(String pathString) {
private static final String CLASSPATH_COLON_SLASH = CLASSPATH_COLON + "/";

private static Path getClassPathRoot() {
return getRootPathFor(CLASSPATH_COLON_SLASH);
return getPathFor(CLASSPATH_COLON_SLASH);
}

public static String toRelativeClassPath(File file) {
Path rootPath = getClassPathRoot();
return rootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
return CLASSPATH_COLON + rootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
}

public static String toRelativeClassPath(Class clazz) {
File dir = getDirContaining(clazz);
return toRelativeClassPath(dir);
}

public static File fromRelativeClassPath(String relativePath) {
Path rootPath = getClassPathRoot();
return rootPath.resolve(relativePath).toFile();
boolean classpath = isClassPath(relativePath);
relativePath = removePrefix(relativePath);
if (classpath) {
return rootPath.resolve(relativePath).toFile();
} else {
return new File(relativePath);
}
}

public static List<FileResource> scanForFeatureFilesOnClassPath() {
return scanForFeatureFiles(CLASSPATH_COLON_SLASH);
}

public static List<FileResource> scanForFeatureFiles(String pathString) {
Path classPathRoot = getClassPathRoot();
Path rootPath = getRootPathFor(pathString);
public static List<FileResource> scanForFeatureFiles(List<String> paths) {
List<FileResource> list = new ArrayList();
for (String path : paths) {
list.addAll(scanForFeatureFiles(path));
}
return list;
}

public static List<FileResource> scanForFeatureFiles(String pathString) {
boolean classpath = isClassPath(pathString);
Path rootPath = classpath ? getClassPathRoot() : getPathFor(null);
Path thisPath = getPathFor(pathString);
List<FileResource> files = new ArrayList();
Stream<Path> stream;
try {
stream = Files.walk(rootPath);
stream = Files.walk(thisPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
for (Iterator<Path> paths = stream.iterator(); paths.hasNext();) {
Path path = paths.next();
if (path.getFileName().toString().endsWith(".feature")) {
File file = path.toFile();
Path relativePath = classPathRoot.relativize(path);
files.add(new FileResource(file, relativePath.toString()));
Path relativePath = rootPath.relativize(path);
String prefix = classpath ? CLASSPATH_COLON : "";
files.add(new FileResource(file, prefix + relativePath.toString()));
}
}
return files;
Expand Down
2 changes: 1 addition & 1 deletion karate-core/src/main/java/com/intuit/karate/Match.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private static Match parse(String exp) {
private Match() {
ScriptEnv env = ScriptEnv.forEnvAndCurrentWorkingDir(null);
CallContext callContext = new CallContext(null, 0, null, -1, false, false,
DummyHttpClient.class.getName(), null, null, null);
DummyHttpClient.class.getName(), null, null, null, null);
context = new ScriptContext(env, callContext);
}

Expand Down
4 changes: 2 additions & 2 deletions karate-core/src/main/java/com/intuit/karate/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -1688,8 +1688,8 @@ public static ScriptValue evalFeatureCall(Feature feature, Object callArg, Scrip
private static ScriptValue evalFeatureCall(Feature feature, ScriptContext context,
Map<String, Object> callArg, int loopIndex, boolean reuseParentConfig) {
CallContext callContext = new CallContext(context, context.callDepth + 1, callArg, loopIndex,
reuseParentConfig, false, null, context.asyncSystem, null, context.stepInterceptor);
// if (context.env.reporter != null) {
reuseParentConfig, false, null, context.asyncSystem, null, context.stepInterceptor, null);
// if (context.env.reporter != null) { TODO call reporting
// context.env.reporter.callBegin(feature, callContext);
// }
FeatureResult result = Engine.executeSync(null, feature, null, callContext);
Expand Down
7 changes: 5 additions & 2 deletions karate-core/src/main/java/com/intuit/karate/StepDefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ private static ScriptEnv getFeatureEnv() {
return ideScriptEnv;
}

public StepDefs(ScriptEnv scriptEnv, CallContext call) {
context = new ScriptContext(scriptEnv, call);
public StepDefs(ScriptEnv scriptEnv, CallContext callContext) {
this.callContext = callContext;
context = new ScriptContext(scriptEnv, callContext);
request = new HttpRequestBuilder();
}

public final ScriptContext context;
public final CallContext callContext;

private HttpRequestBuilder request;
private HttpResponse response;

Expand Down
37 changes: 21 additions & 16 deletions karate-core/src/main/java/com/intuit/karate/core/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,11 @@ public static FeatureResult executeSync(String envString, Feature feature, Strin
ScriptEnv env = ScriptEnv.forEnvTagsAndFeatureFile(envString, tagSelector, file);
if (callContext == null) {
callContext = new CallContext(null, true);
}
StepDefs stepDefs = new StepDefs(env, callContext);
String basePath = feature.getPackageQualifiedName();
LogAppender appender = new FileLogAppender(getBuildDir() + "/surefire-reports/" + basePath + ".log", stepDefs.context.logger);
FeatureExecutionUnit unit = new FeatureExecutionUnit(feature, stepDefs, appender);
unit.submit(SYNC_EXECUTOR, NO_OP);
FeatureResult result = unit.getFeatureResult();
result.setResultVars(stepDefs.context.getVars());
return result;
}
ExecutionContext exec = new ExecutionContext(feature, env, callContext);
FeatureExecutionUnit unit = new FeatureExecutionUnit(exec);
unit.submit(SYNC_EXECUTOR, NO_OP);
return exec.result;
}

public static Result execute(Scenario scenario, Step step, StepDefs stepDefs) {
Expand Down Expand Up @@ -126,19 +122,23 @@ public static Result execute(Scenario scenario, Step step, StepDefs stepDefs) {
try {
match.method.invoke(stepDefs, args);
return Result.passed(getElapsedTime(startTime));
} catch (KarateAbortException ke) {
return Result.aborted(getElapsedTime(startTime));
} catch (InvocationTargetException e) { // target will be KarateException
return Result.failed(getElapsedTime(startTime), e.getTargetException(), scenario, step);
if (e.getTargetException() instanceof KarateAbortException) {
return Result.aborted(getElapsedTime(startTime));
} else {
return Result.failed(getElapsedTime(startTime), e.getTargetException(), scenario, step);
}
} catch (Exception e) {
return Result.failed(getElapsedTime(startTime), e, scenario, step);
}
}

public static void saveResultJson(String targetDir, FeatureResult result) {
public static File saveResultJson(String targetDir, FeatureResult result) {
List<FeatureResult> single = Collections.singletonList(result);
String json = JsonUtils.toPrettyJsonString(JsonUtils.toJsonDoc(single));
FileUtils.writeToFile(new File(targetDir + "/" + result.getPackageQualifiedName() + ".json"), json);
File file = new File(targetDir + "/" + result.getPackageQualifiedName() + ".json");
FileUtils.writeToFile(file, json);
return file;
}

private static String formatNanos(long nanos, DecimalFormat formatter) {
Expand Down Expand Up @@ -172,7 +172,7 @@ private static Throwable appendSteps(List<StepResult> steps, StringBuilder sb) {
return error;
}

public static void saveResultXml(String targetDir, FeatureResult result) {
public static File saveResultXml(String targetDir, FeatureResult result) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
formatter.applyPattern("0.######");
Document doc = XmlUtils.newDocument();
Expand Down Expand Up @@ -232,7 +232,9 @@ public static void saveResultXml(String targetDir, FeatureResult result) {
root.setAttribute("failures", failureCount + "");
root.setAttribute("time", formatNanos(totalDuration, formatter));
String xml = XmlUtils.toString(doc, true);
FileUtils.writeToFile(new File(targetDir + "/" + baseName + ".xml"), xml);
File file = new File(targetDir + "/" + baseName + ".xml");
FileUtils.writeToFile(file, xml);
return file;
}

private static long getElapsedTime(long startTime) {
Expand All @@ -251,6 +253,9 @@ private static List<MethodMatch> findMethodsMatching(String text) {
}

public static String fromCucumberOptionsTags(String ... tags) {
if (tags == null) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tags.length; i++) {
String and = tags[i];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* The MIT License
*
* Copyright 2018 Intuit Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate.core;

import com.intuit.karate.CallContext;
import com.intuit.karate.FileLogAppender;
import com.intuit.karate.LogAppender;
import com.intuit.karate.ScriptEnv;
import static com.intuit.karate.core.Engine.getBuildDir;

/**
*
* @author pthomas3
*/
public class ExecutionContext {

public final Feature feature;
public final ScriptEnv env;
public final CallContext callContext;
public final FeatureResult result;
public final LogAppender appender;

public ExecutionContext(Feature feature, ScriptEnv env, CallContext callContext) {
this.feature = feature;
result = new FeatureResult(feature);
this.env = env;
this.callContext = callContext;
String basePath = feature.getPackageQualifiedName();
this.appender = new FileLogAppender(getBuildDir() + "/surefire-reports/" + basePath + ".log", env.logger);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
*/
package com.intuit.karate.core;

import com.intuit.karate.LogAppender;
import com.intuit.karate.StepDefs;
import com.intuit.karate.exception.KarateException;
import java.util.Iterator;
import java.util.function.BiConsumer;
Expand All @@ -36,37 +34,28 @@
*/
public class FeatureExecutionUnit implements ExecutionUnit<FeatureResult> {

private final StepDefs stepDefs;
private final LogAppender appender;

private final FeatureResult featureResult;

private final ExecutionContext exec;

private final Iterator<FeatureSection> iterator;

public FeatureExecutionUnit(Feature feature, StepDefs stepDefs, LogAppender appender) {
this.stepDefs = stepDefs;
this.appender = appender;
featureResult = new FeatureResult(feature);
iterator = feature.getSections().iterator();
}

public FeatureResult getFeatureResult() {
return featureResult;
}
public FeatureExecutionUnit(ExecutionContext exec) {
this.exec = exec;
iterator = exec.feature.getSections().iterator();
}

@Override
public void submit(Consumer<Runnable> system, BiConsumer<FeatureResult, KarateException> next) {
if (iterator.hasNext()) {
FeatureSection section = iterator.next();
SectionExecutionUnit unit = new SectionExecutionUnit(section, stepDefs, featureResult, appender);
SectionExecutionUnit unit = new SectionExecutionUnit(section, exec);
system.accept(() -> {
unit.submit(system, (r, e) -> {
FeatureExecutionUnit.this.submit(system, next);
});
});
} else {
appender.close();
next.accept(featureResult, null);
exec.appender.close();
next.accept(exec.result, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,8 @@ public static Feature parse(File file) {
}

public static Feature parse(String path) {
if (FileUtils.isClassPath(path)) {
path = FileUtils.removePrefix(path);
return new FeatureParser(path).feature;
} else {
if (FileUtils.isFilePath(path)) {
path = FileUtils.removePrefix(path);
}
return new FeatureParser(new File(path)).feature;
}
File file = FileUtils.fromRelativeClassPath(path);
return parse(file, path);
}

public static Feature parse(File file, String relativePath) {
Expand All @@ -76,10 +69,6 @@ private FeatureParser(File file) {
this(file, FileUtils.toRelativeClassPath(file));
}

private FeatureParser(String relativePath) {
this(FileUtils.fromRelativeClassPath(relativePath), relativePath);
}

private FeatureParser(File file, String relativePath) {
feature = new Feature(file, relativePath);
CharStream stream;
Expand Down
Loading

0 comments on commit 05e9c11

Please sign in to comment.