1717import static com .google .common .truth .Truth .assertThat ;
1818import static org .junit .Assert .assertThrows ;
1919
20+ import com .google .common .collect .ImmutableList ;
21+ import com .google .common .collect .ImmutableMap ;
22+ import com .google .testing .junit .testparameterinjector .TestParameter ;
2023import com .google .testing .junit .testparameterinjector .TestParameterInjector ;
2124import com .google .testing .junit .testparameterinjector .TestParameters ;
2225import dev .cel .common .CelAbstractSyntaxTree ;
2326import dev .cel .common .CelFunctionDecl ;
27+ import dev .cel .common .CelOptions ;
2428import dev .cel .common .CelOverloadDecl ;
2529import dev .cel .common .CelValidationException ;
2630import dev .cel .common .types .SimpleType ;
31+ import dev .cel .common .types .StructTypeReference ;
2732import dev .cel .compiler .CelCompiler ;
2833import dev .cel .compiler .CelCompilerFactory ;
2934import dev .cel .parser .CelStandardMacro ;
3035import dev .cel .runtime .CelRuntime ;
3136import dev .cel .runtime .CelRuntime .CelFunctionBinding ;
3237import dev .cel .runtime .CelRuntimeFactory ;
38+ import dev .cel .testing .testdata .proto3 .TestAllTypesProto .TestAllTypes ;
3339import java .util .Arrays ;
40+ import java .util .concurrent .atomic .AtomicInteger ;
3441import org .junit .Test ;
3542import org .junit .runner .RunWith ;
3643
@@ -45,25 +52,46 @@ public final class CelBindingsExtensionsTest {
4552
4653 private static final CelRuntime RUNTIME = CelRuntimeFactory .standardCelRuntimeBuilder ().build ();
4754
55+ private enum BindingTestCase {
56+ BOOL_LITERAL ("cel.bind(t, true, t)" ),
57+ STRING_CONCAT ("cel.bind(msg, \" hello\" , msg + msg + msg) == \" hellohellohello\" " ),
58+ NESTED_BINDS ("cel.bind(t1, true, cel.bind(t2, true, t1 && t2))" ),
59+ NESTED_BINDS_SPECIFIER_ONLY (
60+ "cel.bind(x, cel.bind(x, \" a\" , x + x), x + \" :\" + x) == \" aa:aa\" " ),
61+ NESTED_BINDS_SPECIFIER_AND_VALUE (
62+ "cel.bind(x, cel.bind(x, \" a\" , x + x), cel.bind(y, x + x, y + \" :\" + y)) =="
63+ + " \" aaaa:aaaa\" " ),
64+ BIND_WITH_EXISTS_TRUE (
65+ "cel.bind(valid_elems, [1, 2, 3], [3, 4, 5].exists(e, e in valid_elems))" ),
66+ BIND_WITH_EXISTS_FALSE ("cel.bind(valid_elems, [1, 2, 3], ![4, 5].exists(e, e in valid_elems))" );
67+
68+ private final String source ;
69+
70+ BindingTestCase (String source ) {
71+ this .source = source ;
72+ }
73+ }
74+
4875 @ Test
49- @ TestParameters ("{expr: 'cel.bind(t, true, t)', expectedResult: true}" )
50- @ TestParameters (
51- "{expr: 'cel.bind(msg, \" hello\" , msg + msg + msg) == \" hellohellohello\" ',"
52- + " expectedResult: true}" )
53- @ TestParameters (
54- "{expr: 'cel.bind(t1, true, cel.bind(t2, true, t1 && t2))', expectedResult: true}" )
55- @ TestParameters (
56- "{expr: 'cel.bind(valid_elems, [1, 2, 3], [3, 4, 5]"
57- + ".exists(e, e in valid_elems))', expectedResult: true}" )
58- @ TestParameters (
59- "{expr: 'cel.bind(valid_elems, [1, 2, 3], ![4, 5].exists(e, e in valid_elems))',"
60- + " expectedResult: true}" )
61- public void binding_success (String expr , boolean expectedResult ) throws Exception {
62- CelAbstractSyntaxTree ast = COMPILER .compile (expr ).getAst ();
76+ public void binding_success (@ TestParameter BindingTestCase testCase ) throws Exception {
77+ CelAbstractSyntaxTree ast = COMPILER .compile (testCase .source ).getAst ();
6378 CelRuntime .Program program = RUNTIME .createProgram (ast );
64- Object evaluatedResult = program .eval ();
79+ boolean evaluatedResult = (boolean ) program .eval ();
80+
81+ assertThat (evaluatedResult ).isTrue ();
82+ }
83+
84+ @ Test
85+ public void binding_lazyEval_success (@ TestParameter BindingTestCase testCase ) throws Exception {
86+ CelAbstractSyntaxTree ast = COMPILER .compile (testCase .source ).getAst ();
87+ CelRuntime .Program program =
88+ CelRuntimeFactory .standardCelRuntimeBuilder ()
89+ .setOptions (CelOptions .current ().enableComprehensionLazyEval (true ).build ())
90+ .build ()
91+ .createProgram (ast );
92+ boolean evaluatedResult = (boolean ) program .eval ();
6593
66- assertThat (evaluatedResult ).isEqualTo ( expectedResult );
94+ assertThat (evaluatedResult ).isTrue ( );
6795 }
6896
6997 @ Test
@@ -105,4 +133,113 @@ public void binding_throwsCompilationException(String expr) throws Exception {
105133
106134 assertThat (e ).hasMessageThat ().contains ("cel.bind() variable name must be a simple identifier" );
107135 }
136+
137+ @ Test
138+ @ SuppressWarnings ("Immutable" ) // Test only
139+ public void lazyBinding_bindingVarNeverReferenced () throws Exception {
140+ CelCompiler celCompiler =
141+ CelCompilerFactory .standardCelCompilerBuilder ()
142+ .setStandardMacros (CelStandardMacro .HAS )
143+ .addMessageTypes (TestAllTypes .getDescriptor ())
144+ .addVar ("msg" , StructTypeReference .create (TestAllTypes .getDescriptor ().getFullName ()))
145+ .addLibraries (CelExtensions .bindings ())
146+ .addFunctionDeclarations (
147+ CelFunctionDecl .newFunctionDeclaration (
148+ "get_true" ,
149+ CelOverloadDecl .newGlobalOverload ("get_true_overload" , SimpleType .BOOL )))
150+ .build ();
151+ AtomicInteger invocation = new AtomicInteger ();
152+ CelRuntime celRuntime =
153+ CelRuntimeFactory .standardCelRuntimeBuilder ()
154+ .addMessageTypes (TestAllTypes .getDescriptor ())
155+ .setOptions (CelOptions .current ().enableComprehensionLazyEval (true ).build ())
156+ .addFunctionBindings (
157+ CelFunctionBinding .from (
158+ "get_true_overload" ,
159+ ImmutableList .of (),
160+ arg -> {
161+ invocation .getAndIncrement ();
162+ return true ;
163+ }))
164+ .build ();
165+ CelAbstractSyntaxTree ast =
166+ celCompiler .compile ("cel.bind(t, get_true(), has(msg.single_int64) ? t : false)" ).getAst ();
167+
168+ boolean result =
169+ (boolean )
170+ celRuntime
171+ .createProgram (ast )
172+ .eval (ImmutableMap .of ("msg" , TestAllTypes .getDefaultInstance ()));
173+
174+ assertThat (result ).isFalse ();
175+ assertThat (invocation .get ()).isEqualTo (0 );
176+ }
177+
178+ @ Test
179+ @ SuppressWarnings ("Immutable" ) // Test only
180+ public void lazyBinding_accuInitEvaluatedOnce () throws Exception {
181+ CelCompiler celCompiler =
182+ CelCompilerFactory .standardCelCompilerBuilder ()
183+ .addLibraries (CelExtensions .bindings ())
184+ .addFunctionDeclarations (
185+ CelFunctionDecl .newFunctionDeclaration (
186+ "get_true" ,
187+ CelOverloadDecl .newGlobalOverload ("get_true_overload" , SimpleType .BOOL )))
188+ .build ();
189+ AtomicInteger invocation = new AtomicInteger ();
190+ CelRuntime celRuntime =
191+ CelRuntimeFactory .standardCelRuntimeBuilder ()
192+ .setOptions (CelOptions .current ().enableComprehensionLazyEval (true ).build ())
193+ .addFunctionBindings (
194+ CelFunctionBinding .from (
195+ "get_true_overload" ,
196+ ImmutableList .of (),
197+ arg -> {
198+ invocation .getAndIncrement ();
199+ return true ;
200+ }))
201+ .build ();
202+ CelAbstractSyntaxTree ast =
203+ celCompiler .compile ("cel.bind(t, get_true(), t && t && t && t)" ).getAst ();
204+
205+ boolean result = (boolean ) celRuntime .createProgram (ast ).eval ();
206+
207+ assertThat (result ).isTrue ();
208+ assertThat (invocation .get ()).isEqualTo (1 );
209+ }
210+
211+ @ Test
212+ @ SuppressWarnings ("Immutable" ) // Test only
213+ public void lazyBinding_withNestedBinds () throws Exception {
214+ CelCompiler celCompiler =
215+ CelCompilerFactory .standardCelCompilerBuilder ()
216+ .addLibraries (CelExtensions .bindings ())
217+ .addFunctionDeclarations (
218+ CelFunctionDecl .newFunctionDeclaration (
219+ "get_true" ,
220+ CelOverloadDecl .newGlobalOverload ("get_true_overload" , SimpleType .BOOL )))
221+ .build ();
222+ AtomicInteger invocation = new AtomicInteger ();
223+ CelRuntime celRuntime =
224+ CelRuntimeFactory .standardCelRuntimeBuilder ()
225+ .setOptions (CelOptions .current ().enableComprehensionLazyEval (true ).build ())
226+ .addFunctionBindings (
227+ CelFunctionBinding .from (
228+ "get_true_overload" ,
229+ ImmutableList .of (),
230+ arg -> {
231+ invocation .getAndIncrement ();
232+ return true ;
233+ }))
234+ .build ();
235+ CelAbstractSyntaxTree ast =
236+ celCompiler
237+ .compile ("cel.bind(t1, get_true(), cel.bind(t2, get_true(), t1 && t2 && t1 && t2))" )
238+ .getAst ();
239+
240+ boolean result = (boolean ) celRuntime .createProgram (ast ).eval ();
241+
242+ assertThat (result ).isTrue ();
243+ assertThat (invocation .get ()).isEqualTo (2 );
244+ }
108245}
0 commit comments