| 90 | |
| 91 | |
| 92 | void collectAndInsertCoverage( |
| 93 | std::string_view test_name, |
| 94 | const std::vector<CovCounter> & name_refs, |
| 95 | const std::vector<IndirectCallEntry> & indirect_calls, |
| 96 | ContextPtr context) |
| 97 | { |
| 98 | if (name_refs.empty()) |
| 99 | { |
| 100 | auto msg = fmt::format("CoverageCollection: No covered counters for test '{}', skipping", test_name); |
| 101 | LOG_INFO(getLogger("CoverageCollection"), "{}", msg); |
| 102 | return; |
| 103 | } |
| 104 | |
| 105 | ensureCoverageMapLoaded(); |
| 106 | |
| 107 | LOG_INFO(getLogger("CoverageCollection"), |
| 108 | "Flushing test '{}': {} covered counters, coverage map size {}", |
| 109 | test_name, name_refs.size(), g_coverage_map.size()); |
| 110 | |
| 111 | /// Collect unique (file, line_start, line_end) triples. |
| 112 | struct LineKey |
| 113 | { |
| 114 | std::string file; |
| 115 | uint32_t line_start; |
| 116 | uint32_t line_end; |
| 117 | |
| 118 | bool operator==(const LineKey & o) const |
| 119 | { |
| 120 | return line_start == o.line_start && line_end == o.line_end && file == o.file; |
| 121 | } |
| 122 | }; |
| 123 | struct LineKeyHash |
| 124 | { |
| 125 | std::size_t operator()(const LineKey & k) const |
| 126 | { |
| 127 | std::size_t h = std::hash<std::string>{}(k.file); |
| 128 | h ^= std::hash<uint32_t>{}(k.line_start) + 0x9e3779b9u + (h << 6) + (h >> 2); |
| 129 | h ^= std::hash<uint32_t>{}(k.line_end) + 0x9e3779b9u + (h << 6) + (h >> 2); |
| 130 | return h; |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | /// Per (file, line_start, line_end) key: output array index + min depth + branch flag. |
| 135 | struct SeenEntry { size_t idx; uint8_t min_depth; uint8_t branch_flag; }; |
| 136 | std::unordered_map<LineKey, SeenEntry, LineKeyHash> seen; |
| 137 | seen.reserve(name_refs.size()); |
| 138 | |
| 139 | std::vector<std::string> files; |
| 140 | std::vector<uint32_t> line_starts; |
| 141 | std::vector<uint32_t> line_ends; |
| 142 | std::vector<uint8_t> min_depths; |
| 143 | /// branch_flags: 0 = code region, 1 = true branch, 2 = false branch. |
| 144 | std::vector<uint8_t> branch_flags; |
| 145 | |
| 146 | for (const auto & [name_hash, func_hash, counter_id, min_depth] : name_refs) |
| 147 | { |
| 148 | const auto it = g_coverage_map.find(CoverageKey{name_hash, func_hash, counter_id}); |
| 149 | if (it == g_coverage_map.end()) |
no test coverage detected