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
@@ -7,8 +7,8 @@ use std::{
|
||||
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers};
|
||||
use irtt_client::{
|
||||
ClientEvent, EventSubscriptionError, ManagedClientGroup, ManagedClientGroupConfig,
|
||||
ManagedGroupEndReason, SubscriberConfig, SubscriberOverflow,
|
||||
EventSubscriptionError, ManagedClientGroup, ManagedClientGroupConfig, ManagedGroupEndReason,
|
||||
ManagedGroupEvent, SubscriberConfig, SubscriberOverflow,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -184,13 +184,17 @@ fn run_group_tui(
|
||||
}
|
||||
|
||||
match events.try_recv() {
|
||||
Ok(Some(target_event)) => {
|
||||
Ok(Some(group_event)) => {
|
||||
saw_target_event = true;
|
||||
last_event_at = Instant::now();
|
||||
if is_terminal_target_event(&target_event.event) {
|
||||
terminal_targets.insert(target_event.target.as_str().to_owned());
|
||||
match group_event {
|
||||
ManagedGroupEvent::Client(target_event) => {
|
||||
state.process_target_event(&target_event);
|
||||
}
|
||||
ManagedGroupEvent::TargetFinished(target) => {
|
||||
terminal_targets.insert(target.id.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
state.process_target_event(&target_event);
|
||||
render_if_due(terminal, state, next_render, false)?;
|
||||
}
|
||||
Ok(None) => {
|
||||
@@ -225,8 +229,10 @@ fn run_group_tui(
|
||||
render_if_due(terminal, state, next_render, true)?;
|
||||
|
||||
let outcome = session.join()?;
|
||||
while let Ok(Some(target_event)) = events.try_recv() {
|
||||
state.process_target_event(&target_event);
|
||||
while let Ok(Some(group_event)) = events.try_recv() {
|
||||
if let ManagedGroupEvent::Client(target_event) = group_event {
|
||||
state.process_target_event(&target_event);
|
||||
}
|
||||
}
|
||||
|
||||
if outcome.end_reason == ManagedGroupEndReason::Cancelled && !interrupted {
|
||||
@@ -265,13 +271,6 @@ impl GroupLoopExit {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_terminal_target_event(event: &ClientEvent) -> bool {
|
||||
matches!(
|
||||
event,
|
||||
ClientEvent::SessionClosed { .. } | ClientEvent::NoTestCompleted { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn estimated_group_completion_grace(args: &TuiArgs) -> Duration {
|
||||
let open_timeout: Duration = args.to_client_config().open_timeouts.iter().sum();
|
||||
open_timeout
|
||||
|
||||
Reference in new issue
Block a user