Skip to content

Commit 3f944c3

Browse files
committed
test(hiroz-tests): improve guard robustness and diagnostics
1 parent 20cf617 commit 3f944c3

3 files changed

Lines changed: 47 additions & 7 deletions

File tree

crates/hiroz-tests/tests/common/mod.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,32 @@ impl Drop for ProducerGuard {
453453
// immediately (no lingering 40s hold, no reconnect-spin), and the
454454
// teardown completes in the background (or is reaped at process exit).
455455
self.stop.store(true, Ordering::Relaxed);
456-
// Drop the JoinHandle without joining, detaching the thread.
457-
self.handle.take();
456+
if let Some(handle) = self.handle.take() {
457+
// A well-behaved producer is still running (holding its entities)
458+
// right up until we set `stop`, so it will normally NOT be finished
459+
// here. If it *is* already finished, the producer exited early —
460+
// typically a panic — which would otherwise be swallowed silently and
461+
// make a later "entity never appeared" failure baffling. Since the
462+
// thread is finished, `join()` returns immediately (still
463+
// non-blocking), so we can observe and surface the panic.
464+
if handle.is_finished() {
465+
if let Err(panic) = handle.join() {
466+
let msg = panic
467+
.downcast_ref::<&str>()
468+
.map(|s| s.to_string())
469+
.or_else(|| panic.downcast_ref::<String>().cloned())
470+
.unwrap_or_else(|| "<non-string panic payload>".to_string());
471+
eprintln!(
472+
"WARNING: test producer thread exited early (before teardown) \
473+
with a panic: {msg}. Downstream 'entity not discovered' \
474+
failures in this test are likely caused by this."
475+
);
476+
}
477+
}
478+
// Otherwise: drop the JoinHandle without joining, detaching the
479+
// still-running thread (its Zenoh-session teardown can block, and we
480+
// must not block the test thread on it).
481+
}
458482
}
459483
}
460484

crates/hiroz-tests/tests/demo_nodes.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ fn test_hiroz_add_two_ints_server_to_rcl_client() {
401401
wait_for_ready(Duration::from_secs(5));
402402

403403
// Start RCL client
404-
let mut client = Command::new("ros2")
404+
let client = Command::new("ros2")
405405
.args(["run", "demo_nodes_cpp", "add_two_ints_client"])
406406
.env("RMW_IMPLEMENTATION", "rmw_zenoh_cpp")
407407
.env("ZENOH_CONFIG_OVERRIDE", router.rmw_zenoh_env())
@@ -411,21 +411,31 @@ fn test_hiroz_add_two_ints_server_to_rcl_client() {
411411
.spawn()
412412
.expect("Failed to start RCL client");
413413

414+
// Wrap the child in its ProcessGuard *before* any reaping so the guard owns
415+
// it throughout. Polling `try_wait` on a separate handle and only then
416+
// handing the (already-reaped) child to a fresh guard would let the guard's
417+
// Drop signal a process group whose PID may have been recycled. Poll through
418+
// the guard's own handle instead.
419+
let mut client_guard = ProcessGuard::new(client, "RCL add_two_ints client");
420+
414421
// Bound the wait on the RCL client's own exit instead of a fixed sleep,
415422
// so a slow-to-discover run fails fast with a clear message rather than
416423
// hanging the hiroz server thread (blocked on its one expected request)
417424
// until nextest's hard kill.
418425
let client_deadline = std::time::Instant::now() + Duration::from_secs(30);
419426
let client_status = loop {
420-
if let Some(status) = client.try_wait().expect("Failed to poll RCL client") {
427+
let child = client_guard
428+
.child
429+
.as_mut()
430+
.expect("client child owned by guard");
431+
if let Some(status) = child.try_wait().expect("Failed to poll RCL client") {
421432
break Some(status);
422433
}
423434
if std::time::Instant::now() >= client_deadline {
424435
break None;
425436
}
426437
thread::sleep(Duration::from_millis(200));
427438
};
428-
let _client_guard = ProcessGuard::new(client, "RCL add_two_ints client");
429439
// Check the exit status is actually success, not just that the process
430440
// exited -- an instantly-failing `ros2 run` (e.g. missing verb plugin,
431441
// bad args) also "exits within 30s" and would otherwise false-pass here.

crates/hiroz-tests/tests/parameter_interop.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,15 @@ fn wait_for_node(node_name: &str, router: &TestRouter, timeout: Duration) {
5656
}
5757

5858
if start.elapsed() > timeout {
59+
let discovered: Vec<String> = ctx
60+
.graph()
61+
.get_node_names()
62+
.iter()
63+
.map(|(name, ns)| format!("{ns}/{name}"))
64+
.collect();
5965
panic!(
60-
"Timed out waiting for node {} after {:?}",
61-
node_name, timeout
66+
"Timed out waiting for node {} after {:?}; nodes currently discovered: {:?}",
67+
node_name, timeout, discovered
6268
);
6369
}
6470

0 commit comments

Comments
 (0)