| 98 | } |
| 99 | |
| 100 | void TraverseAndCalculateCoverage( |
| 101 | const CheckedExpr& checked_expr, const NavigableProtoAstNode& node, |
| 102 | const absl::flat_hash_map<int64_t, CoverageIndex::NodeCoverageStats>& |
| 103 | stats_map, |
| 104 | bool log_unencountered, std::string preceeding_tabs, |
| 105 | CoverageIndex::CoverageReport& report, std::string& dot_graph) { |
| 106 | int64_t node_id = node.expr()->id(); |
| 107 | |
| 108 | const CoverageIndex::NodeCoverageStats& stats = stats_map.at(node_id); |
| 109 | report.nodes++; |
| 110 | |
| 111 | absl::StatusOr<std::string> unparsed = |
| 112 | google::api::expr::Unparse(*node.expr()); |
| 113 | std::string expr_text = unparsed.ok() ? *unparsed : "unparse_failed"; |
| 114 | |
| 115 | bool is_interesting_bool_node = |
| 116 | stats.is_boolean_node && !node.expr()->has_const_expr() && |
| 117 | (!node.expr()->has_call_expr() || |
| 118 | node.expr()->call_expr().function() != "cel.@block"); |
| 119 | |
| 120 | absl::string_view node_coverage_style = kUncoveredNodeStyle; |
| 121 | if (stats.covered) { |
| 122 | if (is_interesting_bool_node) { |
| 123 | if (stats.has_true_branch && stats.has_false_branch) { |
| 124 | node_coverage_style = kCompletelyCoveredNodeStyle; |
| 125 | } else { |
| 126 | node_coverage_style = kPartiallyCoveredNodeStyle; |
| 127 | } |
| 128 | } else { |
| 129 | node_coverage_style = kCompletelyCoveredNodeStyle; |
| 130 | } |
| 131 | } |
| 132 | std::string escaped_expr_text = EscapeSpecialCharacters(expr_text); |
| 133 | dot_graph += absl::StrFormat( |
| 134 | "%d [shape=record, %s, label=\"{<1> exprID: %d | <2> %s} | <3> %s\"];\n", |
| 135 | node_id, node_coverage_style, node_id, KindToString(node), |
| 136 | escaped_expr_text); |
| 137 | |
| 138 | bool node_covered = stats.covered; |
| 139 | if (node_covered) { |
| 140 | report.covered_nodes++; |
| 141 | } else if (log_unencountered) { |
| 142 | if (is_interesting_bool_node) { |
| 143 | report.unencountered_nodes.push_back( |
| 144 | absl::StrCat("Expression ID ", node_id, " ('", expr_text, "')")); |
| 145 | } |
| 146 | log_unencountered = false; |
| 147 | } |
| 148 | |
| 149 | if (is_interesting_bool_node) { |
| 150 | report.branches += 2; |
| 151 | if (stats.has_true_branch) { |
| 152 | report.covered_boolean_outcomes++; |
| 153 | } else if (log_unencountered) { |
| 154 | report.unencountered_branches.push_back( |
| 155 | absl::StrCat("\n", preceeding_tabs, "Expression ID ", node_id, " ('", |
| 156 | expr_text, "'): Never evaluated to 'true'")); |
| 157 | preceeding_tabs += "\t\t"; |
no test coverage detected