use quote::ToTokens;
pub fn classify_test_file(path_str: &str) -> bool {
const TEST_DIR_PATTERNS: &[&str] = &[
"/tests/",
"/test/",
"/testing/",
"/mocks/",
"/mock/",
"/fixtures/",
"/fixture/",
"/test_helpers/",
"/test_utils/",
"/test_",
"/mock",
"/scenario",
"\\tests\\",
"\\test\\", ];
const TEST_FILE_SUFFIXES: &[&str] = &["_test.rs", "_tests.rs", "/tests.rs", "/test.rs"];
let has_test_dir = TEST_DIR_PATTERNS
.iter()
.any(|pattern| path_str.contains(pattern));
let has_test_suffix = TEST_FILE_SUFFIXES
.iter()
.any(|suffix| path_str.ends_with(suffix));
has_test_dir || has_test_suffix
}
pub fn is_test_function(name: &str, item_fn: &syn::ItemFn) -> bool {
has_test_attribute(&item_fn.attrs) || has_test_name_pattern(name)
}
pub fn has_test_attribute(attrs: &[syn::Attribute]) -> bool {
attrs.iter().any(|attr| match () {
_ if attr.path().is_ident("test") => true,
_ if attr
.path()
.segments
.last()
.is_some_and(|seg| seg.ident == "test") =>
{
true
}
_ if attr.path().is_ident("cfg") => {
attr.meta.to_token_stream().to_string().contains("test")
}
_ => false,
})
}
pub fn has_test_name_pattern(name: &str) -> bool {
const TEST_PREFIXES: &[&str] = &["test_", "it_", "should_"];
const MOCK_PATTERNS: &[&str] = &["mock", "stub", "fake"];
let name_lower = name.to_lowercase();
match () {
_ if TEST_PREFIXES.iter().any(|prefix| name.starts_with(prefix)) => true,
_ if MOCK_PATTERNS
.iter()
.any(|pattern| name_lower.contains(pattern)) =>
{
true
}
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_classify_test_file_with_test_directories() {
assert!(classify_test_file("src/tests/mod.rs"));
assert!(classify_test_file("src/test/utils.rs"));
assert!(classify_test_file("src/testing/helpers.rs"));
assert!(classify_test_file("src/mocks/data.rs"));
assert!(classify_test_file("src/mock/server.rs"));
assert!(classify_test_file("src/fixtures/sample.rs"));
assert!(classify_test_file("src/fixture/db.rs"));
assert!(classify_test_file("src/test_helpers/common.rs"));
assert!(classify_test_file("src/test_utils/setup.rs"));
assert!(classify_test_file("src/test_integration.rs"));
assert!(classify_test_file("src/mockito/client.rs"));
assert!(classify_test_file("src/scenario/basic.rs"));
}
#[test]
fn test_classify_test_file_with_test_suffixes() {
assert!(classify_test_file("src/lib_test.rs"));
assert!(classify_test_file("src/module_tests.rs"));
assert!(classify_test_file("src/tests.rs"));
assert!(classify_test_file("src/test.rs"));
assert!(classify_test_file("integration_test.rs"));
assert!(classify_test_file("unit_tests.rs"));
}
#[test]
fn test_classify_test_file_with_windows_paths() {
assert!(classify_test_file("src\\tests\\mod.rs"));
}
#[test]
fn test_classify_test_file_non_test_files() {
assert!(!classify_test_file("src/main.rs"));
assert!(!classify_test_file("src/lib.rs"));
assert!(!classify_test_file("src/core/module.rs"));
}
#[test]
fn test_classify_test_file_negative_cases() {
assert!(!classify_test_file("src/main.rs"));
assert!(!classify_test_file("src/lib.rs"));
assert!(!classify_test_file("src/analyzer.rs"));
assert!(!classify_test_file("src/core/processor.rs"));
assert!(!classify_test_file("src/utils/helper.rs"));
assert!(!classify_test_file("src/latest.rs"));
assert!(!classify_test_file("src/contest.rs"));
assert!(!classify_test_file("src/protest.rs"));
}
#[test]
fn test_classify_test_file_edge_cases() {
assert!(classify_test_file("/tests/"));
assert!(classify_test_file("/tests/file.rs"));
assert!(classify_test_file("path/test.rs"));
assert!(classify_test_file("path/tests.rs"));
assert!(!classify_test_file(""));
assert!(!classify_test_file("/"));
assert!(classify_test_file("deeply/nested/tests/file.rs"));
assert!(classify_test_file("very/deep/path/test_utils/util.rs"));
}
#[test]
fn test_has_test_name_pattern_with_test_prefix() {
assert!(has_test_name_pattern("test_something"));
assert!(has_test_name_pattern("test_"));
}
#[test]
fn test_has_test_name_pattern_with_it_prefix() {
assert!(has_test_name_pattern("it_should_work"));
assert!(has_test_name_pattern("it_"));
}
#[test]
fn test_has_test_name_pattern_with_should_prefix() {
assert!(has_test_name_pattern("should_do_something"));
assert!(has_test_name_pattern("should_"));
}
#[test]
fn test_has_test_name_pattern_with_mock() {
assert!(has_test_name_pattern("mock_service"));
assert!(has_test_name_pattern("get_mock"));
assert!(has_test_name_pattern("MockBuilder"));
}
#[test]
fn test_has_test_name_pattern_with_stub() {
assert!(has_test_name_pattern("stub_response"));
assert!(has_test_name_pattern("get_stub"));
assert!(has_test_name_pattern("StubFactory"));
}
#[test]
fn test_has_test_name_pattern_with_fake() {
assert!(has_test_name_pattern("fake_data"));
assert!(has_test_name_pattern("create_fake"));
assert!(has_test_name_pattern("FakeImpl"));
}
#[test]
fn test_has_test_name_pattern_regular_name() {
assert!(!has_test_name_pattern("regular_function"));
assert!(!has_test_name_pattern("process_data"));
assert!(!has_test_name_pattern("handle_request"));
}
}