Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 36faf32

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Support checked_expression, raw cel_expressions and cel files via bzl macro
PiperOrigin-RevId: 807110591
1 parent 31f0895 commit 36faf32

File tree

10 files changed

+489
-28
lines changed

10 files changed

+489
-28
lines changed

testing/testrunner/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,21 @@ cc_library(
113113
name = "runner",
114114
srcs = ["runner_bin.cc"],
115115
deps = [
116+
":cel_expression_source",
116117
":cel_test_context",
117118
":cel_test_factories",
118119
":runner_lib",
120+
"//internal:status_macros",
119121
"//internal:testing_no_main",
120122
"@com_google_absl//absl/flags:flag",
121123
"@com_google_absl//absl/log:absl_check",
122124
"@com_google_absl//absl/log:absl_log",
123125
"@com_google_absl//absl/status",
124126
"@com_google_absl//absl/status:statusor",
125127
"@com_google_absl//absl/strings",
128+
"@com_google_cel_spec//proto/cel/expr:checked_cc_proto",
126129
"@com_google_cel_spec//proto/cel/expr/conformance/test:suite_cc_proto",
127130
"@com_google_protobuf//:protobuf",
128-
"@com_google_protobuf//src/google/protobuf/io",
129131
],
130132
alwayslink = True,
131133
)

testing/testrunner/cel_cc_test.bzl

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
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

