2828import com .google .protobuf .DescriptorProtos .FileDescriptorSet ;
2929import com .google .protobuf .DynamicMessage ;
3030import com .google .rpc .context .AttributeContext ;
31+ import com .google .testing .junit .testparameterinjector .TestParameter ;
3132import com .google .testing .junit .testparameterinjector .TestParameterInjector ;
3233import com .google .testing .junit .testparameterinjector .TestParameters ;
3334import dev .cel .bundle .Cel ;
@@ -403,28 +404,75 @@ public void trace_withVariableResolver() throws Exception {
403404 }
404405
405406 @ Test
406- public void trace_shortCircuitingDisabled_logicalAndAllBranchesVisited () throws Exception {
407+ public void trace_shortCircuitingDisabled_logicalAndAllBranchesVisited (
408+ @ TestParameter boolean first , @ TestParameter boolean second , @ TestParameter boolean third )
409+ throws Exception {
410+ String expression = String .format ("%s && %s && %s" , first , second , third );
407411 ImmutableList .Builder <Boolean > branchResults = ImmutableList .builder ();
408412 CelEvaluationListener listener =
409413 (expr , res ) -> {
410414 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )) {
411415 branchResults .add ((Boolean ) res );
412416 }
413417 };
418+ Cel celWithShortCircuit =
419+ CelFactory .standardCelBuilder ()
420+ .setOptions (CelOptions .current ().enableShortCircuiting (true ).build ())
421+ .build ();
422+ Cel cel =
423+ CelFactory .standardCelBuilder ()
424+ .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
425+ .build ();
426+ CelAbstractSyntaxTree ast = cel .compile (expression ).getAst ();
427+
428+ boolean result = (boolean ) cel .createProgram (ast ).trace (listener );
429+ boolean shortCircuitedResult =
430+ (boolean )
431+ celWithShortCircuit
432+ .createProgram (celWithShortCircuit .compile (expression ).getAst ())
433+ .eval ();
434+
435+ assertThat (result ).isEqualTo (shortCircuitedResult );
436+ assertThat (branchResults .build ()).containsExactly (first , second , third ).inOrder ();
437+ }
438+
439+ @ Test
440+ @ TestParameters ("{source: 'false && false && x'}" )
441+ @ TestParameters ("{source: 'false && x && false'}" )
442+ @ TestParameters ("{source: 'x && false && false'}" )
443+ public void trace_shortCircuitingDisabledWithUnknownsAndedToFalse_returnsFalse (String source )
444+ throws Exception {
445+ ImmutableList .Builder <Object > branchResults = ImmutableList .builder ();
446+ CelEvaluationListener listener =
447+ (expr , res ) -> {
448+ if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
449+ || expr .identOrDefault ().name ().equals ("x" )) {
450+ if (InterpreterUtil .isUnknown (res )) {
451+ branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
452+ } else {
453+ branchResults .add (res );
454+ }
455+ }
456+ };
414457 Cel cel =
415458 CelFactory .standardCelBuilder ()
459+ .addVar ("x" , SimpleType .BOOL )
416460 .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
417461 .build ();
418- CelAbstractSyntaxTree ast = cel .compile ("false && true && false" ).getAst ();
462+ CelAbstractSyntaxTree ast = cel .compile (source ).getAst ();
419463
420464 boolean result = (boolean ) cel .createProgram (ast ).trace (listener );
421465
422466 assertThat (result ).isFalse ();
423- assertThat (branchResults .build ()).containsExactly (false , true , false );
467+ assertThat (branchResults .build ()).containsExactly (false , false , "x" );
424468 }
425469
426470 @ Test
427- public void trace_shortCircuitingDisabled_logicalAndWithUnknowns () throws Exception {
471+ @ TestParameters ("{source: 'true && true && x'}" )
472+ @ TestParameters ("{source: 'true && x && true'}" )
473+ @ TestParameters ("{source: 'x && true && true'}" )
474+ public void trace_shortCircuitingDisabledWithUnknownAndedToTrue_returnsUnknown (String source )
475+ throws Exception {
428476 ImmutableList .Builder <Object > branchResults = ImmutableList .builder ();
429477 CelEvaluationListener listener =
430478 (expr , res ) -> {
@@ -438,37 +486,53 @@ public void trace_shortCircuitingDisabled_logicalAndWithUnknowns() throws Except
438486 .addVar ("x" , SimpleType .BOOL )
439487 .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
440488 .build ();
441- CelAbstractSyntaxTree ast = cel .compile ("false && false && x" ).getAst ();
489+ CelAbstractSyntaxTree ast = cel .compile (source ).getAst ();
442490
443491 Object unknownResult = cel .createProgram (ast ).trace (listener );
444492
445493 assertThat (InterpreterUtil .isUnknown (unknownResult )).isTrue ();
446- assertThat (branchResults .build ()).containsExactly (false , false , unknownResult );
494+ assertThat (branchResults .build ()).containsExactly (true , true , unknownResult );
447495 }
448496
449497 @ Test
450- public void trace_shortCircuitingDisabled_logicalOrAllBranchesVisited () throws Exception {
498+ public void trace_shortCircuitingDisabled_logicalOrAllBranchesVisited (
499+ @ TestParameter boolean first , @ TestParameter boolean second , @ TestParameter boolean third )
500+ throws Exception {
501+ String expression = String .format ("%s || %s || %s" , first , second , third );
451502 ImmutableList .Builder <Boolean > branchResults = ImmutableList .builder ();
452503 CelEvaluationListener listener =
453504 (expr , res ) -> {
454505 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )) {
455506 branchResults .add ((Boolean ) res );
456507 }
457508 };
509+ Cel celWithShortCircuit =
510+ CelFactory .standardCelBuilder ()
511+ .setOptions (CelOptions .current ().enableShortCircuiting (true ).build ())
512+ .build ();
458513 Cel cel =
459514 CelFactory .standardCelBuilder ()
460515 .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
461516 .build ();
462- CelAbstractSyntaxTree ast = cel .compile ("true || false || true" ).getAst ();
517+ CelAbstractSyntaxTree ast = cel .compile (expression ).getAst ();
463518
464519 boolean result = (boolean ) cel .createProgram (ast ).trace (listener );
465-
466- assertThat (result ).isTrue ();
467- assertThat (branchResults .build ()).containsExactly (true , false , true );
520+ boolean shortCircuitedResult =
521+ (boolean )
522+ celWithShortCircuit
523+ .createProgram (celWithShortCircuit .compile (expression ).getAst ())
524+ .eval ();
525+
526+ assertThat (result ).isEqualTo (shortCircuitedResult );
527+ assertThat (branchResults .build ()).containsExactly (first , second , third ).inOrder ();
468528 }
469529
470530 @ Test
471- public void trace_shortCircuitingDisabled_logicalOrWithUnknowns () throws Exception {
531+ @ TestParameters ("{source: 'false || false || x'}" )
532+ @ TestParameters ("{source: 'false || x || false'}" )
533+ @ TestParameters ("{source: 'x || false || false'}" )
534+ public void trace_shortCircuitingDisabledWithUnknownsOredToFalse_returnsUnknown (String source )
535+ throws Exception {
472536 ImmutableList .Builder <Object > branchResults = ImmutableList .builder ();
473537 CelEvaluationListener listener =
474538 (expr , res ) -> {
@@ -482,14 +546,45 @@ public void trace_shortCircuitingDisabled_logicalOrWithUnknowns() throws Excepti
482546 .addVar ("x" , SimpleType .BOOL )
483547 .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
484548 .build ();
485- CelAbstractSyntaxTree ast = cel .compile ("false || false || x" ).getAst ();
549+ CelAbstractSyntaxTree ast = cel .compile (source ).getAst ();
486550
487551 Object unknownResult = cel .createProgram (ast ).trace (listener );
488552
489553 assertThat (InterpreterUtil .isUnknown (unknownResult )).isTrue ();
490554 assertThat (branchResults .build ()).containsExactly (false , false , unknownResult );
491555 }
492556
557+ @ Test
558+ @ TestParameters ("{source: 'true || true || x'}" )
559+ @ TestParameters ("{source: 'true || x || true'}" )
560+ @ TestParameters ("{source: 'x || true || true'}" )
561+ public void trace_shortCircuitingDisabledWithUnknownOredToTrue_returnsTrue (String source )
562+ throws Exception {
563+ ImmutableList .Builder <Object > branchResults = ImmutableList .builder ();
564+ CelEvaluationListener listener =
565+ (expr , res ) -> {
566+ if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
567+ || expr .identOrDefault ().name ().equals ("x" )) {
568+ if (InterpreterUtil .isUnknown (res )) {
569+ branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
570+ } else {
571+ branchResults .add (res );
572+ }
573+ }
574+ };
575+ Cel cel =
576+ CelFactory .standardCelBuilder ()
577+ .addVar ("x" , SimpleType .BOOL )
578+ .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
579+ .build ();
580+ CelAbstractSyntaxTree ast = cel .compile (source ).getAst ();
581+
582+ boolean result = (boolean ) cel .createProgram (ast ).trace (listener );
583+
584+ assertThat (result ).isTrue ();
585+ assertThat (branchResults .build ()).containsExactly (true , true , "x" );
586+ }
587+
493588 @ Test
494589 public void trace_shortCircuitingDisabled_ternaryAllBranchesVisited () throws Exception {
495590 ImmutableList .Builder <Boolean > branchResults = ImmutableList .builder ();
@@ -537,6 +632,45 @@ public void trace_shortCircuitingDisabled_ternaryWithUnknowns(String source) thr
537632 assertThat (branchResults .build ()).containsExactly (false , unknownResult , true );
538633 }
539634
635+ @ Test
636+ @ TestParameters (
637+ "{expression: 'false ? (1 / 0) > 2 : false', firstVisited: false, secondVisited: false}" )
638+ @ TestParameters (
639+ "{expression: 'false ? (1 / 0) > 2 : true', firstVisited: false, secondVisited: true}" )
640+ @ TestParameters (
641+ "{expression: 'true ? false : (1 / 0) > 2', firstVisited: true, secondVisited: false}" )
642+ @ TestParameters (
643+ "{expression: 'true ? true : (1 / 0) > 2', firstVisited: true, secondVisited: true}" )
644+ public void trace_shortCircuitingDisabled_ternaryWithError (
645+ String expression , boolean firstVisited , boolean secondVisited ) throws Exception {
646+ ImmutableList .Builder <Object > branchResults = ImmutableList .builder ();
647+ CelEvaluationListener listener =
648+ (expr , res ) -> {
649+ if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )) {
650+ branchResults .add (res );
651+ }
652+ };
653+ Cel celWithShortCircuit =
654+ CelFactory .standardCelBuilder ()
655+ .setOptions (CelOptions .current ().enableShortCircuiting (true ).build ())
656+ .build ();
657+ Cel cel =
658+ CelFactory .standardCelBuilder ()
659+ .setOptions (CelOptions .current ().enableShortCircuiting (false ).build ())
660+ .build ();
661+ CelAbstractSyntaxTree ast = cel .compile (expression ).getAst ();
662+
663+ boolean result = (boolean ) cel .createProgram (ast ).trace (listener );
664+ boolean shortCircuitedResult =
665+ (boolean )
666+ celWithShortCircuit
667+ .createProgram (celWithShortCircuit .compile (expression ).getAst ())
668+ .eval ();
669+
670+ assertThat (result ).isEqualTo (shortCircuitedResult );
671+ assertThat (branchResults .build ()).containsExactly (firstVisited , secondVisited ).inOrder ();
672+ }
673+
540674 @ Test
541675 public void standardEnvironmentDisabledForRuntime_throws () throws Exception {
542676 CelCompiler celCompiler =
0 commit comments