aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/catch2/tests/SelfTest/helpers/range_test_helpers.hpp
blob: 22c2c3cddf2f6069b6dd0d358eabfdd250dceadd (plain) (blame)
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