diff --git a/jouledesk/jouledesk-rs/Cargo.lock b/jouledesk/jouledesk-rs/Cargo.lock index 5876b5edc..b5eb9e6ff 100644 --- a/jouledesk/jouledesk-rs/Cargo.lock +++ b/jouledesk/jouledesk-rs/Cargo.lock @@ -1116,6 +1116,7 @@ dependencies = [ "hex", "jcp-receipt", "jcp-schema", + "serde", "serde_json", "thiserror 2.0.20", ] diff --git a/joulehook/joulehook-rs/Cargo.lock b/joulehook/joulehook-rs/Cargo.lock index c7d87299f..ea9836e82 100644 --- a/joulehook/joulehook-rs/Cargo.lock +++ b/joulehook/joulehook-rs/Cargo.lock @@ -412,6 +412,7 @@ dependencies = [ "hex", "jcp-receipt", "jcp-schema", + "serde", "serde_json", "thiserror", ] diff --git a/sandbox/sandbox-rs/Cargo.lock b/sandbox/sandbox-rs/Cargo.lock index 04882f2bf..0777052a6 100644 --- a/sandbox/sandbox-rs/Cargo.lock +++ b/sandbox/sandbox-rs/Cargo.lock @@ -1066,6 +1066,7 @@ dependencies = [ "hex", "jcp-receipt", "jcp-schema", + "serde", "serde_json", "thiserror 2.0.20", ] diff --git a/sandbox/sandbox-rs/crates/sandbox-meter/tests/tree_energy.rs b/sandbox/sandbox-rs/crates/sandbox-meter/tests/tree_energy.rs index cdf75aae3..6538ddf51 100644 --- a/sandbox/sandbox-rs/crates/sandbox-meter/tests/tree_energy.rs +++ b/sandbox/sandbox-rs/crates/sandbox-meter/tests/tree_energy.rs @@ -106,6 +106,32 @@ fn ps_tree(root: i32) -> Vec { tree } +/// `root`'s tree once it has exactly `want` members. Panics after 20 s, +/// naming the tree it last saw. +/// +/// A server's reply does not mean its worker is gone. The worker prints and +/// the parent reads that line while the worker still has its work to do and +/// its exit and reaping after that, so a tree walked straight after a reply +/// can still hold it. Waiting for the shape a test assumes is the only way to +/// assert that shape; asserting it on the first walk asserts a race, and that +/// race is real — the count came back 62 instead of 61 in 2 runs of 5 on this +/// host, more often the busier it is. +fn settled_tree(root: i32, want: usize) -> Vec { + let deadline = Instant::now() + Duration::from_secs(20); + loop { + let tree = ps_tree(root); + if tree.len() == want { + return tree; + } + assert!( + Instant::now() < deadline, + "the tree of {root} never settled to {want} members; last saw {}: {tree:?}", + tree.len() + ); + std::thread::sleep(Duration::from_millis(20)); + } +} + /// Each of `pids`' rusage once every one of them is quiet by the oracle's /// rule: not running or queued (or exited) at two reads 20 ms apart, with /// the same energy at both. Panics after 20 s. @@ -277,11 +303,14 @@ fn a_worker_reaped_while_its_tree_is_walked_is_never_missed() { ], ); let root = s.pid() as u32; - // The first round trip runs once all 60 are forked; the tree is quiet - // before the first span. + // The first round trip runs once all 60 are forked. It does not leave the + // tree quiet, though — the worker it forks prints its pid before doing its + // work, so the reply arrives while that worker is still running — hence + // the wait below rather than a bare walk. let _ = s.ask("warm up"); - let tree = ps_tree(s.pid()); - assert_eq!(tree.len(), 61, "setup: the server and its 60 idle children: {tree:?}"); + // Not `ps_tree` + `assert_eq!` here: the warm-up round trip returns while + // its worker is still the server's child, so the first walk sees 62. + let tree = settled_tree(s.pid(), 61); quiet(&tree); let meter = s.meter(); let (mut exited, mut reaped, mut unsettled) = (0, 0, 0);