Skip to content

Commit

Permalink
cukexit wip server-side / netty using new engine ref #444
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrthomas committed Aug 26, 2018
1 parent 128f1a0 commit f22cfa0
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 119 deletions.
12 changes: 5 additions & 7 deletions karate-core/src/main/java/com/intuit/karate/core/Engine.java
Expand Up @@ -95,23 +95,21 @@ public static FeatureResult executeSync(String envString, Feature feature, Strin
return exec.result;
}

public static Result execute(Scenario scenario, Step step, StepDefs stepDefs) {
public static Result execute(String featurePath, Step step, StepDefs stepDefs) {
String text = step.getText();
List<MethodMatch> matches = findMethodsMatching(text);
if (matches.isEmpty()) {
KarateException e = new KarateException("no step-definition method match found for: " + text);
return Result.failed(0, e, scenario, step);
return Result.failed(0, e, featurePath, step);
} else if (matches.size() > 1) {
KarateException e = new KarateException("more than one step-definition method matched: " + text + " - " + matches);
return Result.failed(0, e, scenario, step);
return Result.failed(0, e, featurePath, step);
}
MethodMatch match = matches.get(0);
Object last;
if (step.getDocString() != null) {
last = step.getDocString();
} else if (step.getTable() != null) {
List<String> keys = new ArrayList(step.getTable().getKeys());
String[] columnNames = keys.toArray(new String[]{});
last = DataTable.create(step.getTable().getRows());
} else {
last = null;
Expand All @@ -125,10 +123,10 @@ public static Result execute(Scenario scenario, Step step, StepDefs stepDefs) {
if (e.getTargetException() instanceof KarateAbortException) {
return Result.aborted(getElapsedTime(startTime));
} else {
return Result.failed(getElapsedTime(startTime), e.getTargetException(), scenario, step);
return Result.failed(getElapsedTime(startTime), e.getTargetException(), featurePath, step);
}
} catch (Exception e) {
return Result.failed(getElapsedTime(startTime), e, scenario, step);
return Result.failed(getElapsedTime(startTime), e, featurePath, step);
}
}

Expand Down
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright 2017 Intuit Inc.
* 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
Expand All @@ -21,14 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate.cucumber;
package com.intuit.karate.core;

import com.intuit.karate.CallContext;
import com.intuit.karate.Script;
import com.intuit.karate.ScriptBindings;
import com.intuit.karate.ScriptContext;
import com.intuit.karate.ScriptEnv;
import com.intuit.karate.ScriptValue;
import com.intuit.karate.ScriptValueMap;
import com.intuit.karate.StepDefs;
import com.intuit.karate.StringUtils;
import com.intuit.karate.exception.KarateException;
import java.util.Map;
Expand All @@ -37,96 +39,111 @@
*
* @author pthomas3
*/
public class FeatureProvider {
public class FeatureBackend {

private final FeatureWrapper feature;
private final KarateBackend backend;
private final Feature feature;
private final StepDefs stepDefs;
private final boolean ssl;
private final boolean corsEnabled;

private final ScriptContext context;
private final String featureName;

public FeatureProvider(FeatureWrapper feature) {
this(feature, null, false);
private static void putBinding(String name, ScriptContext context) {
String function = "function(s){ return " + ScriptBindings.KARATE + "." + name + "(s) }";
context.getVars().put(name, Script.evalJsExpression(function, context));
}

public FeatureProvider(FeatureWrapper feature, Map<String, Object> vars) {
this(feature, vars, false);
public boolean isCorsEnabled() {
return corsEnabled;
}

public boolean isSsl() {
return ssl;
}

public boolean isCorsEnabled() {
return corsEnabled;
public ScriptContext getContext() {
return context;
}

public final ScriptContext getContext() {
return backend.getStepDefs().context;
public FeatureBackend(Feature feature) {
this(feature, null, false);
}

private static void putBinding(String name, ScriptContext context) {
String function = "function(s){ return " + ScriptBindings.KARATE + "." + name + "(s) }";
context.getVars().put(name, Script.evalJsExpression(function, context));
public FeatureBackend(Feature feature, Map<String, Object> vars) {
this(feature, vars, false);
}

public FeatureProvider(FeatureWrapper feature, Map<String, Object> vars, boolean ssl) {
public FeatureBackend(Feature feature, Map<String, Object> vars, boolean ssl) {
this.feature = feature;
featureName = feature.getFile().getName();
this.ssl = ssl;
CallContext callContext = new CallContext(null, false);
backend = CucumberUtils.getBackendWithGlue(feature, callContext);
ScriptContext context = getContext();
ScriptEnv env = ScriptEnv.forEnvAndFeatureFile(null, feature.getFile());
stepDefs = new StepDefs(env, callContext);
context = stepDefs.context;
putBinding(ScriptBindings.PATH_MATCHES, context);
putBinding(ScriptBindings.METHOD_IS, context);
putBinding(ScriptBindings.PARAM_VALUE, context);
putBinding(ScriptBindings.TYPE_CONTAINS, context);
putBinding(ScriptBindings.ACCEPT_CONTAINS, context);
putBinding(ScriptBindings.BODY_PATH, context);
if (vars != null) {
ScriptValueMap backendVars = backend.getVars();
ScriptValueMap backendVars = context.getVars();
vars.forEach((k, v) -> backendVars.put(k, v));
}
call(feature, backend, CallType.BACKGROUND_ONLY);
// the background is evaluated one-time
if (feature.getBackground() != null) {
for (Step step : feature.getBackground().getSteps()) {
Result result = Engine.execute(feature.getRelativePath(), step, stepDefs);
if (result.isFailed()) {

String message = "server-side background init failed - " + featureName + ":" + step.getLine();
stepDefs.context.logger.error(message);
throw new KarateException(message, result.getError());
}
}
}
// this is a special case, we support the auto-handling of cors
// only if '* configure cors = true' has been done in the Background
corsEnabled = context.getConfig().isCorsEnabled();
}

public ScriptValueMap handle(ScriptValueMap vars) {
backend.getVars().putAll(vars);
call(feature, backend, CallType.SCENARIO_ONLY);
return getContext().getVars();
}

private static ScriptValueMap call(FeatureWrapper feature, KarateBackend backend, CallType callType) {
boolean matched = callType != CallType.SCENARIO_ONLY;
for (FeatureSection section : feature.getSections()) {
if (section.isOutline()) {
ScenarioOutlineWrapper outline = section.getScenarioOutline();
for (ScenarioWrapper scenario : outline.getScenarios()) {
call(scenario, backend, callType);
}
} else {
ScenarioWrapper scenario = section.getScenario();
if (callType == CallType.SCENARIO_ONLY) {
if (isMatchingScenario(scenario, backend)) {
call(scenario, backend, callType);
matched = true;
break; // only execute first matching scenario
public ScriptValueMap handle(ScriptValueMap args) {
boolean matched = false;
ScriptValueMap vars = stepDefs.context.getVars();
vars.putAll(args);
for (FeatureSection fs : feature.getSections()) {
if (fs.isOutline()) {
stepDefs.context.logger.warn("skipping scenario outline - {}:{}", featureName, fs.getScenarioOutline().getLine());
break;
}
Scenario scenario = fs.getScenario();
if (isMatchingScenario(scenario)) {
matched = true;
for (Step step : scenario.getSteps()) {
Result result = Engine.execute(feature.getRelativePath(), step, stepDefs);
if (result.isAborted()) {
stepDefs.context.logger.debug("abort at {}:{}", featureName, step.getLine());
break;
}
if (result.isFailed()) {
String message = "server-side scenario failed - " + featureName + ":" + step.getLine();
stepDefs.context.logger.error(message);
throw new KarateException(message, result.getError());
}
} else {
call(scenario, backend, callType);
}
break; // process only first matching scenario
}
}
if (!matched) {
backend.getEnv().logger.warn("no scenarios matched");
context.logger.warn("no scenarios matched");
}
return backend.getStepDefs().context.getVars();
return vars;
}

private static boolean isMatchingScenario(ScenarioWrapper scenario, KarateBackend backend) {
String expression = StringUtils.trimToNull(scenario.getNameAndDescription());
ScriptContext context = backend.getStepDefs().context;
private boolean isMatchingScenario(Scenario scenario) {
String expression = StringUtils.trimToNull(scenario.getName() + scenario.getDescription());
if (expression == null) {
context.logger.debug("scenario matched: (empty)");
return true;
Expand All @@ -146,32 +163,4 @@ private static boolean isMatchingScenario(ScenarioWrapper scenario, KarateBacken
}
}

private static void call(ScenarioWrapper scenario, KarateBackend backend, CallType callType) {
for (StepWrapper step : scenario.getSteps()) {
if (callType == CallType.BACKGROUND_ONLY && !step.isBackground()) {
continue;
}
if (callType == CallType.SCENARIO_ONLY && step.isBackground()) {
continue;
}
StepResult result = CucumberUtils.runCalledStep(step, backend);
if (result.isAbort()) {
backend.getEnv().logger.debug("abort at {}:{}", scenario.getFeature().getPath(), step.getStep().getLine());
break;
}
if (!result.isPass()) {
FeatureWrapper feature = scenario.getFeature();
String scenarioName = StringUtils.trimToNull(scenario.getScenario().getGherkinModel().getName());
String message = "called: " + feature.getPath();
if (scenarioName != null) {
message = message + ", scenario: " + scenarioName;
}
message = message + ", line: " + step.getStep().getLine();
// verbose for server / mock
backend.getEnv().logger.error("{}, {}", result.getError(), message);
throw new KarateException(message, result.getError());
}
}
}

}
@@ -1,4 +1,4 @@
/*
/*
* The MIT License
*
* Copyright 2018 Intuit Inc.
Expand Down
5 changes: 2 additions & 3 deletions karate-core/src/main/java/com/intuit/karate/core/Result.java
Expand Up @@ -68,9 +68,8 @@ public static Result passed(long duration) {
return new Result(PASSED, duration, null, false);
}

public static Result failed(long duration, Throwable error, Scenario scenario, Step step) {
StackTraceElement[] originalTrace = error.getStackTrace();
String featurePath = scenario.getFeature().getRelativePath();
public static Result failed(long duration, Throwable error, String featurePath, Step step) {
StackTraceElement[] originalTrace = error.getStackTrace();
StackTraceElement[] newTrace = new StackTraceElement[]{
new StackTraceElement("✽", step.getPrefix() + ' ' + step.getText(), featurePath, step.getLine()),
originalTrace[0]
Expand Down
Expand Up @@ -48,13 +48,14 @@ public StepExecutionUnit(Step step, Scenario scenario, StepDefs stepDefs) {
@Override
public void submit(Consumer<Runnable> system, BiConsumer<Result, KarateException> next) {
system.accept(() -> {
Result result = Engine.execute(scenario, step, stepDefs);
String relativePath = scenario.getFeature().getRelativePath();
Result result = Engine.execute(relativePath, step, stepDefs);
if (result.isAborted()) {
stepDefs.context.logger.debug("abort at {}:{}", scenario.getFeature().getRelativePath(), step.getLine());
stepDefs.context.logger.debug("abort at {}:{}", relativePath, step.getLine());
next.accept(result, null); // same flow as passed
} else if (result.isFailed()) {
String scenarioName = StringUtils.trimToNull(scenario.getName());
String message = "called: " + scenario.getFeature().getRelativePath();
String message = "called: " + relativePath;
if (scenarioName != null) {
message = message + ", scenario: " + scenarioName;
}
Expand Down
Expand Up @@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate.cucumber;
package com.intuit.karate.core;

import com.intuit.karate.FileUtils;
import com.intuit.karate.Match;
Expand All @@ -37,9 +37,9 @@
*
* @author pthomas3
*/
public class FeatureProviderTest {
public class FeatureBackendTest {

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

private ScriptValueMap getRequest(String name) {
return Match.init()
Expand All @@ -50,11 +50,11 @@ private ScriptValueMap getRequest(String name) {
@Test
public void testServer() {
File file = FileUtils.getFileRelativeTo(getClass(), "server.feature");
FeatureWrapper featureWrapper = FeatureWrapper.fromFileAndTag(file, null);
FeatureProvider provider = new FeatureProvider(featureWrapper);
ScriptValueMap vars = provider.handle(getRequest("Billie"));
Feature feature = FeatureParser.parse(file);
FeatureBackend backend = new FeatureBackend(feature);
ScriptValueMap vars = backend.handle(getRequest("Billie"));
Match.equals(vars.get("response").getAsMap(), "{ id: 1, name: 'Billie' }");
vars = provider.handle(getRequest("Wild"));
vars = backend.handle(getRequest("Wild"));
Match.equals(vars.get("response").getAsMap(), "{ id: 2, name: 'Wild' }");
List<Map> list = vars.get("cats").getAsList();
Match.equals(list, "[{ id: 1, name: 'Billie' }, { id: 2, name: 'Wild' }]");
Expand Down

0 comments on commit f22cfa0

Please sign in to comment.