fix(cli): report multi-target failures and return nonzero
Consume structured per-target outcomes in the streaming CLI and TUI so mixed failures remain visible while successful targets still produce output. Return a failure status when every target fails, and keep interruption behavior distinct. Add strict-timeout integration coverage for mixed continuous completion and all-open-failure exit status, plus focused TUI outcome-state tests.
This commit is contained in:
5 files changed
+428
-18
No files matched your search
@@ -10,7 +10,8 @@ use std::{
|
||||
|
||||
use irtt_client::{
|
||||
ClientEvent, EventSubscriptionError, ManagedClientGroup, ManagedClientGroupConfig,
|
||||
ManagedGroupEndReason, ManagedGroupEvent, SubscriberConfig, SubscriberOverflow, TargetEvent,
|
||||
ManagedGroupEndReason, ManagedGroupEvent, ManagedTargetOutcome, SubscriberConfig,
|
||||
SubscriberOverflow, TargetEvent,
|
||||
};
|
||||
#[cfg(all(test, feature = "stats"))]
|
||||
use irtt_client::{ClientTimestamp, PacketMeta, RttSample, SignedDuration};
|
||||
@@ -347,6 +348,7 @@ fn run_group_stream(
|
||||
}
|
||||
}
|
||||
ManagedGroupEvent::TargetFinished(target) => {
|
||||
report_target_failure(&target);
|
||||
terminal_targets.insert(target.id.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
@@ -374,18 +376,25 @@ fn run_group_stream(
|
||||
|
||||
let outcome = session.join()?;
|
||||
while let Ok(Some(group_event)) = events.try_recv() {
|
||||
if let ManagedGroupEvent::Client(target_event) = group_event {
|
||||
#[cfg(feature = "stats")]
|
||||
{
|
||||
let label = target_event.target.as_str();
|
||||
let stats = stats
|
||||
.entry(label.to_owned())
|
||||
.or_insert_with(|| StatsCollector::new(stats_config(continuous)));
|
||||
print_target_event_with_stats(&mut stream_output, &target_event, stats)?;
|
||||
match group_event {
|
||||
ManagedGroupEvent::Client(target_event) => {
|
||||
#[cfg(feature = "stats")]
|
||||
{
|
||||
let label = target_event.target.as_str();
|
||||
let stats = stats
|
||||
.entry(label.to_owned())
|
||||
.or_insert_with(|| StatsCollector::new(stats_config(continuous)));
|
||||
print_target_event_with_stats(&mut stream_output, &target_event, stats)?;
|
||||
}
|
||||
#[cfg(not(feature = "stats"))]
|
||||
{
|
||||
print_target_event_with_stats(&mut stream_output, &target_event)?;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "stats"))]
|
||||
{
|
||||
print_target_event_with_stats(&mut stream_output, &target_event)?;
|
||||
ManagedGroupEvent::TargetFinished(target) => {
|
||||
if terminal_targets.insert(target.id.as_str().to_owned()) {
|
||||
report_target_failure(&target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,6 +402,26 @@ fn run_group_stream(
|
||||
if outcome.end_reason == ManagedGroupEndReason::Cancelled && !interrupted {
|
||||
return Err("managed client group was cancelled".into());
|
||||
}
|
||||
for target in &outcome.targets {
|
||||
if terminal_targets.insert(target.id.as_str().to_owned()) {
|
||||
report_target_failure(target);
|
||||
}
|
||||
}
|
||||
let successful_targets = outcome
|
||||
.targets
|
||||
.iter()
|
||||
.filter(|target| target.is_success())
|
||||
.count();
|
||||
let failed_targets = outcome
|
||||
.targets
|
||||
.iter()
|
||||
.filter(|target| target.end_reason.failure().is_some())
|
||||
.count();
|
||||
if !interrupted && successful_targets == 0 && failed_targets > 0 {
|
||||
return Err(
|
||||
format!("no managed target completed successfully ({failed_targets} failed)").into(),
|
||||
);
|
||||
}
|
||||
|
||||
let print_final_summary = should_print_final_summary(continuous, interrupted);
|
||||
stream_output.print_final_summary = print_final_summary;
|
||||
@@ -415,6 +444,15 @@ fn run_group_stream(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_target_failure(target: &ManagedTargetOutcome) {
|
||||
if let Some(failure) = target.end_reason.failure() {
|
||||
eprintln!(
|
||||
"irtt-rs: target {} failed ({}): {}",
|
||||
target.id, failure.kind, failure.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn estimated_group_completion_grace(args: &ClientArgs) -> Duration {
|
||||
let open_timeout: Duration = args.to_client_config().open_timeouts.iter().sum();
|
||||
open_timeout
|
||||
|
||||
Reference in new issue
Block a user