@@ -522,7 +522,7 @@ public void trace_shortCircuitingDisabledWithUnknownsAndedToFalse_returnsFalse(S
522522 (expr , res ) -> {
523523 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
524524 || expr .identOrDefault ().name ().equals ("x" )) {
525- if (InterpreterUtil . isUnknown ( res ) ) {
525+ if (res instanceof CelUnknownSet ) {
526526 branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
527527 } else {
528528 branchResults .add (res );
@@ -577,7 +577,7 @@ public void trace_shortCircuitingDisabledWithUnknownAndedToTrue_returnsUnknown(S
577577 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
578578 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
579579
580- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
580+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
581581 assertThat (branchResults .build ()).containsExactly (true , true , unknownResult );
582582 }
583583
@@ -653,7 +653,7 @@ public void trace_shortCircuitingDisabledWithUnknownsOredToFalse_returnsUnknown(
653653 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
654654 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
655655
656- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
656+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
657657 assertThat (branchResults .build ()).containsExactly (false , false , unknownResult );
658658 }
659659
@@ -668,7 +668,7 @@ public void trace_shortCircuitingDisabledWithUnknownOredToTrue_returnsTrue(Strin
668668 (expr , res ) -> {
669669 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
670670 || expr .identOrDefault ().name ().equals ("x" )) {
671- if (InterpreterUtil . isUnknown ( res ) ) {
671+ if (res instanceof CelUnknownSet ) {
672672 branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
673673 } else {
674674 branchResults .add (res );
@@ -748,7 +748,7 @@ public void trace_shortCircuitingDisabled_ternaryWithUnknowns(String source) thr
748748 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
749749 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
750750
751- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
751+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
752752 assertThat (branchResults .build ()).containsExactly (false , unknownResult , true );
753753 }
754754
@@ -944,4 +944,148 @@ public void trace_shortCircuitingDisabled_logicalOrPrefersFirstError() throws Ex
944944 CelEvaluationException e = assertThrows (CelEvaluationException .class , () -> program .eval ());
945945 assertThat (e ).hasCauseThat ().hasMessageThat ().contains ("error 1" );
946946 }
947+
948+ @ Test
949+ // Field selection
950+ @ TestParameters ("{expression: 'getMsg().single_int32'}" )
951+ @ TestParameters ("{expression: 'getMsg().single_nested_message.bb'}" )
952+ // Binary & unary operators
953+ @ TestParameters ("{expression: 'getMsg().single_int32 == 100'}" )
954+ @ TestParameters ("{expression: 'getMsg().single_int32 + 5 == 10'}" )
955+ @ TestParameters ("{expression: '-getMsg().single_int32 == -10'}" )
956+ // Boolean operators & ternary
957+ @ TestParameters ("{expression: 'true && (getMsg().single_int32 == 100)'}" )
958+ @ TestParameters ("{expression: 'false || (getMsg().single_int32 == 100)'}" )
959+ @ TestParameters ("{expression: '(getMsg().single_int32 == 100) ? \" match\" : \" no-match\" '}" )
960+ // Comprehensions
961+ @ TestParameters ("{expression: '[1, 2, 3].exists(x, x == getMsg().single_int32)'}" )
962+ @ TestParameters ("{expression: '[1, 2, 3].all(x, x > 0 && getMsg().single_int32 > 0)'}" )
963+ @ TestParameters ("{expression: '[1, 2, 3].map(x, x + getMsg().single_int32)'}" )
964+ @ TestParameters ("{expression: '[1, 2, 3].filter(x, x == getMsg().single_int32)'}" )
965+ public void evaluate_customFunctionReturningCelUnknownSet_propagatesUnknown (String expression )
966+ throws Exception {
967+ Cel cel =
968+ runtimeFlavor
969+ .builder ()
970+ .setStandardMacros (CelStandardMacro .STANDARD_MACROS )
971+ .addMessageTypes (TestAllTypes .getDescriptor ())
972+ .addFunctionDeclarations (
973+ CelFunctionDecl .newFunctionDeclaration (
974+ "getMsg" ,
975+ CelOverloadDecl .newGlobalOverload (
976+ "getMsg_overload" ,
977+ StructTypeReference .create (TestAllTypes .getDescriptor ().getFullName ()),
978+ ImmutableList .of ())))
979+ .addFunctionBindings (
980+ CelFunctionBinding .from (
981+ "getMsg_overload" ,
982+ ImmutableList .of (),
983+ args -> CelUnknownSet .create (CelAttribute .create ("custom_msg" ))))
984+ .build ();
985+
986+ Object result = cel .createProgram (cel .compile (expression ).getAst ()).eval ();
987+
988+ assertThat (result ).isInstanceOf (CelUnknownSet .class );
989+ }
990+
991+ @ Test
992+ // Short-circuited boolean operators
993+ @ TestParameters ("{expression: 'false && (getMsg().single_int32 == 100)', expected: false}" )
994+ @ TestParameters ("{expression: 'true || (getMsg().single_int32 == 100)', expected: true}" )
995+ // Short-circuited comprehensions
996+ @ TestParameters (
997+ "{expression: '[1, 2, 3].exists(x, x == 1 || x == getMsg().single_int32)', expected: true}" )
998+ @ TestParameters (
999+ "{expression: '[1, 2, 3].all(x, x == 0 && getMsg().single_int32 > 0)', expected: false}" )
1000+ public void evaluate_customFunctionReturningCelUnknownSet_shortCircuits (
1001+ String expression , boolean expected ) throws Exception {
1002+ Cel cel =
1003+ runtimeFlavor
1004+ .builder ()
1005+ .setStandardMacros (CelStandardMacro .STANDARD_MACROS )
1006+ .addMessageTypes (TestAllTypes .getDescriptor ())
1007+ .addFunctionDeclarations (
1008+ CelFunctionDecl .newFunctionDeclaration (
1009+ "getMsg" ,
1010+ CelOverloadDecl .newGlobalOverload (
1011+ "getMsg_overload" ,
1012+ StructTypeReference .create (TestAllTypes .getDescriptor ().getFullName ()),
1013+ ImmutableList .of ())))
1014+ .addFunctionBindings (
1015+ CelFunctionBinding .from (
1016+ "getMsg_overload" ,
1017+ ImmutableList .of (),
1018+ args -> CelUnknownSet .create (CelAttribute .create ("custom_msg" ))))
1019+ .build ();
1020+
1021+ Object result = cel .createProgram (cel .compile (expression ).getAst ()).eval ();
1022+
1023+ assertThat (result ).isEqualTo (expected );
1024+ }
1025+
1026+ @ Test
1027+ public void evaluate_customFunctionReturningCelUnknownSet_differentArities () throws Exception {
1028+ Cel cel =
1029+ runtimeFlavor
1030+ .builder ()
1031+ .addFunctionDeclarations (
1032+ CelFunctionDecl .newFunctionDeclaration (
1033+ "unkZero" ,
1034+ CelOverloadDecl .newGlobalOverload (
1035+ "unk_zero" , SimpleType .INT , ImmutableList .of ())),
1036+ CelFunctionDecl .newFunctionDeclaration (
1037+ "unkUnary" ,
1038+ CelOverloadDecl .newGlobalOverload ("unk_unary" , SimpleType .INT , SimpleType .INT )),
1039+ CelFunctionDecl .newFunctionDeclaration (
1040+ "unkBinary" ,
1041+ CelOverloadDecl .newGlobalOverload (
1042+ "unk_binary" , SimpleType .INT , SimpleType .INT , SimpleType .INT )),
1043+ CelFunctionDecl .newFunctionDeclaration (
1044+ "unkMember" ,
1045+ CelOverloadDecl .newMemberOverload (
1046+ "unk_member" , SimpleType .INT , SimpleType .STRING , SimpleType .INT )),
1047+ CelFunctionDecl .newFunctionDeclaration (
1048+ "unkVarargs" ,
1049+ CelOverloadDecl .newGlobalOverload (
1050+ "unk_varargs" ,
1051+ SimpleType .INT ,
1052+ SimpleType .INT ,
1053+ SimpleType .INT ,
1054+ SimpleType .INT )))
1055+ .addFunctionBindings (
1056+ CelFunctionBinding .from (
1057+ "unk_zero" ,
1058+ ImmutableList .of (),
1059+ args -> CelUnknownSet .create (CelAttribute .create ("attr_zero" ))),
1060+ CelFunctionBinding .from (
1061+ "unk_unary" ,
1062+ Long .class ,
1063+ arg -> CelUnknownSet .create (CelAttribute .create ("attr_unary" ))),
1064+ CelFunctionBinding .from (
1065+ "unk_binary" ,
1066+ Long .class ,
1067+ Long .class ,
1068+ (a , b ) -> CelUnknownSet .create (CelAttribute .create ("attr_binary" ))),
1069+ CelFunctionBinding .from (
1070+ "unk_member" ,
1071+ String .class ,
1072+ Long .class ,
1073+ (target , arg ) -> CelUnknownSet .create (CelAttribute .create ("attr_member" ))),
1074+ CelFunctionBinding .from (
1075+ "unk_varargs" ,
1076+ ImmutableList .of (Long .class , Long .class , Long .class ),
1077+ args -> CelUnknownSet .create (CelAttribute .create ("attr_varargs" ))))
1078+ .build ();
1079+
1080+ assertThat (cel .createProgram (cel .compile ("unkZero() + 1" ).getAst ()).eval ())
1081+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_zero" )));
1082+ assertThat (cel .createProgram (cel .compile ("unkUnary(1) + 1" ).getAst ()).eval ())
1083+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_unary" )));
1084+ assertThat (cel .createProgram (cel .compile ("unkBinary(1, 2) + 1" ).getAst ()).eval ())
1085+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_binary" )));
1086+ assertThat (cel .createProgram (cel .compile ("'target'.unkMember(1) + 1" ).getAst ()).eval ())
1087+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_member" )));
1088+ assertThat (cel .createProgram (cel .compile ("unkVarargs(1, 2, 3) + 1" ).getAst ()).eval ())
1089+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_varargs" )));
1090+ }
9471091}
0 commit comments