From 08ba44719a1c237d0279c2c6da0e1c6413d9077a Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Wed, 11 Nov 2026 17:49:01 -0800 Subject: [PATCH] tests: wait for the fake OpenVMM's whole command line (flaky state read) The Windows job of CI run 43131964028, on #1496, which doesn't touch this code, failed in the CLI test start_opens_the_stable_caller_paths_that_provision_checked: tests\cli\harness.rs:538:33: ...\build\fake-openvmm-state.json: EOF while parsing a value at line 1 column 0 The test starts a sandbox in the background, waits until the fake OpenVMM's record of its command line, fake-openvmm-state.json, exists, and then parses it. The fake writes the record with fs::write, which creates the file before it writes its content, so a test that reads it in between finds it empty. #1460 added the parse right after that wait, which until then only told the test that OpenVMM had started. wait_for_json, which the same file already uses for the fake's record of its block devices, waits for exactly this. The test now takes the arguments from wait_for_json, which returns once the record holds a whole JSON document, and checks the same things in the same order: the record of the block devices still doesn't exist then, since the fake opens its images 1.5 s after it starts. wait_for's comment now sends a test that reads a file's content to wait_for_json. The fake keeps writing its records with fs::write. Its other readers run once the command that launched it has returned, or once it has exited, so after it wrote them. A temporary file and a rename would spare future readers the wait, but on Windows a rename fails while another process, such as a scanner, holds the record that it replaces open without sharing its deletion, and the fake ignores its write errors, so a second launch of the same sandbox could leave the first one's record in place. Evidence, at 14ca2671205, with a fake that waits 300 ms between creating the record and writing it: the base test failed with the CI symptom on a Windows 11 workstation and on nvx-kvm-test (Linux, KVM), and this change passed on both. Without that wait, this change passed 50 of 50 runs of the test and 3 of 3 runs of the CLI suite on each. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- nvx-rs/tests/cli.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nvx-rs/tests/cli.rs b/nvx-rs/tests/cli.rs index 48a490237..8888c6d9f 100644 --- a/nvx-rs/tests/cli.rs +++ b/nvx-rs/tests/cli.rs @@ -1885,7 +1885,8 @@ fn a_running_sandbox_claims_its_scratch_image() { assert!(output.status.success(), "{}", text(&output.stderr)); } -/// Waits until the file at `path` exists. +/// Waits until the file at `path` exists. A test that reads the file's content waits with +/// [`wait_for_json`] instead, since a file can exist before its writer has written its content. fn wait_for(path: &Path) { let deadline = std::time::Instant::now() + Duration::from_secs(60); while !path.exists() { @@ -1947,13 +1948,14 @@ fn start_opens_the_stable_caller_paths_that_provision_checked() { .unwrap(); // OpenVMM has started, and has not opened its images yet. - wait_for(&root.path.join("build").join("fake-openvmm-state.json")); + let arguments: Vec = + serde_json::from_value(wait_for_json(&root.below("build/fake-openvmm-state.json"))) + .unwrap(); let blocks = root .path .join("build") .join("fake-openvmm-state-blocks.json"); assert!(!blocks.exists()); - let arguments = root.openvmm_arguments("build/fake-openvmm-state.json"); let mut options = openvmm::Options::parse(&arguments); openvmm::assert_stable_blocks(&root, &options.take("--microvm-sandbox-block"), true); let journal = wait_for_json(&root.below("state/launch.json"));