Skip to content

Commit

Permalink
[rewrite] #1281 wip callonce working, also graal js function context …
Browse files Browse the repository at this point in the history
…switching
  • Loading branch information
ptrthomas committed Oct 10, 2020
1 parent ceaefcd commit ed50402
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 93 deletions.
16 changes: 15 additions & 1 deletion karate-core2/src/main/java/com/intuit/karate/data/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.intuit.karate.FileUtils;
import com.intuit.karate.ScriptValue;
import com.intuit.karate.graal.JsValue;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JsonProvider;
Expand All @@ -35,15 +36,19 @@
import de.siegmar.fastcsv.reader.CsvReader;
import de.siegmar.fastcsv.reader.CsvRow;
import de.siegmar.fastcsv.writer.CsvWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minidev.json.JSONStyle;
import net.minidev.json.JSONValue;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.reader.JsonWriter;
import net.minidev.json.reader.JsonWriterI;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

Expand All @@ -57,10 +62,19 @@ private JsonUtils() {
// only static methods
}

private static class JsValueWriter implements JsonWriterI<JsValue> {

@Override
public <E extends JsValue> void writeJSONString(E value, Appendable out, JSONStyle compression) throws IOException {
JsonWriter.toStringWriter.writeJSONString("\"#" + value.type + "\"", out, compression);
}

}

