diff --git a/.bazelversion b/.bazelversion index b13d146a7b..3b74042132 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -9.3.0 +9.3.1 diff --git a/crates/tracedecay-cli/tests/host_journeys_suite/host_lifecycle_cli_acceptance/sweep_outcomes.rs b/crates/tracedecay-cli/tests/host_journeys_suite/host_lifecycle_cli_acceptance/sweep_outcomes.rs index ecde88c2c1..c2b40102ce 100644 --- a/crates/tracedecay-cli/tests/host_journeys_suite/host_lifecycle_cli_acceptance/sweep_outcomes.rs +++ b/crates/tracedecay-cli/tests/host_journeys_suite/host_lifecycle_cli_acceptance/sweep_outcomes.rs @@ -7,7 +7,7 @@ use std::path::Path; use std::path::PathBuf; use std::process::Output; #[cfg(unix)] -use std::process::{Child, Stdio}; +use std::process::{Child, Command, Stdio}; use tracedecay_agent_hosts::agents::host_bundle::{HostComponentV1, HostKindV1}; #[cfg(unix)] @@ -477,6 +477,19 @@ impl Drop for ProfileDaemon { #[cfg(unix)] #[test] fn doctor_reaches_the_canonical_daemon_socket_for_a_long_profile_path() { + if std::env::var_os(SOCKET_ENV).is_some() { + let status = Command::new(std::env::current_exe().unwrap()) + .args([ + "--exact", + "host_lifecycle_cli_acceptance::sweep_outcomes::doctor_reaches_the_canonical_daemon_socket_for_a_long_profile_path", + ]) + .env_remove(SOCKET_ENV) + .status() + .unwrap(); + assert!(status.success(), "the isolated Doctor journey failed"); + return; + } + let mut cli = IsolatedCli::new(); cli.profile = cli.home.path().join("p".repeat(120)).join("profile"); install_cline(&cli); diff --git a/crates/tracedecay/tests/memory_suite/memory_eval_test.rs b/crates/tracedecay/tests/memory_suite/memory_eval_test.rs index a94d7cf031..9c8fa9a360 100644 --- a/crates/tracedecay/tests/memory_suite/memory_eval_test.rs +++ b/crates/tracedecay/tests/memory_suite/memory_eval_test.rs @@ -3,7 +3,6 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; -use std::thread::JoinHandle; use std::time::{Duration, Instant}; use serde::Deserialize; @@ -133,47 +132,16 @@ fn load_scenario(id: &str) -> Scenario { scenario } -/// Drains one child pipe on its own thread so a child blocked on a full pipe -/// cannot be mistaken for a hang (see the same harness in the CLI suite). -fn drain_pipe(mut pipe: R) -> JoinHandle> { - std::thread::spawn(move || { - let mut buf = Vec::new(); - pipe.read_to_end(&mut buf) - .unwrap_or_else(|error| panic!("failed to drain child pipe: {error}")); - buf - }) -} - fn run_with_timeout(mut command: Command, timeout: Duration) -> Output { - let mut child = command + let child = command .spawn() .unwrap_or_else(|error| panic!("failed to spawn tracedecay: {error}")); - let stdout = child.stdout.take().map(drain_pipe); - let stderr = child.stderr.take().map(drain_pipe); - let started = Instant::now(); - loop { - if let Some(status) = child - .try_wait() - .unwrap_or_else(|error| panic!("failed to poll tracedecay: {error}")) - { - let join = |handle: Option>>| { - handle.map_or_default(|handle| { - handle.join().expect("child pipe drain thread panicked") - }) - }; - return Output { - status, - stdout: join(stdout), - stderr: join(stderr), - }; - } - assert!( - started.elapsed() < timeout, - "tracedecay hung after {:?}", - started.elapsed() - ); - std::thread::sleep(Duration::from_millis(5)); - } + // Arm the canonical owner before starting either pipe reader. A thread + // admission panic or wait failure must reap the command before its + // fixture is removed. + common::TestChildProcess::new(child) + .wait_with_output(timeout) + .unwrap_or_else(|error| panic!("failed to collect tracedecay output: {error}")) } fn run_ok(fixture: &Fixture, args: &[&str]) -> Output { @@ -774,6 +742,9 @@ fn run_scenario(id: &str) { "[{id}] well-behaved phase failed:\n{}", format_outcomes(&outcomes) ); + // The violation uses an independent store; retire this daemon before + // starting the next one instead of retaining both until the test ends. + drop(fixture); let Some(violation) = &scenario.deterministic.violation else { return;