blob: 3a318737b3f6015dab9dd835f6a4a1b2ac815574 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::matcher::{Matcher, MatcherResult};
use crate::matcher_support::count_elements::count_elements;
use std::{fmt::Debug, marker::PhantomData};
/// Matches a container whose number of elements matches `expected`.
///
/// This matches against a container over which one can iterate. This includes
/// the standard Rust containers, arrays, and (when dereferenced) slices.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let array = [1,2,3];
/// verify_that!(array, len(eq(3)))?;
/// let vec = vec![1,2,3];
/// verify_that!(vec, len(eq(3)))?;
/// let slice = vec.as_slice();
/// verify_that!(*slice, len(eq(3)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The parameter `expected` can be any integer numeric matcher.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let vec = vec![1,2,3];
/// verify_that!(vec, len(gt(1)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn len<T: Debug + ?Sized, E: Matcher<ActualT = usize>>(expected: E) -> impl Matcher<ActualT = T>
where
for<'a> &'a T: IntoIterator,
{
LenMatcher { expected, phantom: Default::default() }
}
struct LenMatcher<T: ?Sized, E> {
expected: E,
phantom: PhantomData<T>,
}
impl<T: Debug + ?Sized, E: Matcher<ActualT = usize>> Matcher for LenMatcher<T, E>
where
for<'a> &'a T: IntoIterator,
{
type ActualT = T;
fn matches(&self, actual: &T) -> MatcherResult {
self.expected.matches(&count_elements(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> String {
match matcher_result {
MatcherResult::Match => {
format!("has length, which {}", self.expected.describe(MatcherResult::Match))
}
MatcherResult::NoMatch => {
format!("has length, which {}", self.expected.describe(MatcherResult::NoMatch))
}
}
}
fn explain_match(&self, actual: &T) -> String {
let actual_size = count_elements(actual);
format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size))
}
}
#[cfg(test)]
mod tests {
use super::len;
use crate::matcher::{Matcher, MatcherResult};
use crate::prelude::*;
use indoc::indoc;
use std::collections::{
BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
};
use std::fmt::Debug;
use std::marker::PhantomData;
#[test]
fn len_matcher_match_vec() -> Result<()> {
let value = vec![1, 2, 3];
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_array_reference() -> Result<()> {
let value = &[1, 2, 3];
verify_that!(*value, len(eq(3)))
}
#[test]
fn len_matcher_match_slice_of_array() -> Result<()> {
let value = &[1, 2, 3];
verify_that!(value[0..1], len(eq(1)))
}
#[test]
fn len_matcher_match_slice_of_vec() -> Result<()> {
let value = vec![1, 2, 3];
let slice = value.as_slice();
verify_that!(*slice, len(eq(3)))
}
#[test]
fn len_matcher_match_sized_slice() -> Result<()> {
let value = [1, 2, 3];
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_btreemap() -> Result<()> {
let value = BTreeMap::from([(1, 2), (2, 3), (3, 4)]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_btreeset() -> Result<()> {
let value = BTreeSet::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_binaryheap() -> Result<()> {
let value = BinaryHeap::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_hashmap() -> Result<()> {
let value = HashMap::from([(1, 2), (2, 3), (3, 4)]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_hashset() -> Result<()> {
let value = HashSet::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_linkedlist() -> Result<()> {
let value = LinkedList::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_vecdeque() -> Result<()> {
let value = VecDeque::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_explain_match() -> Result<()> {
struct TestMatcher<T>(PhantomData<T>);
impl<T: Debug> Matcher for TestMatcher<T> {
type ActualT = T;
fn matches(&self, _: &T) -> MatcherResult {
false.into()
}
fn describe(&self, _: MatcherResult) -> String {
"called described".into()
}
fn explain_match(&self, _: &T) -> String {
"called explain_match".into()
}
}
verify_that!(
len(TestMatcher(Default::default())).explain_match(&[1, 2, 3]),
displays_as(eq("which has length 3, called explain_match"))
)
}
#[test]
fn len_matcher_error_message() -> Result<()> {
let result = verify_that!(vec![1, 2, 3, 4], len(eq(3)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 2, 3, 4]
Expected: has length, which is equal to 3
Actual: [1, 2, 3, 4],
which has length 4, which isn't equal to 3"
))))
)
}
}