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