diff options
Diffstat (limited to 'third_party/rust/once_cell/tests/it')
-rw-r--r-- | third_party/rust/once_cell/tests/it/main.rs | 12 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/race.rs | 128 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/race_once_box.rs | 145 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/sync_lazy.rs | 176 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/sync_once_cell.rs | 309 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/unsync_lazy.rs | 134 | ||||
-rw-r--r-- | third_party/rust/once_cell/tests/it/unsync_once_cell.rs | 154 |
7 files changed, 1058 insertions, 0 deletions
diff --git a/third_party/rust/once_cell/tests/it/main.rs b/third_party/rust/once_cell/tests/it/main.rs new file mode 100644 index 0000000000..b8e56ccc6f --- /dev/null +++ b/third_party/rust/once_cell/tests/it/main.rs @@ -0,0 +1,12 @@ +mod unsync_once_cell; +#[cfg(any(feature = "std", feature = "critical-section"))] +mod sync_once_cell; + +mod unsync_lazy; +#[cfg(any(feature = "std", feature = "critical-section"))] +mod sync_lazy; + +#[cfg(feature = "race")] +mod race; +#[cfg(all(feature = "race", feature = "alloc"))] +mod race_once_box; diff --git a/third_party/rust/once_cell/tests/it/race.rs b/third_party/rust/once_cell/tests/it/race.rs new file mode 100644 index 0000000000..24884dd827 --- /dev/null +++ b/third_party/rust/once_cell/tests/it/race.rs @@ -0,0 +1,128 @@ +#[cfg(feature = "std")] +use std::sync::Barrier; +use std::{ + num::NonZeroUsize, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, + thread::scope, +}; + +use once_cell::race::{OnceBool, OnceNonZeroUsize}; + +#[test] +fn once_non_zero_usize_smoke_test() { + let cnt = AtomicUsize::new(0); + let cell = OnceNonZeroUsize::new(); + let val = NonZeroUsize::new(92).unwrap(); + scope(|s| { + s.spawn(|| { + assert_eq!( + cell.get_or_init(|| { + cnt.fetch_add(1, SeqCst); + val + }), + val + ); + assert_eq!(cnt.load(SeqCst), 1); + + assert_eq!( + cell.get_or_init(|| { + cnt.fetch_add(1, SeqCst); + val + }), + val + ); + assert_eq!(cnt.load(SeqCst), 1); + }); + }); + assert_eq!(cell.get(), Some(val)); + assert_eq!(cnt.load(SeqCst), 1); +} + +#[test] +fn once_non_zero_usize_set() { + let val1 = NonZeroUsize::new(92).unwrap(); + let val2 = NonZeroUsize::new(62).unwrap(); + + let cell = OnceNonZeroUsize::new(); + + assert!(cell.set(val1).is_ok()); + assert_eq!(cell.get(), Some(val1)); + + assert!(cell.set(val2).is_err()); + assert_eq!(cell.get(), Some(val1)); +} + +#[cfg(feature = "std")] +#[test] +fn once_non_zero_usize_first_wins() { + let val1 = NonZeroUsize::new(92).unwrap(); + let val2 = NonZeroUsize::new(62).unwrap(); + + let cell = OnceNonZeroUsize::new(); + + let b1 = Barrier::new(2); + let b2 = Barrier::new(2); + let b3 = Barrier::new(2); + scope(|s| { + s.spawn(|| { + let r1 = cell.get_or_init(|| { + b1.wait(); + b2.wait(); + val1 + }); + assert_eq!(r1, val1); + b3.wait(); + }); + b1.wait(); + s.spawn(|| { + let r2 = cell.get_or_init(|| { + b2.wait(); + b3.wait(); + val2 + }); + assert_eq!(r2, val1); + }); + }); + + assert_eq!(cell.get(), Some(val1)); +} + +#[test] +fn once_bool_smoke_test() { + let cnt = AtomicUsize::new(0); + let cell = OnceBool::new(); + scope(|s| { + s.spawn(|| { + assert_eq!( + cell.get_or_init(|| { + cnt.fetch_add(1, SeqCst); + false + }), + false + ); + assert_eq!(cnt.load(SeqCst), 1); + + assert_eq!( + cell.get_or_init(|| { + cnt.fetch_add(1, SeqCst); + false + }), + false + ); + assert_eq!(cnt.load(SeqCst), 1); + }); + }); + assert_eq!(cell.get(), Some(false)); + assert_eq!(cnt.load(SeqCst), 1); +} + +#[test] +fn once_bool_set() { + let cell = OnceBool::new(); + + assert!(cell.set(false).is_ok()); + assert_eq!(cell.get(), Some(false)); + + assert!(cell.set(true).is_err()); + assert_eq!(cell.get(), Some(false)); +} diff --git a/third_party/rust/once_cell/tests/it/race_once_box.rs b/third_party/rust/once_cell/tests/it/race_once_box.rs new file mode 100644 index 0000000000..0bf3852d7a --- /dev/null +++ b/third_party/rust/once_cell/tests/it/race_once_box.rs @@ -0,0 +1,145 @@ +#[cfg(feature = "std")] +use std::sync::Barrier; +use std::sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + Arc, +}; + +use once_cell::race::OnceBox; + +#[derive(Default)] +struct Heap { + total: Arc<AtomicUsize>, +} + +#[derive(Debug)] +struct Pebble<T> { + val: T, + total: Arc<AtomicUsize>, +} + +impl<T> Drop for Pebble<T> { + fn drop(&mut self) { + self.total.fetch_sub(1, SeqCst); + } +} + +impl Heap { + fn total(&self) -> usize { + self.total.load(SeqCst) + } + fn new_pebble<T>(&self, val: T) -> Pebble<T> { + self.total.fetch_add(1, SeqCst); + Pebble { val, total: Arc::clone(&self.total) } + } +} + +#[cfg(feature = "std")] +#[test] +fn once_box_smoke_test() { + use std::thread::scope; + + let heap = Heap::default(); + let global_cnt = AtomicUsize::new(0); + let cell = OnceBox::new(); + let b = Barrier::new(128); + scope(|s| { + for _ in 0..128 { + s.spawn(|| { + let local_cnt = AtomicUsize::new(0); + cell.get_or_init(|| { + global_cnt.fetch_add(1, SeqCst); + local_cnt.fetch_add(1, SeqCst); + b.wait(); + Box::new(heap.new_pebble(())) + }); + assert_eq!(local_cnt.load(SeqCst), 1); + + cell.get_or_init(|| { + global_cnt.fetch_add(1, SeqCst); + local_cnt.fetch_add(1, SeqCst); + Box::new(heap.new_pebble(())) + }); + assert_eq!(local_cnt.load(SeqCst), 1); + }); + } + }); + assert!(cell.get().is_some()); + assert!(global_cnt.load(SeqCst) > 10); + + assert_eq!(heap.total(), 1); + drop(cell); + assert_eq!(heap.total(), 0); +} + +#[test] +fn once_box_set() { + let heap = Heap::default(); + let cell = OnceBox::new(); + assert!(cell.get().is_none()); + + assert!(cell.set(Box::new(heap.new_pebble("hello"))).is_ok()); + assert_eq!(cell.get().unwrap().val, "hello"); + assert_eq!(heap.total(), 1); + + assert!(cell.set(Box::new(heap.new_pebble("world"))).is_err()); + assert_eq!(cell.get().unwrap().val, "hello"); + assert_eq!(heap.total(), 1); + + drop(cell); + assert_eq!(heap.total(), 0); +} + +#[cfg(feature = "std")] +#[test] +fn once_box_first_wins() { + use std::thread::scope; + + let cell = OnceBox::new(); + let val1 = 92; + let val2 = 62; + + let b1 = Barrier::new(2); + let b2 = Barrier::new(2); + let b3 = Barrier::new(2); + scope(|s| { + s.spawn(|| { + let r1 = cell.get_or_init(|| { + b1.wait(); + b2.wait(); + Box::new(val1) + }); + assert_eq!(*r1, val1); + b3.wait(); + }); + b1.wait(); + s.spawn(|| { + let r2 = cell.get_or_init(|| { + b2.wait(); + b3.wait(); + Box::new(val2) + }); + assert_eq!(*r2, val1); + }); + }); + + assert_eq!(cell.get(), Some(&val1)); +} + +#[test] +fn once_box_reentrant() { + let cell = OnceBox::new(); + let res = cell.get_or_init(|| { + cell.get_or_init(|| Box::new("hello".to_string())); + Box::new("world".to_string()) + }); + assert_eq!(res, "hello"); +} + +#[test] +fn once_box_default() { + struct Foo; + + let cell: OnceBox<Foo> = Default::default(); + assert!(cell.get().is_none()); +} diff --git a/third_party/rust/once_cell/tests/it/sync_lazy.rs b/third_party/rust/once_cell/tests/it/sync_lazy.rs new file mode 100644 index 0000000000..44d70fa0ce --- /dev/null +++ b/third_party/rust/once_cell/tests/it/sync_lazy.rs @@ -0,0 +1,176 @@ +use std::{ + cell::Cell, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, + thread::scope, +}; + +use once_cell::sync::{Lazy, OnceCell}; + +#[test] +fn lazy_new() { + let called = AtomicUsize::new(0); + let x = Lazy::new(|| { + called.fetch_add(1, SeqCst); + 92 + }); + + assert_eq!(called.load(SeqCst), 0); + + scope(|s| { + s.spawn(|| { + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.load(SeqCst), 1); + }); + }); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.load(SeqCst), 1); +} + +#[test] +fn lazy_deref_mut() { + let called = AtomicUsize::new(0); + let mut x = Lazy::new(|| { + called.fetch_add(1, SeqCst); + 92 + }); + + assert_eq!(called.load(SeqCst), 0); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.load(SeqCst), 1); + + *x /= 2; + assert_eq!(*x, 46); + assert_eq!(called.load(SeqCst), 1); +} + +#[test] +fn lazy_force_mut() { + let called = Cell::new(0); + let mut x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + assert_eq!(called.get(), 0); + let v = Lazy::force_mut(&mut x); + assert_eq!(called.get(), 1); + + *v /= 2; + assert_eq!(*x, 46); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_get_mut() { + let called = Cell::new(0); + let mut x: Lazy<u32, _> = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + assert_eq!(*x, 92); + + let mut_ref: &mut u32 = Lazy::get_mut(&mut x).unwrap(); + assert_eq!(called.get(), 1); + + *mut_ref /= 2; + assert_eq!(*x, 46); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn static_lazy() { + static XS: Lazy<Vec<i32>> = Lazy::new(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }); + scope(|s| { + s.spawn(|| { + assert_eq!(&*XS, &vec![1, 2, 3]); + }); + }); + assert_eq!(&*XS, &vec![1, 2, 3]); +} + +#[test] +fn static_lazy_via_fn() { + fn xs() -> &'static Vec<i32> { + static XS: OnceCell<Vec<i32>> = OnceCell::new(); + XS.get_or_init(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }) + } + assert_eq!(xs(), &vec![1, 2, 3]); +} + +#[test] +fn lazy_into_value() { + let l: Lazy<i32, _> = Lazy::new(|| panic!()); + assert!(matches!(Lazy::into_value(l), Err(_))); + let l = Lazy::new(|| -> i32 { 92 }); + Lazy::force(&l); + assert!(matches!(Lazy::into_value(l), Ok(92))); +} + +#[test] +fn lazy_poisoning() { + let x: Lazy<String> = Lazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = std::panic::catch_unwind(|| x.len()); + assert!(res.is_err()); + } +} + +#[test] +// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 +fn arrrrrrrrrrrrrrrrrrrrrr() { + let lazy: Lazy<&String, _>; + { + let s = String::new(); + lazy = Lazy::new(|| &s); + _ = *lazy; + } +} + +#[test] +fn lazy_is_sync_send() { + fn assert_traits<T: Send + Sync>() {} + assert_traits::<Lazy<String>>(); +} diff --git a/third_party/rust/once_cell/tests/it/sync_once_cell.rs b/third_party/rust/once_cell/tests/it/sync_once_cell.rs new file mode 100644 index 0000000000..252adeacfc --- /dev/null +++ b/third_party/rust/once_cell/tests/it/sync_once_cell.rs @@ -0,0 +1,309 @@ +use std::{ + sync::atomic::{AtomicUsize, Ordering::SeqCst}, + thread::scope, +}; + +#[cfg(feature = "std")] +use std::sync::Barrier; + +#[cfg(not(feature = "std"))] +use core::cell::Cell; + +use once_cell::sync::{Lazy, OnceCell}; + +#[test] +fn once_cell() { + let c = OnceCell::new(); + assert!(c.get().is_none()); + scope(|s| { + s.spawn(|| { + c.get_or_init(|| 92); + assert_eq!(c.get(), Some(&92)); + }); + }); + c.get_or_init(|| panic!("Kabom!")); + assert_eq!(c.get(), Some(&92)); +} + +#[test] +fn once_cell_with_value() { + static CELL: OnceCell<i32> = OnceCell::with_value(12); + assert_eq!(CELL.get(), Some(&12)); +} + +#[test] +fn once_cell_get_mut() { + let mut c = OnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +fn once_cell_get_unchecked() { + let c = OnceCell::new(); + c.set(92).unwrap(); + unsafe { + assert_eq!(c.get_unchecked(), &92); + } +} + +#[test] +fn once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = OnceCell::new(); + scope(|s| { + s.spawn(|| { + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + }); + }); + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn once_cell_drop_empty() { + let x = OnceCell::<String>::new(); + drop(x); +} + +#[test] +fn clone() { + let s = OnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello".to_string()).unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(String::as_str), Some("hello")); +} + +#[test] +fn get_or_try_init() { + let cell: OnceCell<String> = OnceCell::new(); + assert!(cell.get().is_none()); + + let res = std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + assert!(cell.get().is_none()); + + assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + + assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); + assert_eq!(cell.get(), Some(&"hello".to_string())); +} + +#[cfg(feature = "std")] +#[test] +fn wait() { + let cell: OnceCell<String> = OnceCell::new(); + scope(|s| { + s.spawn(|| cell.set("hello".to_string())); + let greeting = cell.wait(); + assert_eq!(greeting, "hello") + }); +} + +#[cfg(feature = "std")] +#[test] +fn get_or_init_stress() { + let n_threads = if cfg!(miri) { 30 } else { 1_000 }; + let n_cells = if cfg!(miri) { 30 } else { 1_000 }; + let cells: Vec<_> = std::iter::repeat_with(|| (Barrier::new(n_threads), OnceCell::new())) + .take(n_cells) + .collect(); + scope(|s| { + for t in 0..n_threads { + let cells = &cells; + s.spawn(move || { + for (i, (b, s)) in cells.iter().enumerate() { + b.wait(); + let j = if t % 2 == 0 { s.wait() } else { s.get_or_init(|| i) }; + assert_eq!(*j, i); + } + }); + } + }); +} + +#[test] +fn from_impl() { + assert_eq!(OnceCell::from("value").get(), Some(&"value")); + assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(OnceCell::from("value") == OnceCell::from("value")); + assert!(OnceCell::from("foo") != OnceCell::from("bar")); + + assert!(OnceCell::<String>::new() == OnceCell::new()); + assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned())); +} + +#[test] +fn into_inner() { + let cell: OnceCell<String> = OnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = OnceCell::new(); + cell.set("hello".to_string()).unwrap(); + assert_eq!(cell.into_inner(), Some("hello".to_string())); +} + +#[test] +fn debug_impl() { + let cell = OnceCell::new(); + assert_eq!(format!("{:#?}", cell), "OnceCell(Uninit)"); + cell.set(vec!["hello", "world"]).unwrap(); + assert_eq!( + format!("{:#?}", cell), + r#"OnceCell( + [ + "hello", + "world", + ], +)"# + ); +} + +#[test] +#[cfg_attr(miri, ignore)] // miri doesn't support processes +#[cfg(feature = "std")] +fn reentrant_init() { + let examples_dir = { + let mut exe = std::env::current_exe().unwrap(); + exe.pop(); + exe.pop(); + exe.push("examples"); + exe + }; + let bin = examples_dir + .join("reentrant_init_deadlocks") + .with_extension(std::env::consts::EXE_EXTENSION); + let mut guard = Guard { child: std::process::Command::new(bin).spawn().unwrap() }; + std::thread::sleep(std::time::Duration::from_secs(2)); + let status = guard.child.try_wait().unwrap(); + assert!(status.is_none()); + + struct Guard { + child: std::process::Child, + } + + impl Drop for Guard { + fn drop(&mut self) { + let _ = self.child.kill(); + } + } +} + +#[cfg(not(feature = "std"))] +#[test] +#[should_panic(expected = "reentrant init")] +fn reentrant_init() { + let x: OnceCell<Box<i32>> = OnceCell::new(); + let dangling_ref: Cell<Option<&i32>> = Cell::new(None); + x.get_or_init(|| { + let r = x.get_or_init(|| Box::new(92)); + dangling_ref.set(Some(r)); + Box::new(62) + }); + eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); +} + +#[test] +fn eval_once_macro() { + macro_rules! eval_once { + (|| -> $ty:ty { + $($body:tt)* + }) => {{ + static ONCE_CELL: OnceCell<$ty> = OnceCell::new(); + fn init() -> $ty { + $($body)* + } + ONCE_CELL.get_or_init(init) + }}; + } + + let fib: &'static Vec<i32> = eval_once! { + || -> Vec<i32> { + let mut res = vec![1, 1]; + for i in 0..10 { + let next = res[i] + res[i + 1]; + res.push(next); + } + res + } + }; + assert_eq!(fib[5], 8) +} + +#[test] +fn once_cell_does_not_leak_partially_constructed_boxes() { + let n_tries = if cfg!(miri) { 10 } else { 100 }; + let n_readers = 10; + let n_writers = 3; + const MSG: &str = "Hello, World"; + + for _ in 0..n_tries { + let cell: OnceCell<String> = OnceCell::new(); + scope(|scope| { + for _ in 0..n_readers { + scope.spawn(|| loop { + if let Some(msg) = cell.get() { + assert_eq!(msg, MSG); + break; + } + }); + } + for _ in 0..n_writers { + let _ = scope.spawn(|| cell.set(MSG.to_owned())); + } + }); + } +} + +#[cfg(feature = "std")] +#[test] +fn get_does_not_block() { + let cell = OnceCell::new(); + let barrier = Barrier::new(2); + scope(|scope| { + scope.spawn(|| { + cell.get_or_init(|| { + barrier.wait(); + barrier.wait(); + "hello".to_string() + }); + }); + barrier.wait(); + assert_eq!(cell.get(), None); + barrier.wait(); + }); + assert_eq!(cell.get(), Some(&"hello".to_string())); +} + +#[test] +// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 +fn arrrrrrrrrrrrrrrrrrrrrr() { + let cell = OnceCell::new(); + { + let s = String::new(); + cell.set(&s).unwrap(); + } +} + +#[test] +fn once_cell_is_sync_send() { + fn assert_traits<T: Send + Sync>() {} + assert_traits::<OnceCell<String>>(); + assert_traits::<Lazy<String>>(); +} diff --git a/third_party/rust/once_cell/tests/it/unsync_lazy.rs b/third_party/rust/once_cell/tests/it/unsync_lazy.rs new file mode 100644 index 0000000000..0c308cab09 --- /dev/null +++ b/third_party/rust/once_cell/tests/it/unsync_lazy.rs @@ -0,0 +1,134 @@ +use core::{ + cell::Cell, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, +}; + +use once_cell::unsync::Lazy; + +#[test] +fn lazy_new() { + let called = Cell::new(0); + let x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_deref_mut() { + let called = Cell::new(0); + let mut x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); + + *x /= 2; + assert_eq!(*x, 46); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_force_mut() { + let called = Cell::new(0); + let mut x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + assert_eq!(called.get(), 0); + let v = Lazy::force_mut(&mut x); + assert_eq!(called.get(), 1); + + *v /= 2; + assert_eq!(*x, 46); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_get_mut() { + let called = Cell::new(0); + let mut x: Lazy<u32, _> = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + assert_eq!(*x, 92); + + let mut_ref: &mut u32 = Lazy::get_mut(&mut x).unwrap(); + assert_eq!(called.get(), 1); + + *mut_ref /= 2; + assert_eq!(*x, 46); + assert_eq!(called.get(), 1); +} + +#[test] +fn lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn lazy_into_value() { + let l: Lazy<i32, _> = Lazy::new(|| panic!()); + assert!(matches!(Lazy::into_value(l), Err(_))); + let l = Lazy::new(|| -> i32 { 92 }); + Lazy::force(&l); + assert!(matches!(Lazy::into_value(l), Ok(92))); +} + +#[test] +#[cfg(feature = "std")] +fn lazy_poisoning() { + let x: Lazy<String> = Lazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = std::panic::catch_unwind(|| x.len()); + assert!(res.is_err()); + } +} + +#[test] +// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 +fn arrrrrrrrrrrrrrrrrrrrrr() { + let lazy: Lazy<&String, _>; + { + let s = String::new(); + lazy = Lazy::new(|| &s); + _ = *lazy; + } +} diff --git a/third_party/rust/once_cell/tests/it/unsync_once_cell.rs b/third_party/rust/once_cell/tests/it/unsync_once_cell.rs new file mode 100644 index 0000000000..124cc3e6d0 --- /dev/null +++ b/third_party/rust/once_cell/tests/it/unsync_once_cell.rs @@ -0,0 +1,154 @@ +use core::{ + cell::Cell, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, +}; + +use once_cell::unsync::OnceCell; + +#[test] +fn once_cell() { + let c = OnceCell::new(); + assert!(c.get().is_none()); + c.get_or_init(|| 92); + assert_eq!(c.get(), Some(&92)); + + c.get_or_init(|| panic!("Kabom!")); + assert_eq!(c.get(), Some(&92)); +} + +#[test] +fn once_cell_with_value() { + const CELL: OnceCell<i32> = OnceCell::with_value(12); + let cell = CELL; + assert_eq!(cell.get(), Some(&12)); +} + +#[test] +fn once_cell_get_mut() { + let mut c = OnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +fn once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = OnceCell::new(); + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn once_cell_drop_empty() { + let x = OnceCell::<String>::new(); + drop(x); +} + +#[test] +fn clone() { + let s = OnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello".to_string()).unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(String::as_str), Some("hello")); +} + +#[test] +fn get_or_try_init() { + let cell: OnceCell<String> = OnceCell::new(); + assert!(cell.get().is_none()); + + let res = std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + assert!(cell.get().is_none()); + + assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + + assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); + assert_eq!(cell.get(), Some(&"hello".to_string())); +} + +#[test] +fn from_impl() { + assert_eq!(OnceCell::from("value").get(), Some(&"value")); + assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(OnceCell::from("value") == OnceCell::from("value")); + assert!(OnceCell::from("foo") != OnceCell::from("bar")); + + assert!(OnceCell::<String>::new() == OnceCell::new()); + assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned())); +} + +#[test] +fn into_inner() { + let cell: OnceCell<String> = OnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = OnceCell::new(); + cell.set("hello".to_string()).unwrap(); + assert_eq!(cell.into_inner(), Some("hello".to_string())); +} + +#[test] +fn debug_impl() { + let cell = OnceCell::new(); + assert_eq!(format!("{:#?}", cell), "OnceCell(Uninit)"); + cell.set(vec!["hello", "world"]).unwrap(); + assert_eq!( + format!("{:#?}", cell), + r#"OnceCell( + [ + "hello", + "world", + ], +)"# + ); +} + +#[test] +#[should_panic(expected = "reentrant init")] +fn reentrant_init() { + let x: OnceCell<Box<i32>> = OnceCell::new(); + let dangling_ref: Cell<Option<&i32>> = Cell::new(None); + x.get_or_init(|| { + let r = x.get_or_init(|| Box::new(92)); + dangling_ref.set(Some(r)); + Box::new(62) + }); + eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); +} + +#[test] +fn aliasing_in_get() { + let x = OnceCell::new(); + x.set(42).unwrap(); + let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option<T>` --+ + let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` | + println!("{}", at_x); // <------- up until here ---------------------------+ +} + +#[test] +// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 +fn arrrrrrrrrrrrrrrrrrrrrr() { + let cell = OnceCell::new(); + { + let s = String::new(); + cell.set(&s).unwrap(); + } +} |