From a684ef1e347afdd315138db547ad74b09b95db0b Mon Sep 17 00:00:00 2001 From: damocles Date: Sat, 24 Oct 2026 09:54:56 +0200 Subject: [PATCH] meta test: retry the fake nix exec when it hits ETXTBSY --- hive-c0re/src/meta.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index 430c65ae..53eedd3a 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -2144,14 +2144,26 @@ mod lock_wait_tests { std::fs::write(&fake, "#!/bin/sh\nexec sleep 30\n").expect("write"); std::fs::set_permissions(&fake, std::os::unix::fs::PermissionsExt::from_mode(0o755)) .expect("chmod"); - let err = nix_output_with( - fake.as_os_str(), - dir.path(), - &["flake", "update", "hyperhive"], - Duration::from_millis(300), - ) - .await - .expect_err("hung nix must time out"); + // Parallel tests fork while the script's write fd is still open, so an + // early exec can hit ETXTBSY. That is a race in building the fake, not + // in `nix_output_with`: retry it, anything else fails the test. + let mut tries = 0; + let err = loop { + let res = nix_output_with( + fake.as_os_str(), + dir.path(), + &["flake", "update", "hyperhive"], + Duration::from_millis(300), + ) + .await; + match res { + Err(e) if tries < 20 && format!("{e:#}").contains("Text file busy") => { + tries += 1; + tokio::time::sleep(Duration::from_millis(50)).await; + } + other => break other.expect_err("hung nix must time out"), + } + }; let msg = format!("{err:#}"); assert!( msg.contains("nix flake update hyperhive in") && msg.contains("timed out after 0.3s"),