diff --git a/tests/specializations.rs b/tests/specializations.rs index fe14234d6..abe41fe92 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -5,6 +5,7 @@ use quickcheck::{quickcheck, TestResult}; use std::fmt::Debug; struct Unspecialized(I); + impl Iterator for Unspecialized where I: Iterator, @@ -17,10 +18,20 @@ where } } -fn test_specializations(it: &Iter) +impl DoubleEndedIterator for Unspecialized +where + I: DoubleEndedIterator, +{ + #[inline(always)] + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + +fn test_specializations(it: &I) where - IterItem: Eq + Debug + Clone, - Iter: Iterator + Clone, + I::Item: Eq + Debug + Clone, + I: Iterator + Clone, { macro_rules! check_specialized { ($src:expr, |$it:pat| $closure:expr) => { @@ -41,7 +52,7 @@ where check_specialized!(it, |i| i.collect::>()); check_specialized!(it, |i| { let mut parameters_from_fold = vec![]; - let fold_result = i.fold(vec![], |mut acc, v: IterItem| { + let fold_result = i.fold(vec![], |mut acc, v: I::Item| { parameters_from_fold.push((acc.clone(), v.clone())); acc.push(v); acc @@ -75,6 +86,44 @@ where } } +fn test_double_ended_specializations(it: &I) +where + I::Item: Eq + Debug + Clone, + I: DoubleEndedIterator + Clone, +{ + macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced. + let mut src = $src.clone(); + for step in 0..8 { + let $it = src.clone(); + let v1 = $closure; + let $it = Unspecialized(src.clone()); + let v2 = $closure; + assert_eq!(v1, v2); + if step % 2 == 0 { + src.next(); + } else { + src.next_back(); + } + } + } + } + check_specialized!(it, |i| { + let mut parameters_from_rfold = vec![]; + let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| { + parameters_from_rfold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_rfold, rfold_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_specialized!(it, |mut i| i.nth_back(n)); + } +} + quickcheck! { fn interleave(v: Vec, w: Vec) -> () { test_specializations(&v.iter().interleave(w.iter())); @@ -144,19 +193,27 @@ quickcheck! { } fn duplicates(v: Vec) -> () { - test_specializations(&v.iter().duplicates()); + let it = v.iter().duplicates(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn duplicates_by(v: Vec) -> () { - test_specializations(&v.iter().duplicates_by(|x| *x % 10)); + let it = v.iter().duplicates_by(|x| *x % 10); + test_specializations(&it); + test_double_ended_specializations(&it); } fn unique(v: Vec) -> () { - test_specializations(&v.iter().unique()); + let it = v.iter().unique(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn unique_by(v: Vec) -> () { - test_specializations(&v.iter().unique_by(|x| *x % 50)); + let it = v.iter().unique_by(|x| *x % 50); + test_specializations(&it); + test_double_ended_specializations(&it); } fn take_while_inclusive(v: Vec) -> () { @@ -169,7 +226,9 @@ quickcheck! { fn pad_using(v: Vec) -> () { use std::convert::TryFrom; - test_specializations(&v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX))); + let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn with_position(v: Vec) -> () { @@ -177,11 +236,15 @@ quickcheck! { } fn positions(v: Vec) -> () { - test_specializations(&v.iter().positions(|x| x % 5 == 0)); + let it = v.iter().positions(|x| x % 5 == 0); + test_specializations(&it); + test_double_ended_specializations(&it); } fn update(v: Vec) -> () { - test_specializations(&v.iter().copied().update(|x| *x = x.wrapping_mul(7))); + let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn tuple_combinations(v: Vec) -> TestResult { @@ -235,7 +298,9 @@ quickcheck! { } fn zip_longest(a: Vec, b: Vec) -> () { - test_specializations(&a.into_iter().zip_longest(b)) + let it = a.into_iter().zip_longest(b); + test_specializations(&it); + test_double_ended_specializations(&it); } fn zip_eq(a: Vec) -> () { @@ -243,8 +308,9 @@ quickcheck! { } fn multizip(a: Vec) -> () { - let its = (a.iter(), a.iter().rev(), a.iter().take(50)); - test_specializations(&itertools::multizip(its)); + let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50))); + test_specializations(&it); + test_double_ended_specializations(&it); } fn izip(a: Vec, b: Vec) -> () { @@ -258,6 +324,12 @@ quickcheck! { test_specializations(&itertools::iproduct!(a, b.iter(), c)); TestResult::passed() } + + fn repeat_n(element: i8, n: u8) -> () { + let it = itertools::repeat_n(element, n as usize); + test_specializations(&it); + test_double_ended_specializations(&it); + } } quickcheck! { @@ -351,11 +423,15 @@ quickcheck! { quickcheck! { fn map_into(v: Vec) -> () { - test_specializations(&v.into_iter().map_into::()); + let it = v.into_iter().map_into::(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn map_ok(v: Vec>) -> () { - test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); + let it = v.into_iter().map_ok(|u| u.checked_add(1)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn filter_ok(v: Vec>) -> () { @@ -368,7 +444,9 @@ quickcheck! { // `Option` because `Vec` would be very slow!! And we can't give `[u8; 3]`. fn flatten_ok(v: Vec, char>>) -> () { - test_specializations(&v.into_iter().flatten_ok()); + let it = v.into_iter().flatten_ok(); + test_specializations(&it); + test_double_ended_specializations(&it); } }