From 82a0f4fb64cddd05542002329ba6a99caff968f7 Mon Sep 17 00:00:00 2001 From: Sean Olson Date: Mon, 29 Jul 2024 10:58:23 -0700 Subject: [PATCH] Integrate unit tests with `rstest`. --- Cargo.toml | 3 + src/iter1.rs | 115 ++++++++++++++++++++++++------ src/vec1.rs | 196 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 214 insertions(+), 100 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b80220..7b637ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,6 @@ optional = true version = "1.0" default-features = false optional = true + +[dev-dependencies] +rstest = "^0.21.0" diff --git a/src/iter1.rs b/src/iter1.rs index 7566ef4..de639d1 100644 --- a/src/iter1.rs +++ b/src/iter1.rs @@ -730,28 +730,101 @@ where items.map(T::into_iter).into_iter().flatten() } +#[cfg(test)] +pub mod harness { + use core::fmt::Debug; + use rstest::fixture; + + use crate::iter1::{self, HeadAndTail, Matched, Query}; + + #[fixture] + pub fn xs1() -> HeadAndTail<[u8; 3]> { + iter1::head_and_tail(0, [1, 2, 3]) + } + + #[fixture] + pub fn matched_some_non_empty() -> Matched> { + Matched { + output: Some(0), + remainder: [1, 2, 3].into_iter(), + } + } + + #[fixture] + pub fn matched_none_non_empty() -> Matched> { + Matched { + output: None, + remainder: [2, 3].into_iter(), + } + } + + pub fn assert_query_eq( + lhs: Query>, + rhs: Query>, + ) where + T: Debug + PartialEq, + U: PartialEq, + { + assert_eq!(lhs.output, rhs.output); + assert!(lhs.remainder.into_iter().eq(rhs.remainder.into_iter())); + } +} + #[cfg(test)] mod tests { - use crate::iter1; - - #[test] - fn maybe_empty_query() { - let (has_zero, remainder) = iter1::head_and_tail(0i32, [1]).any(|x| x == 0).into(); - assert!(has_zero); - assert_eq!(remainder.into_iter().next(), Some(1)); - - let x = iter1::head_and_tail(0i32, [1, 2, 3]) - .map(|x| x + 1) - .find(|&x| x == 3) - .matched(); - assert_eq!(x, Some(3)); - - let x = iter1::head_and_tail(0i32, [1, 2, 3]) - .map(|x| x + 1) - .find(|&x| x == 3) - .with_remainder_and_then_output(|remainder| { - assert_eq!(remainder.count(), 1); - }); - assert_eq!(x, Some(3)); + use rstest::rstest; + + use crate::iter1::harness::{self, matched_none_non_empty, matched_some_non_empty, xs1}; + use crate::iter1::{HeadAndTail, IsMatch, Matched}; + + #[rstest] + #[should_panic] + fn some_and_then_remainder_with_some_matched_then_called( + matched_some_non_empty: Matched>, + ) { + let _remainder = matched_some_non_empty.some_and_then_remainder(|_| panic!()); + } + + #[rstest] + fn some_and_then_remainder_with_none_matched_then_not_called( + matched_none_non_empty: Matched>, + ) { + let _remainder = matched_none_non_empty.some_and_then_remainder(|_| panic!()); + } + + #[rstest] + #[case::first(0, IsMatch { output: true, remainder: [1, 2, 3] })] + #[case::last(3, IsMatch { output: true, remainder: [] })] + #[case::none(4, IsMatch { output: false, remainder: [] })] + fn any_eq_in_iter1_then_is_match_eq( + xs1: HeadAndTail<[u8; 3]>, + #[case] any: u8, + #[case] expected: IsMatch>, + ) { + harness::assert_query_eq(xs1.any(|x| x == any), expected); + } + + #[rstest] + #[case::first(0, IsMatch { output: false, remainder: [2, 3] })] + #[case::last(3, IsMatch { output: false, remainder: [1, 2, 3] })] + #[case::none(4, IsMatch { output: false, remainder: [1, 2, 3] })] + fn all_eq_in_iter1_then_is_match_eq( + xs1: HeadAndTail<[u8; 3]>, + #[case] any: u8, + #[case] expected: IsMatch>, + ) { + harness::assert_query_eq(xs1.all(|x| x == any), expected); + } + + #[rstest] + #[case::first(0, Matched { output: Some(0), remainder: [1, 2, 3] })] + #[case::last(3, Matched { output: Some(3), remainder: [] })] + #[case::none(4, Matched { output: None, remainder: [] })] + fn find_eq_in_iter1_then_matched_eq( + xs1: HeadAndTail<[u8; 3]>, + #[case] find: u8, + #[case] expected: Matched>, + ) { + harness::assert_query_eq(xs1.find(|&x| x == find), expected); } } diff --git a/src/vec1.rs b/src/vec1.rs index da7a86a..fed3060 100644 --- a/src/vec1.rs +++ b/src/vec1.rs @@ -1042,90 +1042,128 @@ macro_rules! vec1 { } pub use vec1; +#[cfg(test)] +pub mod harness { + use rstest::fixture; + + use crate::iter1::FromIterator1; + use crate::vec1::Vec1; + + #[fixture] + pub fn xs1(#[default(4)] end: u8) -> Vec1 { + Vec1::try_from_iter(0..=end).unwrap_or_else(|_| panic!("range `0..={}` is empty", end)) + } +} + #[cfg(test)] mod tests { use core::mem; + use core::ops::RangeBounds; + use rstest::rstest; - use crate::vec1::vec1; + use crate::segment::range::{PositionalRange, Project}; + use crate::slice1::{slice1, Slice1}; + use crate::vec1::harness::{self, xs1}; + use crate::vec1::Vec1; use crate::Segmentation; - #[test] - fn segmentation() { - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().clear(); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().tail().clear(); - assert_eq!(xs.as_slice(), &[0, 1]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().rtail().clear(); - assert_eq!(xs.as_slice(), &[0, 3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.rtail().drain(..); - assert_eq!(xs.as_slice(), &[3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.rtail().drain(0..0); - assert_eq!(xs.as_slice(), &[0, 1, 2, 3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().drain(0..0); - assert_eq!(xs.as_slice(), &[0, 1, 2, 3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().rtail().drain(..); - assert_eq!(xs.as_slice(), &[0, 3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.tail().rtail().drain(0..2); - assert_eq!(xs.as_slice(), &[0, 3]); - - let mut xs = vec1![0i32, 1, 2, 3]; - let mut rtail = xs.rtail(); - let drain = rtail.drain(0..2); - mem::forget(drain); - assert_eq!(xs.as_slice(), &[2]); - - let mut xs = vec1![0i32, 1, 2, 3]; - let mut tail = xs.tail(); - let mut rtail = tail.rtail(); - let drain = rtail.drain(0..2); - mem::forget(drain); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32, 1, 2, 3]; - let mut rtail = xs.rtail(); - let drain = rtail.drain(0..0); - mem::forget(drain); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32, 1, 2, 3]; - let mut rtail = xs.rtail(); - let drain = rtail.drain(1..1); - mem::forget(drain); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32, 1, 2, 3]; - let mut tail = xs.tail(); - let drain = tail.drain(0..0); - mem::forget(drain); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32, 1, 2, 3]; - xs.rtail().clear(); - assert_eq!(xs.as_slice(), &[3]); - - let mut xs = vec1![0i32]; - assert_eq!(xs.tail().len(), 0); - xs.tail().clear(); - assert_eq!(xs.as_slice(), &[0]); - - let mut xs = vec1![0i32]; - assert_eq!(xs.rtail().len(), 0); - xs.rtail().clear(); - assert_eq!(xs.as_slice(), &[0]); + #[rstest] + fn pop_from_vec1_until_only_then_vec1_eq_first(mut xs1: Vec1) { + let first = *xs1.first(); + let mut tail = xs1.as_slice()[1..].to_vec(); + while let Ok(item) = xs1.pop_or_get_only() { + assert_eq!(tail.pop().unwrap(), item); + } + for _ in 0..3 { + assert_eq!(xs1.pop_or_get_only(), Err(&first)); + } + assert_eq!(xs1.as_slice(), &[first]); + } + + #[rstest] + #[case::empty_tail(harness::xs1(0))] + #[case::one_tail(harness::xs1(1))] + #[case::many_tail(harness::xs1(2))] + fn clear_tail_of_vec1_then_vec1_eq_head(#[case] mut xs1: Vec1) { + xs1.tail().clear(); + assert_eq!(xs1.as_slice(), &[0]); + } + + #[rstest] + #[case::empty_rtail(harness::xs1(0))] + #[case::one_rtail(harness::xs1(1))] + #[case::many_rtail(harness::xs1(2))] + fn clear_rtail_of_vec1_then_vec1_eq_tail(#[case] mut xs1: Vec1) { + let tail = *xs1.last(); + xs1.rtail().clear(); + assert_eq!(xs1.as_slice(), &[tail]); + } + + #[rstest] + #[case::empty_tail(harness::xs1(0))] + #[case::one_tail_empty_rtail(harness::xs1(1))] + #[case::many_tail_one_rtail(harness::xs1(2))] + #[case::many_tail_many_rtail(harness::xs1(3))] + fn clear_tail_rtail_of_vec1_then_vec1_eq_head_and_tail(#[case] mut xs1: Vec1) { + let n = xs1.len().get(); + let head_and_tail = [0, *xs1.last()]; + xs1.tail().rtail().clear(); + assert_eq!( + xs1.as_slice(), + if n > 1 { + &head_and_tail[..] + } + else { + &head_and_tail[..1] + } + ); + } + + #[rstest] + #[case::empty_tail(harness::xs1(0), 1.., .., [0])] + #[case::one_tail(harness::xs1(1), 1.., .., [0])] + #[case::many_tail(harness::xs1(2), 1.., .., [0])] + #[case::many_tail(harness::xs1(2), 1.., 1.., [0, 1])] + #[case::many_tail(harness::xs1(2), 1.., ..1, [0, 2])] + #[case::empty_rtail(harness::xs1(0), ..0, .., [0])] + #[case::one_rtail(harness::xs1(1), ..1, .., [1])] + #[case::many_rtail(harness::xs1(2), ..2, .., [2])] + #[case::many_rtail(harness::xs1(2), ..2, 1.., [0, 2])] + fn drain_vec1_segment_then_vec1_eq( + #[case] mut xs1: Vec1, + #[case] segment: S, + #[case] drain: D, + #[case] expected: impl AsRef<[u8]>, + ) where + PositionalRange: Project, + S: RangeBounds, + D: RangeBounds, + { + xs1.segment(segment).drain(drain); + assert_eq!(xs1.as_slice(), expected.as_ref()); + } + + #[rstest] + #[case::empty_tail(harness::xs1(0), 1.., .., slice1![0])] + #[case::one_tail(harness::xs1(1), 1.., .., slice1![0])] + #[case::many_tail(harness::xs1(2), 1.., .., slice1![0])] + #[case::many_tail(harness::xs1(2), 1.., 1.., slice1![0, 1])] + #[case::empty_rtail(harness::xs1(0), ..0, .., slice1![0])] + #[case::one_rtail(harness::xs1(1), ..1, .., slice1![1])] + #[case::many_rtail(harness::xs1(2), ..2, .., slice1![2])] + #[case::many_rtail(harness::xs1(2), ..2, 1.., slice1![0])] + fn leak_drain_of_vec1_segment_then_vec1_eq( + #[case] mut xs1: Vec1, + #[case] segment: S, + #[case] drain: D, + #[case] expected: &Slice1, + ) where + PositionalRange: Project, + S: RangeBounds, + D: RangeBounds, + { + let mut segment = xs1.segment(segment); + mem::forget(segment.drain(drain)); + assert_eq!(xs1.as_slice(), expected.as_ref()); } }