diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel index f8e4bfc8c..454b2a2fd 100644 --- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel @@ -122,6 +122,7 @@ java_library( ":extension_library", "//checker:checker_builder", "//common:compiler_common", + "//common:options", "//common/ast", "//common/exceptions:numeric_overflow", "//common/internal:comparison_functions", diff --git a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel index 19fd3657e..eed240317 100644 --- a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel @@ -12,6 +12,7 @@ java_library( "//bundle:cel", "//bundle:cel_experimental_factory", "//common:cel_ast", + "//common:cel_exception", "//common:compiler_common", "//common:container", "//common:options", diff --git a/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java index b87967d0e..00fcad473 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java @@ -23,7 +23,6 @@ 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.CelOverloadDecl; @@ -36,36 +35,24 @@ import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelFunctionBinding; -import dev.cel.testing.CelRuntimeFlavor; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelBindingsExtensionsTest { - - @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); - cel = - runtimeFlavor - .builder() - .setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build()) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) - .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) - .build(); +public final class CelBindingsExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build()) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) + .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) + .build(); } @Test @@ -331,21 +318,5 @@ public void lazyBinding_boundAttributeInNestedComprehension() throws Exception { assertThat(invocation.get()).isEqualTo(1); } - private Object eval(Cel cel, String expression) throws Exception { - return eval(cel, expression, ImmutableMap.of()); - } - - 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()); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java index 207178cfe..42dc3e07d 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java @@ -19,7 +19,6 @@ import static org.junit.Assert.assertThrows; import com.google.common.base.Throwables; -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; @@ -40,15 +39,13 @@ import dev.cel.parser.CelUnparserFactory; import dev.cel.runtime.CelEvaluationException; import dev.cel.testing.CelRuntimeFlavor; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** Test for {@link CelExtensions#comprehensions()} */ @RunWith(TestParameterInjector.class) -public class CelComprehensionsExtensionsTest { +public class CelComprehensionsExtensionsTest extends CelExtensionTestBase { private static final CelOptions CEL_OPTIONS = CelOptions.current() @@ -57,29 +54,21 @@ public class CelComprehensionsExtensionsTest { .populateMacroCalls(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) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelExtensions.comprehensions()) - .addCompilerLibraries(CelExtensions.lists()) - .addCompilerLibraries(CelExtensions.strings()) - .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) - .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) - .addRuntimeLibraries(CelExtensions.lists()) - .addRuntimeLibraries(CelExtensions.strings()) - .addRuntimeLibraries(CelExtensions.comprehensions()) - .build(); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CEL_OPTIONS) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelExtensions.comprehensions()) + .addCompilerLibraries(CelExtensions.lists()) + .addCompilerLibraries(CelExtensions.strings()) + .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) + .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) + .addRuntimeLibraries(CelExtensions.lists()) + .addRuntimeLibraries(CelExtensions.strings()) + .addRuntimeLibraries(CelExtensions.comprehensions()) + .build(); } private static final CelUnparser UNPARSER = CelUnparserFactory.newUnparser(); @@ -376,17 +365,5 @@ public void mutableMapValue_select_missingKeyException() throws Exception { 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()); - } - 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); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java index b0a501ddb..afeaa9105 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java @@ -19,44 +19,32 @@ 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.runtime.CelEvaluationException; -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 { +public class CelEncoderExtensionsTest extends CelExtensionTestBase { 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 = - runtimeFlavor - .builder() - .setOptions(CEL_OPTIONS) - .addCompilerLibraries(CelExtensions.encoders(CEL_OPTIONS)) - .addRuntimeLibraries(CelExtensions.encoders(CEL_OPTIONS)) - .addVar("stringVar", SimpleType.STRING) - .build(); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CEL_OPTIONS) + .addCompilerLibraries(CelExtensions.encoders(CEL_OPTIONS)) + .addRuntimeLibraries(CelExtensions.encoders(CEL_OPTIONS)) + .addVar("stringVar", SimpleType.STRING) + .build(); } @Test @@ -132,12 +120,5 @@ public void decode_malformedBase64Char_throwsEvaluationException() throws Except 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/CelExtensionTestBase.java b/extensions/src/test/java/dev/cel/extensions/CelExtensionTestBase.java new file mode 100644 index 000000000..c80ee38b6 --- /dev/null +++ b/extensions/src/test/java/dev/cel/extensions/CelExtensionTestBase.java @@ -0,0 +1,66 @@ +// 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.extensions; + +import com.google.common.collect.ImmutableMap; +import com.google.testing.junit.testparameterinjector.TestParameter; +import dev.cel.bundle.Cel; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelException; +import dev.cel.testing.CelRuntimeFlavor; +import java.util.Map; +import org.junit.Assume; +import org.junit.Before; + +/** + * Abstract base class for extension tests to facilitate executing tests with both legacy and + * planner runtime, along with parsed-only and checked expression evaluations for the planner. + */ +abstract class CelExtensionTestBase { + @TestParameter public CelRuntimeFlavor runtimeFlavor; + @TestParameter public boolean isParseOnly; + + @Before + public void setUpBase() { + // Legacy runtime does not support parsed-only evaluation. + Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); + this.cel = newCelEnv(); + } + + protected Cel cel; + + /** + * Subclasses must implement this to provide a Cel instance configured with the specific + * extensions being tested. + */ + protected abstract Cel newCelEnv(); + + protected Object eval(String expr) throws CelException { + return eval(cel, expr, ImmutableMap.of()); + } + + protected Object eval(String expr, Map variables) throws CelException { + return eval(cel, expr, variables); + } + + protected Object eval(Cel cel, String expr) throws CelException { + return eval(cel, expr, ImmutableMap.of()); + } + + protected Object eval(Cel cel, String expr, Map variables) throws CelException { + CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); + return cel.createProgram(ast).eval(variables); + } +} diff --git a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java index f36d90e2d..f5536da4e 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java @@ -19,12 +19,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMultiset; import com.google.common.collect.ImmutableSortedSet; -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.CelValidationException; import dev.cel.common.CelValidationResult; @@ -32,25 +29,24 @@ import dev.cel.expr.conformance.test.SimpleTest; import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; -import dev.cel.testing.CelRuntimeFlavor; -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 class CelListsExtensionsTest { - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; +public class CelListsExtensionsTest extends CelExtensionTestBase { - 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()); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelExtensions.lists()) + .addRuntimeLibraries(CelExtensions.lists()) + .setContainer(CelContainer.ofName("cel.expr.conformance.test")) + .addMessageTypes(SimpleTest.getDescriptor()) + .addVar("non_list", SimpleType.DYN) + .build(); } @Test @@ -322,23 +318,5 @@ public void sortBy_throws_evaluationException(String expression, String expected .contains(expectedError); } - private static Cel setupEnv(CelBuilder celBuilder) { - return celBuilder - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelExtensions.lists()) - .addRuntimeLibraries(CelExtensions.lists()) - .setContainer(CelContainer.ofName("cel.expr.conformance.test")) - .addMessageTypes(SimpleTest.getDescriptor()) - .addVar("non_list", SimpleType.DYN) - .build(); - } - - private Object eval(Cel cel, String expr) throws Exception { - return eval(cel, expr, ImmutableMap.of()); - } - private Object eval(Cel cel, String expr, Map 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/CelProtoExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java index 2e55619db..f46ea5b1a 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java @@ -26,7 +26,6 @@ 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.CelContainer; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; @@ -41,34 +40,23 @@ import dev.cel.parser.CelMacro; import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelFunctionBinding; -import dev.cel.testing.CelRuntimeFlavor; -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 CelProtoExtensionsTest { - - @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.protos()) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addFileTypes(TestAllTypesExtensions.getDescriptor()) - .addVar("msg", StructTypeReference.create("cel.expr.conformance.proto2.TestAllTypes")) - .setContainer(CelContainer.ofName("cel.expr.conformance.proto2")) - .build(); +public final class CelProtoExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.protos()) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addFileTypes(TestAllTypesExtensions.getDescriptor()) + .addVar("msg", StructTypeReference.create("cel.expr.conformance.proto2.TestAllTypes")) + .setContainer(CelContainer.ofName("cel.expr.conformance.proto2")) + .build(); } private static final TestAllTypes PACKAGE_SCOPED_EXT_MSG = @@ -342,13 +330,5 @@ public void parseErrors(@TestParameter ParseErrorTestCase testcase) { assertThat(e).hasMessageThat().isEqualTo(testcase.error); } - private Object eval(String expression, Map variables) throws Exception { - return eval(this.cel, expression, variables); - } - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast = - this.isParseOnly ? cel.parse(expression).getAst() : cel.compile(expression).getAst(); - return cel.createProgram(ast).eval(variables); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java index 924344b25..97d0cc90c 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java @@ -21,35 +21,23 @@ 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.runtime.CelEvaluationException; -import dev.cel.testing.CelRuntimeFlavor; import java.util.Optional; -import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelRegexExtensionsTest { - - @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.regex()) - .addRuntimeLibraries(CelExtensions.regex()) - .build(); +public final class CelRegexExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.regex()) + .addRuntimeLibraries(CelExtensions.regex()) + .build(); } @@ -276,9 +264,5 @@ public void extractAll_multipleCaptureGroups_throwsException(String target, Stri .contains("Regular expression has more than one capturing group:"); } - private Object eval(String expr) throws Exception { - CelAbstractSyntaxTree ast = - isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); - return cel.createProgram(ast).eval(); - } + } diff --git a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java index 9007bba2e..091d456f5 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java @@ -23,7 +23,6 @@ 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; @@ -39,27 +38,39 @@ import dev.cel.runtime.CelRuntime; 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 { +public final class CelSetsExtensionsTest extends CelExtensionTestBase { 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()); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .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(); } @Test @@ -375,7 +386,7 @@ public void setsExtension_containsFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.contains([1, 2], [2])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.contains([1, 2], [2])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -391,7 +402,7 @@ public void setsExtension_equivalentFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.equivalent([1, 1], [1])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.equivalent([1, 1], [1])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -407,7 +418,7 @@ public void setsExtension_intersectsFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.intersects([1, 1], [1])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.intersects([1, 1], [1])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -450,45 +461,5 @@ public void setsExtension_evaluateUnallowedFunction_throws() throws Exception { } } - 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); - } - 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 e7542b7b7..4b242ddcd 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java @@ -33,40 +33,29 @@ import dev.cel.extensions.CelStringExtensions.Function; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelRuntime; -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 { - - @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(); +public final class CelStringExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return 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 @@ -388,13 +377,10 @@ public void split_withLimit_separatorIsNonString_throwsException() { @Test public void split_withLimitOverflow_throwsException() throws Exception { + ImmutableMap variables = ImmutableMap.of("limit", 2147483648L); // INT_MAX + 1 CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "'test'.split('', limit)", - ImmutableMap.of("limit", 2147483648L))); // INT_MAX + 1 + CelEvaluationException.class, () -> eval("'test'.split('', limit)", variables)); assertThat(exception) .hasMessageThat() @@ -454,13 +440,10 @@ public void substring_beginAndEndIndex_unicode_success( @TestParameters("{string: '', beginIndex: 2}") public void substring_beginIndexOutOfRange_ascii_throwsException(String string, int beginIndex) throws Exception { + ImmutableMap variables = ImmutableMap.of("s", string, "beginIndex", beginIndex); CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex))); + CelEvaluationException.class, () -> eval("s.substring(beginIndex)", variables)); String exceptionMessage = String.format( @@ -478,13 +461,10 @@ 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 { + ImmutableMap variables = ImmutableMap.of("s", string, "beginIndex", beginIndex); CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex))); + CelEvaluationException.class, () -> eval("s.substring(beginIndex)", variables)); String exceptionMessage = String.format( @@ -501,13 +481,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 { + ImmutableMap variables = + ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex); CelEvaluationException exception = assertThrows( CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex, endIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex))); + () -> eval("s.substring(beginIndex, endIndex)", variables)); String exceptionMessage = String.format("substring failure: Range [%d, %d) out of bounds", beginIndex, endIndex); @@ -516,13 +495,11 @@ public void substring_beginAndEndIndexOutOfRange_throwsException( @Test public void substring_beginIndexOverflow_throwsException() throws Exception { + ImmutableMap variables = + ImmutableMap.of("beginIndex", 2147483648L); // INT_MAX + 1 CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "'abcd'.substring(beginIndex)", - ImmutableMap.of("beginIndex", 2147483648L))); // INT_MAX + 1 + CelEvaluationException.class, () -> eval("'abcd'.substring(beginIndex)", variables)); assertThat(exception) .hasMessageThat() @@ -1381,10 +1358,7 @@ public void stringExtension_functionSubset_success() throws Exception { .build(); Object evaluatedResult = - eval( - customCel, - "'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'", - ImmutableMap.of()); + eval(customCel, "'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'"); assertThat(evaluatedResult).isEqualTo(true); } @@ -1499,21 +1473,5 @@ public void stringExtension_evaluateUnallowedFunction_throws() throws Exception 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()); - } - private Object eval(String expression, Map variables) throws Exception { - return eval(this.cel, expression, variables); - } }