|
19 | 19 | import static org.junit.Assert.assertThrows;
|
20 | 20 |
|
21 | 21 | import com.google.common.base.Ascii;
|
| 22 | +import com.google.common.collect.ImmutableMap; |
22 | 23 | import com.google.common.collect.ImmutableSet;
|
23 | 24 | import com.google.common.io.Resources;
|
24 | 25 | import com.google.rpc.context.AttributeContext;
|
|
29 | 30 | import dev.cel.bundle.CelEnvironment.OverloadDecl;
|
30 | 31 | import dev.cel.bundle.CelEnvironment.TypeDecl;
|
31 | 32 | import dev.cel.bundle.CelEnvironment.VariableDecl;
|
| 33 | +import dev.cel.common.CelAbstractSyntaxTree; |
32 | 34 | import dev.cel.common.CelOptions;
|
| 35 | +import dev.cel.common.CelSource; |
| 36 | +import dev.cel.common.ast.CelExpr; |
| 37 | +import dev.cel.common.types.SimpleType; |
| 38 | +import dev.cel.parser.CelUnparserFactory; |
| 39 | +import dev.cel.runtime.CelEvaluationListener; |
| 40 | +import dev.cel.runtime.CelLateFunctionBindings; |
| 41 | +import dev.cel.runtime.CelRuntime.CelFunctionBinding; |
33 | 42 | import java.io.IOException;
|
34 | 43 | import java.net.URL;
|
35 | 44 | import java.util.Optional;
|
| 45 | +import java.util.concurrent.atomic.AtomicReference; |
36 | 46 | import org.junit.Test;
|
37 | 47 | import org.junit.runner.RunWith;
|
38 | 48 |
|
@@ -723,6 +733,83 @@ public void environment_withYamlResource(@TestParameter EnvironmentYamlResourceT
|
723 | 733 | assertThat(environment).isEqualTo(testCase.expectedEnvironment);
|
724 | 734 | }
|
725 | 735 |
|
| 736 | + @Test |
| 737 | + public void lateBoundFunction_evaluate_callExpr() throws Exception { |
| 738 | + String configSource = |
| 739 | + "name: late_bound_function_config\n" |
| 740 | + + "functions:\n" |
| 741 | + + " - name: 'test'\n" |
| 742 | + + " overloads:\n" |
| 743 | + + " - id: 'test_bool'\n" |
| 744 | + + " args:\n" |
| 745 | + + " - type_name: 'bool'\n" |
| 746 | + + " return:\n" |
| 747 | + + " type_name: 'bool'"; |
| 748 | + CelEnvironment celEnvironment = ENVIRONMENT_PARSER.parse(configSource); |
| 749 | + Cel celDetails = |
| 750 | + CelFactory.standardCelBuilder() |
| 751 | + .addVar("a", SimpleType.INT) |
| 752 | + .addVar("b", SimpleType.INT) |
| 753 | + .addVar("c", SimpleType.INT) |
| 754 | + .build(); |
| 755 | + Cel cel = celEnvironment.extend(celDetails, CelOptions.DEFAULT); |
| 756 | + CelAbstractSyntaxTree ast = cel.compile("a < 0 && b < 0 && c < 0 && test(a<0)").getAst(); |
| 757 | + CelLateFunctionBindings bindings = |
| 758 | + CelLateFunctionBindings.from( |
| 759 | + CelFunctionBinding.from("test_bool", Boolean.class, result -> result)); |
| 760 | + |
| 761 | + boolean result = |
| 762 | + (boolean) cel.createProgram(ast).eval(ImmutableMap.of("a", -1, "b", -1, "c", -4), bindings); |
| 763 | + |
| 764 | + assertThat(result).isTrue(); |
| 765 | + } |
| 766 | + |
| 767 | + @Test |
| 768 | + public void lateBoundFunction_trace_callExpr_identifyFalseBranch() throws Exception { |
| 769 | + AtomicReference<CelExpr> capturedExpr = new AtomicReference<>(); |
| 770 | + CelEvaluationListener listener = |
| 771 | + (expr, res) -> { |
| 772 | + if (res instanceof Boolean && !(boolean) res && capturedExpr.get() == null) { |
| 773 | + capturedExpr.set(expr); |
| 774 | + } |
| 775 | + }; |
| 776 | + |
| 777 | + String configSource = |
| 778 | + "name: late_bound_function_config\n" |
| 779 | + + "functions:\n" |
| 780 | + + " - name: 'test'\n" |
| 781 | + + " overloads:\n" |
| 782 | + + " - id: 'test_bool'\n" |
| 783 | + + " args:\n" |
| 784 | + + " - type_name: 'bool'\n" |
| 785 | + + " return:\n" |
| 786 | + + " type_name: 'bool'"; |
| 787 | + |
| 788 | + CelEnvironment celEnvironment = ENVIRONMENT_PARSER.parse(configSource); |
| 789 | + Cel celDetails = |
| 790 | + CelFactory.standardCelBuilder() |
| 791 | + .addVar("a", SimpleType.INT) |
| 792 | + .addVar("b", SimpleType.INT) |
| 793 | + .addVar("c", SimpleType.INT) |
| 794 | + .build(); |
| 795 | + Cel cel = celEnvironment.extend(celDetails, CelOptions.DEFAULT); |
| 796 | + CelAbstractSyntaxTree ast = cel.compile("a < 0 && b < 0 && c < 0 && test(a<0)").getAst(); |
| 797 | + CelLateFunctionBindings bindings = |
| 798 | + CelLateFunctionBindings.from( |
| 799 | + CelFunctionBinding.from("test_bool", Boolean.class, result -> result)); |
| 800 | + |
| 801 | + boolean result = |
| 802 | + (boolean) |
| 803 | + cel.createProgram(ast) |
| 804 | + .trace(ImmutableMap.of("a", -1, "b", 1, "c", -4), bindings, listener); |
| 805 | + |
| 806 | + assertThat(result).isFalse(); |
| 807 | + // Demonstrate that "b < 0" is what caused the expression to be false |
| 808 | + CelAbstractSyntaxTree subtree = |
| 809 | + CelAbstractSyntaxTree.newParsedAst(capturedExpr.get(), CelSource.newBuilder().build()); |
| 810 | + assertThat(CelUnparserFactory.newUnparser().unparse(subtree)).isEqualTo("b < 0"); |
| 811 | + } |
| 812 | + |
726 | 813 | private static String readFile(String path) throws IOException {
|
727 | 814 | URL url = Resources.getResource(Ascii.toLowerCase(path));
|
728 | 815 | return Resources.toString(url, UTF_8);
|
|
0 commit comments