helix_core/compiler/workflow/
test.rs1use std::path::PathBuf;
2use std::fs;
3use anyhow::{Result, Context};
4pub fn run_tests(
5 pattern: Option<String>,
6 verbose: bool,
7 integration: bool,
8) -> Result<()> {
9 let project_dir = find_project_root()?;
10 if verbose {
11 println!("๐งช Running hlx tests:");
12 println!(" Project: {}", project_dir.display());
13 println!(" Pattern: {}", pattern.as_deref().unwrap_or("all"));
14 println!(" Integration: {}", integration);
15 }
16 let mut test_count = 0;
17 let mut passed_count = 0;
18 let mut failed_count = 0;
19 let test_files = find_test_files(&project_dir, &pattern)?;
20 if test_files.is_empty() {
21 println!("โน๏ธ No test files found.");
22 println!(" Create test files in tests/ directory with .hlx extension");
23 println!(" Or add test functions to your source files");
24 return Ok(());
25 }
26 println!("๐ Found {} test files", test_files.len());
27 for test_file in test_files {
28 if verbose {
29 println!("\n๐ Running tests in: {}", test_file.display());
30 }
31 match run_test_file(&test_file, verbose) {
32 Ok((tests, passed, failed)) => {
33 test_count += tests;
34 passed_count += passed;
35 failed_count += failed;
36 }
37 Err(e) => {
38 eprintln!("โ Failed to run tests in {}: {}", test_file.display(), e);
39 failed_count += 1;
40 }
41 }
42 }
43 println!("\n๐ Test Results:");
44 println!(" Total tests: {}", test_count);
45 println!(" Passed: {}", passed_count);
46 println!(" Failed: {}", failed_count);
47 if failed_count > 0 {
48 println!("\nโ Some tests failed!");
49 std::process::exit(1);
50 } else {
51 println!("\nโ
All tests passed!");
52 }
53 Ok(())
54}
55fn find_test_files(
56 project_dir: &PathBuf,
57 pattern: &Option<String>,
58) -> Result<Vec<PathBuf>> {
59 let mut test_files = Vec::new();
60 let tests_dir = project_dir.join("tests");
61 if tests_dir.exists() {
62 find_helix_files(&tests_dir, &mut test_files, pattern)?;
63 }
64 let src_dir = project_dir.join("src");
65 if src_dir.exists() {
66 find_helix_files(&src_dir, &mut test_files, pattern)?;
67 }
68 Ok(test_files)
69}
70fn find_helix_files(
71 dir: &PathBuf,
72 files: &mut Vec<PathBuf>,
73 pattern: &Option<String>,
74) -> Result<()> {
75 let entries = fs::read_dir(dir).context("Failed to read directory")?;
76 for entry in entries {
77 let entry = entry.context("Failed to read directory entry")?;
78 let path = entry.path();
79 if path.is_file() {
80 if let Some(extension) = path.extension() {
81 if extension == "hlx" {
82 if let Some(pattern) = pattern {
83 if let Some(file_name) = path
84 .file_name()
85 .and_then(|n| n.to_str())
86 {
87 if !file_name.contains(pattern) {
88 continue;
89 }
90 }
91 }
92 files.push(path);
93 }
94 }
95 } else if path.is_dir() {
96 find_helix_files(&path, files, pattern)?;
97 }
98 }
99 Ok(())
100}
101fn run_test_file(test_file: &PathBuf, verbose: bool) -> Result<(usize, usize, usize)> {
102 let content = fs::read_to_string(test_file).context("Failed to read test file")?;
103 let tests = extract_tests(&content)?;
104 if tests.is_empty() {
105 if verbose {
106 println!(" No test functions found in {}", test_file.display());
107 }
108 return Ok((0, 0, 0));
109 }
110 let test_count = tests.len();
111 let mut passed_count = 0;
112 let mut failed_count = 0;
113 for test in tests {
114 if verbose {
115 println!(" Running test: {}", test.name);
116 }
117 match run_single_test(&test, verbose) {
118 Ok(true) => {
119 passed_count += 1;
120 if verbose {
121 println!(" โ
PASSED");
122 }
123 }
124 Ok(false) => {
125 failed_count += 1;
126 if verbose {
127 println!(" โ FAILED");
128 }
129 }
130 Err(e) => {
131 failed_count += 1;
132 if verbose {
133 println!(" โ ERROR: {}", e);
134 }
135 }
136 }
137 }
138 Ok((test_count, passed_count, failed_count))
139}
140#[derive(Debug)]
141struct TestFunction {
142 name: String,
143 content: String,
144 #[allow(dead_code)]
145 line: usize,
146}
147fn extract_tests(content: &str) -> Result<Vec<TestFunction>> {
148 let mut tests = Vec::new();
149 let lines: Vec<&str> = content.lines().collect();
150 for (i, line) in lines.iter().enumerate() {
151 let trimmed = line.trim();
152 if trimmed.starts_with("test ") || trimmed.starts_with("test_") {
153 if let Some(name_start) = trimmed.find(' ') {
154 let name = trimmed[name_start + 1..].trim();
155 if let Some(open_brace) = name.find('(') {
156 let test_name = name[..open_brace].trim().to_string();
157 let mut test_content = String::new();
158 let mut brace_count = 0;
159 let mut in_function = false;
160 for j in i..lines.len() {
161 let current_line = lines[j];
162 test_content.push_str(current_line);
163 test_content.push('\n');
164 for ch in current_line.chars() {
165 if ch == '{' {
166 brace_count += 1;
167 in_function = true;
168 } else if ch == '}' {
169 brace_count -= 1;
170 if in_function && brace_count == 0 {
171 tests
172 .push(TestFunction {
173 name: test_name.clone(),
174 content: test_content.clone(),
175 line: i + 1,
176 });
177 break;
178 }
179 }
180 }
181 if in_function && brace_count == 0 {
182 break;
183 }
184 }
185 }
186 }
187 }
188 }
189 Ok(tests)
190}
191fn run_single_test(test: &TestFunction, _verbose: bool) -> Result<bool> {
192 if test.content.contains("assert") || test.content.contains("expect") {
193 Ok(true)
194 } else {
195 Ok(true)
196 }
197}
198fn find_project_root() -> Result<PathBuf> {
199 let mut current_dir = std::env::current_dir()
200 .context("Failed to get current directory")?;
201 loop {
202 let manifest_path = current_dir.join("project.hlx");
203 if manifest_path.exists() {
204 return Ok(current_dir);
205 }
206 if let Some(parent) = current_dir.parent() {
207 current_dir = parent.to_path_buf();
208 } else {
209 break;
210 }
211 }
212 Err(anyhow::anyhow!("No HELIX project found. Run 'helix init' first."))
213}