1use crate::rules::RuleId;
2
3#[derive(Debug, Clone, Default)]
4pub struct TestAnalysis {
5 pub assertion_count: usize,
6 pub mock_count: usize,
7 pub mock_classes: Vec<String>,
8 pub line_count: usize,
9 pub how_not_what_count: usize,
10 pub fixture_count: usize,
11 pub has_wait: bool,
12 pub assertion_message_count: usize,
13 pub duplicate_literal_count: usize,
14 pub suppressed_rules: Vec<RuleId>,
15}
16
17#[derive(Debug, Clone)]
18pub struct TestFunction {
19 pub name: String,
20 pub file: String,
21 pub line: usize,
22 pub end_line: usize,
23 pub analysis: TestAnalysis,
24}
25
26#[derive(Debug, Clone)]
33pub struct FileAnalysis {
34 pub file: String,
35 pub functions: Vec<TestFunction>,
36 pub has_pbt_import: bool,
37 pub has_contract_import: bool,
38 pub has_error_test: bool,
39 pub has_relational_assertion: bool,
40 pub parameterized_count: usize,
41}
42
43pub trait LanguageExtractor {
44 fn extract_test_functions(&self, source: &str, file_path: &str) -> Vec<TestFunction>;
45
46 fn extract_file_analysis(&self, source: &str, file_path: &str) -> FileAnalysis {
50 let functions = self.extract_test_functions(source, file_path);
51 FileAnalysis {
52 file: file_path.to_string(),
53 functions,
54 has_pbt_import: false,
55 has_contract_import: false,
56 has_error_test: false,
57 has_relational_assertion: false,
58 parameterized_count: 0,
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_analysis_default_all_zero_or_empty() {
69 let analysis = TestAnalysis::default();
70 assert_eq!(analysis.assertion_count, 0);
71 assert_eq!(analysis.mock_count, 0);
72 assert!(analysis.mock_classes.is_empty());
73 assert_eq!(analysis.line_count, 0);
74 assert_eq!(analysis.how_not_what_count, 0);
75 assert_eq!(analysis.fixture_count, 0);
76 assert!(!analysis.has_wait);
77 assert_eq!(analysis.assertion_message_count, 0);
78 assert_eq!(analysis.duplicate_literal_count, 0);
79 assert!(analysis.suppressed_rules.is_empty());
80 }
81
82 #[test]
83 fn file_analysis_fields_accessible() {
84 let fa = FileAnalysis {
85 file: "test.py".to_string(),
86 functions: vec![],
87 has_pbt_import: true,
88 has_contract_import: false,
89 has_error_test: true,
90 has_relational_assertion: false,
91 parameterized_count: 3,
92 };
93 assert_eq!(fa.file, "test.py");
94 assert!(fa.functions.is_empty());
95 assert!(fa.has_pbt_import);
96 assert!(!fa.has_contract_import);
97 assert!(fa.has_error_test);
98 assert!(!fa.has_relational_assertion);
99 assert_eq!(fa.parameterized_count, 3);
100 }
101
102 struct DummyExtractor;
103 impl LanguageExtractor for DummyExtractor {
104 fn extract_test_functions(&self, _source: &str, file_path: &str) -> Vec<TestFunction> {
105 vec![TestFunction {
106 name: "test_dummy".to_string(),
107 file: file_path.to_string(),
108 line: 1,
109 end_line: 3,
110 analysis: TestAnalysis::default(),
111 }]
112 }
113 }
114
115 #[test]
116 fn default_extract_file_analysis_delegates_to_extract_test_functions() {
117 let extractor = DummyExtractor;
118 let fa = extractor.extract_file_analysis("x = 1", "test.py");
119 assert_eq!(fa.functions.len(), 1);
120 assert!(!fa.has_pbt_import);
121 assert!(!fa.has_contract_import);
122 assert!(!fa.has_error_test);
123 assert!(!fa.has_relational_assertion);
124 assert_eq!(fa.parameterized_count, 0);
125 }
126}