#![doc(hidden)]
#[macro_export]
#[doc(hidden)]
macro_rules! __unordered_elements_are {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(),
$crate::matchers::__internal_unstable_do_not_depend_on_these::
Requirements::PerfectMatch)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::
Requirements::PerfectMatch)
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __contains_each {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __is_contained_in {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(), $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}}
}
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::match_matrix::internal::{MatchMatrix, Requirements};
use std::fmt::Debug;
pub trait MatcherTuple<T: Debug + Copy> {
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>>;
}
impl<T: Debug + Copy> MatcherTuple<T> for () {
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
vec![]
}
}
macro_rules! matcher_tuple_n {
($([$field_number:tt, $matcher_type:ident]),*) => {
impl<T: Debug + Copy, $($matcher_type: Matcher<T>),*> MatcherTuple<T>
for ($($matcher_type,)*) {
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
vec![$(Box::new(&self.$field_number)),*]
}
}
};
}
matcher_tuple_n!([0, M0]);
matcher_tuple_n!([0, M0], [1, M1]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6], [7, M7]);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9],
[10, M10]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9],
[10, M10],
[11, M11]
);
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct UnorderedElementsAreMatcher<M> {
matchers: M,
requirements: Requirements,
}
impl<M> UnorderedElementsAreMatcher<M> {
pub fn new(matchers: M, requirements: Requirements) -> Self {
Self { matchers, requirements }
}
}
impl<T: Debug + Copy, ContainerT: Debug + Copy, M> Matcher<ContainerT>
for UnorderedElementsAreMatcher<M>
where
ContainerT: IntoIterator<Item = T>,
M: MatcherTuple<T>,
{
fn matches(&self, actual: ContainerT) -> MatcherResult {
let matchers = self.matchers.to_matchers();
let match_matrix = MatchMatrix::generate(actual, &matchers);
match_matrix.is_match_for(self.requirements).into()
}
fn explain_match(&self, actual: ContainerT) -> Description {
let matchers = self.matchers.to_matchers();
if let Some(size_mismatch_explanation) =
self.requirements.explain_size_mismatch(actual, matchers.len())
{
return size_mismatch_explanation;
}
let match_matrix = MatchMatrix::generate(actual, &matchers);
if let Some(unmatchable_explanation) =
match_matrix.explain_unmatchable(self.requirements)
{
return unmatchable_explanation;
}
let best_match = match_matrix.find_best_match();
best_match
.get_explanation(actual, &matchers, self.requirements)
.unwrap_or("whose elements all match".into())
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
let matchers = self.matchers.to_matchers();
format!(
"{} elements matching in any order:\n{}",
if matcher_result.into() { "contains" } else { "doesn't contain" },
matchers
.iter()
.map(|matcher| matcher.describe(MatcherResult::Match))
.collect::<Description>()
.enumerate()
.indent()
)
.into()
}
}
}
#[cfg(test)]
mod tests {
use crate as googletest;
use crate::description::Description;
use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::collections::{HashMap, HashSet};
#[test]
fn has_correct_description_for_map() -> googletest::Result<()> {
let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
let matcher = unordered_elements_are![
(matchers.0 .0, matchers.0 .1),
(matchers.1 .0, matchers.1 .1),
(matchers.2 .0, matchers.2 .1)
];
verify_that!(
Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
contains elements matching in any order:
0. is a tuple whose values respectively match:
is equal to 2
is equal to \"Two\"
1. is a tuple whose values respectively match:
is equal to 1
is equal to \"One\"
2. is a tuple whose values respectively match:
is equal to 3
is equal to \"Three\""
)))
)
}
#[test]
fn unordered_elements_are_description_no_full_match_with_map() -> googletest::Result<()> {
let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
let matcher = unordered_elements_are![
(matchers.0 .0, matchers.0 .1),
(matchers.1 .0, matchers.1 .1),
(matchers.2 .0, matchers.2 .1),
];
verify_that!(
matcher.explain_match(&value),
all![
displays_as(contains_regex(
"Actual element \\(2, 2\\) at index [0-2] matched expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index [0-2]."
)),
displays_as(contains_regex(
"Actual element \\(\n [0-1],\n [0-1],\n \\) at index [0-2] did not match any remaining expected element."
)),
displays_as(contains_substring(
"Expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index 2 did not match any remaining actual element."
))
]
)
}
#[test]
fn works_with_inner_matcher_that_modifies_the_input() -> googletest::Result<()> {
fn matches_filtered<'a, M>(
to_filter: &'a HashSet<i32>,
inner: M,
) -> impl Matcher<&'a HashSet<i32>>
where
M: for<'b> Matcher<&'b HashSet<i32>>,
{
#[derive(MatcherBase)]
struct FilteredMatcher<'a, M> {
to_filter: &'a HashSet<i32>,
inner: M,
}
impl<'a, M> Matcher<&'a HashSet<i32>> for FilteredMatcher<'a, M>
where
M: for<'b> Matcher<&'b HashSet<i32>>,
{
fn matches(&self, actual: &'a HashSet<i32>) -> MatcherResult {
let filtered: HashSet<i32> =
actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
self.inner.matches(&filtered)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.inner.describe(matcher_result)
}
fn explain_match(&self, actual: &'a HashSet<i32>) -> Description {
let filtered: HashSet<i32> =
actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
self.inner.explain_match(&filtered)
}
}
FilteredMatcher { to_filter, inner }
}
let before: HashSet<i32> = HashSet::from([1]);
let after: HashSet<i32> = HashSet::from([1, 2]);
verify_that!(&after, matches_filtered(&before, unordered_elements_are![eq(&2)]))?;
Ok(())
}
}