assertor/assertions/
set.rs

1// Copyright 2021 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
15use std::borrow::Borrow;
16use std::collections::{BTreeSet, HashSet};
17use std::fmt::Debug;
18use std::hash::Hash;
19
20use crate::assertions::iterator::{check_is_empty, IteratorAssertion};
21use crate::base::{AssertionApi, AssertionResult, AssertionStrategy, Subject};
22use crate::EqualityAssertion;
23
24/// Trait for set assertion.
25///
26/// # Example
27/// ```
28/// use assertor::*;
29/// use std::collections::HashSet;
30///
31/// let mut set = HashSet::new();
32/// assert_that!(set).is_empty();
33///
34/// set.insert("a");
35/// set.insert("b");
36/// set.insert("c");
37///
38/// assert_that!(set).contains("a");
39/// assert_that!(set).has_length(3);
40/// ```
41/// ```should_panic
42/// use assertor::*;
43/// use std::collections::HashSet;
44///
45/// let mut set = HashSet::new();
46/// set.insert("a");
47/// assert_that!(set).contains("z");
48/// // expected to contain  : "z"
49/// // but did not
50/// // though it did contain: ["a"]
51/// ```
52pub trait SetAssertion<'a, S, T, R> {
53    /// Checks that the subject has the given length.
54    #[track_caller]
55    fn has_length(&self, length: usize) -> R;
56
57    /// Checks that the subject is empty.
58    #[track_caller]
59    fn is_empty(&self) -> R
60    where
61        T: Debug;
62
63    /// Checks that the subject has `expected`.
64    #[track_caller]
65    fn contains<B: Borrow<T>>(&self, expected: B) -> R
66    where
67        T: PartialEq + Eq + Debug + Hash;
68
69    /// Checks that the subject does not contain `element`.
70    #[track_caller]
71    fn does_not_contain<B>(&self, element: B) -> R
72    where
73        B: Borrow<T>,
74        T: PartialEq + Debug;
75
76    /// Checks that the subject does not contain any element of `elements`.
77    #[track_caller]
78    fn does_not_contain_any<B: Borrow<Vec<T>>>(&self, elements: B) -> R
79    where
80        T: PartialEq + Debug;
81}
82
83impl<'a, T, R, ST> SetAssertion<'a, ST, T, R> for Subject<'a, ST, (), R>
84where
85    AssertionResult: AssertionStrategy<R>,
86    T: Eq + Debug,
87    ST: SetLike<T>,
88{
89    fn has_length(&self, length: usize) -> R {
90        self.new_subject(
91            &self.actual().len(),
92            Some(format!("{}.len()", self.description_or_expr())),
93            (),
94        )
95        .is_equal_to(length)
96    }
97
98    fn is_empty(&self) -> R
99    where
100        T: Debug,
101    {
102        check_is_empty(self.new_result(), self.actual().iter())
103    }
104
105    fn contains<B: Borrow<T>>(&self, expected: B) -> R
106    where
107        T: PartialEq + Eq + Debug + Hash,
108    {
109        self.new_owned_subject(self.actual().iter(), None, ())
110            .contains(expected.borrow())
111    }
112
113    fn does_not_contain<B>(&self, element: B) -> R
114    where
115        B: Borrow<T>,
116        T: PartialEq + Debug,
117    {
118        self.new_owned_subject(self.actual().iter(), None, ())
119            .does_not_contain(element.borrow())
120    }
121
122    fn does_not_contain_any<B: Borrow<Vec<T>>>(&self, elements: B) -> R
123    where
124        T: PartialEq + Debug,
125    {
126        self.new_owned_subject(self.actual().iter(), None, ())
127            .does_not_contain_any(elements.borrow().iter())
128    }
129}
130
131/// Trait for sorted set assertions.
132///
133/// # Example
134/// ```
135/// use assertor::*;
136/// use std::collections::BTreeSet;
137///
138/// let mut set = BTreeSet::new();
139/// assert_that!(set).is_empty();
140///
141/// set.insert("a");
142/// set.insert("b");
143/// set.insert("c");
144///
145/// assert_that!(set).contains("a");
146/// assert_that!(set).has_length(3);
147/// assert_that!(set).contains_all_of_in_order(BTreeSet::from(["b", "c"]));
148/// ```
149/// ```should_panic
150/// use assertor::*;
151/// use std::collections::BTreeSet;
152///
153/// let mut set = BTreeSet::new();
154/// set.insert("a");
155/// assert_that!(set).contains("z");
156/// // expected to contain  : "z"
157/// // but did not
158/// // though it did contain: ["a"]
159/// ```
160pub trait OrderedSetAssertion<'a, ST, T, R>: SetAssertion<'a, ST, T, R>
161where
162    AssertionResult: AssertionStrategy<R>,
163    T: PartialOrd + Eq + Debug,
164    ST: OrderedSetLike<T>,
165{
166    /// Checks that the subject contains at least all elements of `expected` in the same order.
167    ///
168    /// # Example
169    /// ```
170    /// use assertor::*;
171    /// use std::collections::BTreeSet;
172    /// assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([1, 2, 3]));
173    /// ```
174    fn contains_all_of_in_order<OSA, OS>(&self, expected: OSA) -> R
175    where
176        T: PartialOrd + Eq + Debug,
177        OS: OrderedSetLike<T>,
178        OSA: Borrow<OS>;
179
180    /// Checks that the subject exactly contains elements of `expected` in the same order.
181    ///
182    /// # Example
183    /// ```
184    /// use assertor::*;
185    /// use std::collections::BTreeSet;
186    /// assert_that!(BTreeSet::from([1, 2, 3])).contains_exactly_in_order(BTreeSet::from([1, 2, 3]));
187    /// ```
188    fn contains_exactly_in_order<OSA, OS>(&self, expected: OSA) -> R
189    where
190        T: PartialOrd + Eq + Debug,
191        OS: OrderedSetLike<T>,
192        OSA: Borrow<OS>;
193}
194
195impl<'a, T, R, ST> OrderedSetAssertion<'a, ST, T, R> for Subject<'a, ST, (), R>
196where
197    AssertionResult: AssertionStrategy<R>,
198    T: Eq + PartialOrd + Debug,
199    ST: OrderedSetLike<T>,
200{
201    fn contains_all_of_in_order<OSA, OS>(&self, expected: OSA) -> R
202    where
203        T: PartialOrd + Eq + Debug,
204        OS: OrderedSetLike<T>,
205        OSA: Borrow<OS>,
206    {
207        self.new_owned_subject(self.actual().iter(), None, ())
208            .contains_all_of_in_order(expected.borrow().iter())
209    }
210
211    fn contains_exactly_in_order<OSA, OS>(&self, expected: OSA) -> R
212    where
213        T: PartialOrd + Eq + Debug,
214        OS: OrderedSetLike<T>,
215        OSA: Borrow<OS>,
216    {
217        self.new_owned_subject(self.actual().iter(), None, ())
218            .contains_exactly_in_order(expected.borrow().iter())
219    }
220}
221
222pub trait SetLike<T: Eq> {
223    type It<'a>: Iterator<Item = &'a T> + Clone
224    where
225        T: 'a,
226        Self: 'a;
227    fn iter<'a>(&'a self) -> Self::It<'a>;
228
229    fn len(&self) -> usize {
230        self.iter().count()
231    }
232}
233
234pub trait OrderedSetLike<T: PartialOrd + Eq>: SetLike<T> {}
235
236impl<T: Hash + Eq> SetLike<T> for HashSet<T> {
237    type It<'a> = std::collections::hash_set::Iter<'a, T> where T: 'a, Self: 'a;
238
239    fn iter<'a>(&'a self) -> Self::It<'a> {
240        self.into_iter()
241    }
242}
243
244impl<T: Eq + PartialOrd> SetLike<T> for BTreeSet<T> {
245    type It<'a> = std::collections::btree_set::Iter<'a, T> where T: 'a, Self: 'a;
246
247    fn iter<'a>(&'a self) -> Self::It<'a> {
248        self.into_iter()
249    }
250}
251
252impl<T: PartialOrd + Eq> OrderedSetLike<T> for BTreeSet<T> {}
253
254#[cfg(test)]
255mod tests {
256    use std::iter::FromIterator;
257
258    use crate::testing::*;
259
260    use super::*;
261
262    #[test]
263    fn has_length() {
264        assert_that!(HashSet::from_iter(vec![1].iter())).has_length(1);
265        assert_that!(HashSet::from_iter(vec![1, 2, 3].iter())).has_length(3);
266        assert_that!(check_that!(HashSet::from_iter(vec![1].iter())).has_length(3)).facts_are(
267            vec![
268                Fact::new("value of", "HashSet::from_iter(vec![1].iter()).len()"),
269                Fact::new("expected", "3"),
270                Fact::new("actual", "1"),
271            ],
272        );
273    }
274
275    #[test]
276    fn is_empty() {
277        assert_that!(HashSet::<&usize>::from_iter(vec![].iter())).is_empty();
278        assert_that!(check_that!(HashSet::from_iter(vec![1].iter())).is_empty()).facts_are(vec![
279            Fact::new_simple_fact("expected to be empty"),
280            Fact::new_splitter(),
281            Fact::new_multi_value_fact("actual", vec!["1"]),
282        ]);
283    }
284
285    #[test]
286    fn contains() {
287        assert_that!(HashSet::from_iter(vec![1, 2, 3].iter())).contains(&3);
288
289        // Failures
290        let result = check_that!(HashSet::from_iter(vec![1, 2, 3].iter())).contains(&10);
291        assert_that!(result).facts_are_at_least(vec![
292            Fact::new("expected to contain", "10"),
293            Fact::new_simple_fact("but did not"),
294        ]);
295        assert_that!(result)
296            .fact_keys()
297            .contains(&"though it did contain".to_string());
298        // Skip test for value because key order is not stable.
299    }
300
301    #[test]
302    fn works_for_btree_set() {
303        let btree_set = BTreeSet::from(["hello", "world"]);
304        let empty: BTreeSet<&str> = BTreeSet::new();
305        assert_that!(btree_set).has_length(2);
306        assert_that!(empty).is_empty();
307        assert_that!(btree_set).contains("hello");
308        assert_that!(btree_set).does_not_contain("nope");
309        assert_that!(btree_set).does_not_contain_any(vec!["one", "two"]);
310    }
311
312    #[test]
313    fn contains_all_of_in_order() {
314        assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([]));
315        assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([1, 2]));
316        assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([2, 3]));
317        assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([1, 3]));
318        assert_that!(BTreeSet::from([1, 2, 3])).contains_all_of_in_order(BTreeSet::from([1, 2, 3]));
319    }
320
321    #[test]
322    fn contains_exactly_in_order() {
323        assert_that!(BTreeSet::from([1, 2, 3]))
324            .contains_exactly_in_order(BTreeSet::from([1, 2, 3]));
325    }
326}