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