diff --git a/common/src/main/java/dev/cel/common/values/BUILD.bazel b/common/src/main/java/dev/cel/common/values/BUILD.bazel
index 53ffdda3d..0d1d5431f 100644
--- a/common/src/main/java/dev/cel/common/values/BUILD.bazel
+++ b/common/src/main/java/dev/cel/common/values/BUILD.bazel
@@ -118,6 +118,38 @@ java_library(
],
)
+java_library(
+ name = "mutable_map_value",
+ srcs = ["MutableMapValue.java"],
+ tags = [
+ ],
+ deps = [
+ "//common/annotations",
+ "//common/exceptions:attribute_not_found",
+ "//common/types",
+ "//common/types:type_providers",
+ "//common/values",
+ "//common/values:cel_value",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ ],
+)
+
+cel_android_library(
+ name = "mutable_map_value_android",
+ srcs = ["MutableMapValue.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel_value_android",
+ "//common/annotations",
+ "//common/exceptions:attribute_not_found",
+ "//common/types:type_providers_android",
+ "//common/types:types_android",
+ "//common/values:values_android",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ ],
+)
+
cel_android_library(
name = "values_android",
srcs = CEL_VALUES_SOURCES,
diff --git a/common/src/main/java/dev/cel/common/values/MutableMapValue.java b/common/src/main/java/dev/cel/common/values/MutableMapValue.java
new file mode 100644
index 000000000..706436b2e
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/values/MutableMapValue.java
@@ -0,0 +1,146 @@
+// Copyright 2026 Google LLC
+//
+// Licensed 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
+//
+// https://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 dev.cel.common.values;
+
+import com.google.errorprone.annotations.Immutable;
+import dev.cel.common.annotations.Internal;
+import dev.cel.common.exceptions.CelAttributeNotFoundException;
+import dev.cel.common.types.CelType;
+import dev.cel.common.types.MapType;
+import dev.cel.common.types.SimpleType;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A custom CelValue implementation that allows O(1) insertions for maps during comprehension.
+ *
+ *
CEL Library Internals. Do Not Use.
+ */
+@Internal
+@Immutable
+@SuppressWarnings("Immutable") // Intentionally mutable for performance reasons
+public final class MutableMapValue extends CelValue
+ implements SelectableValue, Map {
+ private final Map internalMap;
+ private final CelType celType;
+
+ public static MutableMapValue create(Map, ?> map) {
+ return new MutableMapValue(map);
+ }
+
+ @Override
+ public int size() {
+ return internalMap.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return internalMap.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return internalMap.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return internalMap.containsValue(value);
+ }
+
+ @Override
+ public Object get(Object key) {
+ return internalMap.get(key);
+ }
+
+ @Override
+ public Object put(Object key, Object value) {
+ return internalMap.put(key, value);
+ }
+
+ @Override
+ public Object remove(Object key) {
+ return internalMap.remove(key);
+ }
+
+ @Override
+ public void putAll(Map, ?> m) {
+ internalMap.putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ internalMap.clear();
+ }
+
+ @Override
+ public Set keySet() {
+ return internalMap.keySet();
+ }
+
+ @Override
+ public Collection values() {
+ return internalMap.values();
+ }
+
+ @Override
+ public Set> entrySet() {
+ return internalMap.entrySet();
+ }
+
+ @Override
+ public Object select(Object field) {
+ Object val = internalMap.get(field);
+ if (val != null) {
+ return val;
+ }
+ if (!internalMap.containsKey(field)) {
+ throw CelAttributeNotFoundException.forMissingMapKey(field.toString());
+ }
+ throw CelAttributeNotFoundException.of(
+ String.format("Map value cannot be null for key: %s", field));
+ }
+
+ @Override
+ public Optional> find(Object field) {
+ if (internalMap.containsKey(field)) {
+ return Optional.ofNullable(internalMap.get(field));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Object value() {
+ return this;
+ }
+
+ @Override
+ public boolean isZeroValue() {
+ return internalMap.isEmpty();
+ }
+
+ @Override
+ public CelType celType() {
+ return celType;
+ }
+
+ private MutableMapValue(Map, ?> map) {
+ this.internalMap = new LinkedHashMap<>(map);
+ this.celType = MapType.create(SimpleType.DYN, SimpleType.DYN);
+ }
+}
diff --git a/common/values/BUILD.bazel b/common/values/BUILD.bazel
index f1fa107b6..74bfa9e0f 100644
--- a/common/values/BUILD.bazel
+++ b/common/values/BUILD.bazel
@@ -47,6 +47,18 @@ cel_android_library(
exports = ["//common/src/main/java/dev/cel/common/values:values_android"],
)
+java_library(
+ name = "mutable_map_value",
+ visibility = ["//:internal"],
+ exports = ["//common/src/main/java/dev/cel/common/values:mutable_map_value"],
+)
+
+cel_android_library(
+ name = "mutable_map_value_android",
+ visibility = ["//:internal"],
+ exports = ["//common/src/main/java/dev/cel/common/values:mutable_map_value_android"],
+)
+
java_library(
name = "base_proto_cel_value_converter",
exports = ["//common/src/main/java/dev/cel/common/values:base_proto_cel_value_converter"],
diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel
index 77663f2fa..2eb26846f 100644
--- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel
+++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel
@@ -307,6 +307,7 @@ java_library(
"//common:options",
"//common/ast",
"//common/types",
+ "//common/values:mutable_map_value",
"//compiler:compiler_builder",
"//extensions:extension_library",
"//parser:macro",
diff --git a/extensions/src/main/java/dev/cel/extensions/CelComprehensionsExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelComprehensionsExtensions.java
index 7c298a773..3bf47c4a6 100644
--- a/extensions/src/main/java/dev/cel/extensions/CelComprehensionsExtensions.java
+++ b/extensions/src/main/java/dev/cel/extensions/CelComprehensionsExtensions.java
@@ -29,6 +29,7 @@
import dev.cel.common.ast.CelExpr;
import dev.cel.common.types.MapType;
import dev.cel.common.types.TypeParamType;
+import dev.cel.common.values.MutableMapValue;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.parser.CelMacro;
import dev.cel.parser.CelMacroExprFactory;
@@ -171,38 +172,46 @@ public void setParserOptions(CelParserBuilder parserBuilder) {
parserBuilder.addMacros(macros());
}
- // TODO: Implement a more efficient map insertion based on mutability once mutable
- // maps are supported in Java stack.
- private static ImmutableMap mapInsertMap(
+ private static Map mapInsertMap(
Map, ?> targetMap, Map, ?> mapToMerge, RuntimeEquality equality) {
- ImmutableMap.Builder resultBuilder =
- ImmutableMap.builderWithExpectedSize(targetMap.size() + mapToMerge.size());
-
- for (Map.Entry, ?> entry : mapToMerge.entrySet()) {
- if (equality.findInMap(targetMap, entry.getKey()).isPresent()) {
+ for (Object key : mapToMerge.keySet()) {
+ if (equality.findInMap(targetMap, key).isPresent()) {
throw new IllegalArgumentException(
- String.format("insert failed: key '%s' already exists", entry.getKey()));
- } else {
- resultBuilder.put(entry.getKey(), entry.getValue());
+ String.format("insert failed: key '%s' already exists", key));
}
}
- return resultBuilder.putAll(targetMap).buildOrThrow();
+
+ if (targetMap instanceof MutableMapValue) {
+ MutableMapValue wrapper = (MutableMapValue) targetMap;
+ wrapper.putAll(mapToMerge);
+ return wrapper;
+ }
+
+ return ImmutableMap.builderWithExpectedSize(targetMap.size() + mapToMerge.size())
+ .putAll(targetMap)
+ .putAll(mapToMerge)
+ .buildOrThrow();
}
- private static ImmutableMap mapInsertKeyValue(
- Object[] args, RuntimeEquality equality) {
- Map, ?> map = (Map, ?>) args[0];
+ private static Map mapInsertKeyValue(Object[] args, RuntimeEquality equality) {
+ Map, ?> mapArg = (Map, ?>) args[0];
Object key = args[1];
Object value = args[2];
- if (equality.findInMap(map, key).isPresent()) {
+ if (equality.findInMap(mapArg, key).isPresent()) {
throw new IllegalArgumentException(
String.format("insert failed: key '%s' already exists", key));
}
+ if (mapArg instanceof MutableMapValue) {
+ MutableMapValue mutableMap = (MutableMapValue) mapArg;
+ mutableMap.put(key, value);
+ return mutableMap;
+ }
+
ImmutableMap.Builder builder =
- ImmutableMap.builderWithExpectedSize(map.size() + 1);
- return builder.put(key, value).putAll(map).buildOrThrow();
+ ImmutableMap.builderWithExpectedSize(mapArg.size() + 1);
+ return builder.put(key, value).putAll(mapArg).buildOrThrow();
}
private static Optional expandAllMacro(
diff --git a/extensions/src/main/java/dev/cel/extensions/CelEncoderExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelEncoderExtensions.java
index a98f9db41..498b8555e 100644
--- a/extensions/src/main/java/dev/cel/extensions/CelEncoderExtensions.java
+++ b/extensions/src/main/java/dev/cel/extensions/CelEncoderExtensions.java
@@ -135,9 +135,13 @@ public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
functions.forEach(
function -> {
if (celOptions.evaluateCanonicalTypesToNativeValues()) {
- runtimeBuilder.addFunctionBindings(function.nativeBytesFunctionBinding);
+ runtimeBuilder.addFunctionBindings(
+ CelFunctionBinding.fromOverloads(
+ function.getFunction(), function.nativeBytesFunctionBinding));
} else {
- runtimeBuilder.addFunctionBindings(function.protoBytesFunctionBinding);
+ runtimeBuilder.addFunctionBindings(
+ CelFunctionBinding.fromOverloads(
+ function.getFunction(), function.protoBytesFunctionBinding));
}
});
}
diff --git a/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java
index 37c8270cc..2bb477b82 100644
--- a/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java
+++ b/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java
@@ -23,7 +23,6 @@
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
import com.google.errorprone.annotations.Immutable;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.common.CelFunctionDecl;
@@ -37,7 +36,6 @@
import dev.cel.runtime.CelFunctionBinding;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -475,7 +473,7 @@ private static String quote(String s) {
sb.append('"');
for (int i = 0; i < s.length(); ) {
int codePoint = s.codePointAt(i);
- if (isMalformedUtf16(s, i, codePoint)) {
+ if (isMalformedUtf16(s, i)) {
sb.append('\uFFFD');
i++;
continue;
@@ -518,7 +516,7 @@ private static String quote(String s) {
return sb.toString();
}
- private static boolean isMalformedUtf16(String s, int index, int codePoint) {
+ private static boolean isMalformedUtf16(String s, int index) {
char currentChar = s.charAt(index);
if (Character.isLowSurrogate(currentChar)) {
return true;
@@ -587,14 +585,14 @@ private static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
- private static List split(String str, String separator) {
+ private static ImmutableList split(String str, String separator) {
return split(str, separator, Integer.MAX_VALUE);
}
/**
* @param args Object array with indices of: [0: string], [1: separator], [2: limit]
*/
- private static List split(Object[] args) throws CelEvaluationException {
+ private static ImmutableList split(Object[] args) throws CelEvaluationException {
long limitInLong = (Long) args[2];
int limit;
try {
@@ -609,16 +607,14 @@ private static List split(Object[] args) throws CelEvaluationException {
return split((String) args[0], (String) args[1], limit);
}
- /** Returns a **mutable** list of strings split on the separator */
- private static List split(String str, String separator, int limit) {
+ /** Returns an immutable list of strings split on the separator */
+ private static ImmutableList split(String str, String separator, int limit) {
if (limit == 0) {
- return new ArrayList<>();
+ return ImmutableList.of();
}
if (limit == 1) {
- List singleElementList = new ArrayList<>();
- singleElementList.add(str);
- return singleElementList;
+ return ImmutableList.of(str);
}
if (limit < 0) {
@@ -630,7 +626,7 @@ private static List split(String str, String separator, int limit) {
}
Iterable splitString = Splitter.on(separator).limit(limit).split(str);
- return Lists.newArrayList(splitString);
+ return ImmutableList.copyOf(splitString);
}
/**
@@ -643,8 +639,8 @@ private static List split(String str, String separator, int limit) {
* This exists because neither the built-in String.split nor Guava's splitter is able to deal
* with separating single printable characters.
*/
- private static List explode(String str, int limit) {
- List exploded = new ArrayList<>();
+ private static ImmutableList explode(String str, int limit) {
+ ImmutableList.Builder exploded = ImmutableList.builder();
CelCodePointArray codePointArray = CelCodePointArray.fromString(str);
if (limit > 0) {
limit -= 1;
@@ -656,7 +652,7 @@ private static List explode(String str, int limit) {
if (codePointArray.length() > limit) {
exploded.add(codePointArray.slice(limit, codePointArray.length()).toString());
}
- return exploded;
+ return exploded.build();
}
private static Object substring(String s, long i) throws CelEvaluationException {
diff --git a/extensions/src/main/java/dev/cel/extensions/README.md b/extensions/src/main/java/dev/cel/extensions/README.md
index c3fbf8c54..b1d3611b4 100644
--- a/extensions/src/main/java/dev/cel/extensions/README.md
+++ b/extensions/src/main/java/dev/cel/extensions/README.md
@@ -522,7 +522,7 @@ Examples:
### Split
-Returns a mutable list of strings split from the input by the given separator. The
+Returns a list of strings split from the input by the given separator. The
function accepts an optional argument specifying a limit on the number of
substrings produced by the split.
@@ -1069,4 +1069,4 @@ Examples:
{valueVar: indexVar}) // returns {1:0, 2:1, 3:2}
{'greeting': 'aloha', 'farewell': 'aloha'}
- .transformMapEntry(k, v, {v: k}) // error, duplicate key
\ No newline at end of file
+ .transformMapEntry(k, v, {v: k}) // error, duplicate key
diff --git a/extensions/src/main/java/dev/cel/extensions/SetsExtensionsRuntimeImpl.java b/extensions/src/main/java/dev/cel/extensions/SetsExtensionsRuntimeImpl.java
index a42fba189..a02fdba8a 100644
--- a/extensions/src/main/java/dev/cel/extensions/SetsExtensionsRuntimeImpl.java
+++ b/extensions/src/main/java/dev/cel/extensions/SetsExtensionsRuntimeImpl.java
@@ -45,28 +45,34 @@ ImmutableSet newFunctionBindings() {
for (SetsFunction function : functions) {
switch (function) {
case CONTAINS:
- bindingBuilder.add(
- CelFunctionBinding.from(
- "list_sets_contains_list",
- Collection.class,
- Collection.class,
- this::containsAll));
+ bindingBuilder.addAll(
+ CelFunctionBinding.fromOverloads(
+ function.getFunction(),
+ CelFunctionBinding.from(
+ "list_sets_contains_list",
+ Collection.class,
+ Collection.class,
+ this::containsAll)));
break;
case EQUIVALENT:
- bindingBuilder.add(
- CelFunctionBinding.from(
- "list_sets_equivalent_list",
- Collection.class,
- Collection.class,
- (listA, listB) -> containsAll(listA, listB) && containsAll(listB, listA)));
+ bindingBuilder.addAll(
+ CelFunctionBinding.fromOverloads(
+ function.getFunction(),
+ CelFunctionBinding.from(
+ "list_sets_equivalent_list",
+ Collection.class,
+ Collection.class,
+ (listA, listB) -> containsAll(listA, listB) && containsAll(listB, listA))));
break;
case INTERSECTS:
- bindingBuilder.add(
- CelFunctionBinding.from(
- "list_sets_intersects_list",
- Collection.class,
- Collection.class,
- this::setIntersects));
+ bindingBuilder.addAll(
+ CelFunctionBinding.fromOverloads(
+ function.getFunction(),
+ CelFunctionBinding.from(
+ "list_sets_intersects_list",
+ Collection.class,
+ Collection.class,
+ this::setIntersects)));
break;
}
}
diff --git a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel
index 0b6502410..19fd3657e 100644
--- a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel
+++ b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel
@@ -15,6 +15,7 @@ java_library(
"//common:compiler_common",
"//common:container",
"//common:options",
+ "//common/exceptions:attribute_not_found",
"//common/exceptions:divide_by_zero",
"//common/exceptions:index_out_of_bounds",
"//common/types",
diff --git a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java
index fbe160cd3..6927841e5 100644
--- a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java
+++ b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java
@@ -28,6 +28,8 @@
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
+import dev.cel.common.CelValidationResult;
+import dev.cel.common.exceptions.CelAttributeNotFoundException;
import dev.cel.common.exceptions.CelDivideByZeroException;
import dev.cel.common.exceptions.CelIndexOutOfBoundsException;
import dev.cel.common.types.SimpleType;
@@ -222,6 +224,7 @@ public void transformMapEntryMacro_twoVarComprehension_success(
+ " 'key2': 'value2'}",
// map.transformMapEntry()
"{'hello': 'world', 'greetings': 'tacocat'}.transformMapEntry(k, v, {}) == {}",
+ "{'a': 1, 'b': 2}.transformMapEntry(k, v, {k: v}) == {'a': 1, 'b': 2}",
"{'a': 1, 'b': 2}.transformMapEntry(k, v, {k + '_new': v * 2}) == {'a_new': 2,"
+ " 'b_new': 4}",
"{'a': 1, 'b': 2, 'c': 3}.transformMapEntry(k, v, v % 2 == 1, {k: v * 10}) == {'a': 10,"
@@ -310,8 +313,9 @@ public void unparseAST_twoVarComprehension(
+ " err: 'no matching overload'}")
public void twoVarComprehension_compilerErrors(String expr, String err) throws Exception {
Assume.assumeFalse(isParseOnly);
+ CelValidationResult result = cel.compile(expr);
CelValidationException e =
- assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst());
+ assertThrows(CelValidationException.class, () -> result.getAst());
assertThat(e).hasMessageThat().contains(err);
}
@@ -364,6 +368,15 @@ public void twoVarComprehension_outOfBounds_runtimeError() throws Exception {
assertThat(e).hasCauseThat().hasMessageThat().contains("Index out of bounds: 1");
}
+ @Test
+ public void mutableMapValue_select_missingKeyException() throws Exception {
+ CelEvaluationException e =
+ assertThrows(
+ CelEvaluationException.class, () -> eval("cel.bind(my_map, {'a': 1}, my_map.b)"));
+ assertThat(e).hasCauseThat().isInstanceOf(CelAttributeNotFoundException.class);
+ assertThat(e).hasCauseThat().hasMessageThat().contains("key 'b' is not present in map.");
+ }
+
private Object eval(String expression) throws Exception {
return eval(this.cel, expression, ImmutableMap.of());
}
diff --git a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java
index 7eed3dd5a..b0a501ddb 100644
--- a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java
+++ b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java
@@ -19,36 +19,45 @@
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableMap;
+import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+import dev.cel.bundle.Cel;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.types.SimpleType;
import dev.cel.common.values.CelByteString;
-import dev.cel.compiler.CelCompiler;
-import dev.cel.compiler.CelCompilerFactory;
import dev.cel.runtime.CelEvaluationException;
-import dev.cel.runtime.CelRuntime;
-import dev.cel.runtime.CelRuntimeFactory;
+import dev.cel.testing.CelRuntimeFlavor;
+import org.junit.Assume;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(TestParameterInjector.class)
public class CelEncoderExtensionsTest {
private static final CelOptions CEL_OPTIONS =
- CelOptions.current().build();
-
- private static final CelCompiler CEL_COMPILER =
- CelCompilerFactory.standardCelCompilerBuilder()
- .addVar("stringVar", SimpleType.STRING)
- .addLibraries(CelExtensions.encoders(CEL_OPTIONS))
- .build();
- private static final CelRuntime CEL_RUNTIME =
- CelRuntimeFactory.standardCelRuntimeBuilder()
- .setOptions(CEL_OPTIONS)
- .addLibraries(CelExtensions.encoders(CEL_OPTIONS))
- .build();
+ CelOptions.current().enableHeterogeneousNumericComparisons(true).build();
+
+ @TestParameter public CelRuntimeFlavor runtimeFlavor;
+ @TestParameter public boolean isParseOnly;
+
+ private Cel cel;
+
+ @Before
+ public void setUp() {
+ // Legacy runtime does not support parsed-only evaluation mode.
+ Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly);
+ this.cel =
+ runtimeFlavor
+ .builder()
+ .setOptions(CEL_OPTIONS)
+ .addCompilerLibraries(CelExtensions.encoders(CEL_OPTIONS))
+ .addRuntimeLibraries(CelExtensions.encoders(CEL_OPTIONS))
+ .addVar("stringVar", SimpleType.STRING)
+ .build();
+ }
@Test
public void library() {
@@ -63,22 +72,14 @@ public void library() {
@Test
public void encode_success() throws Exception {
- String encodedBytes =
- (String)
- CEL_RUNTIME
- .createProgram(CEL_COMPILER.compile("base64.encode(b'hello')").getAst())
- .eval();
+ String encodedBytes = (String) eval("base64.encode(b'hello')");
assertThat(encodedBytes).isEqualTo("aGVsbG8=");
}
@Test
public void decode_success() throws Exception {
- CelByteString decodedBytes =
- (CelByteString)
- CEL_RUNTIME
- .createProgram(CEL_COMPILER.compile("base64.decode('aGVsbG8=')").getAst())
- .eval();
+ CelByteString decodedBytes = (CelByteString) eval("base64.decode('aGVsbG8=')");
assertThat(decodedBytes.size()).isEqualTo(5);
assertThat(new String(decodedBytes.toByteArray(), ISO_8859_1)).isEqualTo("hello");
@@ -86,12 +87,7 @@ public void decode_success() throws Exception {
@Test
public void decode_withoutPadding_success() throws Exception {
- CelByteString decodedBytes =
- (CelByteString)
- CEL_RUNTIME
- // RFC2045 6.8, padding can be ignored.
- .createProgram(CEL_COMPILER.compile("base64.decode('aGVsbG8')").getAst())
- .eval();
+ CelByteString decodedBytes = (CelByteString) eval("base64.decode('aGVsbG8')");
assertThat(decodedBytes.size()).isEqualTo(5);
assertThat(new String(decodedBytes.toByteArray(), ISO_8859_1)).isEqualTo("hello");
@@ -99,50 +95,49 @@ public void decode_withoutPadding_success() throws Exception {
@Test
public void roundTrip_success() throws Exception {
- String encodedString =
- (String)
- CEL_RUNTIME
- .createProgram(CEL_COMPILER.compile("base64.encode(b'Hello World!')").getAst())
- .eval();
+ String encodedString = (String) eval("base64.encode(b'Hello World!')");
CelByteString decodedBytes =
(CelByteString)
- CEL_RUNTIME
- .createProgram(CEL_COMPILER.compile("base64.decode(stringVar)").getAst())
- .eval(ImmutableMap.of("stringVar", encodedString));
+ eval("base64.decode(stringVar)", ImmutableMap.of("stringVar", encodedString));
assertThat(new String(decodedBytes.toByteArray(), ISO_8859_1)).isEqualTo("Hello World!");
}
@Test
public void encode_invalidParam_throwsCompilationException() {
+ Assume.assumeFalse(isParseOnly);
CelValidationException e =
assertThrows(
- CelValidationException.class,
- () -> CEL_COMPILER.compile("base64.encode('hello')").getAst());
+ CelValidationException.class, () -> cel.compile("base64.encode('hello')").getAst());
assertThat(e).hasMessageThat().contains("found no matching overload for 'base64.encode'");
}
@Test
public void decode_invalidParam_throwsCompilationException() {
+ Assume.assumeFalse(isParseOnly);
CelValidationException e =
assertThrows(
- CelValidationException.class,
- () -> CEL_COMPILER.compile("base64.decode(b'aGVsbG8=')").getAst());
+ CelValidationException.class, () -> cel.compile("base64.decode(b'aGVsbG8=')").getAst());
assertThat(e).hasMessageThat().contains("found no matching overload for 'base64.decode'");
}
@Test
public void decode_malformedBase64Char_throwsEvaluationException() throws Exception {
- CelAbstractSyntaxTree ast = CEL_COMPILER.compile("base64.decode('z!')").getAst();
-
CelEvaluationException e =
- assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval());
+ assertThrows(CelEvaluationException.class, () -> eval("base64.decode('z!')"));
- assertThat(e)
- .hasMessageThat()
- .contains("Function 'base64_decode_string' failed with arg(s) 'z!'");
+ assertThat(e).hasMessageThat().contains("failed with arg(s) 'z!'");
assertThat(e).hasCauseThat().hasMessageThat().contains("Illegal base64 character");
}
+
+ private Object eval(String expr) throws Exception {
+ return eval(expr, ImmutableMap.of());
+ }
+
+ private Object eval(String expr, ImmutableMap vars) throws Exception {
+ CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst();
+ return cel.createProgram(ast).eval(vars);
+ }
}
diff --git a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java
index b36e0e92e..7a5e7598e 100644
--- a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java
+++ b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java
@@ -27,6 +27,7 @@
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelContainer;
import dev.cel.common.CelValidationException;
+import dev.cel.common.CelValidationResult;
import dev.cel.common.types.SimpleType;
import dev.cel.expr.conformance.test.SimpleTest;
import dev.cel.parser.CelStandardMacro;
@@ -300,10 +301,11 @@ public void sortBy_success(String expression, String expected) throws Exception
+ "expectedError: 'variable name must be a simple identifier'}")
public void sortBy_throws_validationException(String expression, String expectedError)
throws Exception {
+ CelValidationResult result = cel.compile(expression);
assertThat(
assertThrows(
CelValidationException.class,
- () -> cel.createProgram(cel.compile(expression).getAst()).eval()))
+ () -> result.getAst()))
.hasMessageThat()
.contains(expectedError);
}
diff --git a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java
index 1aac5a023..f203a66b8 100644
--- a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java
+++ b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java
@@ -19,8 +19,11 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import com.google.testing.junit.testparameterinjector.TestParameters;
+import dev.cel.bundle.Cel;
+import dev.cel.bundle.CelBuilder;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelContainer;
import dev.cel.common.CelFunctionDecl;
@@ -30,47 +33,34 @@
import dev.cel.common.CelValidationResult;
import dev.cel.common.types.ListType;
import dev.cel.common.types.SimpleType;
-import dev.cel.compiler.CelCompiler;
-import dev.cel.compiler.CelCompilerFactory;
import dev.cel.expr.conformance.proto3.TestAllTypes;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelFunctionBinding;
import dev.cel.runtime.CelRuntime;
-import dev.cel.runtime.CelRuntimeFactory;
+import dev.cel.testing.CelRuntimeFlavor;
import java.util.List;
+import java.util.Map;
+import org.junit.Assume;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(TestParameterInjector.class)
public final class CelSetsExtensionsTest {
- private static final CelOptions CEL_OPTIONS = CelOptions.current().build();
- private static final CelCompiler COMPILER =
- CelCompilerFactory.standardCelCompilerBuilder()
- .addMessageTypes(TestAllTypes.getDescriptor())
- .setOptions(CEL_OPTIONS)
- .setContainer(CelContainer.ofName("cel.expr.conformance.proto3"))
- .addLibraries(CelExtensions.sets(CEL_OPTIONS))
- .addVar("list", ListType.create(SimpleType.INT))
- .addVar("subList", ListType.create(SimpleType.INT))
- .addFunctionDeclarations(
- CelFunctionDecl.newFunctionDeclaration(
- "new_int",
- CelOverloadDecl.newGlobalOverload(
- "new_int_int64", SimpleType.INT, SimpleType.INT)))
- .build();
-
- private static final CelRuntime RUNTIME =
- CelRuntimeFactory.standardCelRuntimeBuilder()
- .addMessageTypes(TestAllTypes.getDescriptor())
- .addLibraries(CelExtensions.sets(CEL_OPTIONS))
- .setOptions(CEL_OPTIONS)
- .addFunctionBindings(
- CelFunctionBinding.from(
- "new_int_int64",
- Long.class,
- // Intentionally return java.lang.Integer to test primitive type adaptation
- Math::toIntExact))
- .build();
+ private static final CelOptions CEL_OPTIONS =
+ CelOptions.current().enableHeterogeneousNumericComparisons(true).build();
+
+ @TestParameter public CelRuntimeFlavor runtimeFlavor;
+ @TestParameter public boolean isParseOnly;
+
+ private Cel cel;
+
+ @Before
+ public void setUp() {
+ // Legacy runtime does not support parsed-only evaluation mode.
+ Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly);
+ this.cel = setupEnv(runtimeFlavor.builder());
+ }
@Test
public void library() {
@@ -87,22 +77,13 @@ public void library() {
public void contains_integerListWithSameValue_succeeds() throws Exception {
ImmutableList list = ImmutableList.of(1, 2, 3, 4);
ImmutableList subList = ImmutableList.of(1, 2, 3, 4);
- CelAbstractSyntaxTree ast = COMPILER.compile("sets.contains(list, subList)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval(ImmutableMap.of("list", list, "subList", subList));
-
- assertThat(result).isEqualTo(true);
+ assertThat(eval("sets.contains(list, subList)", ImmutableMap.of("list", list, "subList", subList)))
+ .isEqualTo(true);
}
@Test
public void contains_integerListAsExpression_succeeds() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("sets.contains([1, 1], [1])").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(true);
+ assertThat(eval("sets.contains([1, 1], [1])")).isEqualTo(true);
}
@Test
@@ -119,12 +100,7 @@ public void contains_integerListAsExpression_succeeds() throws Exception {
+ " [TestAllTypes{single_int64: 2, single_uint64: 3u}])', expected: false}")
public void contains_withProtoMessage_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -133,12 +109,7 @@ public void contains_withProtoMessage_succeeds(String expression, boolean expect
@TestParameters("{expression: 'sets.contains([new_int(2)], [1])', expected: false}")
public void contains_withFunctionReturningInteger_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -157,12 +128,8 @@ public void contains_withFunctionReturningInteger_succeeds(String expression, bo
@TestParameters("{list: [1], subList: [1, 2], expected: false}")
public void contains_withIntTypes_succeeds(
List list, List subList, boolean expected) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("sets.contains(list, subList)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval(ImmutableMap.of("list", list, "subList", subList));
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval("sets.contains(list, subList)", ImmutableMap.of("list", list, "subList", subList)))
+ .isEqualTo(expected);
}
@Test
@@ -177,12 +144,8 @@ public void contains_withIntTypes_succeeds(
@TestParameters("{list: [2, 3.0], subList: [2, 3], expected: true}")
public void contains_withDoubleTypes_succeeds(
List list, List subList, boolean expected) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("sets.contains(list, subList)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval(ImmutableMap.of("list", list, "subList", subList));
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval("sets.contains(list, subList)", ImmutableMap.of("list", list, "subList", subList)))
+ .isEqualTo(expected);
}
@Test
@@ -193,12 +156,7 @@ public void contains_withDoubleTypes_succeeds(
@TestParameters("{expression: 'sets.contains([[1], [2, 3.0]], [[2, 3]])', expected: true}")
public void contains_withNestedLists_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -206,19 +164,16 @@ public void contains_withNestedLists_succeeds(String expression, boolean expecte
@TestParameters("{expression: 'sets.contains([1], [1, \"1\"])', expected: false}")
public void contains_withMixingIntAndString_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
- @TestParameters("{expression: 'sets.contains([1], [\"1\"])'}")
- @TestParameters("{expression: 'sets.contains([\"1\"], [1])'}")
- public void contains_withMixingIntAndString_throwsException(String expression) throws Exception {
- CelValidationResult invalidData = COMPILER.compile(expression);
+ public void contains_withMixingIntAndString_throwsException(
+ @TestParameter({"sets.contains([1], [\"1\"])", "sets.contains([\"1\"], [1])"})
+ String expression)
+ throws Exception {
+ Assume.assumeFalse(isParseOnly);
+ CelValidationResult invalidData = cel.compile(expression);
assertThat(invalidData.getErrors()).hasSize(1);
assertThat(invalidData.getErrors().get(0).getMessage())
@@ -227,12 +182,7 @@ public void contains_withMixingIntAndString_throwsException(String expression) t
@Test
public void contains_withMixedValues_succeeds() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("sets.contains([1, 2], [2u, 2.0])").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(true);
+ assertThat(eval("sets.contains([1, 2], [2u, 2.0])")).isEqualTo(true);
}
@Test
@@ -249,12 +199,7 @@ public void contains_withMixedValues_succeeds() throws Exception {
"{expression: 'sets.contains([[[[[[5]]]]]], [[1], [2, 3.0], [[[[[5]]]]]])', expected: false}")
public void contains_withMultiLevelNestedList_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -269,12 +214,7 @@ public void contains_withMultiLevelNestedList_succeeds(String expression, boolea
+ " false}")
public void contains_withMapValues_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -289,12 +229,7 @@ public void contains_withMapValues_succeeds(String expression, boolean expected)
@TestParameters("{expression: 'sets.equivalent([1, 2], [2, 2, 2])', expected: false}")
public void equivalent_withIntTypes_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -308,12 +243,7 @@ public void equivalent_withIntTypes_succeeds(String expression, boolean expected
@TestParameters("{expression: 'sets.equivalent([1, 2], [1u, 2, 2.3])', expected: false}")
public void equivalent_withMixedTypes_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -338,12 +268,7 @@ public void equivalent_withMixedTypes_succeeds(String expression, boolean expect
+ " [TestAllTypes{single_int64: 2, single_uint64: 3u}])', expected: false}")
public void equivalent_withProtoMessage_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -361,12 +286,7 @@ public void equivalent_withProtoMessage_succeeds(String expression, boolean expe
+ " expected: false}")
public void equivalent_withMapValues_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -391,12 +311,7 @@ public void equivalent_withMapValues_succeeds(String expression, boolean expecte
@TestParameters("{expression: 'sets.intersects([1], [1.1, 2u])', expected: false}")
public void intersects_withMixedTypes_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object result = program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -414,12 +329,11 @@ public void intersects_withMixedTypes_succeeds(String expression, boolean expect
@TestParameters("{expression: 'sets.intersects([{2: 1}], [{1: 1}])', expected: false}")
public void intersects_withMapValues_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
+ // The LEGACY runtime is not spec compliant, because decimal keys are not allowed for maps.
+ Assume.assumeFalse(
+ runtimeFlavor.equals(CelRuntimeFlavor.PLANNER) && expression.contains("1.0:"));
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
@@ -444,25 +358,21 @@ public void intersects_withMapValues_succeeds(String expression, boolean expecte
+ " [TestAllTypes{single_int64: 2, single_uint64: 3u}])', expected: false}")
public void intersects_withProtoMessage_succeeds(String expression, boolean expected)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(expression).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- boolean result = (boolean) program.eval();
-
- assertThat(result).isEqualTo(expected);
+ assertThat(eval(expression)).isEqualTo(expected);
}
@Test
public void setsExtension_containsFunctionSubset_succeeds() throws Exception {
CelSetsExtensions setsExtensions =
CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.CONTAINS);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(setsExtensions).build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder().addLibraries(setsExtensions).build();
+ Cel cel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(setsExtensions)
+ .addRuntimeLibraries(setsExtensions)
+ .build();
- Object evaluatedResult =
- celRuntime.createProgram(celCompiler.compile("sets.contains([1, 2], [2])").getAst()).eval();
+ Object evaluatedResult = eval(cel, "sets.contains([1, 2], [2])", ImmutableMap.of());
assertThat(evaluatedResult).isEqualTo(true);
}
@@ -471,15 +381,14 @@ public void setsExtension_containsFunctionSubset_succeeds() throws Exception {
public void setsExtension_equivalentFunctionSubset_succeeds() throws Exception {
CelSetsExtensions setsExtensions =
CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.EQUIVALENT);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(setsExtensions).build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder().addLibraries(setsExtensions).build();
+ Cel cel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(setsExtensions)
+ .addRuntimeLibraries(setsExtensions)
+ .build();
- Object evaluatedResult =
- celRuntime
- .createProgram(celCompiler.compile("sets.equivalent([1, 1], [1])").getAst())
- .eval();
+ Object evaluatedResult = eval(cel, "sets.equivalent([1, 1], [1])", ImmutableMap.of());
assertThat(evaluatedResult).isEqualTo(true);
}
@@ -488,44 +397,95 @@ public void setsExtension_equivalentFunctionSubset_succeeds() throws Exception {
public void setsExtension_intersectsFunctionSubset_succeeds() throws Exception {
CelSetsExtensions setsExtensions =
CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.INTERSECTS);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(setsExtensions).build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder().addLibraries(setsExtensions).build();
+ Cel cel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(setsExtensions)
+ .addRuntimeLibraries(setsExtensions)
+ .build();
- Object evaluatedResult =
- celRuntime
- .createProgram(celCompiler.compile("sets.intersects([1, 1], [1])").getAst())
- .eval();
+ Object evaluatedResult = eval(cel, "sets.intersects([1, 1], [1])", ImmutableMap.of());
assertThat(evaluatedResult).isEqualTo(true);
}
@Test
public void setsExtension_compileUnallowedFunction_throws() {
+ Assume.assumeFalse(isParseOnly);
CelSetsExtensions setsExtensions =
CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.EQUIVALENT);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(setsExtensions).build();
+ Cel cel = runtimeFlavor.builder().addCompilerLibraries(setsExtensions).build();
assertThrows(
- CelValidationException.class,
- () -> celCompiler.compile("sets.contains([1, 2], [2])").getAst());
+ CelValidationException.class, () -> cel.compile("sets.contains([1, 2], [2])").getAst());
}
@Test
public void setsExtension_evaluateUnallowedFunction_throws() throws Exception {
CelSetsExtensions setsExtensions =
CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.CONTAINS, SetsFunction.EQUIVALENT);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(setsExtensions).build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder()
- .addLibraries(CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.EQUIVALENT))
+ CelSetsExtensions runtimeLibrary =
+ CelExtensions.sets(CelOptions.DEFAULT, SetsFunction.EQUIVALENT);
+ Cel cel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(setsExtensions)
+ .addRuntimeLibraries(runtimeLibrary)
.build();
- CelAbstractSyntaxTree ast = celCompiler.compile("sets.contains([1, 2], [2])").getAst();
+ CelAbstractSyntaxTree ast =
+ isParseOnly
+ ? cel.parse("sets.contains([1, 2], [2])").getAst()
+ : cel.compile("sets.contains([1, 2], [2])").getAst();
+
+ if (runtimeFlavor.equals(CelRuntimeFlavor.PLANNER) && !isParseOnly) {
+ // Fails at plan time
+ assertThrows(CelEvaluationException.class, () -> cel.createProgram(ast));
+ } else {
+ CelRuntime.Program program = cel.createProgram(ast);
+ assertThrows(CelEvaluationException.class, () -> program.eval());
+ }
+ }
+
+ private Object eval(Cel cel, String expression, Map variables) throws Exception {
+ CelAbstractSyntaxTree ast;
+ if (isParseOnly) {
+ ast = cel.parse(expression).getAst();
+ } else {
+ ast = cel.compile(expression).getAst();
+ }
+ return cel.createProgram(ast).eval(variables);
+ }
+
+ private Object eval(String expression) throws Exception {
+ return eval(this.cel, expression, ImmutableMap.of());
+ }
+
+ private Object eval(String expression, Map variables) throws Exception {
+ return eval(this.cel, expression, variables);
+ }
- assertThrows(CelEvaluationException.class, () -> celRuntime.createProgram(ast).eval());
+ private static Cel setupEnv(CelBuilder celBuilder) {
+ return celBuilder
+ .addMessageTypes(TestAllTypes.getDescriptor())
+ .setOptions(CEL_OPTIONS)
+ .setContainer(CelContainer.ofName("cel.expr.conformance.proto3"))
+ .addCompilerLibraries(CelExtensions.sets(CEL_OPTIONS))
+ .addRuntimeLibraries(CelExtensions.sets(CEL_OPTIONS))
+ .addVar("list", ListType.create(SimpleType.INT))
+ .addVar("subList", ListType.create(SimpleType.INT))
+ .addFunctionDeclarations(
+ CelFunctionDecl.newFunctionDeclaration(
+ "new_int",
+ CelOverloadDecl.newGlobalOverload("new_int_int64", SimpleType.INT, SimpleType.INT)))
+ .addFunctionBindings(
+ CelFunctionBinding.fromOverloads(
+ "new_int",
+ CelFunctionBinding.from(
+ "new_int_int64",
+ Long.class,
+ // Intentionally return java.lang.Integer to test primitive type adaptation
+ Math::toIntExact)))
+ .build();
}
}
diff --git a/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java
index 3624e0902..2ce394b6b 100644
--- a/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java
+++ b/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java
@@ -18,43 +18,56 @@
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import com.google.testing.junit.testparameterinjector.TestParameters;
+import dev.cel.bundle.Cel;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
+import dev.cel.common.CelValidationResult;
import dev.cel.common.types.SimpleType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
import dev.cel.extensions.CelStringExtensions.Function;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelRuntime;
-import dev.cel.runtime.CelRuntimeFactory;
+import dev.cel.testing.CelRuntimeFlavor;
import java.util.List;
+import java.util.Map;
+import org.junit.Assume;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(TestParameterInjector.class)
public final class CelStringExtensionsTest {
- private static final CelCompiler COMPILER =
- CelCompilerFactory.standardCelCompilerBuilder()
- .addLibraries(CelExtensions.strings())
- .addVar("s", SimpleType.STRING)
- .addVar("separator", SimpleType.STRING)
- .addVar("index", SimpleType.INT)
- .addVar("offset", SimpleType.INT)
- .addVar("indexOfParam", SimpleType.STRING)
- .addVar("beginIndex", SimpleType.INT)
- .addVar("endIndex", SimpleType.INT)
- .addVar("limit", SimpleType.INT)
- .build();
-
- private static final CelRuntime RUNTIME =
- CelRuntimeFactory.standardCelRuntimeBuilder().addLibraries(CelExtensions.strings()).build();
+ @TestParameter public CelRuntimeFlavor runtimeFlavor;
+ @TestParameter public boolean isParseOnly;
+
+ private Cel cel;
+
+ @Before
+ public void setUp() {
+ // Legacy runtime does not support parsed-only evaluation mode.
+ Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly);
+ this.cel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(CelExtensions.strings())
+ .addRuntimeLibraries(CelExtensions.strings())
+ .addVar("s", SimpleType.STRING)
+ .addVar("separator", SimpleType.STRING)
+ .addVar("index", SimpleType.INT)
+ .addVar("offset", SimpleType.INT)
+ .addVar("indexOfParam", SimpleType.STRING)
+ .addVar("beginIndex", SimpleType.INT)
+ .addVar("endIndex", SimpleType.INT)
+ .addVar("limit", SimpleType.INT)
+ .build();
+ }
@Test
public void library() {
@@ -92,10 +105,8 @@ public void library() {
@TestParameters("{string: '😁😑😦', beginIndex: 3, expectedResult: ''}")
public void substring_beginIndex_success(String string, int beginIndex, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "beginIndex", beginIndex));
+ Object evaluatedResult =
+ eval("s.substring(beginIndex)", ImmutableMap.of("s", string, "beginIndex", beginIndex));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -108,10 +119,7 @@ public void substring_beginIndex_success(String string, int beginIndex, String e
@TestParameters(
"{string: 'A!@#$%^&*()-_+=?/<>.,;:''\"\\', expectedResult: 'a!@#$%^&*()-_+=?/<>.,;:''\"\\'}")
public void lowerAscii_success(String string, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lowerAscii()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.lowerAscii()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -127,10 +135,7 @@ public void lowerAscii_success(String string, String expectedResult) throws Exce
@TestParameters("{string: 'A😁B 😑C가😦D', expectedResult: 'a😁b 😑c가😦d'}")
public void lowerAscii_outsideAscii_success(String string, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lowerAscii()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.lowerAscii()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -161,10 +166,8 @@ public void lowerAscii_outsideAscii_success(String string, String expectedResult
+ " ['The quick brown ', ' jumps over the lazy dog']}")
public void split_ascii_success(String string, String separator, List expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.split(separator)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "separator", separator));
+ Object evaluatedResult =
+ eval("s.split(separator)", ImmutableMap.of("s", string, "separator", separator));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -182,34 +185,30 @@ public void split_ascii_success(String string, String separator, List ex
@TestParameters("{string: '😁a😦나😑 😦', separator: '😁a😦나😑 😦', expectedResult: ['','']}")
public void split_unicode_success(String string, String separator, List expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.split(separator)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "separator", separator));
+ Object evaluatedResult =
+ eval("s.split(separator)", ImmutableMap.of("s", string, "separator", separator));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@Test
@SuppressWarnings("unchecked") // Test only, need List cast to test mutability
- public void split_collectionIsMutable() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.split('')").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
+ public void split_collectionIsImmutable() throws Exception {
+ CelAbstractSyntaxTree ast = cel.compile("'test'.split('')").getAst();
+ CelRuntime.Program program = cel.createProgram(ast);
List evaluatedResult = (List) program.eval();
- evaluatedResult.add("a");
- evaluatedResult.add("b");
- evaluatedResult.add("c");
- evaluatedResult.remove("c");
- assertThat(evaluatedResult).containsExactly("t", "e", "s", "t", "a", "b").inOrder();
+ assertThrows(UnsupportedOperationException.class, () -> evaluatedResult.add("a"));
}
@Test
public void split_separatorIsNonString_throwsException() {
+ // This is a type-check failure.
+ Assume.assumeFalse(isParseOnly);
+ CelValidationResult result = cel.compile("'12'.split(2)");
CelValidationException exception =
- assertThrows(
- CelValidationException.class, () -> COMPILER.compile("'12'.split(2)").getAst());
+ assertThrows(CelValidationException.class, () -> result.getAst());
assertThat(exception).hasMessageThat().contains("found no matching overload for 'split'");
}
@@ -295,11 +294,10 @@ public void split_separatorIsNonString_throwsException() {
+ " expectedResult: ['The quick brown ', ' jumps over the lazy dog']}")
public void split_asciiWithLimit_success(
String string, String separator, int limit, List expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.split(separator, limit)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "separator", separator, "limit", limit));
+ eval(
+ "s.split(separator, limit)",
+ ImmutableMap.of("s", string, "separator", separator, "limit", limit));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -351,11 +349,10 @@ public void split_asciiWithLimit_success(
"{string: '😁a😦나😑 😦', separator: '😁a😦나😑 😦', limit: -1, expectedResult: ['','']}")
public void split_unicodeWithLimit_success(
String string, String separator, int limit, List expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.split(separator, limit)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "separator", separator, "limit", limit));
+ eval(
+ "s.split(separator, limit)",
+ ImmutableMap.of("s", string, "separator", separator, "limit", limit));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -368,35 +365,36 @@ public void split_unicodeWithLimit_success(
@TestParameters("{separator: 'te', limit: 1}")
@TestParameters("{separator: 'te', limit: 2}")
@SuppressWarnings("unchecked") // Test only, need List cast to test mutability
- public void split_withLimit_collectionIsMutable(String separator, int limit) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.split(separator, limit)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
+ public void split_withLimit_collectionIsImmutable(String separator, int limit) throws Exception {
List evaluatedResult =
- (List) program.eval(ImmutableMap.of("separator", separator, "limit", limit));
- evaluatedResult.add("a");
+ (List)
+ eval(
+ "'test'.split(separator, limit)",
+ ImmutableMap.of("separator", separator, "limit", limit));
- assertThat(Iterables.getLast(evaluatedResult)).isEqualTo("a");
+ assertThrows(UnsupportedOperationException.class, () -> evaluatedResult.add("a"));
}
@Test
public void split_withLimit_separatorIsNonString_throwsException() {
+ // This is a type-check failure.
+ Assume.assumeFalse(isParseOnly);
+ CelValidationResult result = cel.compile("'12'.split(2, 3)");
CelValidationException exception =
- assertThrows(
- CelValidationException.class, () -> COMPILER.compile("'12'.split(2, 3)").getAst());
+ assertThrows(CelValidationException.class, () -> result.getAst());
assertThat(exception).hasMessageThat().contains("found no matching overload for 'split'");
}
@Test
public void split_withLimitOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.split('', limit)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("limit", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval(
+ "'test'.split('', limit)",
+ ImmutableMap.of("limit", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -416,11 +414,10 @@ public void split_withLimitOverflow_throwsException() throws Exception {
@TestParameters("{string: '', beginIndex: 0, endIndex: 0, expectedResult: ''}")
public void substring_beginAndEndIndex_ascii_success(
String string, int beginIndex, int endIndex, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex, endIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex));
+ eval(
+ "s.substring(beginIndex, endIndex)",
+ ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -444,11 +441,10 @@ public void substring_beginAndEndIndex_ascii_success(
@TestParameters("{string: 'a😁나', beginIndex: 3, endIndex: 3, expectedResult: ''}")
public void substring_beginAndEndIndex_unicode_success(
String string, int beginIndex, int endIndex, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex, endIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex));
+ eval(
+ "s.substring(beginIndex, endIndex)",
+ ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -458,13 +454,13 @@ public void substring_beginAndEndIndex_unicode_success(
@TestParameters("{string: '', beginIndex: 2}")
public void substring_beginIndexOutOfRange_ascii_throwsException(String string, int beginIndex)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("s", string, "beginIndex", beginIndex)));
+ () ->
+ eval(
+ "s.substring(beginIndex)",
+ ImmutableMap.of("s", string, "beginIndex", beginIndex)));
String exceptionMessage =
String.format(
@@ -482,13 +478,13 @@ public void substring_beginIndexOutOfRange_ascii_throwsException(String string,
@TestParameters("{string: '😁가나', beginIndex: 4, uniqueCharCount: 3}")
public void substring_beginIndexOutOfRange_unicode_throwsException(
String string, int beginIndex, int uniqueCharCount) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("s", string, "beginIndex", beginIndex)));
+ () ->
+ eval(
+ "s.substring(beginIndex)",
+ ImmutableMap.of("s", string, "beginIndex", beginIndex)));
String exceptionMessage =
String.format(
@@ -505,14 +501,12 @@ public void substring_beginIndexOutOfRange_unicode_throwsException(
@TestParameters("{string: '😁😑😦', beginIndex: 2, endIndex: 1}")
public void substring_beginAndEndIndexOutOfRange_throwsException(
String string, int beginIndex, int endIndex) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.substring(beginIndex, endIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
() ->
- program.eval(
+ eval(
+ "s.substring(beginIndex, endIndex)",
ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex)));
String exceptionMessage =
@@ -522,13 +516,13 @@ public void substring_beginAndEndIndexOutOfRange_throwsException(
@Test
public void substring_beginIndexOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'abcd'.substring(beginIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("beginIndex", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval(
+ "'abcd'.substring(beginIndex)",
+ ImmutableMap.of("beginIndex", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -540,13 +534,13 @@ public void substring_beginIndexOverflow_throwsException() throws Exception {
@TestParameters("{beginIndex: 2147483648, endIndex: 2147483648}")
public void substring_beginOrEndIndexOverflow_throwsException(long beginIndex, long endIndex)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'abcd'.substring(beginIndex, endIndex)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("beginIndex", beginIndex, "endIndex", endIndex)));
+ () ->
+ eval(
+ "'abcd'.substring(beginIndex, endIndex)",
+ ImmutableMap.of("beginIndex", beginIndex, "endIndex", endIndex)));
assertThat(exception)
.hasMessageThat()
@@ -563,10 +557,7 @@ public void substring_beginOrEndIndexOverflow_throwsException(long beginIndex, l
@TestParameters("{string: 'world', index: 5, expectedResult: ''}")
public void charAt_ascii_success(String string, long index, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.charAt(index)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "index", index));
+ Object evaluatedResult = eval("s.charAt(index)", ImmutableMap.of("s", string, "index", index));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -588,10 +579,7 @@ public void charAt_ascii_success(String string, long index, String expectedResul
@TestParameters("{string: 'a😁나', index: 3, expectedResult: ''}")
public void charAt_unicode_success(String string, long index, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.charAt(index)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "index", index));
+ Object evaluatedResult = eval("s.charAt(index)", ImmutableMap.of("s", string, "index", index));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -602,26 +590,21 @@ public void charAt_unicode_success(String string, long index, String expectedRes
@TestParameters("{string: '😁😑😦', index: -1}")
@TestParameters("{string: '😁😑😦', index: 4}")
public void charAt_outOfBounds_throwsException(String string, long index) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.charAt(index)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("s", string, "index", index)));
+ () -> eval("s.charAt(index)", ImmutableMap.of("s", string, "index", index)));
assertThat(exception).hasMessageThat().contains("charAt failure: Index out of range");
}
@Test
public void charAt_indexOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.charAt(index)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("index", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval("'test'.charAt(index)", ImmutableMap.of("index", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -650,10 +633,8 @@ public void charAt_indexOverflow_throwsException() throws Exception {
@TestParameters("{string: 'hello mellow', indexOf: ' ', expectedResult: -1}")
public void indexOf_ascii_success(String string, String indexOf, int expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.indexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "indexOfParam", indexOf));
+ Object evaluatedResult =
+ eval("s.indexOf(indexOfParam)", ImmutableMap.of("s", string, "indexOfParam", indexOf));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -682,10 +663,8 @@ public void indexOf_ascii_success(String string, String indexOf, int expectedRes
@TestParameters("{string: 'a😁😑 나😦😁😑다', indexOf: 'a😁😑 나😦😁😑다😁', expectedResult: -1}")
public void indexOf_unicode_success(String string, String indexOf, int expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.indexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string, "indexOfParam", indexOf));
+ Object evaluatedResult =
+ eval("s.indexOf(indexOfParam)", ImmutableMap.of("s", string, "indexOfParam", indexOf));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -697,13 +676,10 @@ public void indexOf_unicode_success(String string, String indexOf, int expectedR
@TestParameters("{indexOf: '나'}")
@TestParameters("{indexOf: '😁'}")
public void indexOf_onEmptyString_throwsException(String indexOf) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("''.indexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("indexOfParam", indexOf)));
+ () -> eval("''.indexOf(indexOfParam)", ImmutableMap.of("indexOfParam", indexOf)));
assertThat(exception).hasMessageThat().contains("indexOf failure: Offset out of range");
}
@@ -728,11 +704,10 @@ public void indexOf_onEmptyString_throwsException(String indexOf) throws Excepti
@TestParameters("{string: 'hello mellow', indexOf: 'l', offset: 10, expectedResult: -1}")
public void indexOf_asciiWithOffset_success(
String string, String indexOf, int offset, int expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.indexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", indexOf, "offset", offset));
+ eval(
+ "s.indexOf(indexOfParam, offset)",
+ ImmutableMap.of("s", string, "indexOfParam", indexOf, "offset", offset));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -779,11 +754,10 @@ public void indexOf_asciiWithOffset_success(
"{string: 'a😁😑 나😦😁😑다', indexOf: 'a😁😑 나😦😁😑다😁', offset: 0, expectedResult: -1}")
public void indexOf_unicodeWithOffset_success(
String string, String indexOf, int offset, int expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.indexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", indexOf, "offset", offset));
+ eval(
+ "s.indexOf(indexOfParam, offset)",
+ ImmutableMap.of("s", string, "indexOfParam", indexOf, "offset", offset));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -797,14 +771,12 @@ public void indexOf_unicodeWithOffset_success(
@TestParameters("{string: '😁😑 😦', indexOf: '😦', offset: 4}")
public void indexOf_withOffsetOutOfBounds_throwsException(
String string, String indexOf, int offset) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.indexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
() ->
- program.eval(
+ eval(
+ "s.indexOf(indexOfParam, offset)",
ImmutableMap.of("s", string, "indexOfParam", indexOf, "offset", offset)));
assertThat(exception).hasMessageThat().contains("indexOf failure: Offset out of range");
@@ -812,13 +784,13 @@ public void indexOf_withOffsetOutOfBounds_throwsException(
@Test
public void indexOf_offsetOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.indexOf('t', offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("offset", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval(
+ "'test'.indexOf('t', offset)",
+ ImmutableMap.of("offset", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -835,10 +807,7 @@ public void indexOf_offsetOverflow_throwsException() throws Exception {
@TestParameters("{list: '[''x'', '' '', '' y '', ''z '']', expectedResult: 'x y z '}")
@TestParameters("{list: '[''hello '', ''world'']', expectedResult: 'hello world'}")
public void join_ascii_success(String list, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(String.format("%s.join()", list)).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- String result = (String) program.eval();
+ String result = (String) eval(String.format("%s.join()", list));
assertThat(result).isEqualTo(expectedResult);
}
@@ -847,10 +816,7 @@ public void join_ascii_success(String list, String expectedResult) throws Except
@TestParameters("{list: '[''가'', ''😁'']', expectedResult: '가😁'}")
@TestParameters("{list: '[''😁😦😑 😦'', ''나'']', expectedResult: '😁😦😑 😦나'}")
public void join_unicode_success(String list, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile(String.format("%s.join()", list)).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- String result = (String) program.eval();
+ String result = (String) eval(String.format("%s.join()", list));
assertThat(result).isEqualTo(expectedResult);
}
@@ -874,11 +840,7 @@ public void join_unicode_success(String list, String expectedResult) throws Exce
"{list: '[''hello '', ''world'']', separator: '/', expectedResult: 'hello /world'}")
public void join_asciiWithSeparator_success(String list, String separator, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER.compile(String.format("%s.join('%s')", list, separator)).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- String result = (String) program.eval();
+ String result = (String) eval(String.format("%s.join('%s')", list, separator));
assertThat(result).isEqualTo(expectedResult);
}
@@ -893,20 +855,17 @@ public void join_asciiWithSeparator_success(String list, String separator, Strin
+ " -😑-나'}")
public void join_unicodeWithSeparator_success(
String list, String separator, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER.compile(String.format("%s.join('%s')", list, separator)).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- String result = (String) program.eval();
+ String result = (String) eval(String.format("%s.join('%s')", list, separator));
assertThat(result).isEqualTo(expectedResult);
}
@Test
public void join_separatorIsNonString_throwsException() {
+ // This is a type-check failure.
+ Assume.assumeFalse(isParseOnly);
CelValidationException exception =
- assertThrows(
- CelValidationException.class, () -> COMPILER.compile("['x','y'].join(2)").getAst());
+ assertThrows(CelValidationException.class, () -> cel.compile("['x','y'].join(2)").getAst());
assertThat(exception).hasMessageThat().contains("found no matching overload for 'join'");
}
@@ -935,11 +894,10 @@ public void join_separatorIsNonString_throwsException() {
@TestParameters("{string: 'hello mellow', lastIndexOf: ' ', expectedResult: -1}")
public void lastIndexOf_ascii_success(String string, String lastIndexOf, int expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lastIndexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", lastIndexOf));
+ eval(
+ "s.lastIndexOf(indexOfParam)",
+ ImmutableMap.of("s", string, "indexOfParam", lastIndexOf));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -969,11 +927,10 @@ public void lastIndexOf_ascii_success(String string, String lastIndexOf, int exp
@TestParameters("{string: 'a😁😑 나😦😁😑다', lastIndexOf: 'a😁😑 나😦😁😑다😁', expectedResult: -1}")
public void lastIndexOf_unicode_success(String string, String lastIndexOf, int expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lastIndexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", lastIndexOf));
+ eval(
+ "s.lastIndexOf(indexOfParam)",
+ ImmutableMap.of("s", string, "indexOfParam", lastIndexOf));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -987,10 +944,8 @@ public void lastIndexOf_unicode_success(String string, String lastIndexOf, int e
@TestParameters("{lastIndexOf: '😁'}")
public void lastIndexOf_strLengthLessThanSubstrLength_returnsMinusOne(String lastIndexOf)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("''.lastIndexOf(indexOfParam)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", "", "indexOfParam", lastIndexOf));
+ Object evaluatedResult =
+ eval("''.lastIndexOf(indexOfParam)", ImmutableMap.of("s", "", "indexOfParam", lastIndexOf));
assertThat(evaluatedResult).isEqualTo(-1);
}
@@ -1022,11 +977,10 @@ public void lastIndexOf_strLengthLessThanSubstrLength_returnsMinusOne(String las
"{string: 'hello mellow', lastIndexOf: 'hello mellowwww ', offset: 11, expectedResult: -1}")
public void lastIndexOf_asciiWithOffset_success(
String string, String lastIndexOf, int offset, int expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lastIndexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", lastIndexOf, "offset", offset));
+ eval(
+ "s.lastIndexOf(indexOfParam, offset)",
+ ImmutableMap.of("s", string, "indexOfParam", lastIndexOf, "offset", offset));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1097,11 +1051,10 @@ public void lastIndexOf_asciiWithOffset_success(
"{string: 'a😁😑 나😦😁😑다', lastIndexOf: 'a😁😑 나😦😁😑다😁', offset: 8, expectedResult: -1}")
public void lastIndexOf_unicodeWithOffset_success(
String string, String lastIndexOf, int offset, int expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lastIndexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(ImmutableMap.of("s", string, "indexOfParam", lastIndexOf, "offset", offset));
+ eval(
+ "s.lastIndexOf(indexOfParam, offset)",
+ ImmutableMap.of("s", string, "indexOfParam", lastIndexOf, "offset", offset));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1115,14 +1068,12 @@ public void lastIndexOf_unicodeWithOffset_success(
@TestParameters("{string: '😁😑 😦', lastIndexOf: '😦', offset: 4}")
public void lastIndexOf_withOffsetOutOfBounds_throwsException(
String string, String lastIndexOf, int offset) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.lastIndexOf(indexOfParam, offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
() ->
- program.eval(
+ eval(
+ "s.lastIndexOf(indexOfParam, offset)",
ImmutableMap.of("s", string, "indexOfParam", lastIndexOf, "offset", offset)));
assertThat(exception).hasMessageThat().contains("lastIndexOf failure: Offset out of range");
@@ -1130,13 +1081,13 @@ public void lastIndexOf_withOffsetOutOfBounds_throwsException(
@Test
public void lastIndexOf_offsetOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.lastIndexOf('t', offset)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("offset", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval(
+ "'test'.lastIndexOf('t', offset)",
+ ImmutableMap.of("offset", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -1163,13 +1114,8 @@ public void lastIndexOf_offsetOverflow_throwsException() throws Exception {
public void replace_ascii_success(
String string, String searchString, String replacement, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER
- .compile(String.format("'%s'.replace('%s', '%s')", string, searchString, replacement))
- .getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval();
+ Object evaluatedResult =
+ eval(String.format("'%s'.replace('%s', '%s')", string, searchString, replacement));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1188,13 +1134,8 @@ public void replace_ascii_success(
public void replace_unicode_success(
String string, String searchString, String replacement, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER
- .compile(String.format("'%s'.replace('%s', '%s')", string, searchString, replacement))
- .getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval();
+ Object evaluatedResult =
+ eval(String.format("'%s'.replace('%s', '%s')", string, searchString, replacement));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1273,15 +1214,10 @@ public void replace_unicode_success(
public void replace_ascii_withLimit_success(
String string, String searchString, String replacement, int limit, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER
- .compile(
- String.format(
- "'%s'.replace('%s', '%s', %d)", string, searchString, replacement, limit))
- .getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval();
+ Object evaluatedResult =
+ eval(
+ String.format(
+ "'%s'.replace('%s', '%s', %d)", string, searchString, replacement, limit));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1334,28 +1270,23 @@ public void replace_ascii_withLimit_success(
public void replace_unicode_withLimit_success(
String string, String searchString, String replacement, int limit, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast =
- COMPILER
- .compile(
- String.format(
- "'%s'.replace('%s', '%s', %d)", string, searchString, replacement, limit))
- .getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval();
+ Object evaluatedResult =
+ eval(
+ String.format(
+ "'%s'.replace('%s', '%s', %d)", string, searchString, replacement, limit));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@Test
public void replace_limitOverflow_throwsException() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("'test'.replace('','',index)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
CelEvaluationException exception =
assertThrows(
CelEvaluationException.class,
- () -> program.eval(ImmutableMap.of("index", 2147483648L))); // INT_MAX + 1
+ () ->
+ eval(
+ "'test'.replace('','',index)",
+ ImmutableMap.of("index", 2147483648L))); // INT_MAX + 1
assertThat(exception)
.hasMessageThat()
@@ -1406,10 +1337,7 @@ private enum TrimTestCase {
@Test
public void trim_success(@TestParameter TrimTestCase testCase) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.trim()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", testCase.text));
+ Object evaluatedResult = eval("s.trim()", ImmutableMap.of("s", testCase.text));
assertThat(evaluatedResult).isEqualTo(testCase.expectedResult);
}
@@ -1422,10 +1350,7 @@ public void trim_success(@TestParameter TrimTestCase testCase) throws Exception
@TestParameters(
"{string: 'a!@#$%^&*()-_+=?/<>.,;:''\"\\', expectedResult: 'A!@#$%^&*()-_+=?/<>.,;:''\"\\'}")
public void upperAscii_success(String string, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.upperAscii()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.upperAscii()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1441,30 +1366,25 @@ public void upperAscii_success(String string, String expectedResult) throws Exce
@TestParameters("{string: 'a😁b 😑c가😦d', expectedResult: 'A😁B 😑C가😦D'}")
public void upperAscii_outsideAscii_success(String string, String expectedResult)
throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.upperAscii()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.upperAscii()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@Test
public void stringExtension_functionSubset_success() throws Exception {
- CelStringExtensions stringExtensions =
- CelExtensions.strings(Function.CHAR_AT, Function.SUBSTRING);
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder().addLibraries(stringExtensions).build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder().addLibraries(stringExtensions).build();
+ Cel customCel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(CelExtensions.strings(Function.CHAR_AT, Function.SUBSTRING))
+ .addRuntimeLibraries(CelExtensions.strings(Function.CHAR_AT, Function.SUBSTRING))
+ .build();
Object evaluatedResult =
- celRuntime
- .createProgram(
- celCompiler
- .compile("'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'")
- .getAst())
- .eval();
+ eval(
+ customCel,
+ "'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'",
+ ImmutableMap.of());
assertThat(evaluatedResult).isEqualTo(true);
}
@@ -1476,10 +1396,7 @@ public void stringExtension_functionSubset_success() throws Exception {
@TestParameters("{string: 'hello world', expectedResult: 'dlrow olleh'}")
@TestParameters("{string: 'ab가cd', expectedResult: 'dc가ba'}")
public void reverse_success(String string, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.reverse()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.reverse()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1490,10 +1407,7 @@ public void reverse_success(String string, String expectedResult) throws Excepti
"{string: '\u180e\u200b\u200c\u200d\u2060\ufeff', expectedResult:"
+ " '\ufeff\u2060\u200d\u200c\u200b\u180e'}")
public void reverse_unicode(String string, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("s.reverse()").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("s.reverse()", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1502,14 +1416,14 @@ public void reverse_unicode(String string, String expectedResult) throws Excepti
@TestParameters("{string: 'hello', expectedResult: '\"hello\"'}")
@TestParameters("{string: '', expectedResult: '\"\"'}")
@TestParameters(
- "{string: 'contains \\\"quotes\\\"', expectedResult: '\"contains \\\\\\\"quotes\\\\\\\"\"'}")
- @TestParameters("{string: 'ends with \\\\', expectedResult: '\"ends with \\\\\\\\\"'}")
- @TestParameters("{string: '\\\\ starts with', expectedResult: '\"\\\\\\\\ starts with\"'}")
+ "{string: 'contains \\\\\\\"quotes\\\\\\\"', expectedResult: '\"contains"
+ + " \\\\\\\\\\\\\\\"quotes\\\\\\\\\\\\\\\"\"'}")
+ @TestParameters(
+ "{string: 'ends with \\\\\\\\', expectedResult: '\"ends with \\\\\\\\\\\\\\\\\"'}")
+ @TestParameters(
+ "{string: '\\\\\\\\ starts with', expectedResult: '\"\\\\\\\\\\\\\\\\ starts with\"'}")
public void quote_success(String string, String expectedResult) throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("strings.quote(s)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
+ Object evaluatedResult = eval("strings.quote(s)", ImmutableMap.of("s", string));
assertThat(evaluatedResult).isEqualTo(expectedResult);
}
@@ -1518,21 +1432,16 @@ public void quote_success(String string, String expectedResult) throws Exception
public void quote_singleWithDoubleQuotes() throws Exception {
String expr = "strings.quote('single-quote with \"double quote\"')";
String expected = "\"\\\"single-quote with \\\\\\\"double quote\\\\\\\"\\\"\"";
- CelAbstractSyntaxTree ast = COMPILER.compile(expr + " == " + expected).getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
- Object evaluatedResult = program.eval();
+ Object evaluatedResult = eval(expr + " == " + expected);
assertThat(evaluatedResult).isEqualTo(true);
}
@Test
public void quote_escapesSpecialCharacters() throws Exception {
- CelAbstractSyntaxTree ast = COMPILER.compile("strings.quote(s)").getAst();
- CelRuntime.Program program = RUNTIME.createProgram(ast);
-
Object evaluatedResult =
- program.eval(
+ eval(
+ "strings.quote(s)",
ImmutableMap.of("s", "\u0007bell\u000Bvtab\bback\ffeed\rret\nline\ttab\\slash 가 😁"));
assertThat(evaluatedResult)
@@ -1541,25 +1450,19 @@ public void quote_escapesSpecialCharacters() throws Exception {
@Test
public void quote_escapesMalformed_endWithHighSurrogate() throws Exception {
- CelRuntime.Program program =
- RUNTIME.createProgram(COMPILER.compile("strings.quote(s)").getAst());
- assertThat(program.eval(ImmutableMap.of("s", "end with high surrogate \uD83D")))
+ assertThat(eval("strings.quote(s)", ImmutableMap.of("s", "end with high surrogate \uD83D")))
.isEqualTo("\"end with high surrogate \uFFFD\"");
}
@Test
public void quote_escapesMalformed_unpairedHighSurrogate() throws Exception {
- CelRuntime.Program program =
- RUNTIME.createProgram(COMPILER.compile("strings.quote(s)").getAst());
- assertThat(program.eval(ImmutableMap.of("s", "bad pair \uD83DA")))
+ assertThat(eval("strings.quote(s)", ImmutableMap.of("s", "bad pair \uD83DA")))
.isEqualTo("\"bad pair \uFFFDA\"");
}
@Test
public void quote_escapesMalformed_unpairedLowSurrogate() throws Exception {
- CelRuntime.Program program =
- RUNTIME.createProgram(COMPILER.compile("strings.quote(s)").getAst());
- assertThat(program.eval(ImmutableMap.of("s", "bad pair \uDC00A")))
+ assertThat(eval("strings.quote(s)", ImmutableMap.of("s", "bad pair \uDC00A")))
.isEqualTo("\"bad pair \uFFFDA\"");
}
@@ -1570,23 +1473,49 @@ public void stringExtension_compileUnallowedFunction_throws() {
.addLibraries(CelExtensions.strings(Function.REPLACE))
.build();
+ // This is a type-check failure.
+ Assume.assumeFalse(isParseOnly);
+ CelValidationResult result = celCompiler.compile("'test'.substring(2) == 'st'");
assertThrows(
CelValidationException.class,
- () -> celCompiler.compile("'test'.substring(2) == 'st'").getAst());
+ () -> result.getAst());
}
@Test
public void stringExtension_evaluateUnallowedFunction_throws() throws Exception {
- CelCompiler celCompiler =
- CelCompilerFactory.standardCelCompilerBuilder()
- .addLibraries(CelExtensions.strings(Function.SUBSTRING))
+ Cel customCompilerCel =
+ runtimeFlavor
+ .builder()
+ .addCompilerLibraries(CelExtensions.strings(Function.SUBSTRING))
.build();
- CelRuntime celRuntime =
- CelRuntimeFactory.standardCelRuntimeBuilder()
- .addLibraries(CelExtensions.strings(Function.REPLACE))
+ Cel customRuntimeCel =
+ runtimeFlavor
+ .builder()
+ .addRuntimeLibraries(CelExtensions.strings(Function.REPLACE))
.build();
- CelAbstractSyntaxTree ast = celCompiler.compile("'test'.substring(2) == 'st'").getAst();
+ CelAbstractSyntaxTree ast =
+ isParseOnly
+ ? customCompilerCel.parse("'test'.substring(2) == 'st'").getAst()
+ : customCompilerCel.compile("'test'.substring(2) == 'st'").getAst();
+
+ assertThrows(CelEvaluationException.class, () -> customRuntimeCel.createProgram(ast).eval());
+ }
+
+ private Object eval(Cel cel, String expression, Map variables) throws Exception {
+ CelAbstractSyntaxTree ast;
+ if (isParseOnly) {
+ ast = cel.parse(expression).getAst();
+ } else {
+ ast = cel.compile(expression).getAst();
+ }
+ return cel.createProgram(ast).eval(variables);
+ }
+
+ private Object eval(String expression) throws Exception {
+ return eval(this.cel, expression, ImmutableMap.of());
+ }
- assertThrows(CelEvaluationException.class, () -> celRuntime.createProgram(ast).eval());
+ private Object eval(String expression, Map variables) throws Exception {
+ return eval(this.cel, expression, variables);
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel
index 824c918d8..96382b9a9 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel
+++ b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel
@@ -365,6 +365,7 @@ java_library(
":activation_wrapper",
":planned_interpretable",
"//common/exceptions:runtime_exception",
+ "//common/values:mutable_map_value",
"//runtime:accumulated_unknowns",
"//runtime:concatenated_list_view",
"//runtime:evaluation_exception",
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalFold.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalFold.java
index 2eb30671e..090a8bfae 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/EvalFold.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalFold.java
@@ -15,8 +15,10 @@
package dev.cel.runtime.planner;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.exceptions.CelRuntimeException;
+import dev.cel.common.values.MutableMapValue;
import dev.cel.runtime.AccumulatedUnknowns;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.ConcatenatedListView;
@@ -131,7 +133,7 @@ private Object evalList(Collection> iterRange, Folder folder, ExecutionFrame f
boolean cond = (boolean) condition.eval(folder, frame);
if (!cond) {
folder.computeResult = true;
- return result.eval(folder, frame);
+ return maybeUnwrapAccumulator(result.eval(folder, frame));
}
folder.accuVal = loopStep.eval(folder, frame);
@@ -139,14 +141,16 @@ private Object evalList(Collection> iterRange, Folder folder, ExecutionFrame f
index++;
}
folder.computeResult = true;
- return result.eval(folder, frame);
+ return maybeUnwrapAccumulator(result.eval(folder, frame));
}
private static Object maybeWrapAccumulator(Object val) {
if (val instanceof Collection) {
return new ConcatenatedListView<>((Collection>) val);
}
- // TODO: Introduce mutable map support (for comp v2)
+ if (val instanceof Map) {
+ return MutableMapValue.create((Map, ?>) val);
+ }
return val;
}
@@ -154,8 +158,9 @@ private static Object maybeUnwrapAccumulator(Object val) {
if (val instanceof ConcatenatedListView) {
return ImmutableList.copyOf((ConcatenatedListView>) val);
}
-
- // TODO: Introduce mutable map support (for comp v2)
+ if (val instanceof MutableMapValue) {
+ return ImmutableMap.copyOf((MutableMapValue) val);
+ }
return val;
}
diff --git a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java
index 54ce24417..73492d126 100644
--- a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java
+++ b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java
@@ -149,9 +149,10 @@ public void toRuntimeBuilder_propertiesCopied() {
assertThat(newRuntimeBuilder.standardFunctionBuilder.build())
.containsExactly(intFunction, equalsOperator)
.inOrder();
- assertThat(newRuntimeBuilder.customFunctionBindings).hasSize(2);
+ assertThat(newRuntimeBuilder.customFunctionBindings).hasSize(3);
assertThat(newRuntimeBuilder.customFunctionBindings).containsKey("string_isEmpty");
assertThat(newRuntimeBuilder.customFunctionBindings).containsKey("list_sets_intersects_list");
+ assertThat(newRuntimeBuilder.customFunctionBindings).containsKey("sets.intersects");
}
@Test