/* * 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. */ package org.elasticsearch.painless; import org.elasticsearch.common.Nullable; import org.hamcrest.Matcher; import static java.util.Collections.singletonMap; /** * Superclass for testing array-like objects (arrays and lists). */ public abstract class ArrayLikeObjectTestCase extends ScriptTestCase { /** * Build the string for declaring the variable holding the array-like-object to test. So {@code int[]} for arrays and {@code List} for * lists. */ protected abstract String declType(String valueType); /** * Build the string for calling the constructor for the array-like-object to test. So {@code new int[5]} for arrays and * {@code [0, 0, 0, 0, 0]} or {@code [null, null, null, null, null]} for lists. */ protected abstract String valueCtorCall(String valueType, int size); /** * Matcher for the message of the out of bounds exceptions thrown for too negative or too positive offsets. */ protected abstract Matcher<String> outOfBoundsExceptionMessageMatcher(int index, int size); private void arrayLoadStoreTestCase(boolean declareAsDef, String valueType, Object val, @Nullable Number valPlusOne) { String declType = declareAsDef ? "def" : declType(valueType); String valueCtorCall = valueCtorCall(valueType, 5); String decl = declType + " x = " + valueCtorCall; assertEquals(5, exec(decl + "; return x.length", true)); assertEquals(val, exec(decl + "; x[ 0] = params.val; return x[ 0];", singletonMap("val", val), true)); assertEquals(val, exec(decl + "; x[ 0] = params.val; return x[-5];", singletonMap("val", val), true)); assertEquals(val, exec(decl + "; x[-5] = params.val; return x[-5];", singletonMap("val", val), true)); expectOutOfBounds( 6, decl + "; return x[ 6]", val); expectOutOfBounds(-1, decl + "; return x[-6]", val); expectOutOfBounds( 6, decl + "; x[ 6] = params.val; return 0", val); expectOutOfBounds(-1, decl + "; x[-6] = params.val; return 0", val); if (valPlusOne != null) { assertEquals(val, exec(decl + "; x[0] = params.val; x[ 0] = x[ 0]++; return x[0];", singletonMap("val", val), true)); assertEquals(val, exec(decl + "; x[0] = params.val; x[ 0] = x[-5]++; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[ 0] = ++x[ 0]; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[ 0] = ++x[-5]; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[ 0]++ ; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[-5]++ ; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[ 0] += 1 ; return x[0];", singletonMap("val", val), true)); assertEquals(valPlusOne, exec(decl + "; x[0] = params.val; x[-5] += 1 ; return x[0];", singletonMap("val", val), true)); expectOutOfBounds( 6, decl + "; return x[ 6]++", val); expectOutOfBounds(-1, decl + "; return x[-6]++", val); expectOutOfBounds( 6, decl + "; return ++x[ 6]", val); expectOutOfBounds(-1, decl + "; return ++x[-6]", val); expectOutOfBounds( 6, decl + "; x[ 6] += 1; return 0", val); expectOutOfBounds(-1, decl + "; x[-6] += 1; return 0", val); } } private void expectOutOfBounds(int index, String script, Object val) { IndexOutOfBoundsException e = expectScriptThrows(IndexOutOfBoundsException.class, () -> exec(script, singletonMap("val", val), true)); try { /* If this fails you *might* be missing -XX:-OmitStackTraceInFastThrow in the test jvm * In Eclipse you can add this by default by going to Preference->Java->Installed JREs, * clicking on the default JRE, clicking edit, and adding the flag to the * "Default VM Arguments". */ assertThat(e.getMessage(), outOfBoundsExceptionMessageMatcher(index, 5)); } catch (AssertionError ae) { ae.addSuppressed(e); // Mark the exception we are testing as suppressed so we get its stack trace. throw ae; } } public void testInts() { arrayLoadStoreTestCase(false, "int", 5, 6); } public void testIntsInDef() { arrayLoadStoreTestCase(true, "int", 5, 6); } public void testLongs() { arrayLoadStoreTestCase(false, "long", 5L, 6L); } public void testLongsInDef() { arrayLoadStoreTestCase(true, "long", 5L, 6L); } public void testShorts() { arrayLoadStoreTestCase(false, "short", (short) 5, (short) 6); } public void testShortsInDef() { arrayLoadStoreTestCase(true, "short", (short) 5, (short) 6); } public void testBytes() { arrayLoadStoreTestCase(false, "byte", (byte) 5, (byte) 6); } public void testBytesInDef() { arrayLoadStoreTestCase(true, "byte", (byte) 5, (byte) 6); } public void testFloats() { arrayLoadStoreTestCase(false, "float", 5.0f, 6.0f); } public void testFloatsInDef() { arrayLoadStoreTestCase(true, "float", 5.0f, 6.0f); } public void testDoubles() { arrayLoadStoreTestCase(false, "double", 5.0d, 6.0d); } public void testDoublesInDef() { arrayLoadStoreTestCase(true, "double", 5.0d, 6.0d); } public void testStrings() { arrayLoadStoreTestCase(false, "String", "cat", null); } public void testStringsInDef() { arrayLoadStoreTestCase(true, "String", "cat", null); } public void testDef() { arrayLoadStoreTestCase(true, "def", 5, null); } }