diff --git a/tests/specializations.rs b/tests/specializations.rs index cb2141053..712311472 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -1,7 +1,9 @@ #![allow(unstable_name_collisions)] use itertools::Itertools; +use quickcheck::Arbitrary; use quickcheck::{quickcheck, TestResult}; +use rand::Rng; use std::fmt::Debug; struct Unspecialized(I); @@ -452,8 +454,8 @@ quickcheck! { test_specializations(&v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None })); } - // `Option` because `Vec` would be very slow!! And we can't give `[u8; 3]`. - fn flatten_ok(v: Vec, char>>) -> () { + // `SmallIter2` because `Vec` is too slow and we get bad coverage from a singleton like Option + fn flatten_ok(v: Vec, char>>) -> () { let it = v.into_iter().flatten_ok(); test_specializations(&it); test_double_ended_specializations(&it); @@ -520,3 +522,61 @@ quickcheck! { } } } + +/// Like `VecIntoIter` with maximum 2 elements. +#[derive(Debug, Clone, Default)] +enum SmallIter2 { + #[default] + Zero, + One(T), + Two(T, T), +} + +impl Arbitrary for SmallIter2 { + fn arbitrary(g: &mut G) -> Self { + match g.gen_range(0u8, 3) { + 0 => Self::Zero, + 1 => Self::One(T::arbitrary(g)), + 2 => Self::Two(T::arbitrary(g), T::arbitrary(g)), + _ => unreachable!(), + } + } + // maybe implement shrink too, maybe not +} + +impl Iterator for SmallIter2 { + type Item = T; + + fn next(&mut self) -> Option { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(val, second) => { + *self = Self::One(second); + Some(val) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = match self { + Self::Zero => 0, + Self::One(_) => 1, + Self::Two(_, _) => 2, + }; + (len, Some(len)) + } +} + +impl DoubleEndedIterator for SmallIter2 { + fn next_back(&mut self) -> Option { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(first, val) => { + *self = Self::One(first); + Some(val) + } + } + } +}