@@ -22,6 +22,299 @@ import (
2222 proto3pb "github.com/google/cel-go/test/proto3pb"
2323)
2424
25+ func TestInliningOptimizerNoopShadow (t * testing.T ) {
26+ type varExpr struct {
27+ name string
28+ alias string
29+ t * cel.Type
30+ expr string
31+ }
32+ tests := []struct {
33+ name string
34+ expr string
35+ vars []varExpr
36+ inlined string
37+ }{
38+ {
39+ name : "shadow at parent" ,
40+ expr : `[0].exists(shadowed_ident, shadowed_ident == 0)` ,
41+ vars : []varExpr {
42+ {
43+ name : "shadowed_ident" ,
44+ t : cel .IntType ,
45+ expr : "1" ,
46+ },
47+ },
48+ inlined : `[0].exists(shadowed_ident, shadowed_ident == 0)` ,
49+ },
50+ {
51+ name : "shadow in ancestor" ,
52+ expr : `[[1]].all(shadowed_ident, shadowed_ident.all(shadowed, shadowed + 1 == 2))` ,
53+ vars : []varExpr {
54+ {
55+ name : "shadowed_ident" ,
56+ t : cel .IntType ,
57+ expr : "42" ,
58+ },
59+ },
60+ inlined : `[[1]].all(shadowed_ident, shadowed_ident.all(shadowed, shadowed + 1 == 2))` ,
61+ },
62+ }
63+ for _ , tst := range tests {
64+ tc := tst
65+ t .Run (tc .name , func (t * testing.T ) {
66+ opts := []cel.EnvOption {
67+ cel .OptionalTypes (),
68+ cel .EnableMacroCallTracking (),
69+ }
70+ varDecls := make ([]cel.EnvOption , len (tc .vars ))
71+ for i , v := range tc .vars {
72+ varDecls [i ] = cel .Variable (v .name , v .t )
73+ }
74+ e , err := cel .NewEnv (append (varDecls , opts ... )... )
75+ if err != nil {
76+ t .Fatalf ("NewEnv() failed: %v" , err )
77+ }
78+ inlinedVars := []* cel.InlineVariable {}
79+ for _ , v := range tc .vars {
80+ if v .expr == "" {
81+ continue
82+ }
83+ checked , iss := e .Compile (v .expr )
84+ if iss .Err () != nil {
85+ t .Fatalf ("Compile(%q) failed: %v" , v .expr , iss .Err ())
86+ }
87+ if v .alias == "" {
88+ inlinedVars = append (inlinedVars , cel .NewInlineVariable (v .name , checked ))
89+ } else {
90+ inlinedVars = append (inlinedVars , cel .NewInlineVariableWithAlias (v .name , v .alias , checked ))
91+ }
92+ }
93+ checked , iss := e .Compile (tc .expr )
94+ if iss .Err () != nil {
95+ t .Fatalf ("Compile() failed: %v" , iss .Err ())
96+ }
97+ opt , err := cel .NewStaticOptimizer (cel .NewInliningOptimizer (inlinedVars ... ))
98+ if err != nil {
99+ t .Fatalf ("NewStaticOptimizer() failed: %v" , err )
100+ }
101+ optimized , iss := opt .Optimize (e , checked )
102+ if iss .Err () != nil {
103+ t .Fatalf ("Optimize() generated an invalid AST: %v" , iss .Err ())
104+ }
105+ inlined , err := cel .AstToString (optimized )
106+ if err != nil {
107+ t .Fatalf ("cel.AstToString() failed: %v" , err )
108+ }
109+ if inlined != tc .inlined {
110+ t .Errorf ("inlined got %q, wanted %q" , inlined , tc .inlined )
111+ }
112+ })
113+ }
114+ }
115+
116+ func TestInliningOptimizerPresenceTests (t * testing.T ) {
117+ type varExpr struct {
118+ name string
119+ alias string
120+ t * cel.Type
121+ expr string
122+ }
123+ tests := []struct {
124+ name string
125+ expr string
126+ vars []varExpr
127+ inlined string
128+ }{
129+ {
130+ name : "presence with bool literal" ,
131+ expr : `has(msg.single_any.processing_purpose)` ,
132+ vars : []varExpr {
133+ {
134+ name : "msg.single_any.processing_purpose" ,
135+ t : cel .BoolType ,
136+ expr : "true" ,
137+ },
138+ },
139+ inlined : `true != false` ,
140+ },
141+ {
142+ name : "presence with bytes literal" ,
143+ expr : `has(msg.single_any.processing_purpose)` ,
144+ vars : []varExpr {
145+ {
146+ name : "msg.single_any.processing_purpose" ,
147+ t : cel .BytesType ,
148+ expr : "b'abc'" ,
149+ },
150+ },
151+ inlined : `b"\141\142\143".size() != 0` ,
152+ },
153+ {
154+ name : "presence with double literal" ,
155+ expr : `has(msg.single_any.processing_purpose)` ,
156+ vars : []varExpr {
157+ {
158+ name : "msg.single_any.processing_purpose" ,
159+ t : cel .DoubleType ,
160+ expr : "42.0" ,
161+ },
162+ },
163+ inlined : `42.0 != 0.0` ,
164+ },
165+ {
166+ name : "presence with duration literal" ,
167+ expr : `has(msg.single_any.processing_purpose)` ,
168+ vars : []varExpr {
169+ {
170+ name : "msg.single_any.processing_purpose" ,
171+ t : cel .DurationType ,
172+ expr : "duration('1s')" ,
173+ },
174+ },
175+ inlined : `duration("1s") != duration("0s")` ,
176+ },
177+ {
178+ name : "presence with int literal" ,
179+ expr : `has(msg.single_any.processing_purpose)` ,
180+ vars : []varExpr {
181+ {
182+ name : "msg.single_any.processing_purpose" ,
183+ t : cel .IntType ,
184+ expr : "1" ,
185+ },
186+ },
187+ inlined : `1 != 0` ,
188+ },
189+ {
190+ name : "presence with list literal" ,
191+ expr : `has(msg.single_any.processing_purpose)` ,
192+ vars : []varExpr {
193+ {
194+ name : "msg.single_any.processing_purpose" ,
195+ t : cel .ListType (cel .StringType ),
196+ expr : "['foo', 'bar']" ,
197+ },
198+ },
199+ inlined : `["foo", "bar"].size() != 0` ,
200+ },
201+ {
202+ name : "presence with map literal" ,
203+ expr : `has(msg.single_any.processing_purpose)` ,
204+ vars : []varExpr {
205+ {
206+ name : "msg.single_any.processing_purpose" ,
207+ t : cel .MapType (cel .StringType , cel .StringType ),
208+ expr : "{'foo': 'bar'}" ,
209+ },
210+ },
211+ inlined : `{"foo": "bar"}.size() != 0` ,
212+ },
213+ {
214+ name : "presence with string literal" ,
215+ expr : `has(msg.single_any.processing_purpose)` ,
216+ vars : []varExpr {
217+ {
218+ name : "msg.single_any.processing_purpose" ,
219+ t : cel .StringType ,
220+ expr : "'foo'" ,
221+ },
222+ },
223+ inlined : `"foo".size() != 0` ,
224+ },
225+ {
226+ name : "presence with struct literal" ,
227+ expr : `has(msg.single_any.processing_purpose)` ,
228+ vars : []varExpr {
229+ {
230+ name : "msg.single_any.processing_purpose" ,
231+ t : cel .ObjectType ("google.expr.proto3.test.TestAllTypes" ),
232+ expr : "TestAllTypes{single_int64: 1}" ,
233+ },
234+ },
235+ inlined : `google.expr.proto3.test.TestAllTypes{single_int64: 1} != google.expr.proto3.test.TestAllTypes{}` ,
236+ },
237+ {
238+ name : "presence with timestamp literal" ,
239+ expr : `has(msg.single_any.processing_purpose)` ,
240+ vars : []varExpr {
241+ {
242+ name : "msg.single_any.processing_purpose" ,
243+ t : cel .TimestampType ,
244+ expr : "timestamp(123)" ,
245+ },
246+ },
247+ inlined : `timestamp(123) != timestamp(0)` ,
248+ },
249+ {
250+ name : "presence with uint literal" ,
251+ expr : `has(msg.single_any.processing_purpose)` ,
252+ vars : []varExpr {
253+ {
254+ name : "msg.single_any.processing_purpose" ,
255+ t : cel .UintType ,
256+ expr : "1u" ,
257+ },
258+ },
259+ inlined : `1u != 0u` ,
260+ },
261+ }
262+ for _ , tst := range tests {
263+ tc := tst
264+ t .Run (tc .name , func (t * testing.T ) {
265+ opts := []cel.EnvOption {
266+ cel .Container ("google.expr.proto3.test" ),
267+ cel .Types (& proto3pb.TestAllTypes {}),
268+ cel .Variable ("msg" , cel .ObjectType ("google.expr.proto3.test.TestAllTypes" )),
269+ cel .OptionalTypes (),
270+ cel .EnableMacroCallTracking (),
271+ }
272+ varDecls := make ([]cel.EnvOption , len (tc .vars ))
273+ for i , v := range tc .vars {
274+ varDecls [i ] = cel .Variable (v .name , v .t )
275+ }
276+ e , err := cel .NewEnv (append (varDecls , opts ... )... )
277+ if err != nil {
278+ t .Fatalf ("NewEnv() failed: %v" , err )
279+ }
280+ inlinedVars := []* cel.InlineVariable {}
281+ for _ , v := range tc .vars {
282+ if v .expr == "" {
283+ continue
284+ }
285+ checked , iss := e .Compile (v .expr )
286+ if iss .Err () != nil {
287+ t .Fatalf ("Compile(%q) failed: %v" , v .expr , iss .Err ())
288+ }
289+ if v .alias == "" {
290+ inlinedVars = append (inlinedVars , cel .NewInlineVariable (v .name , checked ))
291+ } else {
292+ inlinedVars = append (inlinedVars , cel .NewInlineVariableWithAlias (v .name , v .alias , checked ))
293+ }
294+ }
295+ checked , iss := e .Compile (tc .expr )
296+ if iss .Err () != nil {
297+ t .Fatalf ("Compile() failed: %v" , iss .Err ())
298+ }
299+ opt , err := cel .NewStaticOptimizer (cel .NewInliningOptimizer (inlinedVars ... ))
300+ if err != nil {
301+ t .Fatalf ("NewStaticOptimizer() failed: %v" , err )
302+ }
303+ optimized , iss := opt .Optimize (e , checked )
304+ if iss .Err () != nil {
305+ t .Fatalf ("Optimize() generated an invalid AST: %v" , iss .Err ())
306+ }
307+ inlined , err := cel .AstToString (optimized )
308+ if err != nil {
309+ t .Fatalf ("cel.AstToString() failed: %v" , err )
310+ }
311+ if inlined != tc .inlined {
312+ t .Errorf ("inlined got %q, wanted %q" , inlined , tc .inlined )
313+ }
314+ })
315+ }
316+ }
317+
25318func TestInliningOptimizer (t * testing.T ) {
26319 type varExpr struct {
27320 name string
0 commit comments