diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 795f396d..5e193dbf 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -154,22 +154,36 @@ fn unload_xdp() { fn test_loaded_at() { let mut bpf = Bpf::load(crate::TEST).unwrap(); let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); - let t1 = SystemTime::now(); - prog.load().unwrap(); - let t2 = SystemTime::now(); - assert_loaded("pass"); - - let loaded_at = prog.info().unwrap().loaded_at(); - let range = t1..t2; + // SystemTime is not monotonic, which can cause this test to flake. We don't expect the clock + // timestamp to continuously jump around, so we add some retries. If the test is ever correct, + // we know that the value returned by loaded_at() was reasonable relative to SystemTime::now(). + let mut failures = Vec::new(); + for _ in 0..5 { + let t1 = SystemTime::now(); + prog.load().unwrap(); + let t2 = SystemTime::now(); + let loaded_at = prog.info().unwrap().loaded_at(); + prog.unload().unwrap(); + let range = t1..t2; + if range.contains(&loaded_at) { + failures.clear(); + break; + } + failures.push(LoadedAtRange(loaded_at, range)); + } assert!( - range.contains(&loaded_at), - "{range:?}.contains({loaded_at:?})" + failures.is_empty(), + "loaded_at was not in range: {failures:?}", ); - prog.unload().unwrap(); - - assert_unloaded("pass"); + struct LoadedAtRange(SystemTime, std::ops::Range); + impl std::fmt::Debug for LoadedAtRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(loaded_at, range) = self; + write!(f, "{range:?}.contains({loaded_at:?})") + } + } } #[test]