Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,23 @@ public final String toString() {

@Override
public Object select(String field) {
FieldLiteDescriptor fd = descriptor().findByFieldName(field);
if (fd == null) {
throw CelAttributeNotFoundException.of(
String.format("field '%s' is not declared in message '%s'", field, protoTypeName()));
}
FieldLiteDescriptor fd = findFieldDescriptor(field);
Object fieldValue = readField(fd);
return fieldValue != null ? fieldValue : protoLiteCelValueConverter().getDefaultCelValue(fd);
}

@Override
public Optional<Object> find(String field) {
// Per SelectableValue#find, a field that doesn't exist is reported as absent.
return Optional.ofNullable(descriptor().findByFieldName(field)).map(this::readField);
return Optional.ofNullable(readField(findFieldDescriptor(field)));
}

private FieldLiteDescriptor findFieldDescriptor(String field) {
FieldLiteDescriptor fd = descriptor().findByFieldName(field);
if (fd == null) {
throw CelAttributeNotFoundException.of(
String.format("field '%s' is not declared in message '%s'", field, protoTypeName()));
}
return fd;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,29 @@ public void select_undeclaredField_throwsCelAttributeNotFoundException() {
+ " 'cel.expr.conformance.proto3.TestAllTypes'");
}

@Test
public void find_undeclaredField_throwsCelAttributeNotFoundException() {
ProtoMessageLiteValue messageLiteValue =
ProtoMessageLiteValue.create(
TestAllTypes.getDefaultInstance(),
TEST_ALL_TYPES_DESCRIPTOR,
PROTO_LITE_CEL_VALUE_CONVERTER);

CelAttributeNotFoundException thrown =
assertThrows(
CelAttributeNotFoundException.class, () -> messageLiteValue.find("undeclared_field"));

assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"field 'undeclared_field' is not declared in message"
+ " 'cel.expr.conformance.proto3.TestAllTypes'");
}

