nextest_runner/show_config/
test_groups.rs1use crate::{
5 config::{
6 CompiledOverride, CustomTestGroup, EarlyProfile, EvaluatableProfile, FinalConfig,
7 MaybeTargetSpec, OverrideId, SettingSource, TestGroup, TestGroupConfig,
8 },
9 errors::ShowTestGroupsError,
10 helpers::QuotedDisplay,
11 indenter::indented,
12 list::{TestInstance, TestList, TestListDisplayFilter},
13 write_str::WriteStr,
14};
15use indexmap::IndexMap;
16use owo_colors::{OwoColorize, Style};
17use std::{
18 collections::{BTreeMap, BTreeSet},
19 io,
20};
21
22#[derive(Debug)]
24pub struct ShowTestGroups<'a> {
25 test_list: &'a TestList<'a>,
26 indexed_overrides: BTreeMap<TestGroup, IndexMap<OverrideId, ShowTestGroupsData<'a>>>,
27 test_group_config: &'a BTreeMap<CustomTestGroup, TestGroupConfig>,
28 non_overrides: Option<TestListDisplayFilter<'a>>,
30}
31
32impl<'a> ShowTestGroups<'a> {
33 pub fn validate_groups(
35 profile: &EarlyProfile<'_>,
36 groups: impl IntoIterator<Item = TestGroup>,
37 ) -> Result<ValidatedTestGroups, ShowTestGroupsError> {
38 let groups = groups.into_iter().collect();
39 let known_groups: BTreeSet<_> =
40 TestGroup::make_all_groups(profile.test_group_config().keys().cloned()).collect();
41 let unknown_groups = &groups - &known_groups;
42 if !unknown_groups.is_empty() {
43 return Err(ShowTestGroupsError::UnknownGroups {
44 unknown_groups,
45 known_groups,
46 });
47 }
48 Ok(ValidatedTestGroups(groups))
49 }
50
51 pub fn new(
53 profile: &'a EvaluatableProfile<'a>,
54 test_list: &'a TestList<'a>,
55 settings: &ShowTestGroupSettings,
56 ) -> Self {
57 let mut indexed_overrides: BTreeMap<_, _> =
58 TestGroup::make_all_groups(profile.test_group_config().keys().cloned())
59 .filter_map(|group| {
60 settings
61 .mode
62 .matches_group(&group)
63 .then(|| (group, IndexMap::new()))
64 })
65 .collect();
66 let mut non_overrides = settings.show_default.then(TestListDisplayFilter::new);
67
68 for suite in test_list.iter() {
69 for (test_name, test_case) in suite.status.test_cases() {
70 let test_instance = TestInstance::new(test_name, suite, test_case);
71 let query = test_instance.to_test_query();
72 let test_settings = profile.settings_with_source_for(&query);
73 let (test_group, source) = test_settings.test_group_with_source();
74
75 match source {
76 SettingSource::Override(source) => {
77 let override_map = match indexed_overrides.get_mut(test_group) {
78 Some(override_map) => override_map,
79 None => continue,
80 };
81 let data = override_map
82 .entry(source.id().clone())
83 .or_insert_with(|| ShowTestGroupsData::new(source));
84 data.matching_tests.insert(&suite.binary_id, test_name);
85 }
86 SettingSource::Profile | SettingSource::Default => {
87 if let Some(non_overrides) = non_overrides.as_mut() {
88 if settings.mode.matches_group(&TestGroup::Global) {
89 non_overrides.insert(&suite.binary_id, test_name);
90 }
91 }
92 }
93 }
94 }
95 }
96
97 Self {
98 test_list,
99 indexed_overrides,
100 test_group_config: profile.test_group_config(),
101 non_overrides,
102 }
103 }
104
105 fn should_show_group(&self, group: &TestGroup) -> bool {
106 match (group, self.non_overrides.is_some()) {
119 (TestGroup::Global, true) => true,
120 (TestGroup::Global, false) => self
121 .indexed_overrides
122 .get(group)
123 .map(|override_map| !override_map.values().all(|data| data.is_empty()))
124 .unwrap_or(false),
125 _ => true,
126 }
127 }
128
129 pub fn write_human(&self, mut writer: &mut dyn WriteStr, colorize: bool) -> io::Result<()> {
131 static INDENT: &str = " ";
132
133 let mut styles = Styles::default();
134 if colorize {
135 styles.colorize();
136 }
137
138 for (test_group, override_map) in &self.indexed_overrides {
139 if !self.should_show_group(test_group) {
140 continue;
141 }
142
143 write!(writer, "group: {}", test_group.style(styles.group))?;
144 if let TestGroup::Custom(group) = test_group {
145 write!(
146 writer,
147 " (max threads = {})",
148 self.test_group_config[group]
149 .max_threads
150 .style(styles.max_threads)
151 )?;
152 }
153 writeln!(writer)?;
154
155 let mut any_printed = false;
156
157 for (override_id, data) in override_map {
158 any_printed = true;
159 write!(
160 writer,
161 " * override for {} profile",
162 override_id.profile_name.style(styles.profile),
163 )?;
164
165 if let Some(expr) = data.override_.filter() {
166 write!(
167 writer,
168 " with filter {}",
169 QuotedDisplay(&expr.parsed).style(styles.filter)
170 )?;
171 }
172 if let MaybeTargetSpec::Provided(target_spec) = data.override_.target_spec() {
173 write!(
174 writer,
175 " on platform {}",
176 QuotedDisplay(target_spec).style(styles.platform)
177 )?;
178 }
179
180 writeln!(writer, ":")?;
181
182 let mut inner_writer = indented(writer).with_str(INDENT);
183 self.test_list.write_human_with_filter(
184 &data.matching_tests,
185 &mut inner_writer,
186 false,
187 colorize,
188 )?;
189 inner_writer.write_str_flush()?;
190 writer = inner_writer.into_inner();
191 }
192
193 if test_group == &TestGroup::Global {
195 if let Some(non_overrides) = &self.non_overrides {
196 any_printed = true;
197 writeln!(writer, " * from default settings:")?;
198 let mut inner_writer = indented(writer).with_str(INDENT);
199 self.test_list.write_human_with_filter(
200 non_overrides,
201 &mut inner_writer,
202 false,
203 colorize,
204 )?;
205 inner_writer.write_str_flush()?;
206 writer = inner_writer.into_inner();
207 }
208 }
209
210 if !any_printed {
211 writeln!(writer, " (no matches)")?;
212 }
213 }
214
215 Ok(())
216 }
217}
218
219#[derive(Clone, Debug)]
221pub struct ShowTestGroupSettings {
222 pub show_default: bool,
224
225 pub mode: ShowTestGroupsMode,
227}
228
229#[derive(Clone, Debug)]
231pub enum ShowTestGroupsMode {
232 All,
234 Only(ValidatedTestGroups),
236}
237
238impl ShowTestGroupsMode {
239 fn matches_group(&self, group: &TestGroup) -> bool {
240 match self {
241 Self::All => true,
242 Self::Only(groups) => groups.0.contains(group),
243 }
244 }
245}
246
247#[derive(Clone, Debug)]
249pub struct ValidatedTestGroups(BTreeSet<TestGroup>);
250
251impl ValidatedTestGroups {
252 pub fn into_inner(self) -> BTreeSet<TestGroup> {
254 self.0
255 }
256}
257
258#[derive(Debug)]
259struct ShowTestGroupsData<'a> {
260 override_: &'a CompiledOverride<FinalConfig>,
261 matching_tests: TestListDisplayFilter<'a>,
262}
263
264impl<'a> ShowTestGroupsData<'a> {
265 fn new(override_: &'a CompiledOverride<FinalConfig>) -> Self {
266 Self {
267 override_,
268 matching_tests: TestListDisplayFilter::new(),
269 }
270 }
271
272 fn is_empty(&self) -> bool {
273 self.matching_tests.test_count() == 0
274 }
275}
276
277#[derive(Clone, Debug, Default)]
278struct Styles {
279 group: Style,
280 max_threads: Style,
281 profile: Style,
282 filter: Style,
283 platform: Style,
284}
285
286impl Styles {
287 fn colorize(&mut self) {
288 self.group = Style::new().bold().underline();
289 self.max_threads = Style::new().bold();
290 self.profile = Style::new().bold();
291 self.filter = Style::new().yellow();
292 self.platform = Style::new().yellow();
293 }
294}