fix(managed): publish terminal outcomes for failed targets
Extend the managed group stream with authoritative per-target terminal outcomes and structured failure classifications. Final client events are published first, followed by exactly one TargetFinished outcome for success, peer close, no-test, opening/runtime failure, removal, or cancellation. Scheduler-owned cleanup now stops the receiver and seals subscriptions on natural completion, eliminating the event-consumer/join circular wait while preserving queued drain. TargetEventSubscription now yields ManagedGroupEvent, so consumers must handle Client and TargetFinished variants. Strict-timeout regressions cover mixed success/failure, all-open failure, runtime failure, no-test ordering, removal, cancellation, and disconnection before join.
This commit is contained in:
5 files changed
+548
-132
No files matched your search
@@ -10,7 +10,7 @@ use std::{
|
||||
|
||||
use irtt_client::{
|
||||
ClientEvent, EventSubscriptionError, ManagedClientGroup, ManagedClientGroupConfig,
|
||||
ManagedGroupEndReason, SubscriberConfig, SubscriberOverflow, TargetEvent,
|
||||
ManagedGroupEndReason, ManagedGroupEvent, SubscriberConfig, SubscriberOverflow, TargetEvent,
|
||||
};
|
||||
#[cfg(all(test, feature = "stats"))]
|
||||
use irtt_client::{ClientTimestamp, PacketMeta, RttSample, SignedDuration};
|
||||
@@ -324,23 +324,31 @@ fn run_group_stream(
|
||||
}
|
||||
|
||||
match events.try_recv() {
|
||||
Ok(Some(target_event)) => {
|
||||
Ok(Some(group_event)) => {
|
||||
last_event_at = Instant::now();
|
||||
saw_target_event = true;
|
||||
let label = target_event.target.as_str();
|
||||
if is_terminal_target_event(&target_event.event) {
|
||||
terminal_targets.insert(label.to_owned());
|
||||
}
|
||||
#[cfg(feature = "stats")]
|
||||
{
|
||||
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)?;
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
ManagedGroupEvent::TargetFinished(target) => {
|
||||
terminal_targets.insert(target.id.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
@@ -365,18 +373,20 @@ fn run_group_stream(
|
||||
}
|
||||
|
||||
let outcome = session.join()?;
|
||||
while let Ok(Some(target_event)) = events.try_recv() {
|
||||
let label = target_event.target.as_str();
|
||||
#[cfg(feature = "stats")]
|
||||
{
|
||||
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)?;
|
||||
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)?;
|
||||
}
|
||||
#[cfg(not(feature = "stats"))]
|
||||
{
|
||||
print_target_event_with_stats(&mut stream_output, &target_event)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,13 +415,6 @@ fn run_group_stream(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_terminal_target_event(event: &ClientEvent) -> bool {
|
||||
matches!(
|
||||
event,
|
||||
ClientEvent::SessionClosed { .. } | ClientEvent::NoTestCompleted { .. }
|
||||
)
|
||||
}
|
||||
|
||||
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