(t testing.TB, policy *Policy, ast *cel.Ast)
| 541 | } |
| 542 | |
| 543 | func verifySourceInfoCoverage(t testing.TB, policy *Policy, ast *cel.Ast) { |
| 544 | t.Helper() |
| 545 | info := ast.SourceInfo() |
| 546 | |
| 547 | exprLines := exprLinesFromPolicy(policy) |
| 548 | coveredLines := make(map[int]bool) |
| 549 | ids := ast.NativeRep().IDs() |
| 550 | for id, offset := range info.GetPositions() { |
| 551 | if offset <= 0 { |
| 552 | t.Errorf("id %d has invalid offset %v", id, offset) |
| 553 | } |
| 554 | // Check that each position in the SourceInfo corresponds to a valid AST node. |
| 555 | if !ids[id] { |
| 556 | t.Errorf("id %d not found in AST", id) |
| 557 | } |
| 558 | loc, found := ast.Source().OffsetLocation(offset) |
| 559 | if found { |
| 560 | coveredLines[loc.Line()] = true |
| 561 | } else { |
| 562 | t.Errorf("invalid source location for offset %d", offset) |
| 563 | } |
| 564 | } |
| 565 | // Verify that each source line inside an expression is covered by the at least one node in the |
| 566 | // AST. |
| 567 | for line := range exprLines { |
| 568 | if !coveredLines[line] { |
| 569 | t.Errorf("Line %d expected to be covered by SourceInfo, but was not", line) |
| 570 | } |
| 571 | } |
| 572 | |
| 573 | if t.Failed() { |
| 574 | checked, err := cel.AstToCheckedExpr(ast) |
| 575 | if err != nil { |
| 576 | t.Logf("cel.AstToCheckedExpr() failed: %v", err) |
| 577 | } else { |
| 578 | t.Logf("AST:\n%s", prototext.Format(checked.GetExpr())) |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | // exprLinesFromPolicy returns a set of line numbers within a policy where expressions (variables, |
| 584 | // conditions, etc.) are defined. |
no test coverage detected