1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::description::Description;
16 use crate::matcher::{Matcher, MatcherResult};
17 use crate::matcher_support::count_elements::count_elements;
18 use std::{fmt::Debug, marker::PhantomData};
19
20 /// Matches a container whose number of elements matches `expected`.
21 ///
22 /// This matches against a container over which one can iterate. This includes
23 /// the standard Rust containers, arrays, and (when dereferenced) slices. More
24 /// precisely, a shared borrow of the actual type must implement
25 /// [`IntoIterator`].
26 ///
27 /// ```
28 /// # use googletest::prelude::*;
29 /// # fn should_pass() -> Result<()> {
30 /// let array = [1,2,3];
31 /// verify_that!(array, len(eq(3)))?;
32 /// let vec = vec![1,2,3];
33 /// verify_that!(vec, len(eq(3)))?;
34 /// let slice = vec.as_slice();
35 /// verify_that!(*slice, len(eq(3)))?;
36 /// # Ok(())
37 /// # }
38 /// # should_pass().unwrap();
39 /// ```
40 ///
41 /// The parameter `expected` can be any integer numeric matcher.
42 ///
43 /// ```
44 /// # use googletest::prelude::*;
45 /// # fn should_pass() -> Result<()> {
46 /// let vec = vec![1,2,3];
47 /// verify_that!(vec, len(gt(1)))?;
48 /// # Ok(())
49 /// # }
50 /// # should_pass().unwrap();
51 /// ```
len<T: Debug + ?Sized, E: Matcher<ActualT = usize>>(expected: E) -> impl Matcher<ActualT = T> where for<'a> &'a T: IntoIterator,52 pub fn len<T: Debug + ?Sized, E: Matcher<ActualT = usize>>(expected: E) -> impl Matcher<ActualT = T>
53 where
54 for<'a> &'a T: IntoIterator,
55 {
56 LenMatcher { expected, phantom: Default::default() }
57 }
58
59 struct LenMatcher<T: ?Sized, E> {
60 expected: E,
61 phantom: PhantomData<T>,
62 }
63
64 impl<T: Debug + ?Sized, E: Matcher<ActualT = usize>> Matcher for LenMatcher<T, E>
65 where
66 for<'a> &'a T: IntoIterator,
67 {
68 type ActualT = T;
69
matches(&self, actual: &T) -> MatcherResult70 fn matches(&self, actual: &T) -> MatcherResult {
71 self.expected.matches(&count_elements(actual))
72 }
73
describe(&self, matcher_result: MatcherResult) -> Description74 fn describe(&self, matcher_result: MatcherResult) -> Description {
75 match matcher_result {
76 MatcherResult::Match => {
77 format!("has length, which {}", self.expected.describe(MatcherResult::Match)).into()
78 }
79 MatcherResult::NoMatch => {
80 format!("has length, which {}", self.expected.describe(MatcherResult::NoMatch))
81 .into()
82 }
83 }
84 }
85
explain_match(&self, actual: &T) -> Description86 fn explain_match(&self, actual: &T) -> Description {
87 let actual_size = count_elements(actual);
88 format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size))
89 .into()
90 }
91 }
92
93 #[cfg(test)]
94 mod tests {
95 use super::len;
96 use crate::description::Description;
97 use crate::matcher::{Matcher, MatcherResult};
98 use crate::prelude::*;
99 use indoc::indoc;
100 use std::collections::{
101 BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
102 };
103 use std::fmt::Debug;
104 use std::marker::PhantomData;
105
106 #[test]
len_matcher_match_vec() -> Result<()>107 fn len_matcher_match_vec() -> Result<()> {
108 let value = vec![1, 2, 3];
109 verify_that!(value, len(eq(3)))
110 }
111
112 #[test]
len_matcher_match_array_reference() -> Result<()>113 fn len_matcher_match_array_reference() -> Result<()> {
114 let value = &[1, 2, 3];
115 verify_that!(*value, len(eq(3)))
116 }
117
118 #[test]
len_matcher_match_slice_of_array() -> Result<()>119 fn len_matcher_match_slice_of_array() -> Result<()> {
120 let value = &[1, 2, 3];
121 verify_that!(value[0..1], len(eq(1)))
122 }
123
124 #[test]
len_matcher_match_slice_of_vec() -> Result<()>125 fn len_matcher_match_slice_of_vec() -> Result<()> {
126 let value = vec![1, 2, 3];
127 let slice = value.as_slice();
128 verify_that!(*slice, len(eq(3)))
129 }
130
131 #[test]
len_matcher_match_sized_slice() -> Result<()>132 fn len_matcher_match_sized_slice() -> Result<()> {
133 let value = [1, 2, 3];
134 verify_that!(value, len(eq(3)))
135 }
136
137 #[test]
len_matcher_match_btreemap() -> Result<()>138 fn len_matcher_match_btreemap() -> Result<()> {
139 let value = BTreeMap::from([(1, 2), (2, 3), (3, 4)]);
140 verify_that!(value, len(eq(3)))
141 }
142
143 #[test]
len_matcher_match_btreeset() -> Result<()>144 fn len_matcher_match_btreeset() -> Result<()> {
145 let value = BTreeSet::from([1, 2, 3]);
146 verify_that!(value, len(eq(3)))
147 }
148
149 #[test]
len_matcher_match_binaryheap() -> Result<()>150 fn len_matcher_match_binaryheap() -> Result<()> {
151 let value = BinaryHeap::from([1, 2, 3]);
152 verify_that!(value, len(eq(3)))
153 }
154
155 #[test]
len_matcher_match_hashmap() -> Result<()>156 fn len_matcher_match_hashmap() -> Result<()> {
157 let value = HashMap::from([(1, 2), (2, 3), (3, 4)]);
158 verify_that!(value, len(eq(3)))
159 }
160
161 #[test]
len_matcher_match_hashset() -> Result<()>162 fn len_matcher_match_hashset() -> Result<()> {
163 let value = HashSet::from([1, 2, 3]);
164 verify_that!(value, len(eq(3)))
165 }
166
167 #[test]
len_matcher_match_linkedlist() -> Result<()>168 fn len_matcher_match_linkedlist() -> Result<()> {
169 let value = LinkedList::from([1, 2, 3]);
170 verify_that!(value, len(eq(3)))
171 }
172
173 #[test]
len_matcher_match_vecdeque() -> Result<()>174 fn len_matcher_match_vecdeque() -> Result<()> {
175 let value = VecDeque::from([1, 2, 3]);
176 verify_that!(value, len(eq(3)))
177 }
178
179 #[test]
len_matcher_explain_match() -> Result<()>180 fn len_matcher_explain_match() -> Result<()> {
181 struct TestMatcher<T>(PhantomData<T>);
182 impl<T: Debug> Matcher for TestMatcher<T> {
183 type ActualT = T;
184
185 fn matches(&self, _: &T) -> MatcherResult {
186 false.into()
187 }
188
189 fn describe(&self, _: MatcherResult) -> Description {
190 "called described".into()
191 }
192
193 fn explain_match(&self, _: &T) -> Description {
194 "called explain_match".into()
195 }
196 }
197 verify_that!(
198 len(TestMatcher(Default::default())).explain_match(&[1, 2, 3]),
199 displays_as(eq("which has length 3, called explain_match"))
200 )
201 }
202
203 #[test]
len_matcher_error_message() -> Result<()>204 fn len_matcher_error_message() -> Result<()> {
205 let result = verify_that!(vec![1, 2, 3, 4], len(eq(3)));
206 verify_that!(
207 result,
208 err(displays_as(contains_substring(indoc!(
209 "
210 Value of: vec![1, 2, 3, 4]
211 Expected: has length, which is equal to 3
212 Actual: [1, 2, 3, 4],
213 which has length 4, which isn't equal to 3"
214 ))))
215 )
216 }
217 }
218