fix(cli): preserve target order in summaries

Final multi-target summaries were printed by iterating the label-keyed
BTreeMap directly, so they came out in lexicographic label order
instead of the order targets were given on the command line. Iterate
setup.targets (the canonical prepared CLI order) instead, looking up
each target's stats by label, and keep the BTreeMap only for
event-time lookup.
This commit is contained in:
Nils Andreas Svee committed 2026-08-20 10:42:31 +07:00
1 parent 0d9826800b
commit 6a4dce68b3
2 files changed
+54 -3

No files matched your search

+6 -3
View File
@@ -176,15 +176,18 @@ pub fn run_stream(
stream_output.print_final_summary = should_print_final_summary(continuous, interrupted); stream_output.print_final_summary = should_print_final_summary(continuous, interrupted);
stream_output.show_running_only_summary_note = stream_output.show_running_only_summary_note =
continuous && interrupted && stream_output.print_final_summary; continuous && interrupted && stream_output.print_final_summary;
for (label, stats) in &stats { for target in &setup.targets {
let target_stats = stats.get(&target.label).expect(
"every prepared target must have a stats collector inserted before the run begins",
);
if multi_target if multi_target
&& stream_output.print_final_summary && stream_output.print_final_summary
&& stream_output.config.prints_summary() && stream_output.config.prints_summary()
{ {
writeln!(stream_output.out)?; writeln!(stream_output.out)?;
writeln!(stream_output.out, "target: {label}")?; writeln!(stream_output.out, "target: {}", target.label)?;
} }
stream_output.print_summary(stats)?; stream_output.print_summary(target_stats)?;
} }
stream_output.out.flush()?; stream_output.out.flush()?;
if let Some(error) = terminal_error { if let Some(error) = terminal_error {
@@ -277,6 +277,54 @@ fn multi_target_default_table_includes_both_labels() {
); );
} }
#[test]
fn multi_target_final_summary_preserves_cli_argument_order() {
// Labels are chosen so lexicographic (BTreeMap) order differs from the
// CLI argument order, to prove the final per-target summary headings
// follow the order targets were given on the command line rather than
// the internal label-keyed lookup map's iteration order.
let z = InTreeServer::start();
let a = InTreeServer::start();
let m = InTreeServer::start();
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
.args([
"--duration",
"40ms",
"--interval",
"10ms",
&format!("z={}", z.addr),
&format!("a={}", a.addr),
&format!("m={}", m.addr),
])
.output()
.unwrap();
drop(z);
drop(a);
drop(m);
assert!(
output.status.success(),
"status={:?}\nstderr={}",
output.status.code(),
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).unwrap();
let heading_position = |label: &str| {
stdout
.find(&format!("target: {label}"))
.unwrap_or_else(|| panic!("missing summary heading for target {label}\n{stdout}"))
};
let z_pos = heading_position("z");
let a_pos = heading_position("a");
let m_pos = heading_position("m");
assert!(
z_pos < a_pos && a_pos < m_pos,
"expected summary headings in CLI argument order z, a, m; got positions z={z_pos} a={a_pos} m={m_pos}\n{stdout}"
);
}
#[test] #[test]
fn mixed_labeled_and_unlabeled_positional_targets_both_appear_in_output() { fn mixed_labeled_and_unlabeled_positional_targets_both_appear_in_output() {
let a = InTreeServer::start(); let a = InTreeServer::start();