1mod collect;
2mod collect_parallel;
3mod coverage;
4mod coverage_weighted;
5mod scope;
6pub(crate) mod detection;
7pub(crate) mod disambiguation;
8
9use crate::graph::DependencyGraph;
10#[cfg(test)]
11use crate::graph::build_dependency_graph;
12use crate::parsing::ParsedFile;
13use crate::units::CodeUnitKind;
14use std::collections::{HashMap, HashSet};
15use std::path::PathBuf;
16
17pub use collect_parallel::test_functions_in;
18pub(crate) use collect_parallel::collect_refs_parallel;
19pub(crate) use coverage::{build_py_coverage_map, is_definition_covered};
20#[cfg(test)]
21pub(crate) use coverage::CoverageContext;
22pub use coverage_weighted::compute_py_weighted_file_pcts;
23pub use coverage_weighted::py_init_marker_pct;
24pub use detection::{has_test_framework_import, is_in_test_directory, is_test_file};
25pub use disambiguation::build_name_file_map;
26pub(crate) use disambiguation::{build_disambiguation_map, file_to_module_suffix};
27
28#[cfg(test)]
29pub(crate) use collect::collect_definitions;
30
31#[derive(Debug, Clone)]
32pub struct CodeDefinition {
33 pub name: String,
34 pub kind: CodeUnitKind,
35 pub file: PathBuf,
36 pub line: usize,
37 pub containing_class: Option<String>,
38}
39
40pub type CoveringTest = (PathBuf, String);
42
43pub(crate) type PerTestUsage = Vec<(PathBuf, Vec<(String, HashSet<String>, HashSet<String>)>)>;
44
45#[derive(Debug, Clone)]
46pub struct TestRefAnalysis {
47 pub definitions: Vec<CodeDefinition>,
48 pub test_references: HashSet<String>,
49 pub call_references: HashSet<String>,
51 pub unreferenced: Vec<CodeDefinition>,
52 pub coverage_map: HashMap<(PathBuf, String), Vec<CoveringTest>>,
54}
55
56#[allow(clippy::too_many_lines)]
57pub fn analyze_test_refs(
58 parsed_files: &[&ParsedFile],
59 graph: Option<&DependencyGraph>,
60) -> TestRefAnalysis {
61 analyze_test_refs_inner(parsed_files, graph, true)
62}
63
64pub fn analyze_test_refs_quick(parsed_files: &[&ParsedFile]) -> TestRefAnalysis {
65 analyze_test_refs_inner(parsed_files, None, false)
66}
67
68pub fn analyze_test_refs_no_map(
69 parsed_files: &[&ParsedFile],
70 graph: Option<&DependencyGraph>,
71) -> TestRefAnalysis {
72 analyze_test_refs_inner(parsed_files, graph, false)
73}
74
75fn analyze_test_refs_inner(
76 parsed_files: &[&ParsedFile],
77 graph: Option<&DependencyGraph>,
78 need_coverage_map: bool,
79) -> TestRefAnalysis {
80 let (
81 definitions,
82 test_references,
83 usage_references,
84 call_references,
85 import_bindings,
86 alias_bindings,
87 per_test_usage,
88 ) = collect_refs_parallel(parsed_files, need_coverage_map);
89
90 let name_files = build_name_file_map(
91 definitions
92 .iter()
93 .map(|d| (d.name.as_str(), d.file.as_path())),
94 );
95 let disambiguation =
96 build_disambiguation_map(&name_files, &test_references, &per_test_usage, graph);
97 let module_suffixes: HashMap<PathBuf, String> = definitions
98 .iter()
99 .map(|d| (d.file.clone(), file_to_module_suffix(&d.file)))
100 .collect();
101
102 let unreferenced: Vec<CodeDefinition> = definitions
103 .iter()
104 .filter(|def| {
105 let ctx = coverage::CoverageContext {
106 name_files: &name_files,
107 disambiguation: &disambiguation,
108 import_bindings: &import_bindings,
109 module_suffixes: &module_suffixes,
110 usage_refs: &usage_references,
111 call_refs: &call_references,
112 alias_bindings: &alias_bindings,
113 };
114 !is_definition_covered(def, &ctx)
115 })
116 .cloned()
117 .collect();
118
119 let coverage_map = if need_coverage_map {
120 build_py_coverage_map(
121 &definitions,
122 &per_test_usage,
123 &name_files,
124 &disambiguation,
125 &import_bindings,
126 &module_suffixes,
127 &alias_bindings,
128 )
129 } else {
130 HashMap::new()
131 };
132
133 TestRefAnalysis {
134 definitions,
135 test_references,
136 call_references,
137 unreferenced,
138 coverage_map,
139 }
140}
141
142#[cfg(test)]
143mod tests;
144#[cfg(test)]
145mod tests_2;
146#[cfg(test)]
147mod tests_3;
148#[cfg(test)]
149mod tests_4;
150#[cfg(test)]
151mod tests_5;
152#[cfg(test)]
153mod tests_weighted;