Skip to content

Commit

Permalink
Integrate unit tests with rstest.
Browse files Browse the repository at this point in the history
  • Loading branch information
olson-sean-k committed Jul 29, 2024
1 parent 1feb60a commit e697410
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 100 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ optional = true
version = "1.0"
default-features = false
optional = true

[dev-dependencies]
rstest = "^0.21.0"
115 changes: 94 additions & 21 deletions src/iter1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,28 +744,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<u8, impl Iterator<Item = u8>> {
Matched {
output: Some(0),
remainder: [1, 2, 3].into_iter(),
}
}

#[fixture]
pub fn matched_none_non_empty() -> Matched<u8, impl Iterator<Item = u8>> {
Matched {
output: None,
remainder: [2, 3].into_iter(),
}
}

pub fn assert_query_eq<T, U>(
lhs: Query<T, impl IntoIterator<Item = U>>,
rhs: Query<T, impl IntoIterator<Item = U>>,
) where
T: Debug + PartialEq<T>,
U: PartialEq<U>,
{
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<u8, impl Iterator<Item = u8>>,
) {
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<u8, impl Iterator<Item = u8>>,
) {
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<impl IntoIterator<Item = u8>>,
) {
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<impl IntoIterator<Item = u8>>,
) {
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<u8, impl IntoIterator<Item = u8>>,
) {
harness::assert_query_eq(xs1.find(|&x| x == find), expected);
}
}
196 changes: 117 additions & 79 deletions src/vec1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
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<u8>) {
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<u8>) {
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<u8>) {
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<u8>) {
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<S, D>(
#[case] mut xs1: Vec1<u8>,
#[case] segment: S,
#[case] drain: D,
#[case] expected: impl AsRef<[u8]>,
) where
PositionalRange: Project<D, Output = PositionalRange>,
S: RangeBounds<usize>,
D: RangeBounds<usize>,
{
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<S, D>(
#[case] mut xs1: Vec1<u8>,
#[case] segment: S,
#[case] drain: D,
#[case] expected: &Slice1<u8>,
) where
PositionalRange: Project<D, Output = PositionalRange>,
S: RangeBounds<usize>,
D: RangeBounds<usize>,
{
let mut segment = xs1.segment(segment);
mem::forget(segment.drain(drain));
assert_eq!(xs1.as_slice(), expected.as_ref());
}
}

0 comments on commit e697410

Please sign in to comment.