| use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; |
| |
| #[cfg(not(panic = "abort"))] |
| mod drop_checks { |
| //! These tests mainly make sure the elements are correctly dropped. |
| use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; |
| |
| #[derive(Debug)] |
| struct DropInfo { |
| dropped_twice: AtomicBool, |
| alive_count: AtomicUsize, |
| } |
| |
| impl DropInfo { |
| const fn new() -> Self { |
| Self { dropped_twice: AtomicBool::new(false), alive_count: AtomicUsize::new(0) } |
| } |
| |
| #[track_caller] |
| fn check(&self) { |
| assert!(!self.dropped_twice.load(SeqCst), "a value was dropped twice"); |
| assert_eq!(self.alive_count.load(SeqCst), 0); |
| } |
| } |
| |
| #[derive(Debug)] |
| struct DropCheck<'a> { |
| info: &'a DropInfo, |
| was_dropped: bool, |
| } |
| |
| impl<'a> DropCheck<'a> { |
| fn new(info: &'a DropInfo) -> Self { |
| info.alive_count.fetch_add(1, SeqCst); |
| |
| Self { info, was_dropped: false } |
| } |
| } |
| |
| impl Drop for DropCheck<'_> { |
| fn drop(&mut self) { |
| if self.was_dropped { |
| self.info.dropped_twice.store(true, SeqCst); |
| } |
| self.was_dropped = true; |
| |
| self.info.alive_count.fetch_sub(1, SeqCst); |
| } |
| } |
| |
| fn iter(info: &DropInfo, len: usize, panic_at: usize) -> impl Iterator<Item = DropCheck<'_>> { |
| (0..len).map(move |i| { |
| if i == panic_at { |
| panic!("intended panic"); |
| } |
| DropCheck::new(info) |
| }) |
| } |
| |
| #[track_caller] |
| fn check<const N: usize>(len: usize, panic_at: usize) { |
| check_drops(|info| { |
| iter(info, len, panic_at).map_windows(|_: &[_; N]| {}).last(); |
| }); |
| } |
| |
| #[track_caller] |
| fn check_drops(f: impl FnOnce(&DropInfo)) { |
| let info = DropInfo::new(); |
| let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { |
| f(&info); |
| })); |
| info.check(); |
| } |
| |
| #[test] |
| fn no_iter_panic_n1() { |
| check::<1>(0, 100); |
| check::<1>(1, 100); |
| check::<1>(2, 100); |
| check::<1>(13, 100); |
| } |
| |
| #[test] |
| fn no_iter_panic_n2() { |
| check::<2>(0, 100); |
| check::<2>(1, 100); |
| check::<2>(2, 100); |
| check::<2>(3, 100); |
| check::<2>(13, 100); |
| } |
| |
| #[test] |
| fn no_iter_panic_n5() { |
| check::<5>(0, 100); |
| check::<5>(1, 100); |
| check::<5>(2, 100); |
| check::<5>(13, 100); |
| check::<5>(30, 100); |
| } |
| |
| #[test] |
| fn panic_in_first_batch() { |
| check::<1>(7, 0); |
| |
| check::<2>(7, 0); |
| check::<2>(7, 1); |
| |
| check::<3>(7, 0); |
| check::<3>(7, 1); |
| check::<3>(7, 2); |
| } |
| |
| #[test] |
| fn panic_in_middle() { |
| check::<1>(7, 1); |
| check::<1>(7, 5); |
| check::<1>(7, 6); |
| |
| check::<2>(7, 2); |
| check::<2>(7, 5); |
| check::<2>(7, 6); |
| |
| check::<5>(13, 5); |
| check::<5>(13, 8); |
| check::<5>(13, 12); |
| } |
| |
| #[test] |
| fn len_equals_n() { |
| check::<1>(1, 100); |
| check::<1>(1, 0); |
| |
| check::<2>(2, 100); |
| check::<2>(2, 0); |
| check::<2>(2, 1); |
| |
| check::<5>(5, 100); |
| check::<5>(5, 0); |
| check::<5>(5, 1); |
| check::<5>(5, 4); |
| } |
| } |
| |
| #[test] |
| fn output_n1() { |
| assert_eq!("".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec![]); |
| assert_eq!("x".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec!['x']); |
| assert_eq!("abcd".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec!['a', 'b', 'c', 'd']); |
| } |
| |
| #[test] |
| fn output_n2() { |
| assert_eq!( |
| "".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), |
| <Vec<[char; 2]>>::new(), |
| ); |
| assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), vec![['a', 'b']]); |
| assert_eq!( |
| "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), |
| vec![['a', 'b'], ['b', 'c'], ['c', 'd']], |
| ); |
| } |
| |
| #[test] |
| fn test_case_from_pr_82413_comment() { |
| for () in std::iter::repeat("0".to_owned()).map_windows(|_: &[_; 3]| {}).take(4) {} |
| } |
| |
| #[test] |
| #[should_panic = "array in `Iterator::map_windows` must contain more than 0 elements"] |
| fn check_zero_window() { |
| let _ = std::iter::repeat(0).map_windows(|_: &[_; 0]| ()); |
| } |
| |
| #[test] |
| fn test_zero_sized_type() { |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| struct Data; |
| let data: Vec<_> = |
| std::iter::repeat(Data).take(10).map_windows(|arr: &[Data; 5]| *arr).collect(); |
| assert_eq!(data, [[Data; 5]; 6]); |
| } |
| |
| #[test] |
| #[should_panic = "array size of `Iterator::map_windows` is too large"] |
| fn test_too_large_array_size() { |
| let _ = std::iter::repeat(()).map_windows(|arr: &[(); usize::MAX]| *arr); |
| } |
| |
| #[test] |
| fn test_laziness() { |
| let counter = AtomicUsize::new(0); |
| let mut iter = (0..5) |
| .inspect(|_| { |
| counter.fetch_add(1, SeqCst); |
| }) |
| .map_windows(|arr: &[i32; 2]| *arr); |
| assert_eq!(counter.load(SeqCst), 0); |
| |
| assert_eq!(iter.next(), Some([0, 1])); |
| // The first iteration consumes N items (N = 2). |
| assert_eq!(counter.load(SeqCst), 2); |
| |
| assert_eq!(iter.next(), Some([1, 2])); |
| assert_eq!(counter.load(SeqCst), 3); |
| |
| assert_eq!(iter.next(), Some([2, 3])); |
| assert_eq!(counter.load(SeqCst), 4); |
| |
| assert_eq!(iter.next(), Some([3, 4])); |
| assert_eq!(counter.load(SeqCst), 5); |
| |
| assert_eq!(iter.next(), None); |
| assert_eq!(counter.load(SeqCst), 5); |
| } |
| |
| #[test] |
| fn test_size_hint() { |
| struct SizeHintCheckHelper((usize, Option<usize>)); |
| |
| impl Iterator for SizeHintCheckHelper { |
| type Item = i32; |
| |
| fn next(&mut self) -> Option<i32> { |
| let (ref mut lo, ref mut hi) = self.0; |
| let next = (*hi != Some(0)).then_some(0); |
| *lo = lo.saturating_sub(1); |
| if let Some(hi) = hi { |
| *hi = hi.saturating_sub(1); |
| } |
| next |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.0 |
| } |
| } |
| |
| fn check_size_hint<const N: usize>( |
| size_hint: (usize, Option<usize>), |
| mut mapped_size_hint: (usize, Option<usize>), |
| ) { |
| let mut iter = SizeHintCheckHelper(size_hint); |
| let mut mapped_iter = iter.by_ref().map_windows(|_: &[_; N]| ()); |
| while mapped_iter.size_hint().0 > 0 { |
| assert_eq!(mapped_iter.size_hint(), mapped_size_hint); |
| assert!(mapped_iter.next().is_some()); |
| mapped_size_hint.0 -= 1; |
| mapped_size_hint.1 = mapped_size_hint.1.map(|hi| hi.saturating_sub(1)); |
| } |
| } |
| |
| check_size_hint::<1>((0, None), (0, None)); |
| check_size_hint::<1>((0, Some(0)), (0, Some(0))); |
| check_size_hint::<1>((0, Some(2)), (0, Some(2))); |
| check_size_hint::<1>((1, None), (1, None)); |
| check_size_hint::<1>((1, Some(1)), (1, Some(1))); |
| check_size_hint::<1>((1, Some(4)), (1, Some(4))); |
| check_size_hint::<1>((5, None), (5, None)); |
| check_size_hint::<1>((5, Some(5)), (5, Some(5))); |
| check_size_hint::<1>((5, Some(10)), (5, Some(10))); |
| |
| check_size_hint::<2>((0, None), (0, None)); |
| check_size_hint::<2>((0, Some(0)), (0, Some(0))); |
| check_size_hint::<2>((0, Some(2)), (0, Some(1))); |
| check_size_hint::<2>((1, None), (0, None)); |
| check_size_hint::<2>((1, Some(1)), (0, Some(0))); |
| check_size_hint::<2>((1, Some(4)), (0, Some(3))); |
| check_size_hint::<2>((5, None), (4, None)); |
| check_size_hint::<2>((5, Some(5)), (4, Some(4))); |
| check_size_hint::<2>((5, Some(10)), (4, Some(9))); |
| |
| check_size_hint::<5>((0, None), (0, None)); |
| check_size_hint::<5>((0, Some(0)), (0, Some(0))); |
| check_size_hint::<5>((0, Some(2)), (0, Some(0))); |
| check_size_hint::<5>((1, None), (0, None)); |
| check_size_hint::<5>((1, Some(1)), (0, Some(0))); |
| check_size_hint::<5>((1, Some(4)), (0, Some(0))); |
| check_size_hint::<5>((5, None), (1, None)); |
| check_size_hint::<5>((5, Some(5)), (1, Some(1))); |
| check_size_hint::<5>((5, Some(10)), (1, Some(6))); |
| } |