Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/bin/handlers/handlers_modules/test.rs
Line
Count
Source
1
//! Refactored test command handler
2
//! Complexity reduced from ~200 lines to ≤10 per function
3
4
use anyhow::Result;
5
use super::test_helpers::{discover_test_files, execute_tests, print_test_summary, generate_json_output, generate_coverage_report, TestResult};
6
use std::path::{Path, PathBuf};
7
use std::time::Instant;
8
9
/// Handle test command - refactored with ≤10 complexity
10
0
pub fn handle_test_command(
11
0
    path: Option<PathBuf>,
12
0
    watch: bool,
13
0
    verbose: bool,
14
0
    filter: Option<&str>,
15
0
    coverage: bool,
16
0
    coverage_format: &str,
17
0
    parallel: usize,
18
0
    threshold: f64,
19
0
    format: &str,
20
0
) -> Result<()> {
21
0
    let test_path = path.unwrap_or_else(|| PathBuf::from("."));
22
    
23
0
    if watch {
24
0
        handle_watch_mode(&test_path, verbose, filter)
25
    } else {
26
0
        run_tests(
27
0
            &test_path,
28
0
            verbose,
29
0
            filter,
30
0
            coverage,
31
0
            coverage_format,
32
0
            parallel,
33
0
            threshold,
34
0
            format,
35
        )
36
    }
37
0
}
38
39
/// Run tests once
40
0
fn run_tests(
41
0
    path: &Path,
42
0
    verbose: bool,
43
0
    filter: Option<&str>,
44
0
    coverage: bool,
45
0
    coverage_format: &str,
46
0
    _parallel: usize, // Unused for now
47
0
    threshold: f64,
48
0
    format: &str,
49
0
) -> Result<()> {
50
    // Discover test files
51
0
    let test_files = discover_test_files(path, filter, verbose)?;
52
    
53
0
    if test_files.is_empty() {
54
0
        println!("āš ļø  No .ruchy test files found in {}", path.display());
55
0
        return Ok(());
56
0
    }
57
    
58
0
    println!("🧪 Running {} .ruchy test files...\n", test_files.len());
59
    
60
    // Execute tests
61
0
    let total_start = Instant::now();
62
0
    let test_results = execute_tests(&test_files, verbose);
63
0
    let total_duration = total_start.elapsed();
64
    
65
    // Print summary
66
0
    print_test_summary(&test_results, total_duration, verbose);
67
    
68
    // Handle output format
69
0
    if format == "json" {
70
0
        let json = generate_json_output(&test_results, total_duration)?;
71
0
        println!("\n{}", json);
72
0
    }
73
    
74
    // Handle coverage if requested
75
0
    if coverage {
76
0
        generate_coverage_report(&test_files, &test_results, coverage_format, threshold)?;
77
0
    }
78
    
79
    // Check for failures
80
0
    check_test_failures(&test_results);
81
    
82
0
    println!("\nāœ… All tests passed!");
83
0
    Ok(())
84
0
}
85
86
/// Handle watch mode
87
0
fn handle_watch_mode(path: &Path, verbose: bool, filter: Option<&str>) -> Result<()> {
88
    use colored::Colorize;
89
    use std::thread;
90
    use std::time::Duration;
91
    
92
0
    println!(
93
0
        "{} Watching {} for test changes...",
94
0
        "šŸ‘".bright_cyan(),
95
0
        path.display()
96
    );
97
0
    println!("Press Ctrl+C to stop watching\n");
98
    
99
    // Initial test run
100
0
    let _ = run_tests(path, verbose, filter, false, "text", 1, 0.0, "text");
101
    
102
    // Watch for changes
103
0
    let mut last_modified = get_latest_modification(path);
104
    
105
    loop {
106
0
        thread::sleep(Duration::from_millis(1000));
107
        
108
0
        let current_modified = get_latest_modification(path);
109
0
        if current_modified > last_modified {
110
0
            last_modified = current_modified;
111
0
            println!("\n{} Files changed, running tests...", "→".bright_cyan());
112
0
            let _ = run_tests(path, verbose, filter, false, "text", 1, 0.0, "text");
113
0
        }
114
    }
115
}
116
117
/// Get latest modification time in directory
118
0
fn get_latest_modification(path: &Path) -> std::time::SystemTime {
119
    use std::fs;
120
    
121
0
    let mut latest = std::time::SystemTime::now();
122
    
123
0
    if let Ok(entries) = fs::read_dir(path) {
124
0
        for entry in entries.flatten() {
125
0
            if let Ok(path) = entry.path().canonicalize() {
126
0
                if path.extension().and_then(|ext| ext.to_str()) == Some("ruchy") {
127
0
                    if let Ok(metadata) = fs::metadata(&path) {
128
0
                        if let Ok(modified) = metadata.modified() {
129
0
                            if modified > latest {
130
0
                                latest = modified;
131
0
                            }
132
0
                        }
133
0
                    }
134
0
                }
135
0
            }
136
        }
137
0
    }
138
    
139
0
    latest
140
0
}
141
142
/// Check if any tests failed and exit if necessary
143
0
fn check_test_failures(test_results: &[TestResult]) {
144
0
    let failed = test_results.iter().filter(|r| !r.success).count();
145
0
    if failed > 0 {
146
0
        std::process::exit(1);
147
0
    }
148
0
}