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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions extensions/src/main/java/dev/cel/extensions/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions extensions/src/test/java/dev/cel/extensions/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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<String, ?> 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()
Expand All @@ -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();
Expand Down Expand Up @@ -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<String, ?> variables) throws Exception {
CelAbstractSyntaxTree ast;
if (isParseOnly) {
ast = cel.parse(expression).getAst();
} else {
ast = cel.compile(expression).getAst();
}
return cel.createProgram(ast).eval(variables);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String, Object> vars) throws Exception {
CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst();
return cel.createProgram(ast).eval(vars);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, ?> 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<String, ?> variables) throws CelException {
CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst();
return cel.createProgram(ast).eval(variables);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,34 @@
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;
import dev.cel.common.types.SimpleType;
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
Expand Down Expand Up @@ -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<String, ?> vars) throws Exception {
CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst();
return cel.createProgram(ast).eval(vars);
}
}
Loading
Loading