Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,19 @@
<version>3.1</version>
</dependency>


<!-- GraalVM dependencies are used for JavaScript testing. -->
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>21.3.0</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>21.3.0</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,26 @@


import dk.kb.netarchivesuite.solrwayback.util.FileUtil;
import jdk.nashorn.api.scripting.JSObject;
import org.junit.Before;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Value;
import org.junit.Test;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
* Unittest method to call the query parser defined in the VUE typescript. The method has been copied to a new file and is now pure javascript.
*
* Note that for JAVA15+ this will not work since Nashorn is removed from the JDK, but can be added with a maven dependency
*
* Unittest method to call the query parser defined in the VUE typescript.
* The method has been copied to a new file and is now pure javascript.
*/

public class JavascriptTests {

//The following two string will patch the java nashorn (ECMA version 5) engine to support the includes function on string and arrays (introduced in EMCA version 6)
// Copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
public static final String NASHORN_POLYFILL_STRING_PROTOTYPE_INCLUDES = "if (!String.prototype.includes) { Object.defineProperty(String.prototype, 'includes', { value: function(search, start) { if (typeof start !== 'number') { start = 0 } if (start + search.length > this.length) { return false } else { return this.indexOf(search, start) !== -1 } } }) }";
// Copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
public static final String NASHORN_POLYFILL_ARRAY_PROTOTYPE_INCLUDES = "if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function(valueToFind, fromIndex) { if (this == null) { throw new TypeError('\"this\" is null or not defined'); } var o = Object(this); var len = o.length >>> 0; if (len === 0) { return false; } var n = fromIndex | 0; var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } while (k < len) { if (sameValueZero(o[k], valueToFind)) { return true; } k++; } return false; } }); }";

private Invocable inv;

@Before
public void initScripts() throws Exception {
ScriptEngine scriptEngine = getJavascriptScriptEnginePatched();
scriptEngine.eval(extractQueryChecker());
inv = (Invocable) scriptEngine;
}

@Test
public void testBooleanCase() throws Exception {
assertClean("foo AND bar");
Expand Down Expand Up @@ -108,28 +86,31 @@ public void testMix() throws Exception {
assertWarning("foo:‟bar‟ AND foo:bar:zoo", "use simple quote signs", "Two colons without quote signs");
}

public void disabledtestExampleQuery() throws Exception {
String query="abc ( def AND or and [ def";
List<String> results = getQueryParseResultFromJavascript(query);
System.out.println(results);
}

/**
* Calls {@code checkQueryForBadSyntax} with {@code query} and checks that the result is the {@code expectedMessage}.
* @param query a query as used in SolrWayback.
* @param expectedContains the response from the sanity checker should contain this text.
*/
private void assertWarning(String query, String... expectedContains) throws Exception {
String warning = getQueryParseResultFromJavascript(query).toString();
if (expectedContains.length == 0) { // Any warning
assertFalse("Sanity checking the query '" + query + "' should deliver a warning",
warning.isEmpty());
}

for (String expected: expectedContains) {
assertTrue("Sanity checking the query '" + query + "' should deliver a warning containing '" +
expected + "' but delivered '" + warning + "'",
warning.contains(expected));
// Defining the engine explicitly so that testing doesn't warn about no runtime compilation of JavaScript
Engine engine = Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build();
try (Context context = Context.newBuilder("js").engine(engine).allowIO(true).build()) {

String jsCode = extractQueryChecker();
context.eval("js", jsCode);

String warning = getQueryParseResultFromJavascript(query, context).toString();
if (expectedContains.length == 0) { // Any warning
assertFalse("Sanity checking the query '" + query + "' should deliver a warning",
warning.isEmpty());
}

for (String expected : expectedContains) {
assertTrue("Sanity checking the query '" + query + "' should deliver a warning containing '" +
expected + "' but delivered '" + warning + "'",
warning.contains(expected));
}
}
}

Expand All @@ -138,25 +119,21 @@ private void assertWarning(String query, String... expectedContains) throws Exce
* @param query a query as used in SolrWayback.
*/
private void assertClean(String query) throws Exception {
String warning = getQueryParseResultFromJavascript(query).toString();
assertTrue("Sanity checking the query '" + query + "' should deliver no warning but returned'" +
warning + "'", "[]".equals(warning));
}

private List<String> getQueryParseResultFromJavascript(String query) throws Exception{
JSObject obj = (JSObject) inv.invokeFunction("checkQueryForBadSyntax", query);
return obj.values().stream().map(Object::toString).collect(Collectors.toList());
// Defining the engine explicitly so that testing doesn't warn about no runtime compilation of JavaScript
Engine engine = Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build();
try (Context context = Context.newBuilder("js").engine(engine).allowIO(true).build()) {
String jsCode = extractQueryChecker();
context.eval("js", jsCode);

String warning = getQueryParseResultFromJavascript(query, context).toString();
assertTrue("Sanity checking the query '" + query + "' should deliver no warning but returned'" +
warning + "'", "[]".equals(warning));
}
}

private static ScriptEngine getJavascriptScriptEnginePatched() throws Exception {
final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
final ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn");

//Define the .includes function in the script engine.
scriptEngine.eval(NASHORN_POLYFILL_ARRAY_PROTOTYPE_INCLUDES);
scriptEngine.eval(NASHORN_POLYFILL_STRING_PROTOTYPE_INCLUDES);

return scriptEngine;
private Value getQueryParseResultFromJavascript(String query, Context context){
Value checkQueryForBadSyntax = context.getBindings("js").getMember("checkQueryForBadSyntax");
return checkQueryForBadSyntax.execute(query);
}

/**
Expand Down