Skip to content

Commit 538788f

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Support checked_expression, raw cel_expressions and cel files via bzl macro
PiperOrigin-RevId: 814508933
1 parent 2ed8960 commit 538788f

12 files changed

Lines changed: 614 additions & 199 deletions

testing/testrunner/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,13 @@ cc_library(
117117
name = "runner",
118118
srcs = ["runner_bin.cc"],
119119
deps = [
120+
":cel_expression_source",
120121
":cel_test_context",
121122
":cel_test_factories",
122123
":coverage_index",
123124
":runner_lib",
124125
"//eval/public:cel_expression",
126+
"//internal:status_macros",
125127
"//internal:testing_no_main",
126128
"//runtime",
127129
"@com_google_absl//absl/flags:flag",
@@ -131,9 +133,9 @@ cc_library(
131133
"@com_google_absl//absl/status:statusor",
132134
"@com_google_absl//absl/strings",
133135
"@com_google_absl//absl/strings:str_format",
136+
"@com_google_cel_spec//proto/cel/expr:checked_cc_proto",
134137
"@com_google_cel_spec//proto/cel/expr/conformance/test:suite_cc_proto",
135138
"@com_google_protobuf//:protobuf",
136-
"@com_google_protobuf//src/google/protobuf/io",
137139
],
138140
alwayslink = True,
139141
)

testing/testrunner/cel_cc_test.bzl

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,20 @@
1414

1515
"""Rules for triggering the cc impl of the CEL test runner."""
1616

17+
load("@bazel_skylib//lib:paths.bzl", "paths")
1718
load("@rules_cc//cc:cc_test.bzl", "cc_test")
1819

20+
expr_src_type = struct(
21+
RAW = "raw",
22+
FILE = "file",
23+
CHECKED = "checked",
24+
)
25+
1926
def cel_cc_test(
2027
name,
2128
test_suite = "",
29+
cel_expr = "",
30+
is_raw_expr = False,
2231
filegroup = "",
2332
deps = [],
2433
enable_coverage = False,
@@ -33,6 +42,10 @@ def cel_cc_test(
3342
name: str name for the generated artifact
3443
test_suite: str label of a file containing a test suite. The file should have a
3544
.textproto extension.
45+
cel_expr: The CEL expression source. The meaning of this argument depends on `is_raw_expr`.
46+
is_raw_expr: bool whether the cel_expr is a raw expression string. If False,
47+
cel_expr is treated as a file path. The file type (.cel or .textproto)
48+
is inferred from the extension.
3649
filegroup: str label of a filegroup containing the test suite, the config and the checked
3750
expression.
3851
deps: list of dependencies for the cc_test rule.
@@ -41,7 +54,14 @@ def cel_cc_test(
4154
test_data_path: absolute path of the directory containing the test files. This is needed only
4255
if the test files are not located in the same directory as the BUILD file.
4356
"""
44-
data, test_data_path = _update_data_with_test_files(data, filegroup, test_data_path, test_suite)
57+
data, test_data_path = _update_data_with_test_files(
58+
data,
59+
filegroup,
60+
test_data_path,
61+
test_suite,
62+
cel_expr,
63+
is_raw_expr,
64+
)
4565
args = []
4666

4767
test_data_path = test_data_path.lstrip("/")
@@ -52,23 +72,52 @@ def cel_cc_test(
5272

5373
args.append("--collect_coverage=" + str(enable_coverage))
5474

75+
if cel_expr != "":
76+
expr_source_type = ""
77+
expr_source = ""
78+
if is_raw_expr:
79+
expr_source_type = expr_src_type.RAW
80+
expr_source = "\"" + cel_expr + "\""
81+
else:
82+
_, ext = paths.split_extension(cel_expr)
83+
84+
# The C++ test runner currently only supports parsing expressions from .cel files.
85+
# Support for other CEL source types (e.g., .celpolicy, .yaml) is not yet implemented.
86+
if ext == ".cel":
87+
expr_source_type = expr_src_type.FILE
88+
expr_source = test_data_path + "/" + cel_expr
89+
else:
90+
expr_source_type = expr_src_type.CHECKED
91+
expr_source = "$(location " + cel_expr + ")"
92+
93+
args.append("--expr_source_type=" + expr_source_type)
94+
args.append("--expr_source=" + expr_source)
95+
5596
cc_test(
5697
name = name,
5798
data = data,
5899
args = args,
59100
deps = ["//testing/testrunner:runner"] + deps,
60101
)
61102

62-
def _update_data_with_test_files(data, filegroup, test_data_path, test_suite):
103+
def _update_data_with_test_files(data, filegroup, test_data_path, test_suite, cel_expr, is_raw_expr):
63104
"""Updates the data with the test files."""
64105

65106
if filegroup != "":
66107
data = data + [filegroup]
67108
elif test_data_path != "" and test_data_path != native.package_name():
68109
if test_suite != "":
69110
data = data + [test_data_path + ":" + test_suite]
111+
if cel_expr != "" and not is_raw_expr:
112+
_, ext = paths.split_extension(cel_expr)
113+
if ext == ".cel":
114+
data = data + [test_data_path + ":" + cel_expr]
115+
else:
116+
data = data + [cel_expr]
70117
else:
71118
test_data_path = native.package_name()
72119
if test_suite != "":
73120
data = data + [test_suite]
121+
if cel_expr != "" and not is_raw_expr:
122+
data = data + [cel_expr]
74123
return data, test_data_path

testing/testrunner/cel_test_context.h

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#define THIRD_PARTY_CEL_CPP_TOOLS_TESTRUNNER_CEL_TEST_CONTEXT_H_
1717

1818
#include <memory>
19-
#include <optional>
2019
#include <string>
2120
#include <utility>
2221

@@ -31,27 +30,6 @@
3130
#include "testing/testrunner/cel_expression_source.h"
3231
namespace cel::test {
3332

34-
// Struct to hold optional parameters for `CelTestContext`.
35-
struct CelTestContextOptions {
36-
// The source for the CEL expression to be evaluated in the test.
37-
std::optional<CelExpressionSource> expression_source;
38-
39-
// An optional CEL compiler. This is required for test cases where
40-
// input or output values are themselves CEL expressions that need to be
41-
// resolved at runtime or cel expression source is raw string or cel file.
42-
std::unique_ptr<const cel::Compiler> compiler = nullptr;
43-
44-
// A map of variable names to values that provides default bindings for the
45-
// evaluation.
46-
//
47-
// These bindings can be considered context-wide defaults. If a variable name
48-
// exists in both these custom bindings and in a specific TestCase's input,
49-
// the value from the TestCase will take precedence and override this one.
50-
// This logic is handled by the test runner when it constructs the final
51-
// activation.
52-
absl::flat_hash_map<std::string, cel::expr::Value> custom_bindings;
53-
};
54-
5533
// The context class for a CEL test, holding configurations needed to evaluate
5634
// compiled CEL expressions.
5735
class CelTestContext {
@@ -76,21 +54,18 @@ class CelTestContext {
7654
// });
7755
static std::unique_ptr<CelTestContext> CreateFromCelExpressionBuilder(
7856
std::unique_ptr<google::api::expr::runtime::CelExpressionBuilder>
79-
cel_expression_builder,
80-
CelTestContextOptions options) {
81-
return absl::WrapUnique(new CelTestContext(
82-
std::move(cel_expression_builder), std::move(options)));
57+
cel_expression_builder) {
58+
return absl::WrapUnique(
59+
new CelTestContext(std::move(cel_expression_builder)));
8360
}
8461

8562
// Creates a CelTestContext using a `cel::Runtime`.
8663
//
8764
// The `cel::Runtime` is used to evaluate the CEL expression by managing
8865
// the state needed to generate Program.
8966
static std::unique_ptr<CelTestContext> CreateFromRuntime(
90-
std::unique_ptr<const cel::Runtime> runtime,
91-
CelTestContextOptions options) {
92-
return absl::WrapUnique(
93-
new CelTestContext(std::move(runtime), std::move(options)));
67+
std::unique_ptr<const cel::Runtime> runtime) {
68+
return absl::WrapUnique(new CelTestContext(std::move(runtime)));
9469
}
9570

9671
const cel::Runtime* absl_nullable runtime() const { return runtime_.get(); }
@@ -101,18 +76,35 @@ class CelTestContext {
10176
}
10277

10378
const cel::Compiler* absl_nullable compiler() const {
104-
return cel_test_context_options_.compiler.get();
79+
return compiler_.get();
10580
}
10681

10782
const CelExpressionSource* absl_nullable expression_source() const {
108-
return cel_test_context_options_.expression_source.has_value()
109-
? &cel_test_context_options_.expression_source.value()
110-
: nullptr;
83+
return expression_source_.get();
11184
}
11285

11386
const absl::flat_hash_map<std::string, cel::expr::Value>&
11487
custom_bindings() const {
115-
return cel_test_context_options_.custom_bindings;
88+
return custom_bindings_;
89+
}
90+
91+
// Allows the runner to inject the expression source
92+
// parsed from command-line flags.
93+
void SetExpressionSource(CelExpressionSource source) {
94+
expression_source_ =
95+
std::make_unique<CelExpressionSource>(std::move(source));
96+
}
97+
98+
// Allows the runner to inject an optional CEL compiler.
99+
void SetCompiler(std::unique_ptr<const cel::Compiler> compiler) {
100+
compiler_ = std::move(compiler);
101+
}
102+
103+
// Allows the runner to inject custom bindings.
104+
void SetCustomBindings(
105+
absl::flat_hash_map<std::string, cel::expr::Value>
106+
custom_bindings) {
107+
custom_bindings_ = std::move(custom_bindings);
116108
}
117109

118110
private:
@@ -123,20 +115,31 @@ class CelTestContext {
123115
CelTestContext& operator=(CelTestContext&&) = delete;
124116

125117
// Make the constructors private to enforce the use of the factory methods.
126-
CelTestContext(
118+
explicit CelTestContext(
127119
std::unique_ptr<google::api::expr::runtime::CelExpressionBuilder>
128-
cel_expression_builder,
129-
CelTestContextOptions options)
130-
: cel_test_context_options_(std::move(options)),
131-
cel_expression_builder_(std::move(cel_expression_builder)) {}
132-
133-
CelTestContext(std::unique_ptr<const cel::Runtime> runtime,
134-
CelTestContextOptions options)
135-
: cel_test_context_options_(std::move(options)),
136-
runtime_(std::move(runtime)) {}
137-
138-
// Configuration for the expression to be executed.
139-
CelTestContextOptions cel_test_context_options_;
120+
cel_expression_builder)
121+
: cel_expression_builder_(std::move(cel_expression_builder)) {}
122+
123+
explicit CelTestContext(std::unique_ptr<const cel::Runtime> runtime)
124+
: runtime_(std::move(runtime)) {}
125+
126+
// An optional CEL compiler. This is required for test cases where
127+
// input or output values are themselves CEL expressions that need to be
128+
// resolved at runtime or cel expression source is raw string or cel file.
129+
std::unique_ptr<const cel::Compiler> compiler_ = nullptr;
130+
131+
// A map of variable names to values that provides default bindings for the
132+
// evaluation.
133+
//
134+
// These bindings can be considered context-wide defaults. If a variable name
135+
// exists in both these custom bindings and in a specific TestCase's input,
136+
// the value from the TestCase will take precedence and override this one.
137+
// This logic is handled by the test runner when it constructs the final
138+
// activation.
139+
absl::flat_hash_map<std::string, cel::expr::Value> custom_bindings_;
140+
141+
// The source for the CEL expression to be evaluated in the test.
142+
std::unique_ptr<CelExpressionSource> expression_source_;
140143

141144
// This helps in setting up the environment for building the CEL
142145
// expression. Users should either provide a runtime, or the

testing/testrunner/resources/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package(default_visibility = ["//visibility:public"])
22

33
exports_files(
4-
["test.cel"],
4+
[
5+
"test.cel",
6+
],
57
)
68

79
filegroup(
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# proto-file: third_party/cel/go/tools/compilecli/compile_input.proto
2+
# proto-message: Environment
3+
4+
declarations: {
5+
name: "x"
6+
ident: {
7+
type: { primitive: INT64 }
8+
}
9+
}
10+
declarations: {
11+
name: "y"
12+
ident: {
13+
type: { primitive: INT64 }
14+
}
15+
}

0 commit comments

Comments
 (0)