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