//! The following is derived from Rust's //! library/std/src/time/tests.rs at revision //! 72a25d05bf1a4b155d74139ef700ff93af6d8e22. #![feature(cfg_target_has_atomic)] #![feature(duration_constants)] #![allow(soft_unstable)] mustang::can_run_this!(); use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; #[cfg(feature = "bench")] #[cfg(not(target_arch = "wasm32"))] use test::{black_box, Bencher}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ let (a, b) = ($a, $b); if a != b { let (a, b) = if a > b { (a, b) } else { (b, a) }; assert!( a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b ); } }}; } #[test] fn instant_monotonic() { let a = Instant::now(); loop { let b = Instant::now(); assert!(b >= a); if b > a { break; } } } #[cfg(feature = "thread")] #[test] #[cfg(not(target_arch = "wasm32"))] #[cfg_attr(not(feature = "slow"), ignore)] fn instant_monotonic_concurrent() -> std::thread::Result<()> { let threads: Vec<_> = (0..8) .map(|_| { std::thread::spawn(|| { let mut old = Instant::now(); for _ in 0..5_000_000 { let new = Instant::now(); assert!(new >= old); old = new; } }) }) .collect(); for t in threads { t.join()?; } Ok(()) } #[test] fn instant_elapsed() { let a = Instant::now(); let _ = a.elapsed(); } #[test] fn instant_math() { let a = Instant::now(); let b = Instant::now(); //println!("a: {:?}", a); //println!("b: {:?}", b); let dur = b.duration_since(a); //println!("dur: {:?}", dur); assert_almost_eq!(b - dur, a); assert_almost_eq!(a + dur, b); let second = Duration::SECOND; assert_almost_eq!(a - second + second, a); assert_almost_eq!( a.checked_sub(second).unwrap().checked_add(second).unwrap(), a ); // checked_add_duration will not panic on overflow let mut maybe_t = Some(Instant::now()); let max_duration = Duration::from_secs(u64::MAX); // in case `Instant` can store `>= now + max_duration`. for _ in 0..2 { maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); } assert_eq!(maybe_t, None); // checked_add_duration calculates the right time and will work for another year let year = Duration::from_secs(60 * 60 * 24 * 365); assert_eq!(a + year, a.checked_add(year).unwrap()); } #[test] fn instant_math_is_associative() { let now = Instant::now(); let offset = Duration::from_millis(5); // Changing the order of instant math shouldn't change the results, // especially when the expression reduces to X + identity. assert_eq!((now + offset) - now, (now - now) + offset); } #[test] fn instant_duration_since_saturates() { let a = Instant::now(); assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); } #[test] fn instant_checked_duration_since_nopanic() { let now = Instant::now(); let earlier = now - Duration::SECOND; let later = now + Duration::SECOND; assert_eq!(earlier.checked_duration_since(now), None); assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND)); assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO)); } #[test] fn instant_saturating_duration_since_nopanic() { let a = Instant::now(); #[allow(deprecated, deprecated_in_future)] let ret = (a - Duration::SECOND).saturating_duration_since(a); assert_eq!(ret, Duration::ZERO); } #[test] fn system_time_math() { let a = SystemTime::now(); let b = SystemTime::now(); match b.duration_since(a) { Ok(Duration::ZERO) => { assert_almost_eq!(a, b); } Ok(dur) => { assert!(b > a); assert_almost_eq!(b - dur, a); assert_almost_eq!(a + dur, b); } Err(dur) => { let dur = dur.duration(); assert!(a > b); assert_almost_eq!(b + dur, a); assert_almost_eq!(a - dur, b); } } let second = Duration::SECOND; assert_almost_eq!(a.duration_since(a - second).unwrap(), second); assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); assert_almost_eq!(a - second + second, a); assert_almost_eq!( a.checked_sub(second).unwrap().checked_add(second).unwrap(), a ); let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND; let one_second_from_epoch2 = UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500); assert_eq!(one_second_from_epoch, one_second_from_epoch2); // checked_add_duration will not panic on overflow let mut maybe_t = Some(SystemTime::UNIX_EPOCH); let max_duration = Duration::from_secs(u64::MAX); // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. for _ in 0..2 { maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); } assert_eq!(maybe_t, None); // checked_add_duration calculates the right time and will work for another year let year = Duration::from_secs(60 * 60 * 24 * 365); assert_eq!(a + year, a.checked_add(year).unwrap()); } #[test] fn system_time_elapsed() { let a = SystemTime::now(); drop(a.elapsed()); } #[test] fn since_epoch() { let ts = SystemTime::now(); let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); let b = ts.duration_since(UNIX_EPOCH).unwrap(); assert!(b > a); assert_eq!(b - a, Duration::SECOND); let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30; // Right now for CI this test is run in an emulator, and apparently the // aarch64 emulator's sense of time is that we're still living in the // 70s. This is also true for riscv (also qemu) // // Otherwise let's assume that we're all running computers later than // 2000. if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { assert!(a > thirty_years); } // let's assume that we're all running computers earlier than 2090. // Should give us ~70 years to fix this! let hundred_twenty_years = thirty_years * 4; assert!(a < hundred_twenty_years); } #[cfg(feature = "monotonize_impl")] #[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] #[test] fn monotonizer_wrapping_backslide() { use core::sync::atomic::AtomicU64; use std::time::monotonic::inner::{monotonize_impl, ZERO}; let reference = AtomicU64::new(0); let time = match ZERO.checked_add_duration(&Duration::from_secs(0xffff_ffff)) { Some(time) => time, None => { // platform cannot represent u32::MAX seconds so it won't have to deal with this kind // of overflow either return; } }; let monotonized = monotonize_impl(&reference, time); let expected = ZERO .checked_add_duration(&Duration::from_secs(1 << 32)) .unwrap(); assert_eq!( monotonized, expected, "64bit monotonizer should handle overflows in the seconds part" ); } #[cfg(feature = "bench")] macro_rules! bench_instant_threaded { ($bench_name:ident, $thread_count:expr) => { #[bench] #[cfg(not(target_arch = "wasm32"))] fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> { use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; let running = Arc::new(AtomicBool::new(true)); let threads: Vec<_> = (0..$thread_count) .map(|_| { let flag = Arc::clone(&running); std::thread::spawn(move || { while flag.load(Ordering::Relaxed) { black_box(Instant::now()); } }) }) .collect(); b.iter(|| { let a = Instant::now(); let b = Instant::now(); assert!(b >= a); }); running.store(false, Ordering::Relaxed); for t in threads { t.join()?; } Ok(()) } }; } #[cfg(feature = "bench")] bench_instant_threaded!(instant_contention_01_threads, 0); #[cfg(feature = "bench")] bench_instant_threaded!(instant_contention_02_threads, 1); #[cfg(feature = "bench")] bench_instant_threaded!(instant_contention_04_threads, 3); #[cfg(feature = "bench")] bench_instant_threaded!(instant_contention_08_threads, 7); #[cfg(feature = "bench")] bench_instant_threaded!(instant_contention_16_threads, 15);