1 /*
2  *  Created by Phil Nash on 21/02/2017.
3  *  Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved.
4  *
5  * Distributed under the Boost Software License, Version 1.0. (See accompanying
6  * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 #ifndef TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
9 #define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
10 
11 #include "catch_matchers.h"
12 
13 #include <algorithm>
14 
15 namespace Catch {
16 namespace Matchers {
17 
18     namespace Vector {
19         namespace Detail {
20             template <typename InputIterator, typename T>
count(InputIterator first,InputIterator last,T const & item)21             size_t count(InputIterator first, InputIterator last, T const& item) {
22                 size_t cnt = 0;
23                 for (; first != last; ++first) {
24                     if (*first == item) {
25                         ++cnt;
26                     }
27                 }
28                 return cnt;
29             }
30             template <typename InputIterator, typename T>
contains(InputIterator first,InputIterator last,T const & item)31             bool contains(InputIterator first, InputIterator last, T const& item) {
32                 for (; first != last; ++first) {
33                     if (*first == item) {
34                         return true;
35                     }
36                 }
37                 return false;
38             }
39         }
40 
41         template<typename T>
42         struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
43 
ContainsElementMatcherContainsElementMatcher44             ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
45 
matchContainsElementMatcher46             bool match(std::vector<T> const &v) const override {
47                 for (auto const& el : v) {
48                     if (el == m_comparator) {
49                         return true;
50                     }
51                 }
52                 return false;
53             }
54 
describeContainsElementMatcher55             std::string describe() const override {
56                 return "Contains: " + ::Catch::Detail::stringify( m_comparator );
57             }
58 
59             T const& m_comparator;
60         };
61 
62         template<typename T>
63         struct ContainsMatcher : MatcherBase<std::vector<T>> {
64 
ContainsMatcherContainsMatcher65             ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
66 
matchContainsMatcher67             bool match(std::vector<T> const &v) const override {
68                 // !TBD: see note in EqualsMatcher
69                 if (m_comparator.size() > v.size())
70                     return false;
71                 for (auto const& comparator : m_comparator) {
72                     auto present = false;
73                     for (const auto& el : v) {
74                         if (el == comparator) {
75                             present = true;
76                             break;
77                         }
78                     }
79                     if (!present) {
80                         return false;
81                     }
82                 }
83                 return true;
84             }
describeContainsMatcher85             std::string describe() const override {
86                 return "Contains: " + ::Catch::Detail::stringify( m_comparator );
87             }
88 
89             std::vector<T> const& m_comparator;
90         };
91 
92         template<typename T>
93         struct EqualsMatcher : MatcherBase<std::vector<T>> {
94 
EqualsMatcherEqualsMatcher95             EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
96 
matchEqualsMatcher97             bool match(std::vector<T> const &v) const override {
98                 // !TBD: This currently works if all elements can be compared using !=
99                 // - a more general approach would be via a compare template that defaults
100                 // to using !=. but could be specialised for, e.g. std::vector<T> etc
101                 // - then just call that directly
102                 if (m_comparator.size() != v.size())
103                     return false;
104                 for (std::size_t i = 0; i < v.size(); ++i)
105                     if (m_comparator[i] != v[i])
106                         return false;
107                 return true;
108             }
describeEqualsMatcher109             std::string describe() const override {
110                 return "Equals: " + ::Catch::Detail::stringify( m_comparator );
111             }
112             std::vector<T> const& m_comparator;
113         };
114 
115         template<typename T>
116         struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcherUnorderedEqualsMatcher117             UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
matchUnorderedEqualsMatcher118             bool match(std::vector<T> const& vec) const override {
119                 // Note: This is a reimplementation of std::is_permutation,
120                 //       because I don't want to include <algorithm> inside the common path
121                 if (m_target.size() != vec.size()) {
122                     return false;
123                 }
124                 auto lfirst = m_target.begin(), llast = m_target.end();
125                 auto rfirst = vec.begin(), rlast = vec.end();
126                 // Cut common prefix to optimize checking of permuted parts
127                 while (lfirst != llast && *lfirst != *rfirst) {
128                     ++lfirst; ++rfirst;
129                 }
130                 if (lfirst == llast) {
131                     return true;
132                 }
133 
134                 for (auto mid = lfirst; mid != llast; ++mid) {
135                     // Skip already counted items
136                     if (Detail::contains(lfirst, mid, *mid)) {
137                         continue;
138                     }
139                     size_t num_vec = Detail::count(rfirst, rlast, *mid);
140                     if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
141                         return false;
142                     }
143                 }
144 
145                 return true;
146             }
147 
describeUnorderedEqualsMatcher148             std::string describe() const override {
149                 return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
150             }
151         private:
152             std::vector<T> const& m_target;
153         };
154 
155     } // namespace Vector
156 
157     // The following functions create the actual matcher objects.
158     // This allows the types to be inferred
159 
160     template<typename T>
Contains(std::vector<T> const & comparator)161     Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
162         return Vector::ContainsMatcher<T>( comparator );
163     }
164 
165     template<typename T>
VectorContains(T const & comparator)166     Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
167         return Vector::ContainsElementMatcher<T>( comparator );
168     }
169 
170     template<typename T>
Equals(std::vector<T> const & comparator)171     Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
172         return Vector::EqualsMatcher<T>( comparator );
173     }
174 
175     template<typename T>
UnorderedEquals(std::vector<T> const & target)176     Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
177         return Vector::UnorderedEqualsMatcher<T>(target);
178     }
179 
180 } // namespace Matchers
181 } // namespace Catch
182 
183 #endif // TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
184