| 1 | use core::{ |
| 2 | cell::Cell, |
| 3 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, |
| 4 | }; |
| 5 | |
| 6 | use once_cell::unsync::OnceCell; |
| 7 | |
| 8 | #[test] |
| 9 | fn once_cell() { |
| 10 | let c = OnceCell::new(); |
| 11 | assert!(c.get().is_none()); |
| 12 | c.get_or_init(|| 92); |
| 13 | assert_eq!(c.get(), Some(&92)); |
| 14 | |
| 15 | c.get_or_init(|| panic!("Kabom!" )); |
| 16 | assert_eq!(c.get(), Some(&92)); |
| 17 | } |
| 18 | |
| 19 | #[test] |
| 20 | fn once_cell_with_value() { |
| 21 | const CELL: OnceCell<i32> = OnceCell::with_value(12); |
| 22 | let cell = CELL; |
| 23 | assert_eq!(cell.get(), Some(&12)); |
| 24 | } |
| 25 | |
| 26 | #[test] |
| 27 | fn once_cell_get_mut() { |
| 28 | let mut c = OnceCell::new(); |
| 29 | assert!(c.get_mut().is_none()); |
| 30 | c.set(90).unwrap(); |
| 31 | *c.get_mut().unwrap() += 2; |
| 32 | assert_eq!(c.get_mut(), Some(&mut 92)); |
| 33 | } |
| 34 | |
| 35 | #[test] |
| 36 | fn once_cell_drop() { |
| 37 | static DROP_CNT: AtomicUsize = AtomicUsize::new(0); |
| 38 | struct Dropper; |
| 39 | impl Drop for Dropper { |
| 40 | fn drop(&mut self) { |
| 41 | DROP_CNT.fetch_add(1, SeqCst); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | let x = OnceCell::new(); |
| 46 | x.get_or_init(|| Dropper); |
| 47 | assert_eq!(DROP_CNT.load(SeqCst), 0); |
| 48 | drop(x); |
| 49 | assert_eq!(DROP_CNT.load(SeqCst), 1); |
| 50 | } |
| 51 | |
| 52 | #[test] |
| 53 | fn once_cell_drop_empty() { |
| 54 | let x = OnceCell::<String>::new(); |
| 55 | drop(x); |
| 56 | } |
| 57 | |
| 58 | #[test] |
| 59 | fn clone() { |
| 60 | let s = OnceCell::new(); |
| 61 | let c = s.clone(); |
| 62 | assert!(c.get().is_none()); |
| 63 | |
| 64 | s.set("hello" .to_string()).unwrap(); |
| 65 | let c = s.clone(); |
| 66 | assert_eq!(c.get().map(String::as_str), Some("hello" )); |
| 67 | } |
| 68 | |
| 69 | #[test] |
| 70 | fn get_or_try_init() { |
| 71 | let cell: OnceCell<String> = OnceCell::new(); |
| 72 | assert!(cell.get().is_none()); |
| 73 | |
| 74 | let res = std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); |
| 75 | assert!(res.is_err()); |
| 76 | assert!(cell.get().is_none()); |
| 77 | |
| 78 | assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); |
| 79 | |
| 80 | assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello" .to_string())), Ok(&"hello" .to_string())); |
| 81 | assert_eq!(cell.get(), Some(&"hello" .to_string())); |
| 82 | } |
| 83 | |
| 84 | #[test] |
| 85 | fn from_impl() { |
| 86 | assert_eq!(OnceCell::from("value" ).get(), Some(&"value" )); |
| 87 | assert_ne!(OnceCell::from("foo" ).get(), Some(&"bar" )); |
| 88 | } |
| 89 | |
| 90 | #[test] |
| 91 | fn partialeq_impl() { |
| 92 | assert!(OnceCell::from("value" ) == OnceCell::from("value" )); |
| 93 | assert!(OnceCell::from("foo" ) != OnceCell::from("bar" )); |
| 94 | |
| 95 | assert!(OnceCell::<String>::new() == OnceCell::new()); |
| 96 | assert!(OnceCell::<String>::new() != OnceCell::from("value" .to_owned())); |
| 97 | } |
| 98 | |
| 99 | #[test] |
| 100 | fn into_inner() { |
| 101 | let cell: OnceCell<String> = OnceCell::new(); |
| 102 | assert_eq!(cell.into_inner(), None); |
| 103 | let cell = OnceCell::new(); |
| 104 | cell.set("hello" .to_string()).unwrap(); |
| 105 | assert_eq!(cell.into_inner(), Some("hello" .to_string())); |
| 106 | } |
| 107 | |
| 108 | #[test] |
| 109 | fn debug_impl() { |
| 110 | let cell = OnceCell::new(); |
| 111 | assert_eq!(format!("{:#?}" , cell), "OnceCell(Uninit)" ); |
| 112 | cell.set(vec!["hello" , "world" ]).unwrap(); |
| 113 | assert_eq!( |
| 114 | format!("{:#?}" , cell), |
| 115 | r#"OnceCell( |
| 116 | [ |
| 117 | "hello", |
| 118 | "world", |
| 119 | ], |
| 120 | )"# |
| 121 | ); |
| 122 | } |
| 123 | |
| 124 | #[test] |
| 125 | #[should_panic (expected = "reentrant init" )] |
| 126 | fn reentrant_init() { |
| 127 | let x: OnceCell<Box<i32>> = OnceCell::new(); |
| 128 | let dangling_ref: Cell<Option<&i32>> = Cell::new(None); |
| 129 | x.get_or_init(|| { |
| 130 | let r = x.get_or_init(|| Box::new(92)); |
| 131 | dangling_ref.set(Some(r)); |
| 132 | Box::new(62) |
| 133 | }); |
| 134 | eprintln!("use after free: {:?}" , dangling_ref.get().unwrap()); |
| 135 | } |
| 136 | |
| 137 | #[test] |
| 138 | fn aliasing_in_get() { |
| 139 | let x = OnceCell::new(); |
| 140 | x.set(42).unwrap(); |
| 141 | let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option<T>` --+ |
| 142 | let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` | |
| 143 | println!("{}" , at_x); // <------- up until here ---------------------------+ |
| 144 | } |
| 145 | |
| 146 | #[test] |
| 147 | // https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 |
| 148 | fn arrrrrrrrrrrrrrrrrrrrrr() { |
| 149 | let cell = OnceCell::new(); |
| 150 | { |
| 151 | let s = String::new(); |
| 152 | cell.set(&s).unwrap(); |
| 153 | } |
| 154 | } |
| 155 | |