From fb915891be599d8769f500bb506167f5df21ab39 Mon Sep 17 00:00:00 2001 From: Angel Rodriguez <24271590+AngelVRodC@users.noreply.github.com> Date: Sun, 13 Sep 2026 21:34:28 -0500 Subject: [PATCH] fix(tests): make dotnet TRX tests deterministic via explicit mtimes Replace thread::sleep-based wall-clock separation in the three flaky dotnet TRX tests with explicit file mtimes set via File::set_modified (stable since Rust 1.75). Sleep margins of 5-10ms fall inside the 1-2s mtime granularity of HFS+/FAT/CI mounts, causing nondeterministic failures. Timestamps are now pinned (old = base - 1 day, new = base + 1 hour) and since-cutoffs derive from the same base value, so outcomes never depend on elapsed real time. Fixes #4037 --- src/cmds/dotnet/dotnet_cmd.rs | 9 +++++---- src/cmds/dotnet/dotnet_trx.rs | 26 ++++++++++++++------------ src/cmds/dotnet/mod.rs | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/cmds/dotnet/dotnet_cmd.rs b/src/cmds/dotnet/dotnet_cmd.rs index e99fa60a4d..bfc5b2a92f 100644 --- a/src/cmds/dotnet/dotnet_cmd.rs +++ b/src/cmds/dotnet/dotnet_cmd.rs @@ -1483,6 +1483,7 @@ fn format_restore_output( #[cfg(test)] mod tests { use super::*; + use crate::cmds::dotnet::test_support; use crate::dotnet_format_report; use std::fs; use std::time::Duration; @@ -2935,16 +2936,16 @@ mod tests { fn test_merge_test_summary_from_trx_ignores_stale_fallback_file() { let temp_dir = tempfile::tempdir().expect("create temp dir"); let fallback = temp_dir.path().join("fallback.trx"); - fs::write(&fallback, trx_with_counts(2, 1, 1)).expect("write fallback trx"); - std::thread::sleep(std::time::Duration::from_millis(5)); - let command_started_at = SystemTime::now(); + let base = SystemTime::now(); + let stale = base - std::time::Duration::from_secs(86_400); + test_support::write_trx_with_mtime(&fallback, &trx_with_counts(2, 1, 1), stale); let missing_primary = temp_dir.path().join("missing.trx"); let filled = merge_test_summary_from_trx( binlog::TestSummary::default(), Some(&missing_primary), Some(fallback.clone()), - command_started_at, + base, ); assert_eq!(filled.total, 0); diff --git a/src/cmds/dotnet/dotnet_trx.rs b/src/cmds/dotnet/dotnet_trx.rs index ef94f00892..cafccdd49c 100644 --- a/src/cmds/dotnet/dotnet_trx.rs +++ b/src/cmds/dotnet/dotnet_trx.rs @@ -385,6 +385,7 @@ fn parse_trx_content(content: &str) -> Option { #[cfg(test)] mod tests { use super::*; + use crate::cmds::dotnet::test_support; use std::time::Duration; #[test] @@ -493,11 +494,14 @@ mod tests { let testresults_dir = temp_dir.path().join("TestResults"); std::fs::create_dir_all(&testresults_dir).expect("create TestResults"); + let base = SystemTime::now(); + let old = base - Duration::from_secs(86_400); + let new = base + Duration::from_secs(3_600); + let old_trx = testresults_dir.join("old.trx"); let new_trx = testresults_dir.join("new.trx"); - std::fs::write(&old_trx, "old").expect("write old"); - std::thread::sleep(Duration::from_millis(5)); - std::fs::write(&new_trx, "new").expect("write new"); + test_support::write_trx_with_mtime(&old_trx, "old", old); + test_support::write_trx_with_mtime(&new_trx, "new", new); let found = find_recent_trx_in_dir(&testresults_dir).expect("should find newest trx"); assert_eq!(found, new_trx); @@ -554,21 +558,19 @@ mod tests { let trx_dir = temp_dir.path().join("TestResults"); std::fs::create_dir_all(&trx_dir).expect("create TestResults"); + let base = SystemTime::now(); + let old = base - Duration::from_secs(86_400); + let new = base + Duration::from_secs(3_600); + let trx_old = r#" "#; - std::fs::write(trx_dir.join("old.trx"), trx_old).expect("write old trx"); - - std::thread::sleep(Duration::from_millis(10)); - - let since = SystemTime::now() - .checked_sub(Duration::from_millis(10)) - .expect("threshold overflow"); + test_support::write_trx_with_mtime(&trx_dir.join("old.trx"), trx_old, old); let trx_new = r#" "#; - std::fs::write(trx_dir.join("new.trx"), trx_new).expect("write new trx"); + test_support::write_trx_with_mtime(&trx_dir.join("new.trx"), trx_new, new); - let summary = parse_trx_files_in_dir_since(&trx_dir, Some(since)).expect("merged summary"); + let summary = parse_trx_files_in_dir_since(&trx_dir, Some(base)).expect("merged summary"); assert_eq!(summary.total, 3); assert_eq!(summary.failed, 1); } diff --git a/src/cmds/dotnet/mod.rs b/src/cmds/dotnet/mod.rs index 113c57d6a8..271f2d08fc 100644 --- a/src/cmds/dotnet/mod.rs +++ b/src/cmds/dotnet/mod.rs @@ -1 +1,19 @@ automod::dir!(pub "src/cmds/dotnet"); + +#[cfg(test)] +pub(crate) mod test_support { + use std::path::Path; + use std::time::SystemTime; + + /// Writes a fixture file and pins its mtime to an explicit timestamp so + /// tests never rely on wall-clock deltas or `thread::sleep` separation. + pub(crate) fn write_trx_with_mtime(path: &Path, contents: &str, modified: SystemTime) { + std::fs::write(path, contents).expect("write fixture"); + std::fs::File::options() + .write(true) + .open(path) + .expect("open fixture for write") + .set_modified(modified) + .expect("set fixture mtime"); + } +}