Skip to content

Commit

Permalink
[rewrite] #1281 wip call feature loop also new support for function
Browse files Browse the repository at this point in the history
todo graal function context switching
  • Loading branch information
ptrthomas committed Oct 10, 2020
1 parent b27854e commit d99e5a4
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ public class ScenarioCall {
private Variable arg;
private boolean globalScope;
private boolean karateConfigDisabled;
private int loopIndex = -1;

public boolean isNone() {
return depth == 0;
}

public int getLoopIndex() {
return loopIndex;
}

public void setLoopIndex(int loopIndex) {
this.loopIndex = loopIndex;
}

public void setGlobalScope(boolean globalScope) {
this.globalScope = globalScope;
Expand All @@ -63,10 +76,6 @@ public boolean isCallonce() {
return callonce;
}

public boolean isNone() {
return depth == 0;
}

public void setCallonce(boolean callonce) {
this.callonce = callonce;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.intuit.karate.core.Feature;
import com.intuit.karate.data.Json;
import com.intuit.karate.data.JsonUtils;
import com.intuit.karate.exception.KarateException;
import com.intuit.karate.graal.JsEngine;
import com.intuit.karate.match.Match;
import com.intuit.karate.match.MatchResult;
Expand Down Expand Up @@ -77,27 +78,27 @@ public Variable eval(String exp) {
return new Variable(JS.eval(exp));
}

public void putHidden(String key, Object value) {
public void setHiddenVariable(String key, Object value) {
if (value instanceof Variable) {
JS.put(key, ((Variable) value).getValue());
} else {
JS.put(key, value);
}
}

public void put(String key, Object value) {
public void setVariable(String key, Object value) {
if (value instanceof Variable) {
vars.put(key, (Variable) value);
} else {
vars.put(key, new Variable(value));
}
}

public void putAll(Map<String, Object> map) {
public void setVariables(Map<String, Object> map) {
if (map == null) {
return;
}
map.forEach((k, v) -> put(k, v));
map.forEach((k, v) -> setVariable(k, v));
}

public Map<String, Variable> copyVariables(boolean deep) {
Expand Down Expand Up @@ -158,9 +159,9 @@ public void assign(AssignType assignType, String name, String exp, boolean valid
}
}
if (assignType == AssignType.TEXT) {
put(name, exp);
setVariable(name, exp);
} else {
put(name, evalAndCastTo(assignType, exp));
setVariable(name, evalAndCastTo(assignType, exp));
}
}

Expand Down Expand Up @@ -782,19 +783,70 @@ public Variable call(boolean callOnce, String exp, boolean reuseParentConfig) {
case JS_FUNCTION:
return arg == null ? called.invokeFunction() : called.invokeFunction(new Object[]{arg.getValue()});
case KARATE_FEATURE:
return callFeature(called.getValue(), arg, reuseParentConfig);
ScenarioRuntime runtime = ScenarioRuntime.LOCAL.get();
return callFeature(runtime, called.getValue(), arg, -1, reuseParentConfig);
default:
throw new RuntimeException("not a callable feature or js function: " + called);
}
}

public Variable callFeature(Feature feature, Variable arg, boolean reuseParentConfig) {
ScenarioRuntime runtime = ScenarioRuntime.LOCAL.get();
ScenarioCall call = new ScenarioCall(runtime, feature);
call.setArg(arg);
FeatureRuntime fr = new FeatureRuntime(call);
fr.run();
return fr.getResultVariable();
public Variable callFeature(ScenarioRuntime runtime, Feature feature, Variable arg, int index, boolean reuseParentConfig) {
if (arg == null || arg.isMap()) {
ScenarioCall call = new ScenarioCall(runtime, feature);
call.setArg(arg);
call.setLoopIndex(index);
FeatureRuntime fr = new FeatureRuntime(call);
fr.run();
if (fr.result.isFailed()) {
KarateException ke = fr.result.getErrorsCombined();
if (index == -1) {
runtime.logError(ke.getMessage());
}
throw ke;
} else {
return fr.getResultVariable();
}
} else if (arg.isList() || arg.isFunction()) {
List result = new ArrayList();
List<String> errors = new ArrayList();
int loopIndex = 0;
boolean isList = arg.isList();
Iterator iterator = isList ? arg.<List>getValue().iterator() : null;
while (true) {
Variable loopArg;
if (isList) {
loopArg = iterator.hasNext() ? new Variable(iterator.next()) : Variable.NULL;
} else { // function
loopArg = arg.invokeFunction(loopIndex);
}
if (!loopArg.isMap()) {
if (!isList) {
logger.info("feature call loop function ended at index {}, returned: {}", loopIndex, loopArg);
}
break;
}
try {
Variable loopResult = callFeature(runtime, feature, loopArg, loopIndex, reuseParentConfig);
result.add(loopResult.getValue());
} catch (Exception e) {
String message = "feature call loop failed at index: " + loopIndex + ", " + e.getMessage();
errors.add(message);
runtime.logError(message);
if (!isList) { // this is a generator function, abort infinite loop !
break;
}
}
loopIndex++;
}
if (errors.isEmpty()) {
return new Variable(result);
} else {
String errorMessage = StringUtils.join(errors, '\n');
throw new KarateException(errorMessage);
}
} else {
throw new RuntimeException("feature call argument is not a json object or array: " + arg);
}
}

public Variable evalJsonPath(Variable v, String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private int nextStepIndex() {
return stepIndex++;
}

private void logError(String message) {
protected void logError(String message) {
if (currentStep != null) {
message = currentStep.getDebugInfo()
+ "\n" + currentStep.toString()
Expand Down Expand Up @@ -197,18 +197,26 @@ public void beforeRun() {
logger.setAppender(appender);
LOCAL.set(this);
engine.init();
engine.putHidden(VariableNames.KARATE, bridge);
engine.putHidden(VariableNames.READ, readFunction);
engine.setHiddenVariable(VariableNames.KARATE, bridge);
engine.setHiddenVariable(VariableNames.READ, readFunction);
if (scenario.isDynamic()) {
steps = scenario.getBackgroundSteps();
} else {
steps = background == null ? scenario.getStepsIncludingBackground() : scenario.getSteps();
if (scenario.isOutline()) { // init examples row magic variables
Map<String, Object> exampleData = scenario.getExampleData();
engine.put("__row", exampleData);
engine.put("__num", scenario.getExampleIndex());
// TODO breaking change configure outlineVariablesAuto
exampleData.forEach((k, v) -> engine.put(k, v));
exampleData.forEach((k, v) -> engine.setVariable(k, v));
engine.setVariable("__row", exampleData);
engine.setVariable("__num", scenario.getExampleIndex());
// TODO breaking change configure outlineVariablesAuto
}
if (!parentCall.isNone()) {
Variable arg = parentCall.getArg();
engine.setVariable("__arg", arg);
engine.setVariable("__loop", parentCall.getLoopIndex());
if (arg != null && arg.isMap()) {
engine.setVariables(arg.getValue());
}
}
}
result.setThreadName(Thread.currentThread().getName());
Expand All @@ -225,7 +233,7 @@ private void evalConfigJs(String js) {
if (js != null) {
Variable fun = engine.evalKarateExpression(js);
if (fun.isFunction()) {
engine.putAll(fun.evalAsMap());
engine.setVariables(fun.evalAsMap());
} else {
logger.warn("config did not evaluate to js function: {}", js);
}
Expand Down Expand Up @@ -291,7 +299,7 @@ public void afterRun() {
public void call(boolean callOnce, String line) {
Variable v = engine.call(callOnce, line, true);
if (v.isMap()) {
engine.putAll(v.getValue());
engine.setVariables(v.getValue());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public Variable(Object o) {
public <T> T getValue() {
return (T) value;
}

public boolean isBytes() {
return type == Type.BYTES;
}
Expand Down Expand Up @@ -157,15 +157,15 @@ public Variable invokeFunction(Object... args) {
return new Variable(result);
}
}

public Map<String, Object> evalAsMap() {
if (isFunction()) {
Variable v = invokeFunction();
return v.isMap() ? v.getValue() : null;
} else {
return isMap() ? getValue() : null;
}
}
}

public Node getAsXml() {
switch (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,23 @@ void testCallKarateFeature() {
"def b = 'bar'",
"def res = call read('called1.feature')"
);
matchVarEquals("res", "{ a: 1, b: 'bar', foo: { hello: 'world' }, configSource: 'normal' }");
matchVarEquals("res", "{ a: 1, b: 'bar', foo: { hello: 'world' }, configSource: 'normal', __arg: null, __loop: -1 }");
run(
"def b = 'bar'",
"def res = call read('called1.feature') { foo: 'bar' }"
);
matchVarEquals("res", "{ a: 1, b: 'bar', foo: { hello: 'world' }, configSource: 'normal', __arg: { foo: 'bar' }, __loop: -1 }");
run(
"def b = 'bar'",
"def res = call read('called1.feature') [{ foo: 'bar' }]"
);
matchVarEquals("res", "[{ a: 1, b: 'bar', foo: { hello: 'world' }, configSource: 'normal', __arg: { foo: 'bar' }, __loop: 0 }]");
// run(
// "def b = 'bar'",
// "def fun = function(i){ return { index: i } }",
// "def res = call read('called1.feature') fun"
// );
// matchVarEquals("res", "[{ a: 1, b: 'bar', foo: { hello: 'world' }, configSource: 'normal', __arg: { index: 0 }, __loop: 0 }]");
}

}

0 comments on commit d99e5a4

Please sign in to comment.