pub mod coverage;
pub mod demangle;
pub mod diagnostics;
pub mod handlers;
pub mod normalize;
pub mod parser;
pub mod query;
pub mod types;
pub use diagnostics::print_coverage_statistics;
pub use normalize::{normalize_demangled_name, strip_trailing_generics};
pub use parser::{parse_lcov_file, parse_lcov_file_with_callback, parse_lcov_file_with_progress};
pub use types::{CoverageProgress, FunctionCoverage, LcovData, NormalizedFunctionName};
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use std::path::PathBuf;
use tempfile::NamedTempFile;
#[test]
fn test_full_parsing_pipeline() {
let lcov_content = r#"TN:
SF:/path/to/file.rs
FN:10,test_function
FNDA:5,test_function
DA:10,5
DA:11,5
LF:2
LH:2
end_of_record
"#;
let mut temp = NamedTempFile::new().unwrap();
temp.write_all(lcov_content.as_bytes()).unwrap();
let data = parse_lcov_file(temp.path()).unwrap();
assert_eq!(data.total_lines, 2);
assert_eq!(data.lines_hit, 2);
assert!(data
.get_function_coverage(std::path::Path::new("/path/to/file.rs"), "test_function")
.is_some());
}
#[test]
fn test_reexports_available() {
let _ = LcovData::new();
let _ = CoverageProgress::Initializing;
let _ = NormalizedFunctionName::simple("test");
}
#[test]
fn test_normalize_function_name_public_api() {
let result = normalize_demangled_name("HashMap<K,V>::insert");
assert_eq!(result.full_path, "HashMap::insert");
assert_eq!(result.method_name, "insert");
}
#[test]
fn test_strip_generics_public_api() {
let result = strip_trailing_generics("method::<T>");
assert_eq!(result, "method");
}
#[test]
fn test_normalize_idempotent() {
let inputs = vec![
"simple_function",
"Module::function",
"HashMap<K,V>::insert",
"<Type>::method",
"method::<T>",
];
for input in inputs {
let result1 = normalize_demangled_name(input);
let result2 = normalize_demangled_name(&result1.full_path);
assert_eq!(
result1.method_name, result2.method_name,
"Method name changed for: {}",
input
);
}
}
#[test]
fn test_coverage_percentage_calculation() {
let lcov_content = r#"TN:
SF:/path/to/file.rs
FN:10,fully_covered
FN:20,partially_covered
FN:30,not_covered
FNDA:10,fully_covered
FNDA:5,partially_covered
FNDA:0,not_covered
DA:10,10
DA:11,10
DA:12,10
DA:20,5
DA:21,5
DA:22,0
DA:23,0
DA:30,0
DA:31,0
DA:32,0
LF:10
LH:5
end_of_record
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(lcov_content.as_bytes()).unwrap();
let data = parse_lcov_file(temp_file.path()).unwrap();
let file_path = PathBuf::from("/path/to/file.rs");
let coverage = data.get_function_coverage(&file_path, "fully_covered");
assert_eq!(coverage, Some(1.0));
let coverage = data.get_function_coverage(&file_path, "partially_covered");
assert_eq!(coverage, Some(0.5));
let coverage = data.get_function_coverage(&file_path, "not_covered");
assert_eq!(coverage, Some(0.0));
}
#[test]
fn test_demangling_in_parsing() {
let lcov_content = r#"TN:
SF:/path/to/file.rs
FN:18,_RNvMNtNtNtCs9MAeJIiYlOV_7debtmap8analysis11attribution14change_trackerNtB2_13ChangeTracker13track_changes
FNDA:5,_RNvMNtNtNtCs9MAeJIiYlOV_7debtmap8analysis11attribution14change_trackerNtB2_13ChangeTracker13track_changes
DA:18,5
LF:1
LH:1
end_of_record
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(lcov_content.as_bytes()).unwrap();
let data = parse_lcov_file(temp_file.path()).unwrap();
let file_path = PathBuf::from("/path/to/file.rs");
let funcs = &data.functions[&file_path];
assert_eq!(funcs.len(), 1);
assert!(
funcs[0].name.contains("ChangeTracker") || funcs[0].name.contains("track_changes"),
"Expected demangled name, got: {}",
funcs[0].name
);
}
}