/*
* Created by Phil Nash on 21/02/2017.
* Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
#include "catch_matchers.h"
#include <algorithm>
namespace Catch {
namespace Matchers {
namespace Vector {
namespace Detail {
template <typename InputIterator, typename T>
size_t count(InputIterator first, InputIterator last, T const& item) {
size_t cnt = 0;
for (; first != last; ++first) {
if (*first == item) {
++cnt;
}
}
return cnt;
}
template <typename InputIterator, typename T>
bool contains(InputIterator first, InputIterator last, T const& item) {
for (; first != last; ++first) {
if (*first == item) {
return true;
}
}
return false;
}
}
template<typename T>
struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
bool match(std::vector<T> const &v) const override {
for (auto const& el : v) {
if (el == m_comparator) {
return true;
}
}
return false;
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
T const& m_comparator;
};
template<typename T>
struct ContainsMatcher : MatcherBase<std::vector<T>> {
ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
// !TBD: see note in EqualsMatcher
if (m_comparator.size() > v.size())
return false;
for (auto const& comparator : m_comparator) {
auto present = false;
for (const auto& el : v) {
if (el == comparator) {
present = true;
break;
}
}
if (!present) {
return false;
}
}
return true;
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct EqualsMatcher : MatcherBase<std::vector<T>> {
EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
// !TBD: This currently works if all elements can be compared using !=
// - a more general approach would be via a compare template that defaults
// to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly
if (m_comparator.size() != v.size())
return false;
for (std::size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != v[i])
return false;
return true;
}
std::string describe() const override {
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
bool match(std::vector<T> const& vec) const override {
// Note: This is a reimplementation of std::is_permutation,
// because I don't want to include <algorithm> inside the common path
if (m_target.size() != vec.size()) {
return false;
}
auto lfirst = m_target.begin(), llast = m_target.end();
auto rfirst = vec.begin(), rlast = vec.end();
// Cut common prefix to optimize checking of permuted parts
while (lfirst != llast && *lfirst == *rfirst) {
++lfirst; ++rfirst;
}
if (lfirst == llast) {
return true;
}
for (auto mid = lfirst; mid != llast; ++mid) {
// Skip already counted items
if (Detail::contains(lfirst, mid, *mid)) {
continue;
}
size_t num_vec = Detail::count(rfirst, rlast, *mid);
if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
return false;
}
}
return true;
}
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
std::vector<T> const& m_target;
};
} // namespace Vector
// The following functions create the actual matcher objects.
// This allows the types to be inferred
template<typename T>
Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
return Vector::ContainsMatcher<T>( comparator );
}
template<typename T>
Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
return Vector::ContainsElementMatcher<T>( comparator );
}
template<typename T>
Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
return Vector::EqualsMatcher<T>( comparator );
}
template<typename T>
Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
return Vector::UnorderedEqualsMatcher<T>(target);
}
} // namespace Matchers
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED