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