use std::fmt::Debug;
use super::super::*;
use std::iter::FromIterator;
pub struct ContainsInAnyOrder<T> {
expected_elements: Vec<T>
}
pub fn contains_in_any_order<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
where T: PartialEq + Debug,
I: IntoIterator<Item=T>,
J: IntoIterator<Item=T>,
ContainsInAnyOrder<T>: Matcher<'a,J> {
Box::new(ContainsInAnyOrder {
expected_elements: expected_elements.into_iter().collect()
})
}
impl<'a,T,I> Matcher<'a,I> for ContainsInAnyOrder<T>
where T: PartialEq + Debug + 'a,
&'a I: IntoIterator<Item=&'a T> + Debug + 'a {
fn check(&self, actual: &'a I) -> MatchResult {
let repr = format!("{:?}", actual);
let builder = MatchResultBuilder::for_("contains_in_any_order");
let mut expected_elements = Vec::from_iter(self.expected_elements.iter());
for ref element in actual.into_iter() {
let maybe_pos = expected_elements.iter()
.position(|candidate| element == candidate);
if let Some(idx) = maybe_pos {
expected_elements.remove(idx);
} else {
return builder.failed_because(
&format!("{} contains an unexpected element: {:?}", repr, element)
);
}
}
if !expected_elements.is_empty() {
builder.failed_because(
&format!("{} did not contain the following elements: {:?}", repr, expected_elements)
)
} else { builder.matched() }
}
}
pub struct ContainsInOrder<T> {
expected_elements: Vec<T>
}
pub fn contains_in_order<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
where T: PartialEq + Debug,
I: IntoIterator<Item=T>,
J: IntoIterator<Item=T>,
ContainsInOrder<T>: Matcher<'a,J> {
Box::new(ContainsInOrder {
expected_elements: expected_elements.into_iter().collect()
})
}
impl<'a, T, I:'a> Matcher<'a,I> for ContainsInOrder<T>
where T: PartialEq + Debug + 'a,
&'a I: IntoIterator<Item=&'a T> + Debug + 'a {
fn check(&self, actual: &'a I) -> MatchResult {
let builder = MatchResultBuilder::for_("contains_in_order");
let actual_list: Vec<_> = actual.into_iter().collect();
if actual_list.len() > self.expected_elements.len() {
return builder.failed_because(
&format!("The expected list is shorter than the actual list by {} elements",
actual_list.len() - self.expected_elements.len())
);
}
if actual_list.len() < self.expected_elements.len() {
return builder.failed_because(
&format!("The actual list is shorter than the expected list by {} elements",
self.expected_elements.len() - actual_list.len())
);
}
let nonmatching: Vec<_> = actual_list.into_iter()
.zip(self.expected_elements.iter())
.filter(|&(act, exp)| act != exp)
.collect();
if !nonmatching.is_empty() {
builder.failed_because(
&format!("the following actual/expected pairs do not match: {:?}", nonmatching)
)
} else { builder.matched() }
}
}
pub struct ContainsSubset<T> {
expected_elements: Vec<T>
}
pub fn contains_subset<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
where T: PartialEq + Debug,
I: IntoIterator<Item=T>,
J: IntoIterator<Item=T>,
ContainsSubset<T>: Matcher<'a,J> {
Box::new(ContainsSubset {
expected_elements: expected_elements.into_iter().collect()
})
}
impl<'a, T, I:'a> Matcher<'a,I> for ContainsSubset<T>
where T: PartialEq + Debug + 'a,
&'a I: IntoIterator<Item=&'a T> + Debug + 'a {
fn check(&self, actual: &'a I) -> MatchResult {
let repr = format!("{:?}", actual);
let builder = MatchResultBuilder::for_("contains_subset");
let mut expected_elements = Vec::from_iter(self.expected_elements.iter());
for element in actual.into_iter() {
let maybe_pos = expected_elements.iter()
.position(|candidate| element == *candidate);
if let Some(idx) = maybe_pos {
expected_elements.remove(idx);
}
}
if !expected_elements.is_empty() {
builder.failed_because(
&format!("{} did not contain the following elements: {:?}", repr, expected_elements)
)
} else { builder.matched() }
}
}
pub struct ContainedIn<T> {
expected_to_contain: Vec<T>
}
pub fn contained_in<'a,T:'a,I>(expected_to_contain: I) -> Box<Matcher<'a,T> + 'a>
where T: PartialEq + Debug,
I: IntoIterator<Item=T> {
Box::new(ContainedIn {
expected_to_contain: expected_to_contain.into_iter().collect()
})
}
impl<'a,T> Matcher<'a,T> for ContainedIn<T>
where T: PartialEq + Debug + 'a {
fn check(&self, element: &T) -> MatchResult {
let builder = MatchResultBuilder::for_("containd_in");
if let None = self.expected_to_contain.iter().position(|e| e == element) {
builder.failed_because(
&format!("{:?} does not contain: {:?}", self.expected_to_contain, element)
)
} else { builder.matched() }
}
}
pub fn sorted_by<'a,T,I,P>(predicate: P, expected_ordering: std::cmp::Ordering) -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a,
P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("sorted_by");
let mut iter = elements.into_iter();
let maybe_prev = iter.next();
if maybe_prev.is_none() { return builder.matched() }
let mut prev = maybe_prev.unwrap();
for cur in iter {
let ordering = predicate(&prev, &cur);
if ordering != std::cmp::Ordering::Equal
&& expected_ordering != ordering {
return builder.failed_because(
&format!("ordering is not monotone: predicate({:?}, {:?}) != {:?}",
prev, cur, expected_ordering)
);
}
prev = cur;
}
builder.matched()
})
}
pub fn sorted_strictly_by<'a,T,I,P>(predicate: P, expected_ordering: std::cmp::Ordering) -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a,
P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("sorted_strictly_by");
let mut iter = elements.into_iter();
let maybe_prev = iter.next();
if maybe_prev.is_none() { return builder.matched() }
let mut prev = maybe_prev.unwrap();
for cur in iter {
let ordering = predicate(&prev, &cur);
if expected_ordering != ordering {
return builder.failed_because(
&format!("ordering is not strictly monotone: predicate({:?}, {:?}) != {:?}", prev, cur, expected_ordering)
);
}
prev = cur;
}
builder.matched()
})
}
pub fn sorted_by_in_any_order<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a,
P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("sorted_by_in_any_order");
let mut iter = elements.into_iter();
let mut expected_ordering: Option<std::cmp::Ordering> = None;
let maybe_prev = iter.next();
if maybe_prev.is_none() {
return MatchResult::Matched { name: "sorted_by_in_any_order".to_owned() };
}
let mut prev = maybe_prev.unwrap();
for cur in iter {
let ordering = predicate(&prev, &cur);
if expected_ordering == None && ordering != std::cmp::Ordering::Equal {
expected_ordering = Some(ordering);
} else if ordering != std::cmp::Ordering::Equal
&& expected_ordering.unwrap() != ordering {
return builder.failed_because(
&format!("ordering is not monotone: predicate({:?}, {:?}) != {:?}",
prev, cur, expected_ordering.unwrap())
);
}
prev = cur;
}
builder.matched()
})
}
pub fn sorted_strictly_by_in_any_order<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a,
P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("sorted_strictly_by_in_any_order");
let mut iter = elements.into_iter();
let mut expected_ordering: Option<std::cmp::Ordering> = None;
let maybe_prev = iter.next();
if maybe_prev.is_none() {
return builder.matched();
}
let mut prev = maybe_prev.unwrap();
for cur in iter {
let ordering = predicate(&prev, &cur);
if ordering == std::cmp::Ordering::Equal {
return builder.failed_because(
&format!("ordering is not strictly monotone: predicate({:?}, {:?}) = {:?}",
prev, cur, ordering)
);
}
if expected_ordering == None {
expected_ordering = Some(ordering);
} else if expected_ordering.unwrap() != ordering {
return builder.failed_because(
&format!("ordering is not strictly monotone: predicate({:?}, {:?}) != {:?}",
prev, cur, expected_ordering.unwrap())
);
}
prev = cur;
}
builder.matched()
})
}
pub fn sorted_ascending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a {
sorted_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Less)
}
pub fn sorted_strictly_ascending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a {
sorted_strictly_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Less)
}
pub fn sorted_descending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a {
sorted_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Greater)
}
pub fn sorted_strictly_descending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
where &'a I: IntoIterator<Item=&'a T> + 'a,
T: Ord + Debug + 'a {
sorted_strictly_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Greater)
}
pub fn all_elements_satisfy<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
where T: Debug + 'a,
&'a I: IntoIterator<Item=&'a T> + 'a,
P: Fn(&'a T) -> bool + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("all_elements_satisfy");
let nonsatisfying_elements: Vec<_> = elements.into_iter().filter(|e| !predicate(e)).collect();
if !nonsatisfying_elements.is_empty() {
builder.failed_because(
&format!("the following elements do not satisfy the predicate: {:?}", nonsatisfying_elements)
)
} else {
builder.matched()
}
})
}
pub fn some_elements_satisfy<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
where T: Debug + 'a,
&'a I: IntoIterator<Item=&'a T> + 'a,
P: Fn(&T) -> bool + 'static {
Box::new(move |elements: &'a I| {
let builder = MatchResultBuilder::for_("some_elements_satisfy");
if !elements.into_iter().any(|ref e| predicate(e)) {
builder.failed_because("no elements satisfy the predicate")
} else {
builder.matched()
}
})
}
pub struct HasEntry<K,V> {
key: K,
value: V
}
pub fn has_entry<'a,K:'a,V:'a,M:'a>(key: K, value: V) -> Box<Matcher<'a,M> + 'a>
where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
HasEntry<K,V>: Matcher<'a,M> {
Box::new(HasEntry {
key: key,
value: value
})
}
impl<'a,K,V,M> Matcher<'a,M> for HasEntry<K,V>
where V: PartialEq + Debug + 'a,
K: PartialEq + Debug + 'a,
&'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
fn check(&self, map: &'a M) -> MatchResult {
let builder = MatchResultBuilder::for_("has_entry");
let mut same_keys = Vec::new();
let mut same_values = Vec::new();
for (key, value) in map.into_iter() {
if key == &self.key && value == &self.value {
return builder.matched()
}
if key == &self.key {
same_keys.push(value);
}
if value == &self.value {
same_values.push(key);
}
}
builder.failed_because(&format!(
"Entry ({:?}, {:?}) not found.\n\tEntries with same key: {:?}\n\tEntries with same value: {:?}",
&self.key, &self.value,
same_keys, same_values
))
}
}
pub struct HasKey<K> {
key: K
}
pub fn has_key<'a,K:'a,V:'a,M:'a>(key: K) -> Box<Matcher<'a,M> + 'a>
where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
HasKey<K>: Matcher<'a,M> {
Box::new(HasKey {
key: key
})
}
impl<'a,K,V,M> Matcher<'a,M> for HasKey<K>
where V: PartialEq + Debug + 'a,
K: PartialEq + Debug + 'a,
&'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
fn check(&self, map: &'a M) -> MatchResult {
let builder = MatchResultBuilder::for_("has_key");
for (key, _) in map.into_iter() {
if key == &self.key {
return builder.matched();
}
}
builder.failed_because(&format!("No entrywith key {:?} found", &self.key))
}
}
pub struct HasValue<V> {
value: V
}
pub fn has_value<'a,K:'a,V:'a,M:'a>(key: K) -> Box<Matcher<'a,M> + 'a>
where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
HasKey<K>: Matcher<'a,M> {
Box::new(HasKey {
key: key
})
}
impl<'a,K,V,M> Matcher<'a,M> for HasValue<V>
where V: PartialEq + Debug + 'a,
K: PartialEq + Debug + 'a,
&'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
fn check(&self, map: &'a M) -> MatchResult {
let builder = MatchResultBuilder::for_("has_value");
for (_, value) in map.into_iter() {
if value == &self.value {
return builder.matched();
}
}
builder.failed_because(&format!("No entry with value {:?} found", &self.value))
}
}