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