Skip to main content

shape_vm/feature_tests/
coverage.rs

1//! Coverage analysis for feature tests
2//!
3//! This module analyzes which grammar rules are covered by tests
4//! and reports on coverage gaps.
5
6use std::collections::{BTreeMap, BTreeSet};
7
8use super::PEST_RULES;
9use super::all_feature_tests;
10
11// ============================================================================
12// Coverage Analysis
13// ============================================================================
14
15/// Analyze coverage gaps between registered tests and grammar features
16pub fn analyze_coverage() -> CoverageReport {
17    let grammar: BTreeSet<&str> = PEST_RULES.iter().copied().collect();
18
19    // Collect all covered features from all test modules
20    let mut covered: BTreeSet<&str> = BTreeSet::new();
21    let mut test_coverage: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
22
23    for test in all_feature_tests() {
24        for &feature in test.covers {
25            covered.insert(feature);
26            test_coverage.entry(feature).or_default().push(test.name);
27        }
28    }
29
30    // Find uncovered grammar rules
31    let uncovered: Vec<&str> = grammar.difference(&covered).copied().collect();
32
33    // Features in tests but not in grammar (possibly stale or internal)
34    let unknown: Vec<&str> = covered.difference(&grammar).copied().collect();
35
36    let coverage_pct = if grammar.is_empty() {
37        0.0
38    } else {
39        (covered.intersection(&grammar).count() as f64 / grammar.len() as f64) * 100.0
40    };
41
42    CoverageReport {
43        total_grammar_features: grammar.len(),
44        covered_features: covered.intersection(&grammar).count(),
45        uncovered_features: uncovered,
46        unknown_features: unknown,
47        test_coverage,
48        coverage_pct,
49    }
50}
51
52#[derive(Debug)]
53pub struct CoverageReport {
54    pub total_grammar_features: usize,
55    pub covered_features: usize,
56    pub uncovered_features: Vec<&'static str>,
57    pub unknown_features: Vec<&'static str>,
58    pub test_coverage: BTreeMap<&'static str, Vec<&'static str>>,
59    pub coverage_pct: f64,
60}
61
62impl std::fmt::Display for CoverageReport {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        writeln!(f, "Feature Coverage Report")?;
65        writeln!(f, "======================")?;
66        writeln!(
67            f,
68            "Coverage: {:.1}% ({}/{} grammar rules)",
69            self.coverage_pct, self.covered_features, self.total_grammar_features
70        )?;
71        writeln!(f)?;
72
73        if !self.uncovered_features.is_empty() {
74            writeln!(
75                f,
76                "Uncovered Grammar Rules ({}):",
77                self.uncovered_features.len()
78            )?;
79            for feature in &self.uncovered_features {
80                writeln!(f, "   - {}", feature)?;
81            }
82            writeln!(f)?;
83        }
84
85        if !self.unknown_features.is_empty() {
86            writeln!(
87                f,
88                "Test features not in grammar ({}):",
89                self.unknown_features.len()
90            )?;
91            for feature in &self.unknown_features {
92                writeln!(f, "   - {}", feature)?;
93            }
94        }
95
96        Ok(())
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_coverage_report() {
106        let report = analyze_coverage();
107        println!("{}", report);
108        assert!(
109            report.total_grammar_features > 0,
110            "Expected grammar features to be extracted from pest"
111        );
112    }
113}