## 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
    }