#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tarantula_perfect_fault() {
let score = tarantula(10, 0, 10, 100);
assert!((score - 1.0).abs() < 0.001);
}
#[test]
fn test_tarantula_perfect_clean() {
let score = tarantula(0, 100, 10, 100);
assert!(score.abs() < 0.001);
}
#[test]
fn test_tarantula_mixed() {
let score = tarantula(5, 50, 10, 100);
assert!(score > 0.0 && score < 1.0);
}
#[test]
fn test_ochiai_perfect_fault() {
let score = ochiai(10, 0, 10);
assert!((score - 1.0).abs() < 0.001);
}
#[test]
fn test_dstar_mixed() {
let score = dstar(5, 50, 10, 2);
assert!((score - 0.4545).abs() < 0.01);
}
#[test]
fn test_localizer_basic() {
let localizer = SbflLocalizer::new();
let coverage = vec![
StatementCoverage::new(StatementId::new("file.rs", 10), 0, 10), StatementCoverage::new(StatementId::new("file.rs", 20), 100, 0), StatementCoverage::new(StatementId::new("file.rs", 30), 50, 5), ];
let result = localizer.localize(&coverage, 100, 10);
assert_eq!(result.rankings.len(), 3);
assert_eq!(result.rankings[0].statement.line, 10); }
#[test]
fn test_lcov_parser() {
let lcov = r#"
SF:src/main.rs
DA:1,10
DA:2,5
DA:3,0
end_of_record
SF:src/lib.rs
DA:10,1
end_of_record
"#;
let result = LcovParser::parse(lcov).unwrap();
assert_eq!(result.len(), 4);
assert_eq!(result[0].0.file.to_str().unwrap(), "src/main.rs");
assert_eq!(result[0].0.line, 1);
assert_eq!(result[0].1, 10);
}
fn aggregate_fixture() -> (String, String) {
let mut passed = String::from("SF:src/lib.rs\n");
let mut failed = String::from("SF:src/lib.rs\n");
for i in 1..=100 {
let (p, f) = if i <= 50 { (10, 1) } else { (1, 8) };
passed.push_str(&format!("DA:{i},{p}\n"));
failed.push_str(&format!("DA:{i},{f}\n"));
}
passed.push_str("end_of_record\n");
failed.push_str("end_of_record\n");
(passed, failed)
}
#[test]
fn aggregate_lcov_hit_counts_are_test_counts_not_booleans() {
let (passed, failed) = aggregate_fixture();
let combined = LcovParser::combine_coverage(
&LcovParser::parse(&passed).unwrap(),
&LcovParser::parse(&failed).unwrap(),
10,
8,
);
assert_eq!(combined.len(), 100);
let by_line: HashMap<usize, &StatementCoverage> =
combined.iter().map(|c| (c.id.line, c)).collect();
let hot = by_line[&51];
assert_eq!(hot.executed_by_failed, 8, "aggregate hit count discarded");
assert_eq!(hot.executed_by_passed, 1);
let cold = by_line[&1];
assert_eq!(cold.executed_by_failed, 1);
assert_eq!(cold.executed_by_passed, 10);
let hot_score = tarantula(8, 1, 8, 10);
let cold_score = tarantula(1, 10, 8, 10);
assert!(
(hot_score - 0.909).abs() < 0.001,
"expected 0.909, got {hot_score}"
);
assert!(
(cold_score - 0.111).abs() < 0.001,
"expected 0.111, got {cold_score}"
);
assert!(
hot_score > cold_score,
"the whole point: the failing half must outrank the passing half"
);
}
#[test]
fn aggregate_lcov_top_n_is_the_failing_half_and_is_reproducible() {
let (passed, failed) = aggregate_fixture();
let p = LcovParser::parse(&passed).unwrap();
let f = LcovParser::parse(&failed).unwrap();
let run = || {
let result =
FaultLocalizer::run_localization(&p, &f, 10, 8, SbflFormula::Tarantula, 10);
result
.rankings
.iter()
.map(|r| r.statement.line)
.collect::<Vec<_>>()
};
let first = run();
assert_eq!(first.len(), 10);
assert!(
first.iter().all(|l| *l > 50),
"top-10 must be the failing half (51..=100), got {first:?}"
);
for _ in 0..5 {
assert_eq!(run(), first, "identical inputs must rank identically");
}
}
#[test]
fn concatenated_per_test_lcov_still_counts_one_record_as_one_test() {
let mut passed = String::new();
for i in 0..10 {
let hits = if i == 0 { 99 } else { 0 };
passed.push_str(&format!("SF:src/lib.rs\nDA:51,{hits}\nend_of_record\n"));
}
let mut failed = String::new();
for _ in 0..8 {
failed.push_str("SF:src/lib.rs\nDA:51,99\nend_of_record\n");
}
let combined = LcovParser::combine_coverage(
&LcovParser::parse(&passed).unwrap(),
&LcovParser::parse(&failed).unwrap(),
10,
8,
);
assert_eq!(combined.len(), 1);
assert_eq!(combined[0].executed_by_failed, 8);
assert_eq!(combined[0].executed_by_passed, 1);
}
#[test]
fn an_aggregate_hit_count_cannot_exceed_the_declared_test_total() {
let combined = LcovParser::combine_coverage(
&LcovParser::parse("SF:a.rs\nDA:1,1000\nend_of_record\n").unwrap(),
&LcovParser::parse("SF:a.rs\nDA:1,7\nend_of_record\n").unwrap(),
10,
3,
);
assert_eq!(combined[0].executed_by_passed, 10);
assert_eq!(combined[0].executed_by_failed, 3);
}
#[test]
fn test_formula_from_str() {
assert!(matches!(
"tarantula".parse::<SbflFormula>().unwrap(),
SbflFormula::Tarantula
));
assert!(matches!(
"ochiai".parse::<SbflFormula>().unwrap(),
SbflFormula::Ochiai
));
assert!(matches!(
"dstar2".parse::<SbflFormula>().unwrap(),
SbflFormula::DStar { exponent: 2 }
));
}
}