#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_todo_patterns() {
let detector = SatdDetector::new();
let source = "// TODO: implement this\n// FIXME: broken code";
let result = detector.detect(source);
assert_eq!(result.count, 2);
assert!(result.patterns.contains(&"TODO".to_string()));
assert!(result.patterns.contains(&"FIXME".to_string()));
}
#[test]
fn test_no_satd_in_clean_code() {
let detector = SatdDetector::new();
let source = "fn clean_function() {\n println!(\"Clean code\");\n}";
let result = detector.detect(source);
assert_eq!(result.count, 0);
assert!(result.patterns.is_empty());
}
#[test]
fn test_satd_patterns_compile_successfully() {
let patterns = &*SATD_PATTERNS;
assert_eq!(patterns.len(), 9);
let pattern_names: Vec<&str> = patterns.iter().map(|(name, _)| *name).collect();
assert!(pattern_names.contains(&"TODO"));
assert!(pattern_names.contains(&"FIXME"));
assert!(pattern_names.contains(&"HACK"));
assert!(pattern_names.contains(&"XXX"));
assert!(pattern_names.contains(&"REFACTOR"));
assert!(pattern_names.contains(&"OPTIMIZE"));
assert!(pattern_names.contains(&"REVIEW"));
assert!(pattern_names.contains(&"DEPRECATED"));
assert!(pattern_names.contains(&"TEMPORARY"));
}
#[test]
fn test_satd_patterns_word_boundary_matching() {
let detector = SatdDetector::new();
let source1 = "// TODO: implement this";
let result1 = detector.detect(source1);
assert_eq!(result1.count, 1);
assert!(result1.patterns.contains(&"TODO".to_string()));
let source2 = "// TODOIST is a task manager";
let result2 = detector.detect(source2);
assert_eq!(result2.count, 0);
let source3 = "XXX: critical issue";
let result3 = detector.detect(source3);
assert_eq!(result3.count, 1);
assert!(result3.patterns.contains(&"XXX".to_string()));
}
#[test]
fn test_detector_initialization_stability() {
for _ in 0..10 {
let detector = SatdDetector::new();
assert_eq!(detector.patterns.len(), 9);
}
let default_detector = SatdDetector::default();
assert_eq!(default_detector.patterns.len(), 9);
let detector1 = SatdDetector::new();
let detector2 = SatdDetector::default();
assert_eq!(detector1.patterns.len(), detector2.patterns.len());
}
#[test]
fn test_all_satd_patterns_match_correctly() {
let detector = SatdDetector::new();
let test_cases = vec![
("// TODO: fix this", "TODO"),
("// FIXME: broken", "FIXME"),
("// HACK: workaround", "HACK"),
("// XXX: warning", "XXX"),
("// REFACTOR: improve", "REFACTOR"),
("// OPTIMIZE: performance", "OPTIMIZE"),
("// REVIEW: check this", "REVIEW"),
("// DEPRECATED: old api", "DEPRECATED"),
("// TEMPORARY: remove later", "TEMPORARY"),
];
for (source, expected_pattern) in test_cases {
let result = detector.detect(source);
assert_eq!(
result.count, 1,
"Expected 1 match for '{expected_pattern}' in '{source}'"
);
assert!(
result.patterns.contains(&expected_pattern.to_string()),
"Expected pattern '{expected_pattern}' not found"
);
}
}
#[test]
fn test_multiple_satd_patterns_in_source() {
let detector = SatdDetector::new();
let source = r#"
// TODO: implement feature A
// FIXME: bug in function B
// HACK: temporary workaround
// TODO: also implement feature C
"#;
let result = detector.detect(source);
assert_eq!(result.count, 4);
assert_eq!(result.patterns.len(), 3);
assert!(result.patterns.contains(&"TODO".to_string()));
assert!(result.patterns.contains(&"FIXME".to_string()));
assert!(result.patterns.contains(&"HACK".to_string()));
}
#[test]
fn test_extract_comments_line_comments() {
let detector = SatdDetector::new();
let source =
"fn foo() {\n let x = 1; // inline comment\n // full line comment\n bar();\n}";
let comments = detector.extract_comments(source);
assert!(comments.contains("// inline comment"));
assert!(comments.contains("// full line comment"));
assert!(!comments.contains("fn foo"));
assert!(!comments.contains("bar()"));
}
#[test]
fn test_extract_comments_block_comments() {
let detector = SatdDetector::new();
let source = "/* multi-line\n block comment */\nfn foo() {}\n/* single line block */";
let comments = detector.extract_comments(source);
assert!(comments.contains("multi-line"));
assert!(comments.contains("block comment"));
assert!(comments.contains("single line block"));
assert!(!comments.contains("fn foo"));
}
#[test]
fn test_extract_comments_mixed() {
let detector = SatdDetector::new();
let source =
"// line comment\n/* block */\ncode();\n// another line\n/* start\nmiddle\nend */";
let comments = detector.extract_comments(source);
assert!(comments.contains("// line comment"));
assert!(comments.contains("/* block */"));
assert!(comments.contains("// another line"));
assert!(comments.contains("middle"));
assert!(!comments.contains("code()"));
}
#[test]
fn test_extract_comments_no_comments() {
let detector = SatdDetector::new();
let source = "fn main() {\n println!(\"hello\");\n}";
let comments = detector.extract_comments(source);
assert!(comments.is_empty());
}
#[test]
fn test_detect_in_comments_only() {
let detector = SatdDetector::new();
let source = "let msg = \"TODO: not a comment\";\n// FIXME: real comment";
let result = detector.detect_in_comments(source);
assert_eq!(result.count, 1);
assert!(result.patterns.contains(&"FIXME".to_string()));
}
#[test]
fn test_detect_in_comments_block_with_satd() {
let detector = SatdDetector::new();
let source =
"fn foo() {}\n/* HACK: temporary workaround\n TODO: fix later */\nfn bar() {}";
let result = detector.detect_in_comments(source);
assert_eq!(result.count, 2);
assert!(result.patterns.contains(&"HACK".to_string()));
assert!(result.patterns.contains(&"TODO".to_string()));
}
#[test]
fn test_extract_comments_block_single_line_with_close() {
let detector = SatdDetector::new();
let source = "/* closed on same line */ code();";
let comments = detector.extract_comments(source);
assert!(comments.contains("closed on same line"));
}
#[test]
fn test_with_extended_creates_extended_detector() {
let detector = SatdDetector::with_extended();
assert!(detector.is_extended());
assert!(detector.patterns.len() > 9);
}
#[test]
fn test_is_extended_standard_detector() {
let detector = SatdDetector::new();
assert!(!detector.is_extended());
}
#[test]
fn test_extended_detects_placeholder() {
let detector = SatdDetector::with_extended();
let source = "// placeholder implementation";
let result = detector.detect(source);
assert!(result.count > 0);
assert!(result.patterns.contains(&"PLACEHOLDER".to_string()));
}
#[test]
fn test_extended_detects_stub() {
let detector = SatdDetector::with_extended();
let source = "// stub for testing";
let result = detector.detect(source);
assert!(result.count > 0);
assert!(result.patterns.contains(&"STUB".to_string()));
}
#[test]
fn test_extended_detects_for_now() {
let detector = SatdDetector::with_extended();
let source = "// this works for now";
let result = detector.detect(source);
assert!(result.count > 0);
assert!(result.patterns.contains(&"FOR_NOW".to_string()));
}
#[test]
fn test_extended_detects_mock_dummy_fake() {
let detector = SatdDetector::with_extended();
let source = "// using mock data and dummy values with fake response";
let result = detector.detect(source);
assert!(result.count >= 3);
assert!(result.patterns.contains(&"MOCK".to_string()));
}
#[test]
fn test_extended_detects_hardcoded() {
let detector = SatdDetector::with_extended();
let source = "// hardcoded value";
let result = detector.detect(source);
assert!(result.patterns.contains(&"HARDCODED".to_string()));
}
#[test]
fn test_extended_detects_simplified() {
let detector = SatdDetector::with_extended();
let source = "// simplified version of the algorithm";
let result = detector.detect(source);
assert!(result.patterns.contains(&"SIMPLIFIED".to_string()));
}
#[test]
fn test_extended_detects_wip() {
let detector = SatdDetector::with_extended();
let source = "// WIP: not finished yet";
let result = detector.detect(source);
assert!(result.patterns.contains(&"WIP".to_string()));
}
#[test]
fn test_extended_detects_skip_bypass() {
let detector = SatdDetector::with_extended();
let source = "// skip validation for now";
let result = detector.detect(source);
assert!(result.count > 0);
}
#[test]
fn test_extended_case_insensitive() {
let detector = SatdDetector::with_extended();
let source = "// PLACEHOLDER Stub HARDCODED Simplified";
let result = detector.detect(source);
assert!(result.count >= 4);
}
#[test]
fn test_extended_detect_in_comments() {
let detector = SatdDetector::with_extended();
let source = "let x = 1; // placeholder for now\ncode();";
let result = detector.detect_in_comments(source);
assert!(result.count > 0);
assert!(result.patterns.contains(&"PLACEHOLDER".to_string()));
assert!(result.patterns.contains(&"FOR_NOW".to_string()));
}
#[test]
fn test_extract_comments_unclosed_block() {
let detector = SatdDetector::new();
let source = "fn foo() {\n /* unclosed block\n still in comment\n}";
let comments = detector.extract_comments(source);
assert!(comments.contains("unclosed block"));
assert!(comments.contains("still in comment"));
}
#[test]
fn test_extract_comments_block_opening_on_own_line() {
let detector = SatdDetector::new();
let source = "/*\n block content\n more content\n*/\ncode();";
let comments = detector.extract_comments(source);
assert!(comments.contains("block content"));
assert!(comments.contains("more content"));
}
}