cli_testing_specialist/utils/
parallel.rs1use crate::types::TestCategory;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ParallelStrategy {
16 Sequential,
23
24 CategoryLevel,
31
32 TestLevel,
39}
40
41#[derive(Debug, Clone)]
43pub struct Workload {
44 pub num_categories: usize,
46
47 pub estimated_tests_per_category: usize,
49
50 pub num_cpus: usize,
52}
53
54impl Workload {
55 pub fn new(
57 categories: &[TestCategory],
58 num_global_options: usize,
59 num_subcommands: usize,
60 ) -> Self {
61 let num_categories = categories.len();
62
63 let estimated_tests_per_category =
66 estimate_tests_per_category(num_global_options, num_subcommands, num_categories);
67
68 let num_cpus = num_cpus::get();
69
70 Self {
71 num_categories,
72 estimated_tests_per_category,
73 num_cpus,
74 }
75 }
76
77 pub fn total_estimated_tests(&self) -> usize {
79 self.num_categories * self.estimated_tests_per_category
80 }
81}
82
83fn estimate_tests_per_category(
85 num_global_options: usize,
86 num_subcommands: usize,
87 num_categories: usize,
88) -> usize {
89 if num_categories == 0 {
90 return 0;
91 }
92
93 let complexity_score = num_global_options + num_subcommands;
100 let avg_tests_per_category = complexity_score.max(1) * 2 / num_categories.max(1);
101
102 avg_tests_per_category.clamp(5, 50)
104}
105
106pub fn choose_strategy(workload: &Workload) -> ParallelStrategy {
127 let total_tests = workload.total_estimated_tests();
128
129 if total_tests < 20 || workload.num_categories <= 1 {
131 log::debug!(
132 "Choosing Sequential strategy (total_tests={}, num_categories={})",
133 total_tests,
134 workload.num_categories
135 );
136 return ParallelStrategy::Sequential;
137 }
138
139 if total_tests < 100 || workload.num_cpus < 4 {
141 log::debug!(
142 "Choosing CategoryLevel strategy (total_tests={}, num_cpus={})",
143 total_tests,
144 workload.num_cpus
145 );
146 return ParallelStrategy::CategoryLevel;
147 }
148
149 log::debug!(
151 "Choosing TestLevel strategy (total_tests={}, num_cpus={})",
152 total_tests,
153 workload.num_cpus
154 );
155 ParallelStrategy::TestLevel
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 fn create_test_workload(
163 num_categories: usize,
164 num_global_options: usize,
165 num_subcommands: usize,
166 ) -> Workload {
167 let categories: Vec<TestCategory> = (0..num_categories)
168 .map(|i| match i % 3 {
169 0 => TestCategory::Basic,
170 1 => TestCategory::Security,
171 _ => TestCategory::Help,
172 })
173 .collect();
174
175 Workload::new(&categories, num_global_options, num_subcommands)
176 }
177
178 #[test]
179 fn test_choose_strategy_sequential_small_workload() {
180 let workload = create_test_workload(1, 5, 0);
182 assert_eq!(choose_strategy(&workload), ParallelStrategy::Sequential);
183 }
184
185 #[test]
186 fn test_choose_strategy_sequential_single_category() {
187 let workload = create_test_workload(1, 50, 10);
189 assert_eq!(choose_strategy(&workload), ParallelStrategy::Sequential);
190 }
191
192 #[test]
193 fn test_choose_strategy_category_level_medium_workload() {
194 let workload = create_test_workload(3, 10, 5);
196 assert_eq!(choose_strategy(&workload), ParallelStrategy::CategoryLevel);
197 }
198
199 #[test]
200 fn test_choose_strategy_test_level_large_workload() {
201 let workload = create_test_workload(6, 30, 50);
203 let total_tests = workload.total_estimated_tests();
204
205 assert!(
207 total_tests >= 100,
208 "Expected large workload (>=100 tests), got {}",
209 total_tests
210 );
211
212 let strategy = choose_strategy(&workload);
213
214 if workload.num_cpus >= 4 {
216 assert_eq!(
217 strategy,
218 ParallelStrategy::TestLevel,
219 "Expected TestLevel with {} CPUs and {} tests",
220 workload.num_cpus,
221 total_tests
222 );
223 } else {
224 assert_eq!(
225 strategy,
226 ParallelStrategy::CategoryLevel,
227 "Expected CategoryLevel with {} CPUs and {} tests",
228 workload.num_cpus,
229 total_tests
230 );
231 }
232 }
233
234 #[test]
235 fn test_estimate_tests_per_category() {
236 let result = estimate_tests_per_category(10, 5, 3);
240 assert_eq!(result, 10);
241 }
242
243 #[test]
244 fn test_estimate_tests_per_category_clamping() {
245 let result = estimate_tests_per_category(1, 0, 5);
247 assert_eq!(result, 5);
248
249 let result = estimate_tests_per_category(100, 50, 1);
251 assert_eq!(result, 50);
252 }
253
254 #[test]
255 fn test_workload_total_estimated_tests() {
256 let workload = create_test_workload(4, 10, 5);
257 let total = workload.total_estimated_tests();
258
259 assert!(total > 0);
261 assert!(total <= 200); }
263}