Skip to main content

code_analyze_mcp/
test_detection.rs

1//! Test file detection using path heuristics.
2//!
3//! Identifies test files based on directory and filename patterns.
4//! Supports Rust, Python, Go, Java, TypeScript, and JavaScript.
5
6use std::path::Path;
7
8/// Detect if a file path represents a test file based on path-based heuristics.
9///
10/// Checks for:
11/// - Directory patterns: tests/, test/, __tests__/, spec/
12/// - Filename patterns:
13///   - Rust: test_*.rs, *_test.rs
14///   - Python: test_*.py, *_test.py
15///   - Go: *_test.go
16///   - Java: Test*.java, *Test.java
17///   - TypeScript/JavaScript: *.test.ts, *.test.js, *.spec.ts, *.spec.js
18///
19/// Returns true if the path matches any test heuristic, false otherwise.
20pub fn is_test_file(path: &Path) -> bool {
21    // Check directory components for test directories
22    for component in path.components() {
23        if let Some("tests" | "test" | "__tests__" | "spec") = component.as_os_str().to_str() {
24            return true;
25        }
26    }
27
28    // Check filename patterns
29    let file_name = match path.file_name().and_then(|n| n.to_str()) {
30        Some(name) => name,
31        None => return false,
32    };
33
34    // Rust patterns
35    if file_name.starts_with("test_") && file_name.ends_with(".rs") {
36        return true;
37    }
38    if file_name.ends_with("_test.rs") {
39        return true;
40    }
41
42    // Python patterns
43    if file_name.starts_with("test_") && file_name.ends_with(".py") {
44        return true;
45    }
46    if file_name.ends_with("_test.py") {
47        return true;
48    }
49
50    // Go patterns
51    if file_name.ends_with("_test.go") {
52        return true;
53    }
54
55    // Java patterns
56    if file_name.starts_with("Test") && file_name.ends_with(".java") {
57        return true;
58    }
59    if file_name.ends_with("Test.java") {
60        return true;
61    }
62
63    // TypeScript/JavaScript patterns
64    if file_name.ends_with(".test.ts") || file_name.ends_with(".test.js") {
65        return true;
66    }
67    if file_name.ends_with(".spec.ts") || file_name.ends_with(".spec.js") {
68        return true;
69    }
70
71    false
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn filename_pattern_detects_test_file() {
80        assert!(is_test_file(Path::new("test_utils.rs")));
81        assert!(is_test_file(Path::new("utils_test.rs")));
82    }
83
84    #[test]
85    fn filename_pattern_rejects_production_file() {
86        assert!(!is_test_file(Path::new("utils.rs")));
87        assert!(!is_test_file(Path::new("main.rs")));
88    }
89
90    #[test]
91    fn directory_pattern_detects_test_file() {
92        assert!(is_test_file(Path::new("tests/utils.rs")));
93    }
94
95    #[test]
96    fn directory_pattern_detects_nested_test_file() {
97        assert!(is_test_file(Path::new("src/tests/utils.rs")));
98        assert!(!is_test_file(Path::new("src/utils.rs")));
99    }
100}