bcore_mutation/
coverage.rs1use crate::error::{MutationError, Result};
2use regex::Regex;
3use std::collections::HashMap;
4use std::fs;
5use std::path::Path;
6
7pub fn parse_coverage_file(coverage_file_path: &Path) -> Result<HashMap<String, Vec<usize>>> {
8 let content = fs::read_to_string(coverage_file_path)?;
9 let mut coverage_data: HashMap<String, Vec<usize>> = HashMap::new();
10 let mut current_file: Option<String> = None;
11
12 let file_pattern = Regex::new(r"^SF:(.+)$")?; let line_pattern = Regex::new(r"^DA:(\d+),(\d+)$")?; for line in content.lines() {
17 let line = line.trim();
18
19 if let Some(captures) = file_pattern.captures(line) {
21 let full_path = &captures[1];
22
23 let relative_path = if let Some(pos) = full_path.find("src/") {
25 &full_path[pos..]
26 } else {
27 full_path };
29
30 current_file = Some(relative_path.to_string());
31 coverage_data.insert(relative_path.to_string(), Vec::new());
32 continue;
33 }
34
35 if let Some(captures) = line_pattern.captures(line) {
37 if let Some(ref file) = current_file {
38 let line_number: usize = captures[1]
39 .parse()
40 .map_err(|_| MutationError::Coverage("Invalid line number".to_string()))?;
41 let hits: usize = captures[2]
42 .parse()
43 .map_err(|_| MutationError::Coverage("Invalid hit count".to_string()))?;
44
45 if hits > 0 {
46 if let Some(lines) = coverage_data.get_mut(file) {
47 if !lines.contains(&line_number) {
48 lines.push(line_number);
49 }
50 }
51 }
52 }
53 }
54 }
55
56 Ok(coverage_data)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use std::io::Write;
63 use tempfile::NamedTempFile;
64
65 #[test]
66 fn test_parse_coverage_file() {
67 let mut temp_file = NamedTempFile::new().unwrap();
68 writeln!(temp_file, "SF:/path/to/file1.cpp").unwrap();
69 writeln!(temp_file, "DA:1,5").unwrap();
70 writeln!(temp_file, "DA:2,0").unwrap();
71 writeln!(temp_file, "DA:3,10").unwrap();
72 writeln!(temp_file, "SF:/path/to/file2.cpp").unwrap();
73 writeln!(temp_file, "DA:10,1").unwrap();
74 writeln!(temp_file, "DA:11,0").unwrap();
75
76 let result = parse_coverage_file(temp_file.path()).unwrap();
77
78 assert_eq!(result.len(), 2);
79 assert_eq!(result["/path/to/file1.cpp"], vec![1, 3]);
80 assert_eq!(result["/path/to/file2.cpp"], vec![10]);
81 }
82}