@SuppressWarnings("ImmutableEnumChecker") // Test only
private enum FindFieldTestCase {
PRESENT("single_string", Optional.of("foo")),
ABSENT("single_int64", Optional.empty()),
UNDECLARED("undeclared_field", Optional.empty());
ABSENT("single_int64", Optional.empty());

private final String fieldName;
private final Optional<Object> expected;
Expand Down
72 changes: 70 additions & 2 deletions conformance/src/test/java/dev/cel/conformance/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
load("@rules_java//java:defs.bzl", "java_library")
load("//conformance/src/test/java/dev/cel/conformance:conformance_test.bzl", "MODE", "conformance_test")
load("//conformance/src/test/java/dev/cel/conformance:conformance_test.bzl", "MODE", "RUNTIME", "conformance_test")

package(default_applicable_licenses = [
"//:license",
Expand All @@ -26,17 +26,33 @@ java_library(
"//common/ast:cel_block",
"//common/types",
"//common/types:cel_proto_types",
"//common/types:message_type_provider",
"//common/types:type_providers",
"//common/values:cel_value_provider",
"//common/values:proto_message_lite_value_provider",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:bindings",
"//extensions:comprehensions_runtime_library",
"//extensions:encoders_runtime_library",
"//extensions:lists_runtime_library",
"//extensions:math_runtime_library",
"//extensions:optional_library",
"//extensions:sets_runtime_library",
"//extensions:strings_runtime_library",
"//parser:macro",
"//parser:parser_builder",
"//parser:parser_factory",
"//runtime",
"//runtime:lite_runtime",
"//runtime:lite_runtime_factory",
"//runtime:program",
"//runtime:runtime_planner_impl",
"//runtime:standard_functions",
"//testing:expr_value_utils",
"//testing/protos:test_all_types_cel_java_proto2",
"//testing/protos:test_all_types_cel_java_proto3",
"@cel_spec//proto/cel/expr:expr_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
Expand All @@ -63,10 +79,15 @@ java_library(
tags = ["conformance_maven"],
deps = MAVEN_JAR_DEPS + [
"//:java_truth",
"//common/values:proto_message_lite_value_provider",
"//compiler:compiler_builder",
"//parser:parser_factory",
"//runtime:lite_runtime",
"//runtime:lite_runtime_factory",
"//runtime:runtime_planner_impl",
"//testing:expr_value_utils",
"//testing/protos:test_all_types_cel_java_proto2",
"//testing/protos:test_all_types_cel_java_proto3",
"@cel_spec//proto/cel/expr:expr_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
Expand Down Expand Up @@ -168,6 +189,46 @@ _TESTS_TO_SKIP_PLANNER = [
"enums/strong_proto3",
]

_TESTS_TO_SKIP_LITE = _TESTS_TO_SKIP_PLANNER + [
# Optionals are not supported in CelLiteRuntime.
"optionals",
"block_ext/basic/optional_list,optional_map,optional_map_chained,optional_message",

# Proto2 extensions are not supported in CelLiteRuntime.
"proto2_ext",
"proto2/extensions_has",
"proto2/extensions_get",

# TODO: Message creation with prepopulated fields, Any unpacking, and Proto
# Differencer equality are not yet supported in CelLiteRuntime.
"comparisons/eq_literal/eq_dyn_json_null,not_eq_dyn_proto2_msg_null,not_eq_dyn_proto3_msg_null",
"comparisons/eq_wrapper",
"comparisons/ne_literal/ne_proto2,ne_proto3,ne_proto2_missing_fields_neq,ne_proto3_missing_fields_neq,ne_proto_different_types,ne_proto_nan_not_equal",
"comparisons/ne_literal/ne_proto2_any_unpack,ne_proto2_any_unpack_bytewise_fallback,ne_proto3_any_unpack,ne_proto3_any_unpack_bytewise_fallback",
"dynamic",
"enums/legacy_proto2/assign_standalone_name,assign_standalone_int,assign_standalone_int_too_big,assign_standalone_int_too_neg,assign_repeated_enum,list_enum_as_list_int,enum_as_int",
"enums/legacy_proto3/assign_standalone_name,assign_standalone_int,assign_standalone_int_big,assign_standalone_int_neg,assign_standalone_int_too_big,assign_standalone_int_too_neg,assign_repeated_enum,list_enum_as_list_int,enum_as_int",
"parse/nest/message_literal",
"parse/repeat/message_literal",
"parse/whitespace",
"parse/comments",
"parse/struct_field_names",
"proto2/empty_field/scalar_with_default",
"proto2/literal_singular",
"proto2/literal_wellknown",
"proto2/set_null",
"proto2/quoted_fields",
"proto2/has/repeated_none_explicit,repeated_one,repeated_many,map_none_explicit,map_one_default,map_one,map_many,required,optional_set_no_default,optional_set_with_default,optional_set_to_default,optional_message_set,optional_enum_set,optional_enum_set_zero,oneof_other_set,oneof_set,oneof_set_default",
"proto3/literal_singular",
"proto3/literal_wellknown",
"proto3/set_null",
"proto3/quoted_fields",
"proto3/has/repeated_none_explicit,repeated_one,repeated_many,map_none_explicit,map_one_default,map_one,map_many,single_set,single_set_to_default,single_message_set,single_message_set_to_default,single_enum_set,single_enum_set_zero,oneof_other_set,oneof_set,oneof_set_default",
"type_deductions/complex_initializers/struct",
"type_deductions/field_access/int_field",
"wrappers",
]

conformance_test(
name = "conformance",
data = _ALL_TESTS,
Expand All @@ -190,6 +251,13 @@ conformance_test(
conformance_test(
name = "conformance_planner",
data = _ALL_TESTS,
runtime = RUNTIME.PLANNER,
skip_tests = _TESTS_TO_SKIP_PLANNER,
use_planner = True,
)

conformance_test(
name = "conformance_lite",
data = _ALL_TESTS,
runtime = RUNTIME.LITE,
skip_tests = _TESTS_TO_SKIP_LITE,
)
129 changes: 92 additions & 37 deletions conformance/src/test/java/dev/cel/conformance/ConformanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package dev.cel.conformance;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static dev.cel.testing.utils.ExprValueUtils.fromValue;
Expand All @@ -24,7 +25,6 @@
import dev.cel.expr.MapValue;
import dev.cel.expr.Type;
import dev.cel.expr.Value;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.ExtensionRegistry;
Expand All @@ -44,13 +44,23 @@
import dev.cel.common.ast.CelConstant;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelTypeProvider;
import dev.cel.common.types.ProtoMessageTypeProvider;
import dev.cel.common.types.SimpleType;
import dev.cel.common.values.CelValueProvider;
import dev.cel.common.values.ProtoMessageLiteValueProvider;
import dev.cel.compiler.CelCompilerFactory;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.expr.conformance.test.SimpleTest;
import dev.cel.extensions.CelBindingsExtensions;
import dev.cel.extensions.CelComprehensionsRuntimeLibrary;
import dev.cel.extensions.CelEncoderRuntimeLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelListsRuntimeLibrary;
import dev.cel.extensions.CelMathRuntimeLibrary;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.extensions.CelSetsRuntimeLibrary;
import dev.cel.extensions.CelStringRuntimeLibrary;
import dev.cel.parser.CelMacro;
import dev.cel.parser.CelMacroExpander;
import dev.cel.parser.CelMacroExprFactory;
Expand All @@ -59,12 +69,14 @@
import dev.cel.parser.CelParserFactory;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntime.Program;
import dev.cel.runtime.CelLiteRuntimeFactory;
import dev.cel.runtime.CelLiteRuntimeLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeFactory;
import dev.cel.runtime.CelRuntimeImpl;
import dev.cel.runtime.CelRuntimeLibrary;
import dev.cel.runtime.CelStandardFunctions;
import dev.cel.runtime.Program;
import java.util.Map;
import java.util.Optional;
import org.junit.runners.model.Statement;
Expand Down Expand Up @@ -104,6 +116,37 @@ public final class ConformanceTest extends Statement {
CelExtensions.strings(),
CelOptionalLibrary.INSTANCE);

private static final ImmutableList<CelLiteRuntimeLibrary> CANONICAL_LITE_RUNTIME_EXTENSIONS =
ImmutableList.of(
CelComprehensionsRuntimeLibrary.comprehensions(),
CelEncoderRuntimeLibrary.encoders(),
CelListsRuntimeLibrary.lists(),
CelMathRuntimeLibrary.math(),
CelSetsRuntimeLibrary.sets(),
CelStringRuntimeLibrary.strings());

private static final CelValueProvider LITE_VALUE_PROVIDER =
ProtoMessageLiteValueProvider.newInstance(
dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(),
dev.cel.expr.conformance.proto2.NestedTestAllTypesCelDescriptor.getDescriptor(),
dev.cel.expr.conformance.proto2.TestRequiredCelDescriptor.getDescriptor(),
dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor.getDescriptor(),
dev.cel.expr.conformance.proto3.NestedTestAllTypesCelDescriptor.getDescriptor());

private static final CelTypeProvider LITE_TYPE_PROVIDER =
ProtoMessageTypeProvider.newBuilder()
.addFileDescriptors(
dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor().getFile(),
dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor().getFile(),
dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor())
.build();

enum RuntimeType {
LEGACY,
PLANNER,
LITE
}

static final TypeRegistry CONFORMANCE_TYPE_REGISTRY =
TypeRegistry.newBuilder()
.add(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor())
Expand Down Expand Up @@ -154,26 +197,6 @@ private static CelChecker getChecker(SimpleTest test) throws Exception {
.build();
}

private static CelRuntime getRuntime(SimpleTest test, boolean usePlanner) {
CelRuntimeBuilder builder =
usePlanner ? CelRuntimeImpl.newBuilder() : CelRuntimeFactory.standardCelRuntimeBuilder();

builder
// CEL-Internal-2
.setOptions(OPTIONS)
.addLibraries(CANONICAL_RUNTIME_EXTENSIONS)
.setExtensionRegistry(CONFORMANCE_EXTENSION_REGISTRY)
.addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor())
.addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor())
.addFileTypes(dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor());

if (usePlanner) {
builder.setContainer(CelContainer.ofName(test.getContainer()));
}

return builder.build();
}

private static ImmutableMap<String, Object> getBindings(SimpleTest test) throws Exception {
ImmutableMap.Builder<String, Object> bindings =
ImmutableMap.builderWithExpectedSize(test.getBindingsCount());
Expand Down Expand Up @@ -204,16 +227,7 @@ private static SimpleTest defaultTestMatcherToTrueIfUnset(SimpleTest test) {
private final String name;
private final SimpleTest test;
private final boolean skip;
private final boolean usePlanner;

public ConformanceTest(String name, SimpleTest test, boolean skip, boolean usePlanner) {
this.name = Preconditions.checkNotNull(name);
this.test =
Preconditions.checkNotNull(
defaultTestMatcherToTrueIfUnset(Preconditions.checkNotNull(test)));
this.skip = skip;
this.usePlanner = usePlanner;
}
private final RuntimeType runtimeType;

public String getName() {
return name;
Expand Down Expand Up @@ -251,16 +265,15 @@ public void evaluate() throws Throwable {
return;
}

if (!usePlanner && test.getDisableCheck()) {
// Only planner supports parsed-only evaluation
if (runtimeType.equals(RuntimeType.LEGACY) && test.getDisableCheck()) {
// Only planner and lite runtime support parsed-only evaluation
return;
}

CelRuntime runtime = getRuntime(test, usePlanner);
ExprValue result = null;
CelEvaluationException error = null;
try {
Program program = runtime.createProgram(ast);
Program program = createProgram(ast);
result = toExprValue(program.eval(getBindings(test)), ast.getResultType());
} catch (CelEvaluationException e) {
error = e;
Expand Down Expand Up @@ -295,6 +308,48 @@ public void evaluate() throws Throwable {
}
}

@SuppressWarnings("deprecation") // Intentionally testing the legacy interpreter runtime.
private Program createProgram(CelAbstractSyntaxTree ast) throws CelEvaluationException {
if (runtimeType.equals(RuntimeType.LITE)) {
return CelLiteRuntimeFactory.newLiteRuntimeBuilder()
.setOptions(OPTIONS)
.setStandardFunctions(CelStandardFunctions.ALL_STANDARD_FUNCTIONS)
.addLibraries(CANONICAL_LITE_RUNTIME_EXTENSIONS)
.setValueProvider(LITE_VALUE_PROVIDER)
.setTypeProvider(LITE_TYPE_PROVIDER)
.setContainer(CelContainer.ofName(test.getContainer()))
.build()
.createProgram(ast);
}

CelRuntimeBuilder builder =
runtimeType.equals(RuntimeType.PLANNER)
? CelRuntimeImpl.newBuilder()
: CelRuntimeFactory.standardCelRuntimeBuilder();

builder
// CEL-Internal-2
.setOptions(OPTIONS)
.addLibraries(CANONICAL_RUNTIME_EXTENSIONS)
.setExtensionRegistry(CONFORMANCE_EXTENSION_REGISTRY)
.addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor())
.addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor())
.addFileTypes(dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor());

if (runtimeType.equals(RuntimeType.PLANNER)) {
builder.setContainer(CelContainer.ofName(test.getContainer()));
}

return builder.build().createProgram(ast);
}

ConformanceTest(String name, SimpleTest test, boolean skip, RuntimeType runtimeType) {
this.name = checkNotNull(name);
this.test = checkNotNull(defaultTestMatcherToTrueIfUnset(checkNotNull(test)));
this.skip = skip;
this.runtimeType = checkNotNull(runtimeType);
}

/**
* Conformance-only library providing macros for the {@code block_ext} test suite.
*
Expand Down
Loading
Loading