1920
def cel_cc_test(
2021
name,
2122
test_suite = "",
23+
cel_expr = "",
24+
is_raw_expr = False,
2225
filegroup = "",
2326
deps = [],
2427
test_data_path = "",
@@ -32,14 +35,25 @@ def cel_cc_test(
3235
name: str name for the generated artifact
3336
test_suite: str label of a file containing a test suite. The file should have a
3437
.textproto extension.
38+
cel_expr: The CEL expression source. The meaning of this argument depends on `is_raw_expr`.
39+
is_raw_expr: bool whether the cel_expr is a raw expression string. If False,
40+
cel_expr is treated as a file path. The file type (.cel or .textproto)
41+
is inferred from the extension.
3542
filegroup: str label of a filegroup containing the test suite, the config and the checked
3643
expression.
3744
deps: list of dependencies for the cc_test rule.
3845
data: list of data dependencies for the cc_test rule.
3946
test_data_path: absolute path of the directory containing the test files. This is needed only
4047
if the test files are not located in the same directory as the BUILD file.
4148
"""
42-
data, test_data_path = _update_data_with_test_files(data, filegroup, test_data_path, test_suite)
49+
data, test_data_path = _update_data_with_test_files(
50+
data,
51+
filegroup,
52+
test_data_path,
53+
test_suite,
54+
cel_expr,
55+
is_raw_expr,
56+
)
4357
args = []
4458

4559
test_data_path = test_data_path.lstrip("/")
@@ -48,23 +62,45 @@ def cel_cc_test(
4862
test_suite = test_data_path + "/" + test_suite
4963
args.append("--test_suite_path=" + test_suite)
5064

65+
if cel_expr != "":
66+
expr_source = ""
67+
cel_expression = ""
68+
if is_raw_expr:
69+
expr_source = "raw"
70+
cel_expression = "\"" + cel_expr + "\""
71+
else:
72+
_, ext = paths.split_extension(cel_expr)
73+
resolved_path = test_data_path + "/" + cel_expr
74+
if ext == ".cel":
75+
expr_source = "file"
76+
else:
77+
expr_source = "checked"
78+
cel_expression = resolved_path
79+
80+
args.append("--expr_source=" + expr_source)
81+
args.append("--cel_expression=" + cel_expression)
82+
5183
cc_test(
5284
name = name,
5385
data = data,
5486
args = args,
5587
deps = ["//testing/testrunner:runner"] + deps,
5688
)
5789

58-
def _update_data_with_test_files(data, filegroup, test_data_path, test_suite):
90+
def _update_data_with_test_files(data, filegroup, test_data_path, test_suite, cel_expr, is_raw_expr):
5991
"""Updates the data with the test files."""
6092

6193
if filegroup != "":
6294
data = data + [filegroup]
6395
elif test_data_path != "" and test_data_path != native.package_name():
6496
if test_suite != "":
6597
data = data + [test_data_path + ":" + test_suite]
98+
if cel_expr != "" and not is_raw_expr:
99+
data = data + [test_data_path + ":" + cel_expr]
66100
else:
67101
test_data_path = native.package_name()
68102
if test_suite != "":
69103
data = data + [test_suite]
104+
if cel_expr != "" and not is_raw_expr:
105+
data = data + [cel_expr]
70106
return data, test_data_path

testing/testrunner/cel_test_context.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ class CelTestContext {
115115
return cel_test_context_options_.custom_bindings;
116116
}
117117

118+
// Allows the runner to inject the expression source
119+
// parsed from command-line flags.
120+
void SetExpressionSource(CelExpressionSource source) {
121+
cel_test_context_options_.expression_source = std::move(source);
122+
}
123+
118124
private:
119125
// Delete copy and move constructors.
120126
CelTestContext(const CelTestContext&) = delete;

testing/testrunner/resources/BUILD

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

33
exports_files(
4-
["test.cel"],
4+
[
5+
"test.cel",
6+
"subtraction_checked_expr.textproto",
7+
"subtraction_checked_expr.binarypb",
8+
],
59
)
610

711
filegroup(
812
name = "resources",
913
srcs = glob([
1014
"*.textproto",
15+
"*.binarypb",
1116
]),
1217
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"2_-_"
2+
x"
3+
y
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# proto-file: google3/google/api/expr/checked.proto
2+
# proto-message: google.api.expr.CheckedExpr
3+
4+
# CEL expression: x - y (where x is Int64, y is Int64)
5+
6+
expr {
7+
id: 1
8+
call_expr {
9+
function: "_-_"
10+
args {
11+
id: 2
12+
ident_expr {
13+
name: "x"
14+
}
15+
}
16+
args {
17+
id: 3
18+
ident_expr {
19+
name: "y"
20+
}
21+
}
22+
}
23+
}
24+
# Type information confirming the final result is an Int64
25+
type_map {
26+
key: 1
27+
value {
28+
primitive: INT64
29+
}
30+
}
31+
type_map {
32+
key: 2
33+
value {
34+
primitive: INT64
35+
}
36+
}
37+
type_map {
38+
key: 3
39+
value {
40+
primitive: INT64
41+
}
42+
}

testing/testrunner/runner_bin.cc

Lines changed: 120 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,44 @@
1818
#include <functional>
1919
#include <ios>
2020
#include <memory>
21+
#include <sstream>
2122
#include <string>
22-
#include <string_view>
2323
#include <utility>
2424

25+
#include "cel/expr/checked.pb.h"
2526
#include "absl/flags/flag.h"
2627
#include "absl/log/absl_check.h"
2728
#include "absl/log/absl_log.h"
2829
#include "absl/status/status.h"
2930
#include "absl/status/statusor.h"
31+
#include "absl/strings/match.h"
3032
#include "absl/strings/str_cat.h"
33+
#include "absl/strings/string_view.h"
34+
#include "internal/status_macros.h"
3135
#include "internal/testing.h"
36+
#include "testing/testrunner/cel_expression_source.h"
3237
#include "testing/testrunner/cel_test_context.h"
3338
#include "testing/testrunner/cel_test_factories.h"
3439
#include "testing/testrunner/runner_lib.h"
3540
#include "cel/expr/conformance/test/suite.pb.h"
36-
#include "google/protobuf/io/zero_copy_stream_impl.h"
3741
#include "google/protobuf/text_format.h"
3842

3943
ABSL_FLAG(std::string, test_suite_path, "",
4044
"The path to the file containing the test suite to run.");
45+
ABSL_FLAG(std::string, expr_source, "",
46+
"The kind of expression source: 'raw', 'file', or 'checked'.");
47+
ABSL_FLAG(std::string, cel_expression, "",
48+
"The value of the CEL expression source. For 'raw', it's the "
49+
"expression string. For 'file' and 'checked', it's the file path.");
4150

4251
namespace {
4352

4453
using ::cel::expr::conformance::test::TestCase;
4554
using ::cel::expr::conformance::test::TestSuite;
55+
using ::cel::test::CelExpressionSource;
56+
using ::cel::test::CelTestContextOptions;
4657
using ::cel::test::TestRunner;
58+
using ::cel::expr::CheckedExpr;
4759

4860
class CelTest : public testing::Test {
4961
public:
@@ -73,21 +85,65 @@ absl::Status RegisterTests(const TestSuite& test_suite,
7385
return absl::OkStatus();
7486
}
7587

76-
TestSuite ReadTestSuiteFromPath(std::string_view test_suite_path) {
77-
TestSuite test_suite;
78-
{
79-
std::ifstream in;
80-
in.open(std::string(test_suite_path),
81-
std::ios_base::in | std::ios_base::binary);
82-
if (!in.is_open()) {
83-
ABSL_LOG(FATAL) << "failed to open file: " << test_suite_path;
84-
}
85-
google::protobuf::io::IstreamInputStream stream(&in);
86-
if (!google::protobuf::TextFormat::Parse(&stream, &test_suite)) {
87-
ABSL_LOG(FATAL) << "failed to parse file: " << test_suite_path;
88-
}
88+
absl::StatusOr<std::string> ReadFileToString(absl::string_view file_path) {
89+
std::ifstream file_stream{std::string(file_path)};
90+
if (!file_stream.is_open()) {
91+
return absl::NotFoundError(
92+
absl::StrCat("Unable to open file: ", file_path));
93+
}
94+
std::stringstream buffer;
95+
buffer << file_stream.rdbuf();
96+
return buffer.str();
97+
}
98+
99+
template <typename T>
100+
absl::StatusOr<T> ReadTextProtoFromFile(absl::string_view file_path) {
101+
CEL_ASSIGN_OR_RETURN(std::string contents, ReadFileToString(file_path));
102+
T message;
103+
if (!google::protobuf::TextFormat::ParseFromString(contents, &message)) {
104+
return absl::InternalError(absl::StrCat(
105+
"Failed to parse text-format proto from file: ", file_path));
89106
}
90-
return test_suite;
107+
return message;
108+
}
109+
110+
absl::StatusOr<CheckedExpr> ReadBinaryProtoFromFile(
111+
absl::string_view file_path) {
112+
CheckedExpr message;
113+
std::ifstream file_stream{std::string(file_path), std::ios::binary};
114+
if (!file_stream.is_open()) {
115+
return absl::NotFoundError(
116+
absl::StrCat("Unable to open file: ", file_path));
117+
}
118+
if (!message.ParseFromIstream(&file_stream)) {
119+
return absl::InternalError(
120+
absl::StrCat("Failed to parse binary proto from file: ", file_path));
121+
}
122+
return message;
123+
}
124+
125+
TestSuite ReadTestSuiteFromPath(absl::string_view test_suite_path) {
126+
absl::StatusOr<TestSuite> test_suite_or =
127+
ReadTextProtoFromFile<TestSuite>(test_suite_path);
128+
129+
if (!test_suite_or.ok()) {
130+
ABSL_LOG(FATAL) << "Failed to load test suite from " << test_suite_path
131+
<< ": " << test_suite_or.status();
132+
}
133+
return *std::move(test_suite_or);
134+
}
135+
136+
absl::StatusOr<CheckedExpr> ReadCheckedExprFromFile(
137+
absl::string_view file_path) {
138+
if (absl::EndsWith(file_path, ".textproto")) {
139+
return ReadTextProtoFromFile<CheckedExpr>(file_path);
140+
}
141+
if (absl::EndsWith(file_path, ".binarypb")) {
142+
return ReadBinaryProtoFromFile(file_path);
143+
}
144+
return absl::InvalidArgumentError(absl::StrCat(
145+
"Unknown file extension for checked expression. ",
146+
"Please use .textproto, .textpb, .pb, or .binarypb: ", file_path));
91147
}
92148

93149
TestSuite GetTestSuite() {
@@ -108,21 +164,61 @@ TestSuite GetTestSuite() {
108164
}
109165
return test_suite_factory();
110166
}
167+
168+
absl::StatusOr<CelTestContextOptions> GetExpressionSourceOptions() {
169+
CelTestContextOptions options;
170+
if (absl::GetFlag(FLAGS_expr_source).empty()) {
171+
return options;
172+
}
173+
174+
std::string kind = absl::GetFlag(FLAGS_expr_source);
175+
std::string value = absl::GetFlag(FLAGS_cel_expression);
176+
177+
if (kind == "raw") {
178+
options.expression_source = CelExpressionSource::FromRawExpression(value);
179+
} else if (kind == "file") {
180+
options.expression_source = CelExpressionSource::FromCelFile(value);
181+
} else if (kind == "checked") {
182+
CEL_ASSIGN_OR_RETURN(CheckedExpr checked_expr,
183+
ReadCheckedExprFromFile(value));
184+
options.expression_source =
185+
CelExpressionSource::FromCheckedExpr(std::move(checked_expr));
186+
} else {
187+
ABSL_LOG(FATAL) << "Unknown expression kind: " << kind;
188+
}
189+
return options;
190+
}
191+
111192
} // namespace
112193

113194
int main(int argc, char** argv) {
114195
testing::InitGoogleTest(&argc, argv);
115-
196+
absl::StatusOr<CelTestContextOptions> test_context_options =
197+
GetExpressionSourceOptions();
198+
if (!test_context_options.ok()) {
199+
ABSL_LOG(FATAL) << "Failed to get expression source options: "
200+
<< test_context_options.status();
201+
}
116202
// Create a test context using the factory function returned by the global
117203
// factory function provider which was initialized by the user.
118-
absl::StatusOr<std::unique_ptr<cel::test::CelTestContext>> cel_test_context =
119-
cel::test::internal::GetCelTestContextFactory()();
120-
if (!cel_test_context.ok()) {
121-
ABSL_LOG(FATAL) << "Failed to create CEL test context: "
122-
<< cel_test_context.status();
204+
absl::StatusOr<std::unique_ptr<cel::test::CelTestContext>>
205+
cel_test_context_or = cel::test::internal::GetCelTestContextFactory()();
206+
if (!cel_test_context_or.ok()) {
207+
ABSL_LOG(FATAL) << "Failed to create CEL test context from factory: "
208+
<< cel_test_context_or.status();
123209
}
124-
auto test_runner =
125-
std::make_shared<TestRunner>(std::move(cel_test_context.value()));
210+
std::unique_ptr<cel::test::CelTestContext> cel_test_context =
211+
std::move(cel_test_context_or.value());
212+
213+
// Inject the expression source from the flags into the context
214+
// using the SetExpressionSource() method if expression source is
215+
// provided via bzl macro.
216+
if (test_context_options->expression_source.has_value()) {
217+
cel_test_context->SetExpressionSource(
218+
std::move(*test_context_options->expression_source));
219+
}
220+
221+
auto test_runner = std::make_shared<TestRunner>(std::move(cel_test_context));
126222
ABSL_CHECK_OK(RegisterTests(GetTestSuite(), test_runner));
127223

128224
return RUN_ALL_TESTS();

0 commit comments

Comments
 (0)