static {
JSONValue.registerWriter(JsValue.class, new JsValueWriter());
// ensure that even if jackson (databind?) is on the classpath, don't switch provider
Configuration.setDefaults(new Configuration.Defaults() {

private final JsonProvider jsonProvider = new JsonSmartJsonProvider();
private final MappingProvider mappingProvider = new JsonSmartMappingProvider();

Expand Down
28 changes: 15 additions & 13 deletions karate-core2/src/main/java/com/intuit/karate/graal/JsEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,7 @@ private static class JsContext {
}

Value eval(String exp) {
try {
return context.eval(JS, exp);
} catch (Exception e) {
logger.error("js eval failed: {} - {}", e, exp);
throw new RuntimeException(e);
}
return context.eval(JS, exp);
}

}
Expand All @@ -91,11 +86,14 @@ public static void remove() {

public static JsEngine local() {
Engine engine = GLOBAL_JS_CONTEXT.get().context.getEngine();
JsEngine je = new JsEngine(new JsContext(engine));
Value bindings = global().bindings();
return new JsEngine(new JsContext(engine));
}

public static JsEngine localWithGlobalBindings() {
JsEngine je = local();
Value bindings = global().jc.bindings;
for (String key : bindings.getMemberKeys()) {
Value v = bindings.getMember(key);
je.putValue(key, v);
je.putValue(key, bindings.getMember(key));
}
return je;
}
Expand All @@ -114,6 +112,10 @@ private JsEngine(JsContext sc) {
this.jc = sc;
}

public Context getGraalContext() {
return jc.context;
}

public Value bindings() {
return jc.bindings;
}
Expand Down Expand Up @@ -141,7 +143,7 @@ public void put(String key, Object value) {
public void putAll(Map<String, Object> map) {
map.forEach((k, v) -> put(k, v));
}

public boolean hasMember(String key) {
return jc.bindings.hasMember(key);
}
Expand All @@ -165,10 +167,10 @@ public String toJson(JsValue jv) {

public void putValue(String key, Value v) {
if (v.isHostObject()) {
bindings().putMember(key, v.asHostObject());
jc.bindings.putMember(key, v.asHostObject());
} else if (v.canExecute()) {
Value fun = evalForValue("(" + v.toString() + ")");
bindings().putMember(key, fun);
jc.bindings.putMember(key, fun);
} else {
put(key, JsValue.toJava(v));
}
Expand Down
19 changes: 14 additions & 5 deletions karate-core2/src/main/java/com/intuit/karate/graal/JsValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

/**
Expand All @@ -45,7 +46,7 @@ public static enum Type {
OTHER
}

private final Value original;
private Value original;
private final Object value;
public final Type type;

Expand Down Expand Up @@ -109,10 +110,22 @@ public List getAsList() {
return (List) value;
}

public void switchContext(JsEngine js) {
Context context = original.getContext();
if (context != null && !context.equals(js.getGraalContext())) {
String temp = "(" + original.toString() + ")";
original = js.evalForValue(temp);
}
}

public Value getOriginal() {
return original;
}

public void setOriginal(Value original) {
this.original = original;
}

public JsValue execute(Object... args) {
return new JsValue(original.execute(args));
}
Expand Down Expand Up @@ -140,10 +153,6 @@ public boolean isOther() {
return type == Type.OTHER;
}

public String asString() {
return original.asString();
}

@Override
public String toString() {
return original.toString();
Expand Down
5 changes: 3 additions & 2 deletions karate-core2/src/main/java/com/intuit/karate/match/Match.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package com.intuit.karate.match;

import com.intuit.karate.graal.JsEngine;
import static com.intuit.karate.match.MatchResult.*;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -69,8 +70,8 @@ public static MatchValue that(Object o) {
return new MatchValue(MatchValue.parseIfJsonOrXml(o));
}

public static MatchResult execute(MatchType matchType, MatchValue actual, MatchValue expected) {
MatchOperation mo = new MatchOperation(matchType, actual, expected);
public static MatchResult execute(JsEngine js, MatchType matchType, MatchValue actual, MatchValue expected) {
MatchOperation mo = new MatchOperation(js, matchType, actual, expected);
mo.execute();
if (mo.pass) {
return PASS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,24 @@
*/
package com.intuit.karate.match;

import com.intuit.karate.graal.JsEngine;

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

public final JsEngine JS;
public final MatchOperation root;
public final int depth;
public final boolean xml;
public final String path;
public final String name;
public final int index;

protected MatchContext(MatchOperation root, boolean xml, int depth, String path, String name, int index) {
protected MatchContext(JsEngine js, MatchOperation root, boolean xml, int depth, String path, String name, int index) {
this.JS = js;
this.root = root;
this.xml = xml;
this.depth = depth;
Expand All @@ -48,19 +52,19 @@ protected MatchContext(MatchOperation root, boolean xml, int depth, String path,
public MatchContext descend(String name) {
if (xml) {
String childPath = path.endsWith("/@") ? path + name : (depth == 0 ? "" : path) + "/" + name;
return new MatchContext(root, xml, depth + 1, childPath, name, -1);
return new MatchContext(JS, root, xml, depth + 1, childPath, name, -1);
} else {
boolean needsQuotes = name.indexOf('-') != -1 || name.indexOf(' ') != -1 || name.indexOf('.') != -1;
String childPath = needsQuotes ? path + "['" + name + "']" : path + '.' + name;
return new MatchContext(root, xml, depth + 1, childPath, name, -1);
return new MatchContext(JS, root, xml, depth + 1, childPath, name, -1);
}
}

public MatchContext descend(int index) {
if (xml) {
return new MatchContext(root, xml, depth + 1, path + "[" + (index + 1) + "]", name, index);
return new MatchContext(JS, root, xml, depth + 1, path + "[" + (index + 1) + "]", name, index);
} else {
return new MatchContext(root, xml, depth + 1, path + "[" + index + "]", name, index);
return new MatchContext(JS, root, xml, depth + 1, path + "[" + index + "]", name, index);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,33 @@ public class MatchOperation {

boolean pass = true;
String failReason;

public MatchOperation(MatchType type, MatchValue actual, MatchValue expected) {
this(null, type, actual, expected);
this(JsEngine.global(), null, type, actual, expected);
}

public MatchOperation(JsEngine js, MatchType type, MatchValue actual, MatchValue expected) {
this(js, null, type, actual, expected);
}

private MatchOperation(MatchContext context, MatchType type, MatchValue actual, MatchValue expected) {
this(null, context, type, actual, expected);
}

public MatchOperation(MatchContext context, MatchType type, MatchValue actual, MatchValue expected) {
private MatchOperation(JsEngine js, MatchContext context, MatchType type, MatchValue actual, MatchValue expected) {
this.type = type;
this.actual = actual;
this.expected = expected;
if (context == null) {
if (js == null) {
js = JsEngine.global();
}
this.failures = new ArrayList();
if (actual.isXml()) {
this.context = new MatchContext(this, true, 0, "/", "", -1);
this.context = new MatchContext(js, this, true, 0, "/", "", -1);
} else {
this.context = new MatchContext(this, false, 0, "$", "", -1);
this.context = new MatchContext(js, this, false, 0, "$", "", -1);
}

} else {
this.context = context;
this.failures = context.root.failures;
Expand Down Expand Up @@ -149,10 +159,9 @@ public boolean execute() {
List list = actual.getValue();
MatchType nestedMatchType = fromMatchEach();
int count = list.size();
JsEngine jsEngine = JsEngine.global();
for (int i = 0; i < count; i++) {
Object o = list.get(i);
jsEngine.put("_$", o);
context.JS.put("_$", o);
MatchOperation mo = new MatchOperation(context.descend(i), nestedMatchType, new MatchValue(o), expected);
mo.execute();
if (!mo.pass) {
Expand Down Expand Up @@ -220,7 +229,7 @@ private boolean macroEqualsExpected(String expStr) {
MatchType nestedType = macroToMatchType(false, macro);
int startPos = matchTypeToStartPos(nestedType);
macro = macro.substring(startPos);
JsValue jv = JsEngine.evalGlobal(macro);
JsValue jv = context.JS.eval(macro);
MatchOperation mo = new MatchOperation(context, nestedType, actual, new MatchValue(jv.getValue()));
return mo.execute();
} else if (macro.startsWith("[")) {
Expand All @@ -233,16 +242,15 @@ private boolean macroEqualsExpected(String expStr) {
String bracketContents = macro.substring(1, closeBracketPos);
List listAct = actual.getValue();
int listSize = listAct.size();
JsEngine jsEngine = JsEngine.global();
jsEngine.put("$", context.root.actual.getValue());
jsEngine.put("_", listSize);
context.JS.put("$", context.root.actual.getValue());
context.JS.put("_", listSize);
String sizeExpr;
if (bracketContents.indexOf('_') != -1) { // #[_ < 5]
sizeExpr = bracketContents;
} else { // #[5] | #[$.foo]
sizeExpr = bracketContents + " == _";
}
JsValue jv = jsEngine.eval(sizeExpr);
JsValue jv = context.JS.eval(sizeExpr);
if (!jv.isTrue()) {
return fail("actual array length is " + listSize);
}
Expand All @@ -264,7 +272,7 @@ private boolean macroEqualsExpected(String expStr) {
MatchType nestedType = macroToMatchType(true, macro); // match each
int startPos = matchTypeToStartPos(nestedType);
macro = macro.substring(startPos);
JsValue jv = JsEngine.evalGlobal(macro);
JsValue jv = context.JS.eval(macro);
MatchOperation mo = new MatchOperation(context, nestedType, actual, new MatchValue(jv.getValue()));
return mo.execute();
}
Expand Down Expand Up @@ -313,10 +321,9 @@ private boolean macroEqualsExpected(String expStr) {
}
macro = StringUtils.trimToNull(macro);
if (macro != null && questionPos != -1) {
JsEngine jsEngine = JsEngine.global();
jsEngine.put("$", context.root.actual.getValue());
jsEngine.put("_", actual.getValue());
JsValue jv = jsEngine.eval(macro);
context.JS.put("$", context.root.actual.getValue());
context.JS.put("_", actual.getValue());
JsValue jv = context.JS.eval(macro);
if (!jv.isTrue()) {
return fail("evaluated to 'false'");
}
Expand Down Expand Up @@ -570,8 +577,9 @@ private static String collectFailureReasons(MatchOperation root) {
sb.append(prefix).append(mo.actual.getAsXmlString()).append('\n');
sb.append(prefix).append(mo.expected.getAsXmlString()).append('\n');
} else {
MatchValue expected = mo.expected.getSortedLike(mo.actual);
sb.append(prefix).append(mo.actual.getWithinSingleQuotesIfString()).append('\n');
sb.append(prefix).append(mo.expected.getWithinSingleQuotesIfString()).append('\n');
sb.append(prefix).append(expected.getWithinSingleQuotesIfString()).append('\n');
}
}
return sb.toString();
Expand Down
24 changes: 24 additions & 0 deletions karate-core2/src/main/java/com/intuit/karate/match/MatchValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
import com.intuit.karate.XmlUtils;
import com.intuit.karate.data.Json;
import com.intuit.karate.data.JsonUtils;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.w3c.dom.Node;

/**
Expand Down Expand Up @@ -177,6 +180,27 @@ public String getAsXmlString() {
return getAsString();
}
}

public MatchValue getSortedLike(MatchValue other) {
if (isMap() && other.isMap()) {
Map<String, Object> reference = other.getValue();
Map<String, Object> source = getValue();
Set<String> remainder = new LinkedHashSet(source.keySet());
Map<String, Object> result = new LinkedHashMap(source.size());
reference.keySet().forEach(key -> {
if (source.containsKey(key)) {
result.put(key, source.get(key));
remainder.remove(key);
}
});
for (String key : remainder) {
result.put(key, source.get(key));
}
return new MatchValue(result);
} else {
return this;
}
}

@Override
public String toString() {
Expand Down
Loading

0 comments on commit ed50402

Please sign in to comment.