1 use std::{
2     sync::atomic::{AtomicUsize, Ordering::SeqCst},
3     thread::scope,
4 };
5 
6 #[cfg(feature = "std")]
7 use std::sync::Barrier;
8 
9 #[cfg(not(feature = "std"))]
10 use core::cell::Cell;
11 
12 use once_cell::sync::{Lazy, OnceCell};
13 
14 #[test]
once_cell()15 fn once_cell() {
16     let c = OnceCell::new();
17     assert!(c.get().is_none());
18     scope(|s| {
19         s.spawn(|| {
20             c.get_or_init(|| 92);
21             assert_eq!(c.get(), Some(&92));
22         });
23     });
24     c.get_or_init(|| panic!("Kabom!"));
25     assert_eq!(c.get(), Some(&92));
26 }
27 
28 #[test]
once_cell_with_value()29 fn once_cell_with_value() {
30     static CELL: OnceCell<i32> = OnceCell::with_value(12);
31     assert_eq!(CELL.get(), Some(&12));
32 }
33 
34 #[test]
once_cell_get_mut()35 fn once_cell_get_mut() {
36     let mut c = OnceCell::new();
37     assert!(c.get_mut().is_none());
38     c.set(90).unwrap();
39     *c.get_mut().unwrap() += 2;
40     assert_eq!(c.get_mut(), Some(&mut 92));
41 }
42 
43 #[test]
once_cell_get_unchecked()44 fn once_cell_get_unchecked() {
45     let c = OnceCell::new();
46     c.set(92).unwrap();
47     unsafe {
48         assert_eq!(c.get_unchecked(), &92);
49     }
50 }
51 
52 #[test]
once_cell_drop()53 fn once_cell_drop() {
54     static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
55     struct Dropper;
56     impl Drop for Dropper {
57         fn drop(&mut self) {
58             DROP_CNT.fetch_add(1, SeqCst);
59         }
60     }
61 
62     let x = OnceCell::new();
63     scope(|s| {
64         s.spawn(|| {
65             x.get_or_init(|| Dropper);
66             assert_eq!(DROP_CNT.load(SeqCst), 0);
67             drop(x);
68         });
69     });
70     assert_eq!(DROP_CNT.load(SeqCst), 1);
71 }
72 
73 #[test]
once_cell_drop_empty()74 fn once_cell_drop_empty() {
75     let x = OnceCell::<String>::new();
76     drop(x);
77 }
78 
79 #[test]
clone()80 fn clone() {
81     let s = OnceCell::new();
82     let c = s.clone();
83     assert!(c.get().is_none());
84 
85     s.set("hello".to_string()).unwrap();
86     let c = s.clone();
87     assert_eq!(c.get().map(String::as_str), Some("hello"));
88 }
89 
90 #[test]
91 #[cfg(not(target_os = "android"))]
get_or_try_init()92 fn get_or_try_init() {
93     let cell: OnceCell<String> = OnceCell::new();
94     assert!(cell.get().is_none());
95 
96     let res = std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
97     assert!(res.is_err());
98     assert!(cell.get().is_none());
99 
100     assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
101 
102     assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string()));
103     assert_eq!(cell.get(), Some(&"hello".to_string()));
104 }
105 
106 #[cfg(feature = "std")]
107 #[test]
wait()108 fn wait() {
109     let cell: OnceCell<String> = OnceCell::new();
110     scope(|s| {
111         s.spawn(|| cell.set("hello".to_string()));
112         let greeting = cell.wait();
113         assert_eq!(greeting, "hello")
114     });
115 }
116 
117 #[cfg(feature = "std")]
118 #[test]
get_or_init_stress()119 fn get_or_init_stress() {
120     let n_threads = if cfg!(miri) { 30 } else { 1_000 };
121     let n_cells = if cfg!(miri) { 30 } else { 1_000 };
122     let cells: Vec<_> = std::iter::repeat_with(|| (Barrier::new(n_threads), OnceCell::new()))
123         .take(n_cells)
124         .collect();
125     scope(|s| {
126         for t in 0..n_threads {
127             let cells = &cells;
128             s.spawn(move || {
129                 for (i, (b, s)) in cells.iter().enumerate() {
130                     b.wait();
131                     let j = if t % 2 == 0 { s.wait() } else { s.get_or_init(|| i) };
132                     assert_eq!(*j, i);
133                 }
134             });
135         }
136     });
137 }
138 
139 #[test]
from_impl()140 fn from_impl() {
141     assert_eq!(OnceCell::from("value").get(), Some(&"value"));
142     assert_ne!(OnceCell::from("foo").get(), Some(&"bar"));
143 }
144 
145 #[test]
partialeq_impl()146 fn partialeq_impl() {
147     assert!(OnceCell::from("value") == OnceCell::from("value"));
148     assert!(OnceCell::from("foo") != OnceCell::from("bar"));
149 
150     assert!(OnceCell::<String>::new() == OnceCell::new());
151     assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned()));
152 }
153 
154 #[test]
into_inner()155 fn into_inner() {
156     let cell: OnceCell<String> = OnceCell::new();
157     assert_eq!(cell.into_inner(), None);
158     let cell = OnceCell::new();
159     cell.set("hello".to_string()).unwrap();
160     assert_eq!(cell.into_inner(), Some("hello".to_string()));
161 }
162 
163 #[test]
debug_impl()164 fn debug_impl() {
165     let cell = OnceCell::new();
166     assert_eq!(format!("{:#?}", cell), "OnceCell(Uninit)");
167     cell.set(vec!["hello", "world"]).unwrap();
168     assert_eq!(
169         format!("{:#?}", cell),
170         r#"OnceCell(
171     [
172         "hello",
173         "world",
174     ],
175 )"#
176     );
177 }
178 
179 #[test]
180 #[cfg_attr(miri, ignore)] // miri doesn't support processes
181 #[cfg(feature = "std")]
182 #[ignore = "Android: ignore for now. Need to compile these binaries separately."]
reentrant_init()183 fn reentrant_init() {
184     let examples_dir = {
185         let mut exe = std::env::current_exe().unwrap();
186         exe.pop();
187         exe.pop();
188         exe.push("examples");
189         exe
190     };
191     let bin = examples_dir
192         .join("reentrant_init_deadlocks")
193         .with_extension(std::env::consts::EXE_EXTENSION);
194     let mut guard = Guard { child: std::process::Command::new(bin).spawn().unwrap() };
195     std::thread::sleep(std::time::Duration::from_secs(2));
196     let status = guard.child.try_wait().unwrap();
197     assert!(status.is_none());
198 
199     struct Guard {
200         child: std::process::Child,
201     }
202 
203     impl Drop for Guard {
204         fn drop(&mut self) {
205             let _ = self.child.kill();
206         }
207     }
208 }
209 
210 #[cfg(not(feature = "std"))]
211 #[test]
212 #[should_panic(expected = "reentrant init")]
213 #[ignore = "Android: ignore for now. Need to compile these binaries separately."]
reentrant_init()214 fn reentrant_init() {
215     let x: OnceCell<Box<i32>> = OnceCell::new();
216     let dangling_ref: Cell<Option<&i32>> = Cell::new(None);
217     x.get_or_init(|| {
218         let r = x.get_or_init(|| Box::new(92));
219         dangling_ref.set(Some(r));
220         Box::new(62)
221     });
222     eprintln!("use after free: {:?}", dangling_ref.get().unwrap());
223 }
224 
225 #[test]
eval_once_macro()226 fn eval_once_macro() {
227     macro_rules! eval_once {
228         (|| -> $ty:ty {
229             $($body:tt)*
230         }) => {{
231             static ONCE_CELL: OnceCell<$ty> = OnceCell::new();
232             fn init() -> $ty {
233                 $($body)*
234             }
235             ONCE_CELL.get_or_init(init)
236         }};
237     }
238 
239     let fib: &'static Vec<i32> = eval_once! {
240         || -> Vec<i32> {
241             let mut res = vec![1, 1];
242             for i in 0..10 {
243                 let next = res[i] + res[i + 1];
244                 res.push(next);
245             }
246             res
247         }
248     };
249     assert_eq!(fib[5], 8)
250 }
251 
252 #[test]
once_cell_does_not_leak_partially_constructed_boxes()253 fn once_cell_does_not_leak_partially_constructed_boxes() {
254     let n_tries = if cfg!(miri) { 10 } else { 100 };
255     let n_readers = 10;
256     let n_writers = 3;
257     const MSG: &str = "Hello, World";
258 
259     for _ in 0..n_tries {
260         let cell: OnceCell<String> = OnceCell::new();
261         scope(|scope| {
262             for _ in 0..n_readers {
263                 scope.spawn(|| loop {
264                     if let Some(msg) = cell.get() {
265                         assert_eq!(msg, MSG);
266                         break;
267                     }
268                 });
269             }
270             for _ in 0..n_writers {
271                 let _ = scope.spawn(|| cell.set(MSG.to_owned()));
272             }
273         });
274     }
275 }
276 
277 #[cfg(feature = "std")]
278 #[test]
get_does_not_block()279 fn get_does_not_block() {
280     let cell = OnceCell::new();
281     let barrier = Barrier::new(2);
282     scope(|scope| {
283         scope.spawn(|| {
284             cell.get_or_init(|| {
285                 barrier.wait();
286                 barrier.wait();
287                 "hello".to_string()
288             });
289         });
290         barrier.wait();
291         assert_eq!(cell.get(), None);
292         barrier.wait();
293     });
294     assert_eq!(cell.get(), Some(&"hello".to_string()));
295 }
296 
297 #[test]
298 // https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669
arrrrrrrrrrrrrrrrrrrrrr()299 fn arrrrrrrrrrrrrrrrrrrrrr() {
300     let cell = OnceCell::new();
301     {
302         let s = String::new();
303         cell.set(&s).unwrap();
304     }
305 }
306 
307 #[test]
once_cell_is_sync_send()308 fn once_cell_is_sync_send() {
309     fn assert_traits<T: Send + Sync>() {}
310     assert_traits::<OnceCell<String>>();
311     assert_traits::<Lazy<String>>();
312 }
313