| 4189 | } |
| 4190 | |
| 4191 | static const char *coverage_path_freshness(cbm_store_t *store, const char *project, |
| 4192 | const char *root_path, const char *rel_path, |
| 4193 | bool *outside) { |
| 4194 | *outside = false; |
| 4195 | if (!root_path || !root_path[0]) { |
| 4196 | return "unavailable"; |
| 4197 | } |
| 4198 | char abs_path[CBM_SZ_4K]; |
| 4199 | int n = snprintf(abs_path, sizeof(abs_path), "%s%s%s", root_path, |
| 4200 | root_path[strlen(root_path) - 1U] == '/' ? "" : "/", rel_path); |
| 4201 | if (n < 0 || (size_t)n >= sizeof(abs_path)) { |
| 4202 | return "unavailable"; |
| 4203 | } |
| 4204 | struct stat st; |
| 4205 | if (stat(abs_path, &st) != 0) { |
| 4206 | return "missing"; |
| 4207 | } |
| 4208 | if (!cbm_path_within_root(root_path, abs_path)) { |
| 4209 | *outside = true; |
| 4210 | return "outside_project"; |
| 4211 | } |
| 4212 | |
| 4213 | cbm_file_hash_t hash = {0}; |
| 4214 | int rc = cbm_store_get_file_hash(store, project, rel_path, &hash); |
| 4215 | if (rc == CBM_STORE_NOT_FOUND) { |
| 4216 | return "not_tracked"; |
| 4217 | } |
| 4218 | if (rc != CBM_STORE_OK) { |
| 4219 | return "unavailable"; |
| 4220 | } |
| 4221 | bool matches = hash.mtime_ns == coverage_stat_mtime_ns(&st) && hash.size == st.st_size; |
| 4222 | cbm_store_clear_file_hash(&hash); |
| 4223 | return matches ? "metadata_match" : "metadata_changed"; |
| 4224 | } |
| 4225 | |
| 4226 | static void coverage_add_ranges(yyjson_mut_doc *doc, yyjson_mut_val *row, const char *detail) { |
| 4227 | if (!detail || !detail[0]) { |
no test coverage detected