1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED
#define CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED
#include <catch2/catch_tostring.hpp>
#include <initializer_list>
#include <list>
#include <memory>
#include <vector>
namespace unrelated {
template <typename T>
class needs_ADL_begin {
std::vector<T> m_elements;
public:
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
needs_ADL_begin( std::initializer_list<T> init ): m_elements( init ) {}
const_iterator Begin() const { return m_elements.begin(); }
const_iterator End() const { return m_elements.end(); }
friend const_iterator begin( needs_ADL_begin const& lhs ) {
return lhs.Begin();
}
friend const_iterator end( needs_ADL_begin const& rhs ) {
return rhs.End();
}
};
struct ADL_empty {
bool Empty() const { return true; }
friend bool empty( ADL_empty e ) { return e.Empty(); }
};
struct ADL_size {
size_t sz() const { return 12; }
friend size_t size( ADL_size s ) { return s.sz(); }
};
} // namespace unrelated
#if defined( __clang__ )
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-function"
#endif
template <typename T>
class has_different_begin_end_types {
// Using std::vector<T> leads to annoying issues when T is bool
// so we just use list because the perf is not critical and ugh.
std::list<T> m_elements;
// Different type for the "end" iterator
struct iterator_end {};
// Fake-ish forward iterator that only compares to a different type
class iterator {
using underlying_iter = typename std::list<T>::const_iterator;
underlying_iter m_start;
underlying_iter m_end;
public:
iterator( underlying_iter start, underlying_iter end ):
m_start( start ), m_end( end ) {}
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using const_reference = T const&;
using pointer = T const*;
friend bool operator==( iterator iter, iterator_end ) {
return iter.m_start == iter.m_end;
}
friend bool operator==(iterator lhs, iterator rhs) {
return lhs.m_start == rhs.m_start && lhs.m_end == rhs.m_end;
}
friend bool operator!=( iterator iter, iterator_end ) {
return iter.m_start != iter.m_end;
}
friend bool operator!=( iterator lhs, iterator rhs ) {
return !( lhs == rhs );
}
iterator& operator++() {
++m_start;
return *this;
}
iterator operator++( int ) {
auto tmp( *this );
++m_start;
return tmp;
}
const_reference operator*() const { return *m_start; }
pointer operator->() const { return m_start; }
};
public:
explicit has_different_begin_end_types( std::initializer_list<T> init ):
m_elements( init ) {}
iterator begin() const { return { m_elements.begin(), m_elements.end() }; }
iterator_end end() const { return {}; }
};
#if defined( __clang__ )
# pragma clang diagnostic pop
#endif
template <typename T>
struct with_mocked_iterator_access {
std::vector<T> m_elements;
// use plain arrays to have nicer printouts with CHECK(...)
mutable std::unique_ptr<bool[]> m_derefed;
// We want to check which elements were dereferenced when iterating, so
// we can check whether iterator-using code traverses range correctly
template <bool is_const>
class basic_iterator {
template <typename U>
using constify_t = std::conditional_t<is_const, std::add_const_t<U>, U>;
constify_t<with_mocked_iterator_access>* m_origin;
size_t m_origin_idx;
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = constify_t<T>;
using const_reference = typename std::vector<T>::const_reference;
using reference = typename std::vector<T>::reference;
using pointer = typename std::vector<T>::pointer;
basic_iterator( constify_t<with_mocked_iterator_access>* origin,
std::size_t origin_idx ):
m_origin{ origin }, m_origin_idx{ origin_idx } {}
friend bool operator==( basic_iterator lhs, basic_iterator rhs ) {
return lhs.m_origin == rhs.m_origin &&
lhs.m_origin_idx == rhs.m_origin_idx;
}
friend bool operator!=( basic_iterator lhs, basic_iterator rhs ) {
return !( lhs == rhs );
}
basic_iterator& operator++() {
++m_origin_idx;
return *this;
}
basic_iterator operator++( int ) {
auto tmp( *this );
++( *this );
return tmp;
}
const_reference operator*() const {
assert( m_origin_idx < m_origin->m_elements.size() &&
"Attempted to deref invalid position" );
m_origin->m_derefed[m_origin_idx] = true;
return m_origin->m_elements[m_origin_idx];
}
pointer operator->() const {
assert( m_origin_idx < m_origin->m_elements.size() &&
"Attempted to deref invalid position" );
return &m_origin->m_elements[m_origin_idx];
}
};
using iterator = basic_iterator<false>;
using const_iterator = basic_iterator<true>;
with_mocked_iterator_access( std::initializer_list<T> init ):
m_elements( init ),
m_derefed( std::make_unique<bool[]>( m_elements.size() ) ) {}
const_iterator begin() const { return { this, 0 }; }
const_iterator end() const { return { this, m_elements.size() }; }
iterator begin() { return { this, 0 }; }
iterator end() { return { this, m_elements.size() }; }
};
namespace Catch {
// make sure with_mocked_iterator_access is not considered a range by Catch,
// so that below StringMaker is used instead of the default one for ranges
template <typename T>
struct is_range<with_mocked_iterator_access<T>> : std::false_type {};
template <typename T>
struct StringMaker<with_mocked_iterator_access<T>> {
static std::string
convert( with_mocked_iterator_access<T> const& access ) {
// We have to avoid the type's iterators, because we check
// their use in tests
return ::Catch::Detail::stringify( access.m_elements );
}
};
} // namespace Catch
#endif // CATCH_TEST_HELPERS_RANGE_TEST_HELPERS_HPP_INCLUDED
|