Speedup lang-painless tests (#605) (#617)

Painless tests would previously create script engine for every single test method.
Now the tests that need to tweak script engine settings create a class
level fixture (BeforeClass/AfterClass) that is used across all the test
methods in that suite.

RegexLimitTests was split into two suites (limit=1 and limit=2) rather
than dynamically applying different settings.

C2 compiler is no longer needed for tests to be fast, instead tests run
faster with C1 only as expected, like the rest of the unit tests.

Signed-off-by: Robert Muir <rmuir@apache.org>
This commit is contained in:
Nick Knize 2021-04-28 11:01:39 -05:00 committed by GitHub
parent a76bbca887
commit f085aa8e11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 654 additions and 421 deletions

View File

@ -68,8 +68,6 @@ restResources {
tasks.named("test").configure {
// in WhenThingsGoWrongTests we intentionally generate an out of memory error, this prevents the heap from being dumped to disk
jvmArgs '-XX:-OmitStackTraceInFastThrow', '-XX:-HeapDumpOnOutOfMemoryError'
// TODO: painless tests unexpectedly run extremely slow without C2
jvmArgs -= '-XX:TieredStopAtLevel=1'
}
/* Build Javadoc for the Java classes in Painless's public API that are in the

View File

@ -26,7 +26,7 @@
*/
/**
* Lexer, parser, and tree {@link Walker} responsible for turning the code
* Lexer, parser, and tree walker responsible for turning the code
* generating nodes in {@link org.opensearch.painless.node}.
*/
/*

View File

@ -508,15 +508,16 @@ public class Augmentation {
}
/**
* Encode a String in Base64. Use {@link Base64.Encoder#encodeToString(byte[])} if you have to encode bytes rather than a string.
* Encode a String in Base64.
* Use {@link java.util.Base64.Encoder#encodeToString(byte[])} if you have to encode bytes rather than a string.
*/
public static String encodeBase64(String receiver) {
return Base64.getEncoder().encodeToString(receiver.getBytes(StandardCharsets.UTF_8));
}
/**
* Decode some Base64 bytes and build a UTF-8 encoded string. Use {@link Base64.Decoder#decode(String)} if you'd prefer bytes to work
* with bytes.
* Decode some Base64 bytes and build a UTF-8 encoded string.
* Use {@link java.util.Base64.Decoder#decode(String)} if you'd prefer bytes to work with bytes.
*/
public static String decodeBase64(String receiver) {
return new String(Base64.getDecoder().decode(receiver.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);

View File

@ -32,6 +32,9 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.painless.spi.WhitelistLoader;
import org.opensearch.script.ScriptContext;
@ -45,15 +48,25 @@ import java.util.Map;
import java.util.regex.Pattern;
public class AugmentationTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = newDefaultContexts();
List<Whitelist> digestWhitelist = new ArrayList<>(Whitelist.BASE_WHITELISTS);
digestWhitelist.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.opensearch.ingest.txt"));
contexts.put(DigestTestScript.CONTEXT, digestWhitelist);
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
return contexts;
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public abstract static class DigestTestScript {
@ -280,7 +293,7 @@ public class AugmentationTests extends ScriptTestCase {
}
public String execDigest(String script) {
return scriptEngine.compile(
return getEngine().compile(
"digest_test",
script,
DigestTestScript.CONTEXT, Collections.emptyMap()

View File

@ -32,6 +32,9 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.script.ScriptContext;
@ -49,8 +52,10 @@ import static org.hamcrest.Matchers.startsWith;
* Tests for Painless implementing different interfaces.
*/
public class BaseClassTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
contexts.put(Gets.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(NoArgs.CONTEXT, Whitelist.BASE_WHITELISTS);
@ -73,7 +78,17 @@ public class BaseClassTests extends ScriptTestCase {
contexts.put(UnknownReturnType.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(UnknownArgTypeInArray.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(TwoExecuteMethods.CONTEXT, Whitelist.BASE_WHITELISTS);
return contexts;
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public abstract static class Gets {
@ -111,15 +126,16 @@ public class BaseClassTests extends ScriptTestCase {
Map<String, Object> map = new HashMap<>();
map.put("s", 1);
assertEquals(1, scriptEngine.compile("testGets0", "testInt", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
assertEquals(1, getEngine().compile("testGets0", "testInt", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
assertEquals(Collections.emptyMap(),
scriptEngine.compile("testGets1", "testMap", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
getEngine().compile("testGets1", "testMap", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
assertEquals(Collections.singletonMap("1", "1"),
scriptEngine.compile("testGets2", "testMap", Gets.CONTEXT, emptyMap())
getEngine().compile("testGets2", "testMap", Gets.CONTEXT, emptyMap())
.newInstance("s", -1, Collections.singletonMap("1", "1")).execute());
assertEquals("s", scriptEngine.compile("testGets3", "testString", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
assertEquals("s",
getEngine().compile("testGets3", "testString", Gets.CONTEXT, emptyMap()).newInstance("s", -1, null).execute());
assertEquals(map,
scriptEngine.compile("testGets4", "testMap.put(testString, testInt); testMap", Gets.CONTEXT, emptyMap())
getEngine().compile("testGets4", "testMap.put(testString, testInt); testMap", Gets.CONTEXT, emptyMap())
.newInstance("s", -1, null).execute());
}
@ -134,14 +150,14 @@ public class BaseClassTests extends ScriptTestCase {
public abstract Object execute();
}
public void testNoArgs() throws Exception {
assertEquals(1, scriptEngine.compile("testNoArgs0", "1", NoArgs.CONTEXT, emptyMap()).newInstance().execute());
assertEquals("foo", scriptEngine.compile("testNoArgs1", "'foo'", NoArgs.CONTEXT, emptyMap()).newInstance().execute());
assertEquals(1, getEngine().compile("testNoArgs0", "1", NoArgs.CONTEXT, emptyMap()).newInstance().execute());
assertEquals("foo", getEngine().compile("testNoArgs1", "'foo'", NoArgs.CONTEXT, emptyMap()).newInstance().execute());
Exception e = expectScriptThrows(IllegalArgumentException.class, () ->
scriptEngine.compile("testNoArgs2", "doc", NoArgs.CONTEXT, emptyMap()));
getEngine().compile("testNoArgs2", "doc", NoArgs.CONTEXT, emptyMap()));
assertEquals("cannot resolve symbol [doc]", e.getMessage());
e = expectScriptThrows(IllegalArgumentException.class, () ->
scriptEngine.compile("testNoArgs3", "_score", NoArgs.CONTEXT, emptyMap()));
getEngine().compile("testNoArgs3", "_score", NoArgs.CONTEXT, emptyMap()));
assertEquals("cannot resolve symbol [_score]", e.getMessage());
String debug = Debugger.toString(NoArgs.class, "int i = 0", new CompilerSettings());
@ -161,9 +177,9 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testOneArg() throws Exception {
Object rando = randomInt();
assertEquals(rando, scriptEngine.compile("testOneArg0", "arg", OneArg.CONTEXT, emptyMap()).newInstance().execute(rando));
assertEquals(rando, getEngine().compile("testOneArg0", "arg", OneArg.CONTEXT, emptyMap()).newInstance().execute(rando));
rando = randomAlphaOfLength(5);
assertEquals(rando, scriptEngine.compile("testOneArg1", "arg", OneArg.CONTEXT, emptyMap()).newInstance().execute(rando));
assertEquals(rando, getEngine().compile("testOneArg1", "arg", OneArg.CONTEXT, emptyMap()).newInstance().execute(rando));
}
public abstract static class ArrayArg {
@ -179,7 +195,7 @@ public class BaseClassTests extends ScriptTestCase {
public void testArrayArg() throws Exception {
String rando = randomAlphaOfLength(5);
assertEquals(rando,
scriptEngine.compile("testArrayArg0", "arg[0]", ArrayArg.CONTEXT, emptyMap())
getEngine().compile("testArrayArg0", "arg[0]", ArrayArg.CONTEXT, emptyMap())
.newInstance().execute(new String[] {rando, "foo"}));
}
@ -196,7 +212,7 @@ public class BaseClassTests extends ScriptTestCase {
public void testPrimitiveArrayArg() throws Exception {
int rando = randomInt();
assertEquals(rando,
scriptEngine.compile("PrimitiveArrayArg0", "arg[0]", PrimitiveArrayArg.CONTEXT, emptyMap())
getEngine().compile("PrimitiveArrayArg0", "arg[0]", PrimitiveArrayArg.CONTEXT, emptyMap())
.newInstance().execute(new int[] {rando, 10}));
}
@ -213,13 +229,13 @@ public class BaseClassTests extends ScriptTestCase {
public void testDefArrayArg()throws Exception {
Object rando = randomInt();
assertEquals(rando,
scriptEngine.compile("testDefArray0", "arg[0]", DefArrayArg.CONTEXT, emptyMap())
getEngine().compile("testDefArray0", "arg[0]", DefArrayArg.CONTEXT, emptyMap())
.newInstance().execute(new Object[] {rando, 10}));
rando = randomAlphaOfLength(5);
assertEquals(rando,
scriptEngine.compile("testDefArray1", "arg[0]", DefArrayArg.CONTEXT, emptyMap())
getEngine().compile("testDefArray1", "arg[0]", DefArrayArg.CONTEXT, emptyMap())
.newInstance().execute(new Object[] {rando, 10}));
assertEquals(5, scriptEngine.compile(
assertEquals(5, getEngine().compile(
"testDefArray2", "arg[0].length()", DefArrayArg.CONTEXT, emptyMap())
.newInstance().execute(new Object[] {rando, 10}));
}
@ -241,22 +257,22 @@ public class BaseClassTests extends ScriptTestCase {
public void testManyArgs() throws Exception {
int rando = randomInt();
assertEquals(rando,
scriptEngine.compile("testManyArgs0", "a", ManyArgs.CONTEXT, emptyMap()).newInstance().execute(rando, 0, 0, 0));
getEngine().compile("testManyArgs0", "a", ManyArgs.CONTEXT, emptyMap()).newInstance().execute(rando, 0, 0, 0));
assertEquals(10,
scriptEngine.compile("testManyArgs1", "a + b + c + d", ManyArgs.CONTEXT, emptyMap()).newInstance().execute(1, 2, 3, 4));
getEngine().compile("testManyArgs1", "a + b + c + d", ManyArgs.CONTEXT, emptyMap()).newInstance().execute(1, 2, 3, 4));
// While we're here we can verify that painless correctly finds used variables
ManyArgs script = scriptEngine.compile("testManyArgs2", "a", ManyArgs.CONTEXT, emptyMap()).newInstance();
ManyArgs script = getEngine().compile("testManyArgs2", "a", ManyArgs.CONTEXT, emptyMap()).newInstance();
assertTrue(script.needsA());
assertFalse(script.needsB());
assertFalse(script.needsC());
assertFalse(script.needsD());
script = scriptEngine.compile("testManyArgs3", "a + b + c", ManyArgs.CONTEXT, emptyMap()).newInstance();
script = getEngine().compile("testManyArgs3", "a + b + c", ManyArgs.CONTEXT, emptyMap()).newInstance();
assertTrue(script.needsA());
assertTrue(script.needsB());
assertTrue(script.needsC());
assertFalse(script.needsD());
script = scriptEngine.compile("testManyArgs4", "a + b + c + d", ManyArgs.CONTEXT, emptyMap()).newInstance();
script = getEngine().compile("testManyArgs4", "a + b + c + d", ManyArgs.CONTEXT, emptyMap()).newInstance();
assertTrue(script.needsA());
assertTrue(script.needsB());
assertTrue(script.needsC());
@ -275,7 +291,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testVarArgs() throws Exception {
assertEquals("foo bar baz",
scriptEngine.compile("testVarArgs0", "String.join(' ', Arrays.asList(arg))", VarArgs.CONTEXT, emptyMap())
getEngine().compile("testVarArgs0", "String.join(' ', Arrays.asList(arg))", VarArgs.CONTEXT, emptyMap())
.newInstance().execute("foo", "bar", "baz"));
}
@ -298,18 +314,18 @@ public class BaseClassTests extends ScriptTestCase {
public void testDefaultMethods() throws Exception {
int rando = randomInt();
assertEquals(rando,
scriptEngine.compile("testDefaultMethods0", "a", DefaultMethods.CONTEXT, emptyMap()).newInstance().execute(rando, 0, 0, 0));
getEngine().compile("testDefaultMethods0", "a", DefaultMethods.CONTEXT, emptyMap()).newInstance().execute(rando, 0, 0, 0));
assertEquals(rando,
scriptEngine.compile("testDefaultMethods1", "a", DefaultMethods.CONTEXT, emptyMap())
getEngine().compile("testDefaultMethods1", "a", DefaultMethods.CONTEXT, emptyMap())
.newInstance().executeWithASingleOne(rando, 0, 0));
assertEquals(10,
scriptEngine.compile("testDefaultMethods2", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
getEngine().compile("testDefaultMethods2", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
.newInstance().execute(1, 2, 3, 4));
assertEquals(4,
scriptEngine.compile("testDefaultMethods3", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
getEngine().compile("testDefaultMethods3", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
.newInstance().executeWithOne());
assertEquals(7,
scriptEngine.compile("testDefaultMethods4", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
getEngine().compile("testDefaultMethods4", "a + b + c + d", DefaultMethods.CONTEXT, emptyMap())
.newInstance().executeWithASingleOne(1, 2, 3));
}
@ -325,9 +341,9 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testReturnsVoid() throws Exception {
Map<String, Object> map = new HashMap<>();
scriptEngine.compile("testReturnsVoid0", "map.a = 'foo'", ReturnsVoid.CONTEXT, emptyMap()).newInstance().execute(map);
getEngine().compile("testReturnsVoid0", "map.a = 'foo'", ReturnsVoid.CONTEXT, emptyMap()).newInstance().execute(map);
assertEquals(Collections.singletonMap("a", "foo"), map);
scriptEngine.compile("testReturnsVoid1", "map.remove('a')", ReturnsVoid.CONTEXT, emptyMap()).newInstance().execute(map);
getEngine().compile("testReturnsVoid1", "map.remove('a')", ReturnsVoid.CONTEXT, emptyMap()).newInstance().execute(map);
assertEquals(emptyMap(), map);
String debug = Debugger.toString(ReturnsVoid.class, "int i = 0", new CompilerSettings());
@ -349,26 +365,26 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testReturnsPrimitiveBoolean() throws Exception {
assertTrue(
scriptEngine.compile("testReturnsPrimitiveBoolean0", "true", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean0", "true", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertFalse(
scriptEngine.compile("testReturnsPrimitiveBoolean1", "false", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean1", "false", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertTrue(
scriptEngine.compile("testReturnsPrimitiveBoolean2", "Boolean.TRUE", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean2", "Boolean.TRUE", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertFalse(
scriptEngine.compile("testReturnsPrimitiveBoolean3", "Boolean.FALSE", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean3", "Boolean.FALSE", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertTrue(
scriptEngine.compile("testReturnsPrimitiveBoolean4", "def i = true; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean4", "def i = true; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertTrue(
scriptEngine.compile("testReturnsPrimitiveBoolean5", "def i = Boolean.TRUE; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean5", "def i = Boolean.TRUE; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertTrue(
scriptEngine.compile("testReturnsPrimitiveBoolean6", "true || false", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean6", "true || false", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
String debug = Debugger.toString(ReturnsPrimitiveBoolean.class, "false", new CompilerSettings());
@ -377,29 +393,29 @@ public class BaseClassTests extends ScriptTestCase {
assertThat(debug, containsString("IRETURN"));
Exception e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean7", "1L",ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean7", "1L",ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals("Cannot cast from [long] to [boolean].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean8", "1.1f", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean8", "1.1f", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals("Cannot cast from [float] to [boolean].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean9", "1.1d", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean9", "1.1d", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals("Cannot cast from [double] to [boolean].", e.getMessage());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean10", "def i = 1L; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean10", "def i = 1L; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean11", "def i = 1.1f; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean11", "def i = 1.1f; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveBoolean12", "def i = 1.1d; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean12", "def i = 1.1d; i", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
assertFalse(
scriptEngine.compile("testReturnsPrimitiveBoolean13", "int i = 0", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveBoolean13", "int i = 0", ReturnsPrimitiveBoolean.CONTEXT, emptyMap())
.newInstance().execute());
}
@ -415,29 +431,29 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testReturnsPrimitiveInt() throws Exception {
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt0", "1", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt0", "1", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt1", "(int) 1L", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt1", "(int) 1L", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1, scriptEngine.compile("testReturnsPrimitiveInt2", "(int) 1.1d", ReturnsPrimitiveInt.CONTEXT, emptyMap())
assertEquals(1, getEngine().compile("testReturnsPrimitiveInt2", "(int) 1.1d", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt3", "(int) 1.1f", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt3", "(int) 1.1f", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt4", "Integer.valueOf(1)", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt4", "Integer.valueOf(1)", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt5", "def i = 1; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt5", "def i = 1; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt6", "def i = Integer.valueOf(1); i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt6", "def i = Integer.valueOf(1); i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(2,
scriptEngine.compile("testReturnsPrimitiveInt7", "1 + 1", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
getEngine().compile("testReturnsPrimitiveInt7", "1 + 1", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
String debug = Debugger.toString(ReturnsPrimitiveInt.class, "1", new CompilerSettings());
assertThat(debug, containsString("ICONST_1"));
@ -445,25 +461,25 @@ public class BaseClassTests extends ScriptTestCase {
assertThat(debug, containsString("IRETURN"));
Exception e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt8", "1L", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
getEngine().compile("testReturnsPrimitiveInt8", "1L", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
assertEquals("Cannot cast from [long] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt9", "1.1f", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
getEngine().compile("testReturnsPrimitiveInt9", "1.1f", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
assertEquals("Cannot cast from [float] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt10", "1.1d", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
getEngine().compile("testReturnsPrimitiveInt10", "1.1d", ReturnsPrimitiveInt.CONTEXT, emptyMap()).newInstance().execute());
assertEquals("Cannot cast from [double] to [int].", e.getMessage());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt11", "def i = 1L; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt11", "def i = 1L; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt12", "def i = 1.1f; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt12", "def i = 1.1f; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveInt13", "def i = 1.1d; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveInt13", "def i = 1.1d; i", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(0, scriptEngine.compile("testReturnsPrimitiveInt14", "int i = 0", ReturnsPrimitiveInt.CONTEXT, emptyMap())
assertEquals(0, getEngine().compile("testReturnsPrimitiveInt14", "int i = 0", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
}
@ -479,30 +495,30 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testReturnsPrimitiveFloat() throws Exception {
assertEquals(1.1f,
scriptEngine.compile("testReturnsPrimitiveFloat0", "1.1f", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat0", "1.1f", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1f,
scriptEngine.compile("testReturnsPrimitiveFloat1", "(float) 1.1d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat1", "(float) 1.1d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1f,
scriptEngine.compile("testReturnsPrimitiveFloat2", "def d = 1.1f; d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat2", "def d = 1.1f; d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1f, scriptEngine.compile(
assertEquals(1.1f, getEngine().compile(
"testReturnsPrimitiveFloat3", "def d = Float.valueOf(1.1f); d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1f + 6.7f,
scriptEngine.compile("testReturnsPrimitiveFloat4", "1.1f + 6.7f", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat4", "1.1f + 6.7f", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
Exception e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveFloat5", "1.1d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat5", "1.1d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals("Cannot cast from [double] to [float].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("testReturnsPrimitiveFloat6", "def d = 1.1d; d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat6", "def d = 1.1d; d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute());
e = expectScriptThrows(ClassCastException.class, () -> scriptEngine.compile(
e = expectScriptThrows(ClassCastException.class, () -> getEngine().compile(
"testReturnsPrimitiveFloat7", "def d = Double.valueOf(1.1); d", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute());
@ -512,7 +528,7 @@ public class BaseClassTests extends ScriptTestCase {
assertThat(debug, containsString("FRETURN"));
assertEquals(0.0f,
scriptEngine.compile("testReturnsPrimitiveFloat8", "int i = 0", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveFloat8", "int i = 0", ReturnsPrimitiveFloat.CONTEXT, emptyMap())
.newInstance().execute(), 0);
}
@ -528,45 +544,45 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testReturnsPrimitiveDouble() throws Exception {
assertEquals(1.0,
scriptEngine.compile("testReturnsPrimitiveDouble0", "1", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble0", "1", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.0,
scriptEngine.compile("testReturnsPrimitiveDouble1", "1L", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble1", "1L", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1,
scriptEngine.compile("testReturnsPrimitiveDouble2", "1.1d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble2", "1.1d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals((double) 1.1f,
scriptEngine.compile("testReturnsPrimitiveDouble3", "1.1f", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble3", "1.1f", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1, scriptEngine.compile(
assertEquals(1.1, getEngine().compile(
"testReturnsPrimitiveDouble4", "Double.valueOf(1.1)", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals((double) 1.1f, scriptEngine.compile(
assertEquals((double) 1.1f, getEngine().compile(
"testReturnsPrimitiveDouble5", "Float.valueOf(1.1f)", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.0,
scriptEngine.compile("testReturnsPrimitiveDouble6", "def d = 1; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble6", "def d = 1; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.0,
scriptEngine.compile("testReturnsPrimitiveDouble7", "def d = 1L; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble7", "def d = 1L; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1,
scriptEngine.compile("testReturnsPrimitiveDouble8", "def d = 1.1d; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap()).
getEngine().compile("testReturnsPrimitiveDouble8", "def d = 1.1d; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap()).
newInstance().execute(), 0);
assertEquals((double) 1.1f,
scriptEngine.compile("testReturnsPrimitiveDouble9", "def d = 1.1f; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble9", "def d = 1.1f; d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1, scriptEngine.compile(
assertEquals(1.1, getEngine().compile(
"testReturnsPrimitiveDouble10", "def d = Double.valueOf(1.1); d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals((double) 1.1f, scriptEngine.compile(
assertEquals((double) 1.1f, getEngine().compile(
"testReturnsPrimitiveDouble11", "def d = Float.valueOf(1.1f); d", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
assertEquals(1.1 + 6.7,
scriptEngine.compile("testReturnsPrimitiveDouble12", "1.1 + 6.7", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble12", "1.1 + 6.7", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
String debug = Debugger.toString(ReturnsPrimitiveDouble.class, "1", new CompilerSettings());
@ -574,7 +590,7 @@ public class BaseClassTests extends ScriptTestCase {
assertThat(debug, containsString("DRETURN"));
assertEquals(0.0,
scriptEngine.compile("testReturnsPrimitiveDouble13", "int i = 0", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
getEngine().compile("testReturnsPrimitiveDouble13", "int i = 0", ReturnsPrimitiveDouble.CONTEXT, emptyMap())
.newInstance().execute(), 0);
}
@ -589,7 +605,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testNoArgsConstant() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testNoArgsConstant0", "1", NoArgsConstant.CONTEXT, emptyMap()).newInstance().execute("constant"));
getEngine().compile("testNoArgsConstant0", "1", NoArgsConstant.CONTEXT, emptyMap()).newInstance().execute("constant"));
assertThat(e.getMessage(), startsWith(
"Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the "
+ "names of the method arguments but [" + NoArgsConstant.class.getName() + "] doesn't have one."));
@ -607,7 +623,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testWrongArgsConstant() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testWrongArgsConstant0", "1", WrongArgsConstant.CONTEXT, emptyMap()));
getEngine().compile("testWrongArgsConstant0", "1", WrongArgsConstant.CONTEXT, emptyMap()));
assertThat(e.getMessage(), startsWith(
"Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the "
+ "names of the method arguments but [" + WrongArgsConstant.class.getName() + "] doesn't have one."));
@ -625,7 +641,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testWrongLengthOfArgConstant() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testWrongLengthOfArgConstant", "1", WrongLengthOfArgConstant.CONTEXT, emptyMap()));
getEngine().compile("testWrongLengthOfArgConstant", "1", WrongLengthOfArgConstant.CONTEXT, emptyMap()));
assertThat(e.getMessage(), startsWith("[" + WrongLengthOfArgConstant.class.getName() + "#ARGUMENTS] has length [2] but ["
+ WrongLengthOfArgConstant.class.getName() + "#execute] takes [1] argument."));
}
@ -642,7 +658,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testUnknownArgType() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testUnknownArgType0", "1", UnknownArgType.CONTEXT, emptyMap()));
getEngine().compile("testUnknownArgType0", "1", UnknownArgType.CONTEXT, emptyMap()));
assertEquals("[foo] is of unknown type [" + UnknownArgType.class.getName() + ". Painless interfaces can only accept arguments "
+ "that are of whitelisted types.", e.getMessage());
}
@ -659,7 +675,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testUnknownReturnType() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testUnknownReturnType0", "1", UnknownReturnType.CONTEXT, emptyMap()));
getEngine().compile("testUnknownReturnType0", "1", UnknownReturnType.CONTEXT, emptyMap()));
assertEquals("Painless can only implement execute methods returning a whitelisted type but [" + UnknownReturnType.class.getName()
+ "#execute] returns [" + UnknownReturnType.class.getName() + "] which isn't whitelisted.", e.getMessage());
}
@ -676,7 +692,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testUnknownArgTypeInArray() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testUnknownAryTypeInArray0", "1", UnknownArgTypeInArray.CONTEXT, emptyMap()));
getEngine().compile("testUnknownAryTypeInArray0", "1", UnknownArgTypeInArray.CONTEXT, emptyMap()));
assertEquals("[foo] is of unknown type [" + UnknownArgTypeInArray.class.getName() + ". Painless interfaces can only accept "
+ "arguments that are of whitelisted types.", e.getMessage());
}
@ -693,7 +709,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public void testTwoExecuteMethods() {
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
scriptEngine.compile("testTwoExecuteMethods0", "null", TwoExecuteMethods.CONTEXT, emptyMap()));
getEngine().compile("testTwoExecuteMethods0", "null", TwoExecuteMethods.CONTEXT, emptyMap()));
assertEquals("Painless can only implement interfaces that have a single method named [execute] but ["
+ TwoExecuteMethods.class.getName() + "] has more than one.", e.getMessage());
}

View File

@ -32,6 +32,9 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.script.ScriptContext;
@ -44,11 +47,23 @@ import java.util.Map;
import static java.util.Collections.emptyMap;
public class BasicStatementTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = newDefaultContexts();
contexts.put(OneArg.CONTEXT, Whitelist.BASE_WHITELISTS);
return contexts;
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testIfStatement() {
@ -295,16 +310,16 @@ public class BasicStatementTests extends ScriptTestCase {
"List rtn = new ArrayList(); rtn.add(0); test(rtn); rtn"));
ArrayList<Integer> input = new ArrayList<>();
scriptEngine.compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return;} arg.add(2);",
getEngine().compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return;} arg.add(2);",
OneArg.CONTEXT, emptyMap()).newInstance().execute(input);
assertEquals(Collections.singletonList(1), input);
input = new ArrayList<>();
scriptEngine.compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return} arg.add(2);",
getEngine().compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return} arg.add(2);",
OneArg.CONTEXT, emptyMap()).newInstance().execute(input);
assertEquals(Collections.singletonList(1), input);
input = new ArrayList<>();
input.add(0);
scriptEngine.compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return} arg.add(2);",
getEngine().compile("testOneArg", "if (arg.isEmpty()) {arg.add(1); return} arg.add(2);",
OneArg.CONTEXT, emptyMap()).newInstance().execute(input);
assertEquals(expected, input);
}

View File

@ -32,6 +32,9 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.painless.spi.WhitelistInstanceBinding;
import org.opensearch.painless.spi.WhitelistLoader;
@ -43,6 +46,39 @@ import java.util.List;
import java.util.Map;
public class BindingsTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = newDefaultContexts();
List<Whitelist> whitelists = new ArrayList<>(Whitelist.BASE_WHITELISTS);
whitelists.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.opensearch.painless.test"));
InstanceBindingTestClass instanceBindingTestClass = new InstanceBindingTestClass(1);
WhitelistInstanceBinding getter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"setInstanceBindingValue", "void", Collections.singletonList("int"), Collections.emptyList());
WhitelistInstanceBinding setter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"getInstanceBindingValue", "int", Collections.emptyList(), Collections.emptyList());
List<WhitelistInstanceBinding> instanceBindingsList = new ArrayList<>();
instanceBindingsList.add(getter);
instanceBindingsList.add(setter);
Whitelist instanceBindingsWhitelist = new Whitelist(instanceBindingTestClass.getClass().getClassLoader(),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), instanceBindingsList);
whitelists.add(instanceBindingsWhitelist);
contexts.put(BindingsTestScript.CONTEXT, whitelists);
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public static class BindingTestClass {
public int state;
@ -108,31 +144,9 @@ public class BindingsTests extends ScriptTestCase {
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("bindings_test", Factory.class);
}
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
List<Whitelist> whitelists = new ArrayList<>(Whitelist.BASE_WHITELISTS);
whitelists.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.opensearch.painless.test"));
InstanceBindingTestClass instanceBindingTestClass = new InstanceBindingTestClass(1);
WhitelistInstanceBinding getter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"setInstanceBindingValue", "void", Collections.singletonList("int"), Collections.emptyList());
WhitelistInstanceBinding setter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"getInstanceBindingValue", "int", Collections.emptyList(), Collections.emptyList());
List<WhitelistInstanceBinding> instanceBindingsList = new ArrayList<>();
instanceBindingsList.add(getter);
instanceBindingsList.add(setter);
Whitelist instanceBindingsWhitelist = new Whitelist(instanceBindingTestClass.getClass().getClassLoader(),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), instanceBindingsList);
whitelists.add(instanceBindingsWhitelist);
contexts.put(BindingsTestScript.CONTEXT, whitelists);
return contexts;
}
public void testBasicClassBinding() {
String script = "addWithState(4, 5, 6, 0.0)";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(15, executableScript.execute(0, 0));
@ -140,7 +154,7 @@ public class BindingsTests extends ScriptTestCase {
public void testRepeatedClassBinding() {
String script = "addWithState(4, 5, test, 0.0)";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(14, executableScript.execute(5, 0));
@ -150,7 +164,7 @@ public class BindingsTests extends ScriptTestCase {
public void testBoundClassBinding() {
String script = "addWithState(4, bound, test, 0.0)";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(10, executableScript.execute(5, 1));
@ -160,7 +174,7 @@ public class BindingsTests extends ScriptTestCase {
public void testThisClassBinding() {
String script = "addThisWithState(4, bound, test, 0.0)";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(17, executableScript.execute(5, 1));
@ -170,7 +184,7 @@ public class BindingsTests extends ScriptTestCase {
public void testEmptyThisClassBinding() {
String script = "addEmptyThisWithState(test)";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(8, executableScript.execute(1, 0));
@ -179,17 +193,17 @@ public class BindingsTests extends ScriptTestCase {
public void testInstanceBinding() {
String script = "getInstanceBindingValue() + test + bound";
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript.Factory factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
BindingsTestScript executableScript = factory.newInstance();
assertEquals(3, executableScript.execute(1, 1));
script = "setInstanceBindingValue(test + bound); getInstanceBindingValue()";
factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
executableScript = factory.newInstance();
assertEquals(4, executableScript.execute(-2, 6));
script = "getInstanceBindingValue() + test + bound";
factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
factory = getEngine().compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
executableScript = factory.newInstance();
assertEquals(8, executableScript.execute(-2, 6));
}

View File

@ -33,6 +33,7 @@
package org.opensearch.painless;
import org.opensearch.painless.action.PainlessExecuteAction.PainlessTestScript;
import org.opensearch.painless.lookup.PainlessLookup;
import org.opensearch.painless.lookup.PainlessLookupBuilder;
import org.opensearch.painless.spi.Whitelist;
import org.objectweb.asm.util.Textifier;
@ -48,13 +49,15 @@ final class Debugger {
return toString(PainlessTestScript.class, source, new CompilerSettings());
}
private static final PainlessLookup LOOKUP = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
/** compiles to bytecode, and returns debugging output */
static String toString(Class<?> iface, String source, CompilerSettings settings) {
StringWriter output = new StringWriter();
PrintWriter outputWriter = new PrintWriter(output);
Textifier textifier = new Textifier();
try {
new Compiler(iface, null, null, PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS))
new Compiler(iface, null, null, LOOKUP)
.compile("<debugging>", source, settings, textifier);
} catch (RuntimeException e) {
textifier.print(outputWriter);

View File

@ -32,6 +32,9 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.script.ScriptContext;
import org.opensearch.script.ScriptException;
@ -47,10 +50,11 @@ import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
public class FactoryTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = newDefaultContexts();
contexts.put(StatefulFactoryTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(FactoryTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(DeterministicFactoryTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
@ -60,8 +64,17 @@ public class FactoryTests extends ScriptTestCase {
contexts.put(FactoryTestConverterScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(FactoryTestConverterScriptBadDef.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(DocFieldsTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
return contexts;
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public abstract static class StatefulFactoryTestScript {
@ -123,7 +136,7 @@ public class FactoryTests extends ScriptTestCase {
}
public void testStatefulFactory() {
StatefulFactoryTestScript.Factory factory = scriptEngine.compile(
StatefulFactoryTestScript.Factory factory = getEngine().compile(
"stateful_factory_test", "test + x + y + d", StatefulFactoryTestScript.CONTEXT, Collections.emptyMap());
StatefulFactoryTestScript.StatefulFactory statefulFactory = factory.newFactory(1, 2);
StatefulFactoryTestScript script = statefulFactory.newInstance(3, 4);
@ -199,7 +212,7 @@ public class FactoryTests extends ScriptTestCase {
public void testFactory() {
FactoryTestScript.Factory factory =
scriptEngine.compile("factory_test", "test + params.get('test')", FactoryTestScript.CONTEXT, Collections.emptyMap());
getEngine().compile("factory_test", "test + params.get('test')", FactoryTestScript.CONTEXT, Collections.emptyMap());
FactoryTestScript script = factory.newInstance(Collections.singletonMap("test", 2));
assertEquals(4, script.execute(2));
assertEquals(5, script.execute(3));
@ -214,7 +227,7 @@ public class FactoryTests extends ScriptTestCase {
public void testDeterministic() {
DeterministicFactoryTestScript.Factory factory =
scriptEngine.compile("deterministic_test", "Integer.parseInt('123')",
getEngine().compile("deterministic_test", "Integer.parseInt('123')",
DeterministicFactoryTestScript.CONTEXT, Collections.emptyMap());
assertTrue(factory.isResultDeterministic());
assertEquals(123, factory.newInstance(Collections.emptyMap()).execute(0));
@ -222,7 +235,7 @@ public class FactoryTests extends ScriptTestCase {
public void testNotDeterministic() {
DeterministicFactoryTestScript.Factory factory =
scriptEngine.compile("not_deterministic_test", "Math.random()",
getEngine().compile("not_deterministic_test", "Math.random()",
DeterministicFactoryTestScript.CONTEXT, Collections.emptyMap());
assertFalse(factory.isResultDeterministic());
Double d = (Double)factory.newInstance(Collections.emptyMap()).execute(0);
@ -231,7 +244,7 @@ public class FactoryTests extends ScriptTestCase {
public void testMixedDeterministicIsNotDeterministic() {
DeterministicFactoryTestScript.Factory factory =
scriptEngine.compile("not_deterministic_test", "Integer.parseInt('123') + Math.random()",
getEngine().compile("not_deterministic_test", "Integer.parseInt('123') + Math.random()",
DeterministicFactoryTestScript.CONTEXT, Collections.emptyMap());
assertFalse(factory.isResultDeterministic());
Double d = (Double)factory.newInstance(Collections.emptyMap()).execute(0);
@ -251,7 +264,7 @@ public class FactoryTests extends ScriptTestCase {
}
public void testEmpty() {
EmptyTestScript.Factory factory = scriptEngine.compile("empty_test", "1", EmptyTestScript.CONTEXT, Collections.emptyMap());
EmptyTestScript.Factory factory = getEngine().compile("empty_test", "1", EmptyTestScript.CONTEXT, Collections.emptyMap());
EmptyTestScript script = factory.newInstance();
assertEquals(1, script.execute());
assertEquals(1, script.execute());
@ -262,7 +275,7 @@ public class FactoryTests extends ScriptTestCase {
public void testTemplate() {
TemplateScript.Factory factory =
scriptEngine.compile("template_test", "params['test']", TemplateScript.CONTEXT, Collections.emptyMap());
getEngine().compile("template_test", "params['test']", TemplateScript.CONTEXT, Collections.emptyMap());
TemplateScript script = factory.newInstance(Collections.singletonMap("test", "abc"));
assertEquals("abc", script.execute());
assertEquals("abc", script.execute());
@ -273,7 +286,7 @@ public class FactoryTests extends ScriptTestCase {
public void testGetterInLambda() {
FactoryTestScript.Factory factory =
scriptEngine.compile("template_test",
getEngine().compile("template_test",
"IntSupplier createLambda(IntSupplier s) { return s; } createLambda(() -> params['x'] + test).getAsInt()",
FactoryTestScript.CONTEXT, Collections.emptyMap());
FactoryTestScript script = factory.newInstance(Collections.singletonMap("x", 1));
@ -293,9 +306,9 @@ public class FactoryTests extends ScriptTestCase {
}
public void testVoidReturn() {
scriptEngine.compile("void_return_test", "int x = 1 + 1; return;", VoidReturnTestScript.CONTEXT, Collections.emptyMap());
getEngine().compile("void_return_test", "int x = 1 + 1; return;", VoidReturnTestScript.CONTEXT, Collections.emptyMap());
IllegalArgumentException iae = expectScriptThrows(IllegalArgumentException.class, () ->
scriptEngine.compile("void_return_test", "1 + 1", VoidReturnTestScript.CONTEXT, Collections.emptyMap()));
getEngine().compile("void_return_test", "1 + 1", VoidReturnTestScript.CONTEXT, Collections.emptyMap()));
assertEquals(iae.getMessage(), "not a statement: result not used from addition operation [+]");
}
@ -360,7 +373,7 @@ public class FactoryTests extends ScriptTestCase {
public void testConverterFactory() {
FactoryTestConverterScript.Factory factory =
scriptEngine.compile("converter_test",
getEngine().compile("converter_test",
"return test;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
FactoryTestConverterScript script = factory.newInstance(Collections.singletonMap("test", 2));
@ -368,103 +381,103 @@ public class FactoryTests extends ScriptTestCase {
script = factory.newInstance(Collections.singletonMap("test", 3));
assertArrayEquals(new long[]{3}, script.execute(3));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"return test + 1;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1001}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"return '100';",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{100}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"long[] a = new long[]{test, 123}; return a;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1000, 123}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"return [test, 123];",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1000, 123}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"ArrayList a = new ArrayList(); a.add(test); a.add(456); a.add('789'); return a;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{123, 456, 789}, script.execute(123));
// autoreturn, no converter
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"new long[]{test}",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{123}, script.execute(123));
// autoreturn, converter
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"test",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{456}, script.execute(456));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"'1001'",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1001}, script.execute(456));
// def tests
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"def a = new long[]{test, 123}; return a;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1000, 123}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"def l = [test, 123]; l;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1000, 123}, script.execute(1000));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"def a = new ArrayList(); a.add(test); a.add(456); a.add('789'); return a;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{123, 456, 789}, script.execute(123));
// autoreturn, no converter
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"def a = new long[]{test}; a;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{123}, script.execute(123));
// autoreturn, converter
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"def a = '1001'; a",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1001}, script.execute(456));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"int x = 1",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(null, script.execute(123));
factory = scriptEngine.compile("converter_test",
factory = getEngine().compile("converter_test",
"short x = 1; return x",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap());
script = factory.newInstance(Collections.singletonMap("test", 2));
assertArrayEquals(new long[]{1}, script.execute(123));
ClassCastException cce = expectScriptThrows(ClassCastException.class, () ->
scriptEngine.compile("converter_test",
getEngine().compile("converter_test",
"return true;",
FactoryTestConverterScript.CONTEXT, Collections.emptyMap()));
assertEquals(cce.getMessage(), "Cannot cast from [boolean] to [long[]].");
@ -499,7 +512,7 @@ public class FactoryTests extends ScriptTestCase {
public void testConverterFactoryBadDef() {
IllegalStateException ise = null;
try {
scriptEngine.compile("converter_def",
getEngine().compile("converter_def",
"return test;",
FactoryTestConverterScriptBadDef.CONTEXT, Collections.emptyMap());
} catch (ScriptException e) {
@ -535,7 +548,7 @@ public class FactoryTests extends ScriptTestCase {
public void testDocFields() {
DocFieldsTestScript.Factory f =
scriptEngine.compile("test", "doc['cat'] + doc['dog']", DocFieldsTestScript.CONTEXT, Collections.emptyMap());
getEngine().compile("test", "doc['cat'] + doc['dog']", DocFieldsTestScript.CONTEXT, Collections.emptyMap());
assertThat(f.docFields(), equalTo(Arrays.asList("cat", "dog")));
assertThat(f.newInstance().execute(), equalTo("meowwoof"));
}

View File

@ -0,0 +1,189 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
public class RegexLimit2Tests extends ScriptTestCase {
// This regex has backtracking due to .*?
private static final String PATTERN = "/abc.*?def/";
private static final String CHAR_SEQUENCE = "'abcdodef'";
private static final String SPLIT_CHAR_SEQUENCE = "'0-abc-1-def-X-abc-2-def-Y-abc-3-def-Z-abc'";
private static PainlessScriptEngine SCRIPT_ENGINE;
@BeforeClass
public static void beforeClass() {
Settings settings = Settings.builder().put(CompilerSettings.REGEX_LIMIT_FACTOR.getKey(), 2).build();
SCRIPT_ENGINE = new PainlessScriptEngine(settings, newDefaultContexts());
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testRegexInject_Matcher() {
String[] scripts = new String[]{PATTERN + ".matcher(" + CHAR_SEQUENCE + ").matches()",
"Matcher m = " + PATTERN + ".matcher(" + CHAR_SEQUENCE + "); m.matches()"};
for (String script : scripts) {
assertEquals(Boolean.TRUE, exec(script));
}
}
public void testRegexInject_Def_Matcher() {
String[] scripts = new String[]{"def p = " + PATTERN + "; p.matcher(" + CHAR_SEQUENCE + ").matches()",
"def p = " + PATTERN + "; def m = p.matcher(" + CHAR_SEQUENCE + "); m.matches()"};
for (String script : scripts) {
assertEquals(Boolean.TRUE, exec(script));
}
}
public void testMethodRegexInject_Ref_Matcher() {
String script =
"boolean isMatch(Function func) { func.apply(" + CHAR_SEQUENCE +").matches(); } " +
"Pattern pattern = " + PATTERN + ";" +
"isMatch(pattern::matcher)";
assertEquals(Boolean.TRUE, exec(script));
}
public void testRegexInject_DefMethodRef_Matcher() {
String script =
"boolean isMatch(Function func) { func.apply(" + CHAR_SEQUENCE +").matches(); } " +
"def pattern = " + PATTERN + ";" +
"isMatch(pattern::matcher)";
assertEquals(Boolean.TRUE, exec(script));
}
public void testRegexInject_SplitLimit() {
String[] scripts = new String[]{PATTERN + ".split(" + SPLIT_CHAR_SEQUENCE + ", 2)",
"Pattern p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ", 2)"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
}
}
public void testRegexInject_Def_SplitLimit() {
String script = "def p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ", 2)";
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_Ref_SplitLimit() {
String script =
"String[] splitLimit(BiFunction func) { func.apply(" + SPLIT_CHAR_SEQUENCE + ", 2); } " +
"Pattern pattern = " + PATTERN + ";" +
"splitLimit(pattern::split)";
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_DefMethodRef_SplitLimit() {
String script =
"String[] splitLimit(BiFunction func) { func.apply(" + SPLIT_CHAR_SEQUENCE + ", 2); } " +
"def pattern = " + PATTERN + ";" +
"splitLimit(pattern::split)";
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_Split() {
String[] scripts = new String[]{PATTERN + ".split(" + SPLIT_CHAR_SEQUENCE + ")",
"Pattern p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ")"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
}
}
public void testRegexInject_Def_Split() {
String script = "def p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ")";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_Ref_Split() {
String script =
"String[] split(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE + "); } " +
"Pattern pattern = " + PATTERN + ";" +
"split(pattern::split)";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_DefMethodRef_Split() {
String script =
"String[] split(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"def pattern = " + PATTERN + ";" +
"split(pattern::split)";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
}
public void testRegexInject_SplitAsStream() {
String[] scripts = new String[]{PATTERN + ".splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)",
"Pattern p = " + PATTERN + "; p.splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
}
}
public void testRegexInject_Def_SplitAsStream() {
String script = "def p = " + PATTERN + "; p.splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
}
public void testRegexInject_Ref_SplitAsStream() {
String script =
"Stream splitStream(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"Pattern pattern = " + PATTERN + ";" +
"splitStream(pattern::splitAsStream).toArray(String[]::new)";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
}
public void testRegexInject_DefMethodRef_SplitAsStream() {
String script =
"Stream splitStream(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"def pattern = " + PATTERN + ";" +
"splitStream(pattern::splitAsStream).toArray(String[]::new)";
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
}
public void testRegexInjectFindOperator() {
String script = "if (" + CHAR_SEQUENCE + " =~ " + PATTERN + ") { return 100; } return 200";
assertEquals(Integer.valueOf(100), (Integer) exec(script));
}
public void testRegexInjectMatchOperator() {
String script = "if (" + CHAR_SEQUENCE + " ==~ " + PATTERN + ") { return 100; } return 200";
assertEquals(Integer.valueOf(100), (Integer) exec(script));
}
}

View File

@ -31,7 +31,8 @@
*/
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.breaker.CircuitBreakingException;
import org.opensearch.common.settings.Settings;
@ -39,284 +40,181 @@ import java.util.Collections;
public class RegexLimitTests extends ScriptTestCase {
// This regex has backtracking due to .*?
private final String pattern = "/abc.*?def/";
private final String charSequence = "'abcdodef'";
private final String splitCharSequence = "'0-abc-1-def-X-abc-2-def-Y-abc-3-def-Z-abc'";
private final String regexCircuitMessage = "[scripting] Regular expression considered too many characters";
private static final String PATTERN = "/abc.*?def/";
private static final String CHAR_SEQUENCE = "'abcdodef'";
private static final String SPLIT_CHAR_SEQUENCE = "'0-abc-1-def-X-abc-2-def-Y-abc-3-def-Z-abc'";
private static final String REGEX_CIRCUIT_MESSAGE = "[scripting] Regular expression considered too many characters";
private static PainlessScriptEngine SCRIPT_ENGINE;
public void testRegexInject_Matcher() {
String[] scripts = new String[]{pattern + ".matcher(" + charSequence + ").matches()",
"Matcher m = " + pattern + ".matcher(" + charSequence + "); m.matches()"};
for (String script : scripts) {
setRegexLimitFactor(2);
assertEquals(Boolean.TRUE, exec(script));
// Backtracking means the regular expression will fail with limit factor 1 (don't consider more than each char once)
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
}
@BeforeClass
public static void beforeClass() {
Settings settings = Settings.builder().put(CompilerSettings.REGEX_LIMIT_FACTOR.getKey(), 1).build();
SCRIPT_ENGINE = new PainlessScriptEngine(settings, newDefaultContexts());
}
public void testRegexInjectUnlimited_Matcher() {
String[] scripts = new String[]{pattern + ".matcher(" + charSequence + ").matches()",
"Matcher m = " + pattern + ".matcher(" + charSequence + "); m.matches()"};
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testRegexInject_Matcher() {
String[] scripts = new String[]{PATTERN + ".matcher(" + CHAR_SEQUENCE + ").matches()",
"Matcher m = " + PATTERN + ".matcher(" + CHAR_SEQUENCE + "); m.matches()"};
for (String script : scripts) {
setRegexEnabled();
assertEquals(Boolean.TRUE, exec(script));
// Backtracking means the regular expression will fail with limit factor 1 (don't consider more than each char once)
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
}
public void testRegexInject_Def_Matcher() {
String[] scripts = new String[]{"def p = " + pattern + "; p.matcher(" + charSequence + ").matches()",
"def p = " + pattern + "; def m = p.matcher(" + charSequence + "); m.matches()"};
String[] scripts = new String[]{"def p = " + PATTERN + "; p.matcher(" + CHAR_SEQUENCE + ").matches()",
"def p = " + PATTERN + "; def m = p.matcher(" + CHAR_SEQUENCE + "); m.matches()"};
for (String script : scripts) {
setRegexLimitFactor(2);
assertEquals(Boolean.TRUE, exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
}
public void testMethodRegexInject_Ref_Matcher() {
String script =
"boolean isMatch(Function func) { func.apply(" + charSequence +").matches(); } " +
"Pattern pattern = " + pattern + ";" +
"boolean isMatch(Function func) { func.apply(" + CHAR_SEQUENCE +").matches(); } " +
"Pattern pattern = " + PATTERN + ";" +
"isMatch(pattern::matcher)";
setRegexLimitFactor(2);
assertEquals(Boolean.TRUE, exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_DefMethodRef_Matcher() {
String script =
"boolean isMatch(Function func) { func.apply(" + charSequence +").matches(); } " +
"def pattern = " + pattern + ";" +
"boolean isMatch(Function func) { func.apply(" + CHAR_SEQUENCE +").matches(); } " +
"def pattern = " + PATTERN + ";" +
"isMatch(pattern::matcher)";
setRegexLimitFactor(2);
assertEquals(Boolean.TRUE, exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_SplitLimit() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ", 2)",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ", 2)"};
String[] scripts = new String[]{PATTERN + ".split(" + SPLIT_CHAR_SEQUENCE + ", 2)",
"Pattern p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ", 2)"};
for (String script : scripts) {
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
}
}
public void testRegexInjectUnlimited_SplitLimit() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ", 2)",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ", 2)"};
for (String script : scripts) {
setRegexEnabled();
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
}
public void testRegexInject_Def_SplitLimit() {
String script = "def p = " + pattern + "; p.split(" + splitCharSequence + ", 2)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
String script = "def p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ", 2)";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_Ref_SplitLimit() {
String script =
"String[] splitLimit(BiFunction func) { func.apply(" + splitCharSequence + ", 2); } " +
"Pattern pattern = " + pattern + ";" +
"String[] splitLimit(BiFunction func) { func.apply(" + SPLIT_CHAR_SEQUENCE + ", 2); } " +
"Pattern pattern = " + PATTERN + ";" +
"splitLimit(pattern::split)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_DefMethodRef_SplitLimit() {
String script =
"String[] splitLimit(BiFunction func) { func.apply(" + splitCharSequence + ", 2); } " +
"def pattern = " + pattern + ";" +
"String[] splitLimit(BiFunction func) { func.apply(" + SPLIT_CHAR_SEQUENCE + ", 2); } " +
"def pattern = " + PATTERN + ";" +
"splitLimit(pattern::split)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_Split() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ")",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ")"};
String[] scripts = new String[]{PATTERN + ".split(" + SPLIT_CHAR_SEQUENCE + ")",
"Pattern p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ")"};
for (String script : scripts) {
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
}
}
public void testRegexInjectUnlimited_Split() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ")",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ")"};
for (String script : scripts) {
setRegexEnabled();
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
}
public void testRegexInject_Def_Split() {
String script = "def p = " + pattern + "; p.split(" + splitCharSequence + ")";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
String script = "def p = " + PATTERN + "; p.split(" + SPLIT_CHAR_SEQUENCE + ")";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_Ref_Split() {
String script =
"String[] split(Function func) { func.apply(" + splitCharSequence + "); } " +
"Pattern pattern = " + pattern + ";" +
"String[] split(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE + "); } " +
"Pattern pattern = " + PATTERN + ";" +
"split(pattern::split)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_DefMethodRef_Split() {
String script =
"String[] split(Function func) { func.apply(" + splitCharSequence +"); } " +
"def pattern = " + pattern + ";" +
"String[] split(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"def pattern = " + PATTERN + ";" +
"split(pattern::split)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_SplitAsStream() {
String[] scripts = new String[]{pattern + ".splitAsStream(" + splitCharSequence + ").toArray(String[]::new)",
"Pattern p = " + pattern + "; p.splitAsStream(" + splitCharSequence + ").toArray(String[]::new)"};
String[] scripts = new String[]{PATTERN + ".splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)",
"Pattern p = " + PATTERN + "; p.splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)"};
for (String script : scripts) {
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
}
}
public void testRegexInjectUnlimited_SplitAsStream() {
String[] scripts = new String[]{pattern + ".splitAsStream(" + splitCharSequence + ").toArray(String[]::new)",
"Pattern p = " + pattern + "; p.splitAsStream(" + splitCharSequence + ").toArray(String[]::new)"};
for (String script : scripts) {
setRegexEnabled();
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
}
public void testRegexInject_Def_SplitAsStream() {
String script = "def p = " + pattern + "; p.splitAsStream(" + splitCharSequence + ").toArray(String[]::new)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
setRegexLimitFactor(1);
String script = "def p = " + PATTERN + "; p.splitAsStream(" + SPLIT_CHAR_SEQUENCE + ").toArray(String[]::new)";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_Ref_SplitAsStream() {
String script =
"Stream splitStream(Function func) { func.apply(" + splitCharSequence +"); } " +
"Pattern pattern = " + pattern + ";" +
"Stream splitStream(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"Pattern pattern = " + PATTERN + ";" +
"splitStream(pattern::splitAsStream).toArray(String[]::new)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInject_DefMethodRef_SplitAsStream() {
String script =
"Stream splitStream(Function func) { func.apply(" + splitCharSequence +"); } " +
"def pattern = " + pattern + ";" +
"Stream splitStream(Function func) { func.apply(" + SPLIT_CHAR_SEQUENCE +"); } " +
"def pattern = " + PATTERN + ";" +
"splitStream(pattern::splitAsStream).toArray(String[]::new)";
setRegexLimitFactor(2);
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
setRegexLimitFactor(1);
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInjectFindOperator() {
String script = "if (" + charSequence + " =~ " + pattern + ") { return 100; } return 200";
setRegexLimitFactor(2);
assertEquals(Integer.valueOf(100), (Integer) exec(script));
setRegexLimitFactor(1);
String script = "if (" + CHAR_SEQUENCE + " =~ " + PATTERN + ") { return 100; } return 200";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testRegexInjectMatchOperator() {
String script = "if (" + charSequence + " ==~ " + pattern + ") { return 100; } return 200";
setRegexLimitFactor(2);
assertEquals(Integer.valueOf(100), (Integer) exec(script));
setRegexLimitFactor(1);
String script = "if (" + CHAR_SEQUENCE + " ==~ " + PATTERN + ") { return 100; } return 200";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
}
public void testSnippetRegex() {
String charSequence = String.join("", Collections.nCopies(100, "abcdef123456"));
String script = "if ('" + charSequence + "' ==~ " + pattern + ") { return 100; } return 200";
setRegexLimitFactor(1);
String script = "if ('" + charSequence + "' ==~ " + PATTERN + ") { return 100; } return 200";
CircuitBreakingException cbe = expectScriptThrows(CircuitBreakingException.class, () -> exec(script));
assertTrue(cbe.getMessage().contains(regexCircuitMessage));
assertTrue(cbe.getMessage().contains(REGEX_CIRCUIT_MESSAGE));
assertTrue(cbe.getMessage().contains(charSequence.subSequence(0, 61) + "..."));
}
private void setRegexLimitFactor(int factor) {
Settings settings = Settings.builder().put(CompilerSettings.REGEX_LIMIT_FACTOR.getKey(), factor).build();
scriptEngine = new PainlessScriptEngine(settings, scriptContexts());
}
private void setRegexEnabled() {
Settings settings = Settings.builder().put(CompilerSettings.REGEX_ENABLED.getKey(), "true").build();
scriptEngine = new PainlessScriptEngine(settings, scriptContexts());
}
}

View File

@ -32,6 +32,8 @@
package org.opensearch.painless;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.script.ScriptException;
@ -43,13 +45,24 @@ import java.util.regex.Pattern;
import static java.util.Collections.singletonMap;
public class RegexTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
@Override
protected Settings scriptEngineSettings() {
// Enable regexes just for this test. They are disabled by default.
return Settings.builder()
@BeforeClass
public static void beforeClass() {
Settings settings = Settings.builder()
.put(CompilerSettings.REGEX_ENABLED.getKey(), true)
.build();
SCRIPT_ENGINE = new PainlessScriptEngine(settings, newDefaultContexts());
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testPatternAfterReturn() {
@ -294,4 +307,41 @@ public class RegexTests extends ScriptTestCase {
});
assertEquals("unexpected token ['b'] was expecting one of [{<EOF>, ';'}].", e.getMessage());
}
// This regex has backtracking due to .*?
private final String pattern = "/abc.*?def/";
private final String charSequence = "'abcdodef'";
private final String splitCharSequence = "'0-abc-1-def-X-abc-2-def-Y-abc-3-def-Z-abc'";
public void testRegexInjectUnlimited_Matcher() {
String[] scripts = new String[]{pattern + ".matcher(" + charSequence + ").matches()",
"Matcher m = " + pattern + ".matcher(" + charSequence + "); m.matches()"};
for (String script : scripts) {
assertEquals(Boolean.TRUE, exec(script));
}
}
public void testRegexInjectUnlimited_SplitLimit() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ", 2)",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ", 2)"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-abc-2-def-Y-abc-3-def-Z-abc"}, (String[])exec(script));
}
}
public void testRegexInjectUnlimited_Split() {
String[] scripts = new String[]{pattern + ".split(" + splitCharSequence + ")",
"Pattern p = " + pattern + "; p.split(" + splitCharSequence + ")"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[])exec(script));
}
}
public void testRegexInjectUnlimited_SplitAsStream() {
String[] scripts = new String[]{pattern + ".splitAsStream(" + splitCharSequence + ").toArray(String[]::new)",
"Pattern p = " + pattern + "; p.splitAsStream(" + splitCharSequence + ").toArray(String[]::new)"};
for (String script : scripts) {
assertArrayEquals(new String[]{"0-", "-X-", "-Y-", "-Z-abc"}, (String[]) exec(script));
}
}
}

View File

@ -33,6 +33,7 @@
package org.opensearch.painless;
import junit.framework.AssertionFailedError;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.antlr.Walker;
import org.opensearch.painless.spi.Whitelist;
@ -40,7 +41,6 @@ import org.opensearch.painless.spi.WhitelistLoader;
import org.opensearch.script.ScriptContext;
import org.opensearch.script.ScriptException;
import org.opensearch.test.OpenSearchTestCase;
import org.junit.Before;
import java.util.ArrayList;
import java.util.Collections;
@ -48,8 +48,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.opensearch.painless.action.PainlessExecuteAction.PainlessTestScript;
import static org.hamcrest.Matchers.hasSize;
import static org.opensearch.painless.action.PainlessExecuteAction.PainlessTestScript;
/**
* Base test case for scripting unit tests.
@ -57,24 +57,10 @@ import static org.hamcrest.Matchers.hasSize;
* Typically just asserts the output of {@code exec()}
*/
public abstract class ScriptTestCase extends OpenSearchTestCase {
protected PainlessScriptEngine scriptEngine;
private static final PainlessScriptEngine SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, newDefaultContexts());
@Before
public void setup() {
scriptEngine = new PainlessScriptEngine(scriptEngineSettings(), scriptContexts());
}
/**
* Settings used to build the script engine. Override to customize settings like {@link RegexTests} does to enable regexes.
*/
protected Settings scriptEngineSettings() {
return Settings.EMPTY;
}
/**
* Script contexts used to build the script engine. Override to customize which script contexts are available.
*/
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
/** Creates a new contexts map with PainlessTextScript = org.opensearch.painless.test */
protected static Map<ScriptContext<?>, List<Whitelist>> newDefaultContexts() {
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
List<Whitelist> whitelists = new ArrayList<>(Whitelist.BASE_WHITELISTS);
whitelists.add(WhitelistLoader.loadFromResourceFiles(Whitelist.class, "org.opensearch.painless.test"));
@ -82,43 +68,20 @@ public abstract class ScriptTestCase extends OpenSearchTestCase {
return contexts;
}
/** Compiles and returns the result of {@code script} */
public Object exec(String script) {
return exec(script, null, true);
}
/** Compiles and returns the result of {@code script} with access to {@code picky} */
public Object exec(String script, boolean picky) {
return exec(script, null, picky);
}
/** Compiles and returns the result of {@code script} with access to {@code vars} */
public Object exec(String script, Map<String, Object> vars, boolean picky) {
Map<String,String> compilerSettings = new HashMap<>();
compilerSettings.put(CompilerSettings.INITIAL_CALL_SITE_DEPTH, random().nextBoolean() ? "0" : "10");
return exec(script, vars, compilerSettings, picky);
}
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, boolean picky) {
// test for ambiguity errors before running the actual script if picky is true
if (picky) {
CompilerSettings pickySettings = new CompilerSettings();
pickySettings.setPicky(true);
pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings()));
Walker.buildPainlessTree(getTestName(), script, pickySettings);
}
// test actual script execution
PainlessTestScript.Factory factory = scriptEngine.compile(null, script, PainlessTestScript.CONTEXT, compileParams);
PainlessTestScript testScript = factory.newInstance(vars == null ? Collections.emptyMap() : vars);
return testScript.execute();
/**
* Get the script engine to use.
* If you override this method in order to customize the settings, it is recommended
* to setup/teardown a class-level fixture and return it directly for performance.
*/
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
/**
* Uses the {@link Debugger} to get the bytecode output for a script and compare
* it against an expected bytecode passed in as a String.
*/
public void assertBytecodeExists(String script, String bytecode) {
public static final void assertBytecodeExists(String script, String bytecode) {
final String asm = Debugger.toString(script);
assertTrue("bytecode not found, got: \n" + asm , asm.contains(bytecode));
}
@ -127,18 +90,18 @@ public abstract class ScriptTestCase extends OpenSearchTestCase {
* Uses the {@link Debugger} to get the bytecode output for a script and compare
* it against an expected bytecode pattern as a regular expression (please try to avoid!)
*/
public void assertBytecodeHasPattern(String script, String pattern) {
public static final void assertBytecodeHasPattern(String script, String pattern) {
final String asm = Debugger.toString(script);
assertTrue("bytecode not found, got: \n" + asm , asm.matches(pattern));
}
/** Checks a specific exception class is thrown (boxed inside ScriptException) and returns it. */
public static <T extends Throwable> T expectScriptThrows(Class<T> expectedType, ThrowingRunnable runnable) {
public static final <T extends Throwable> T expectScriptThrows(Class<T> expectedType, ThrowingRunnable runnable) {
return expectScriptThrows(expectedType, true, runnable);
}
/** Checks a specific exception class is thrown (boxed inside ScriptException) and returns it. */
public static <T extends Throwable> T expectScriptThrows(Class<T> expectedType, boolean shouldHaveScriptStack,
public static final <T extends Throwable> T expectScriptThrows(Class<T> expectedType, boolean shouldHaveScriptStack,
ThrowingRunnable runnable) {
try {
runnable.run();
@ -178,7 +141,7 @@ public abstract class ScriptTestCase extends OpenSearchTestCase {
/**
* Asserts that the script_stack looks right.
*/
public static void assertScriptStack(ScriptException e, String... stack) {
public static final void assertScriptStack(ScriptException e, String... stack) {
// This particular incantation of assertions makes the error messages more useful
try {
assertThat(e.getScriptStack(), hasSize(stack.length));
@ -191,4 +154,35 @@ public abstract class ScriptTestCase extends OpenSearchTestCase {
}
}
/** Compiles and returns the result of {@code script} */
public final Object exec(String script) {
return exec(script, null, true);
}
/** Compiles and returns the result of {@code script} with access to {@code picky} */
public final Object exec(String script, boolean picky) {
return exec(script, null, picky);
}
/** Compiles and returns the result of {@code script} with access to {@code vars} */
public final Object exec(String script, Map<String, Object> vars, boolean picky) {
Map<String,String> compilerSettings = new HashMap<>();
compilerSettings.put(CompilerSettings.INITIAL_CALL_SITE_DEPTH, random().nextBoolean() ? "0" : "10");
return exec(script, vars, compilerSettings, picky);
}
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
public final Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, boolean picky) {
// test for ambiguity errors before running the actual script if picky is true
if (picky) {
CompilerSettings pickySettings = new CompilerSettings();
pickySettings.setPicky(true);
pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(Settings.EMPTY));
Walker.buildPainlessTree(getTestName(), script, pickySettings);
}
// test actual script execution
PainlessTestScript.Factory factory = getEngine().compile(null, script, PainlessTestScript.CONTEXT, compileParams);
PainlessTestScript testScript = factory.newInstance(vars == null ? Collections.emptyMap() : vars);
return testScript.execute();
}
}

View File

@ -35,6 +35,9 @@ package org.opensearch.painless;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.Scorable;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.script.ScriptContext;
import org.opensearch.script.ScriptedMetricAggContexts;
@ -53,18 +56,30 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ScriptedMetricAggContextsTests extends ScriptTestCase {
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
private static PainlessScriptEngine SCRIPT_ENGINE;
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
contexts.put(ScriptedMetricAggContexts.InitScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(ScriptedMetricAggContexts.MapScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(ScriptedMetricAggContexts.CombineScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(ScriptedMetricAggContexts.ReduceScript.CONTEXT, Whitelist.BASE_WHITELISTS);
return contexts;
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testInitBasic() {
ScriptedMetricAggContexts.InitScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.InitScript.Factory factory = getEngine().compile("test",
"state.testField = params.initialVal", ScriptedMetricAggContexts.InitScript.CONTEXT, Collections.emptyMap());
Map<String, Object> params = new HashMap<>();
@ -80,7 +95,7 @@ public class ScriptedMetricAggContextsTests extends ScriptTestCase {
}
public void testMapBasic() throws IOException {
ScriptedMetricAggContexts.MapScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.MapScript.Factory factory = getEngine().compile("test",
"state.testField = 2*_score", ScriptedMetricAggContexts.MapScript.CONTEXT, Collections.emptyMap());
Map<String, Object> params = new HashMap<>();
@ -105,7 +120,7 @@ public class ScriptedMetricAggContextsTests extends ScriptTestCase {
}
public void testReturnSource() throws IOException {
ScriptedMetricAggContexts.MapScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.MapScript.Factory factory = getEngine().compile("test",
"state._source = params._source", ScriptedMetricAggContexts.MapScript.CONTEXT, Collections.emptyMap());
Map<String, Object> params = new HashMap<>();
@ -132,7 +147,7 @@ public class ScriptedMetricAggContextsTests extends ScriptTestCase {
}
public void testMapSourceAccess() throws IOException {
ScriptedMetricAggContexts.MapScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.MapScript.Factory factory = getEngine().compile("test",
"state.testField = params._source.three", ScriptedMetricAggContexts.MapScript.CONTEXT, Collections.emptyMap());
Map<String, Object> params = new HashMap<>();
@ -158,7 +173,7 @@ public class ScriptedMetricAggContextsTests extends ScriptTestCase {
}
public void testCombineBasic() {
ScriptedMetricAggContexts.CombineScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.CombineScript.Factory factory = getEngine().compile("test",
"state.testField = params.initialVal; return state.testField + params.inc", ScriptedMetricAggContexts.CombineScript.CONTEXT,
Collections.emptyMap());
@ -177,7 +192,7 @@ public class ScriptedMetricAggContextsTests extends ScriptTestCase {
}
public void testReduceBasic() {
ScriptedMetricAggContexts.ReduceScript.Factory factory = scriptEngine.compile("test",
ScriptedMetricAggContexts.ReduceScript.Factory factory = getEngine().compile("test",
"states[0].testField + states[1].testField", ScriptedMetricAggContexts.ReduceScript.CONTEXT, Collections.emptyMap());
Map<String, Object> params = new HashMap<>();

View File

@ -49,6 +49,9 @@ import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.ByteBuffersDirectory;
import org.apache.lucene.store.Directory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.opensearch.common.settings.Settings;
import org.opensearch.index.similarity.ScriptedSimilarity;
import org.opensearch.painless.spi.Whitelist;
import org.opensearch.script.ScriptContext;
@ -62,17 +65,28 @@ import java.util.List;
import java.util.Map;
public class SimilarityScriptTests extends ScriptTestCase {
private static PainlessScriptEngine SCRIPT_ENGINE;
@Override
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
@BeforeClass
public static void beforeClass() {
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
contexts.put(SimilarityScript.CONTEXT, Whitelist.BASE_WHITELISTS);
contexts.put(SimilarityWeightScript.CONTEXT, Whitelist.BASE_WHITELISTS);
return contexts;
SCRIPT_ENGINE = new PainlessScriptEngine(Settings.EMPTY, contexts);
}
@AfterClass
public static void afterClass() {
SCRIPT_ENGINE = null;
}
@Override
protected PainlessScriptEngine getEngine() {
return SCRIPT_ENGINE;
}
public void testBasics() throws IOException {
SimilarityScript.Factory factory = scriptEngine.compile(
SimilarityScript.Factory factory = getEngine().compile(
"foobar", "return query.boost * doc.freq / doc.length", SimilarityScript.CONTEXT, Collections.emptyMap());
ScriptedSimilarity sim = new ScriptedSimilarity("foobar", null, "foobaz", factory::newInstance, true);
Directory dir = new ByteBuffersDirectory();
@ -109,9 +123,9 @@ public class SimilarityScriptTests extends ScriptTestCase {
}
public void testWeightScript() throws IOException {
SimilarityWeightScript.Factory weightFactory = scriptEngine.compile(
SimilarityWeightScript.Factory weightFactory = getEngine().compile(
"foobar", "return query.boost", SimilarityWeightScript.CONTEXT, Collections.emptyMap());
SimilarityScript.Factory factory = scriptEngine.compile(
SimilarityScript.Factory factory = getEngine().compile(
"foobar", "return weight * doc.freq / doc.length", SimilarityScript.CONTEXT, Collections.emptyMap());
ScriptedSimilarity sim = new ScriptedSimilarity("foobar", weightFactory::newInstance, "foobaz", factory::newInstance, true);
Directory dir = new ByteBuffersDirectory();