Skip to content

Commit

Permalink
[rewrite] #1281 js engine looks ok, working through broken tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrthomas committed Nov 3, 2020
1 parent 6df8c81 commit d967b67
Show file tree
Hide file tree
Showing 42 changed files with 211 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ public Value getOriginal() {
public JsValue invoke(Object... args) {
return new JsValue(original.execute(args));
}

public boolean isNull() {
return value == null;
}

public boolean isObject() {
return type == Type.OBJECT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public boolean execute() {
context.JS.put("_$", o);
MatchOperation mo = new MatchOperation(context.descend(i), nestedMatchType, new MatchValue(o), expected);
mo.execute();
context.JS.bindings().removeMember("_$");
if (!mo.pass) {
return fail("match each failed at index " + i);
}
Expand Down Expand Up @@ -236,7 +237,11 @@ private boolean macroEqualsExpected(String expStr) {
MatchType nestedType = macroToMatchType(false, macro);
int startPos = matchTypeToStartPos(nestedType);
macro = macro.substring(startPos);
context.JS.put("$", context.root.actual.getValue());
context.JS.put("_", actual.getValue());
JsValue jv = context.JS.eval(macro);
context.JS.bindings().removeMember("$");
context.JS.bindings().removeMember("_");
MatchOperation mo = new MatchOperation(context, nestedType, actual, new MatchValue(jv.getValue()));
return mo.execute();
} else if (macro.startsWith("[")) {
Expand All @@ -258,6 +263,8 @@ private boolean macroEqualsExpected(String expStr) {
sizeExpr = bracketContents + " == _";
}
JsValue jv = context.JS.eval(sizeExpr);
context.JS.bindings().removeMember("$");
context.JS.bindings().removeMember("_");
if (!jv.isTrue()) {
return fail("actual array length is " + listSize);
}
Expand Down Expand Up @@ -331,6 +338,8 @@ private boolean macroEqualsExpected(String expStr) {
context.JS.put("$", context.root.actual.getValue());
context.JS.put("_", actual.getValue());
JsValue jv = context.JS.eval(macro);
context.JS.bindings().removeMember("$");
context.JS.bindings().removeMember("_");
if (!jv.isTrue()) {
return fail("evaluated to 'false'");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ public FeatureRuntime(ScenarioCall call) {
this(call.parentRuntime.featureRuntime.suite, call.feature, call);
Variable arg = call.getArg();
result.setLoopIndex(call.getLoopIndex());
// if (arg != null) {
// result.setCallArg(arg.getValue());
// }
if (arg != null) {
result.setCallArg(arg.getValue());
}
}

private FeatureRuntime(SuiteRuntime suite, Feature feature, ScenarioCall parentCall) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ public Response handle(Request req) {
engine.setVariable(REQUEST_FILES, files); // TODO add to docs
}
// highly unlikely but support mocks calling other mocks in the same jvm
ScenarioEngine prevEngine = ScenarioEngine.LOCAL.get();
ScenarioEngine.LOCAL.set(engine);
ScenarioEngine prevEngine = ScenarioEngine.get();
ScenarioEngine.set(engine);
engine.init();
for (FeatureSection fs : feature.getSections()) {
if (fs.isOutline()) {
Expand Down Expand Up @@ -159,7 +159,7 @@ public Response handle(Request req) {
responseHeaders = vars.remove(ScenarioEngine.RESPONSE_HEADERS);
responseDelay = vars.remove(RESPONSE_DELAY);
} // END TRANSACTION ===========================================
ScenarioEngine.LOCAL.set(prevEngine);
ScenarioEngine.set(prevEngine);
if (result.isFailed()) {
response = new Variable(result.getError().getMessage());
responseStatus = new Variable(500);
Expand Down Expand Up @@ -187,7 +187,7 @@ public Response handle(Request req) {
return res;
}
}
ScenarioEngine.LOCAL.set(prevEngine);
ScenarioEngine.set(prevEngine);
runtime.logger.warn("no scenarios matched, returning 404: {}", req);
return new Response(404);
}
Expand Down Expand Up @@ -218,7 +218,7 @@ public boolean pathMatches(String pattern) {
if (pathParams == null) {
return false;
} else {
ScenarioEngine.LOCAL.get().setVariable(PATH_PARAMS, pathParams);
ScenarioEngine.get().setVariable(PATH_PARAMS, pathParams);
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import com.intuit.karate.core.Scenario;
import com.intuit.karate.data.Json;
import com.intuit.karate.data.JsonUtils;
import com.intuit.karate.exception.KarateAbortException;
import com.intuit.karate.exception.KarateException;
import com.intuit.karate.graal.JsList;
import com.intuit.karate.graal.JsMap;
Expand Down Expand Up @@ -337,7 +336,7 @@ public Object get(String exp, Object defaultValue) {
// TODO migrate these to functions not properties
//
public ScenarioEngine getEngine() {
return ScenarioEngine.LOCAL.get();
return ScenarioEngine.get();
}

public String getEnv() {
Expand Down Expand Up @@ -520,8 +519,8 @@ public void set(Map<String, Object> map) {
getEngine().setVariables(map);
}

public void set(String name, Object o) {
getEngine().setVariable(name, o);
public void set(String name, Value value) {
getEngine().setVariable(name, new Variable(value));
}

// this makes sense mainly for xpath manipulation from within js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public class ScenarioEngine {
private boolean aborted;
private Throwable failedReason;

protected JsEngine JS;
private JsEngine JS;

// only used by mock server
public ScenarioEngine(ScenarioRuntime runtime) {
Expand All @@ -129,6 +129,20 @@ public ScenarioEngine(Config config, ScenarioRuntime runtime, Map<String, Variab
this.logger = logger;
}

private static final ThreadLocal<ScenarioEngine> THREAD_LOCAL = new ThreadLocal<ScenarioEngine>();

public static ScenarioEngine get() {
return THREAD_LOCAL.get();
}

protected static void set(ScenarioEngine se) {
THREAD_LOCAL.set(se);
}

protected static void remove() {
THREAD_LOCAL.remove();
}

// engine ==================================================================
//
public boolean isAborted() {
Expand Down Expand Up @@ -236,7 +250,7 @@ public void invokeAfterHookIfConfigured(boolean afterFeature) {
Variable v = afterFeature ? config.getAfterFeature() : config.getAfterScenario();
if (v.isFunction()) {
try {
v.invokeFunction(JS);
execute(v.getValue());
} catch (Exception e) {
String prefix = afterFeature ? "afterFeature" : "afterScenario";
logger.warn("{} hook failed: {}", prefix, e.getMessage());
Expand Down Expand Up @@ -566,11 +580,11 @@ private void httpInvoke() {
}

private void httpInvokeOnce() {
Map<String, Map> cookies = config.getCookies().evalAsMap(JS);
Map<String, Map> cookies = getOrEvalAsMap(config.getCookies());
if (cookies != null) {
http.cookies(cookies.values());
}
Map<String, Object> headers = config.getHeaders().evalAsMap(JS);
Map<String, Object> headers = getOrEvalAsMap(config.getHeaders());
if (headers != null) {
http.headers(headers);
}
Expand Down Expand Up @@ -682,7 +696,7 @@ private void updateConfigCookies(Map<String, Map> cookies) {
if (config.getCookies().isNull()) {
config.setCookies(new Variable(cookies));
} else {
Map<String, Object> map = config.getCookies().evalAsMap(JS);
Map<String, Object> map = getOrEvalAsMap(config.getCookies());
map.putAll(cookies);
config.setCookies(new Variable(map));
}
Expand Down Expand Up @@ -778,14 +792,11 @@ public void setRobot(Plugin robot) {
this.robot = robot;
}

//==========================================================================
//
protected static final ThreadLocal<ScenarioEngine> LOCAL = new ThreadLocal<ScenarioEngine>();

// not in constructor because it has to be on Runnable.run() thread
public void init() {
//==========================================================================
//
protected void init() { // not in constructor because it has to be on Runnable.run() thread
JS = JsEngine.local();
logger.debug("js context: {}", JS);
logger.trace("js context: {}", JS);
attachVariablesToJsContext();
setHiddenVariable(KARATE, bridge);
setHiddenVariable(READ, readFunction);
Expand All @@ -794,7 +805,7 @@ public void init() {
http = new HttpRequestBuilder(client);
}

protected void attachVariablesToJsContext() {
private void attachVariablesToJsContext() {
vars.forEach((k, v) -> {
switch (v.type) {
case FUNCTION:
Expand All @@ -812,7 +823,7 @@ protected void attachVariablesToJsContext() {
});
}

protected Object attachToJsContext(Object o) {
private Object attachToJsContext(Object o) {
// do this check first as graal functions are maps as well
if (o instanceof Function) {
return JS.attachFunction((Function) o);
Expand Down Expand Up @@ -841,6 +852,25 @@ protected Object attachToJsContext(Object o) {
}
}

protected <T> Map<String, T> getOrEvalAsMap(Variable var) {
if (var.isFunction()) {
Variable v = execute(var.getValue());
return v.isMap() ? v.getValue() : null;
} else {
return var.isMap() ? var.getValue() : null;
}
}

protected Variable execute(Function fun, Object... args) {
Value v = JS.attachFunction(fun);
// we have to convert any arguments that may have originated from js
for (int i = 0; i < args.length; i++) {
args[i] = JsValue.fromJava(args[i]);
}
Value res = v.execute(args);
return new Variable(res);
}

public Variable evalJs(String js) {
try {
return new Variable(JS.eval(js));
Expand Down Expand Up @@ -1064,8 +1094,13 @@ private EmbedAction recurseEmbeddedExpressions(Variable node) {
}
boolean remove = value.charAt(1) == '#';
value = value.substring(remove ? 2 : 1);
Variable result = evalJs(value);
return remove && result.isNull() ? EmbedAction.remove() : EmbedAction.update(result.getValue());
try {
JsValue result = JS.eval(value);
return remove && result.isNull() ? EmbedAction.remove() : EmbedAction.update(result.getValue());
} catch (Exception e) {
logger.trace("embedded expression failed {}: {}", value, e.getMessage());
return null;
}
default:
// do nothing
return null;
Expand Down Expand Up @@ -1094,7 +1129,7 @@ private void recurseXmlEmbeddedExpressions(Node node) {
attrib.setValue(v.getAsString());
}
} catch (Exception e) {
logger.trace("embedded xml-attribute eval failed, path: {}, reason: {}", attrib.getName(), e.getMessage());
logger.trace("xml-attribute embedded expression failed, {}: {}", attrib.getName(), e.getMessage());
}
}
}
Expand Down Expand Up @@ -1136,7 +1171,7 @@ private void recurseXmlEmbeddedExpressions(Node node) {
}
}
} catch (Exception e) {
logger.trace("embedded xml-text eval failed, path: {}, reason: {}", child.getNodeName(), e.getMessage());
logger.trace("xml embedded expression failed, {}: {}", child.getNodeName(), e.getMessage());
}
}
} else if (child.hasChildNodes() || child.hasAttributes()) {
Expand Down Expand Up @@ -1510,7 +1545,7 @@ public static StringUtils.Pair parseCallArgs(String line) {
public Variable call(Variable called, Variable arg, boolean sharedScope) {
switch (called.type) {
case FUNCTION:
return arg == null ? called.invokeFunction(JS) : called.invokeFunction(JS, new Object[]{arg.getValue()});
return arg == null ? execute(called.getValue()) : execute(called.getValue(), new Object[]{arg.getValue()});
case FEATURE:
Variable res = callFeature(called.getValue(), arg, -1, sharedScope);
Object val = res.getValue(); // will always be a map
Expand Down Expand Up @@ -1593,7 +1628,7 @@ public Variable callFeature(Feature feature, Variable arg, int index, boolean sh
FeatureRuntime fr = new FeatureRuntime(call);
fr.run();
// VERY IMPORTANT ! switch back from called feature js context
ScenarioEngine.LOCAL.set(this);
THREAD_LOCAL.set(this);
runtime.addCallResult(fr.result);
if (fr.result.isFailed()) {
KarateException ke = fr.result.getErrorsCombined();
Expand All @@ -1612,7 +1647,7 @@ public Variable callFeature(Feature feature, Variable arg, int index, boolean sh
if (isList) {
loopArg = iterator.hasNext() ? new Variable(iterator.next()) : Variable.NULL;
} else { // function
loopArg = arg.invokeFunction(JS, loopIndex);
loopArg = execute(arg.getValue(), loopIndex);
}
if (!loopArg.isMap()) {
if (!isList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public boolean hasNext() {
Variable rowValue;
if (expressionValue.isFunction()) {
try {
rowValue = expressionValue.invokeFunction(ScenarioEngine.LOCAL.get().JS, rowIndex);
rowValue = ScenarioEngine.get().execute(expressionValue.getValue(), rowIndex);
} catch (Exception e) {
String message = "dynamic function expression evaluation failed at index " + rowIndex + ": " + e.getMessage();
background.result.addFakeStepResult(message, e);
Expand Down
Loading

0 comments on commit d967b67

Please sign in to comment.