summaryrefslogtreecommitdiff
path: root/projects/SelfTest/UsageTests/ToStringWhich.tests.cpp
blob: cd4a15183b2bb335b155ea2c43c5f9edfe0027d9 (plain)
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
/*
 * Demonstrate which version of toString/StringMaker is being used
 * for various types
 */

// Replace fallback stringifier for this TU
// We should avoid ODR violations because these specific types aren't
// present in different TUs
#include <string>
template <typename T>
std::string fallbackStringifier(T const&) {
    return "{ !!! }";
}

#define CATCH_CONFIG_FALLBACK_STRINGIFIER fallbackStringifier
#include "catch.hpp"



#if defined(__GNUC__)
// This has to be left enabled until end of the TU, because the GCC
// frontend reports operator<<(std::ostream& os, const has_maker_and_operator&)
// as unused anyway
#    pragma GCC diagnostic ignored "-Wunused-function"
#endif

namespace {

struct has_operator { };
struct has_maker {};
struct has_maker_and_operator {};
struct has_neither {};
struct has_template_operator {};

std::ostream& operator<<(std::ostream& os, const has_operator&) {
    os << "operator<<( has_operator )";
    return os;
}

std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) {
    os << "operator<<( has_maker_and_operator )";
    return os;
}

template <typename StreamT>
StreamT& operator<<(StreamT& os, const has_template_operator&) {
    os << "operator<<( has_template_operator )";
    return os;
}

} // end anonymous namespace

namespace Catch {
    template<>
    struct StringMaker<has_maker> {
        static std::string convert( const has_maker& ) {
            return "StringMaker<has_maker>";
        }
    };
    template<>
    struct StringMaker<has_maker_and_operator> {
        static std::string convert( const has_maker_and_operator& ) {
            return "StringMaker<has_maker_and_operator>";
        }
    };
}

// Call the operator
TEST_CASE( "stringify( has_operator )", "[toString]" ) {
    has_operator item;
    REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" );
}

// Call the stringmaker
TEST_CASE( "stringify( has_maker )", "[toString]" ) {
    has_maker item;
    REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" );
}

// Call the stringmaker
TEST_CASE( "stringify( has_maker_and_operator )", "[toString]" ) {
    has_maker_and_operator item;
    REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" );
}

TEST_CASE("stringify( has_neither )", "[toString]") {
    has_neither item;
    REQUIRE( ::Catch::Detail::stringify(item) == "{ !!! }" );
}

// Call the templated operator
TEST_CASE( "stringify( has_template_operator )", "[toString]" ) {
    has_template_operator item;
    REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_template_operator )" );
}


// Vectors...

TEST_CASE( "stringify( vectors<has_operator> )", "[toString]" ) {
    std::vector<has_operator> v(1);
    REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
}

TEST_CASE( "stringify( vectors<has_maker> )", "[toString]" ) {
    std::vector<has_maker> v(1);
    REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
}

TEST_CASE( "stringify( vectors<has_maker_and_operator> )", "[toString]" ) {
    std::vector<has_maker_and_operator> v(1);
    REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
}

namespace {

// Range-based conversion should only be used if other possibilities fail
struct int_iterator {
    using iterator_category = std::input_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = int;
    using reference = int&;
    using pointer = int*;

    int_iterator() = default;
    int_iterator(int i) :val(i) {}

    value_type operator*() const { return val; }
    bool operator==(int_iterator rhs) const { return val == rhs.val; }
    bool operator!=(int_iterator rhs) const { return val != rhs.val; }
    int_iterator operator++() { ++val; return *this; }
    int_iterator operator++(int) {
        auto temp(*this);
        ++val;
        return temp;
    }
private:
    int val = 5;
};

struct streamable_range {
    int_iterator begin() const { return int_iterator{ 1 }; }
    int_iterator end() const { return {}; }
};

std::ostream& operator<<(std::ostream& os, const streamable_range&) {
    os << "op<<(streamable_range)";
    return os;
}

struct stringmaker_range {
    int_iterator begin() const { return int_iterator{ 1 }; }
    int_iterator end() const { return {}; }
};

} // end anonymous namespace

namespace Catch {
template <>
struct StringMaker<stringmaker_range> {
    static std::string convert(stringmaker_range const&) {
        return "stringmaker(streamable_range)";
    }
};
}

namespace {

struct just_range {
    int_iterator begin() const { return int_iterator{ 1 }; }
    int_iterator end() const { return {}; }
};

struct disabled_range {
    int_iterator begin() const { return int_iterator{ 1 }; }
    int_iterator end() const { return {}; }
};

} // end anonymous namespace

namespace Catch {
template <>
struct is_range<disabled_range> {
    static const bool value = false;
};
}

TEST_CASE("stringify ranges", "[toString]") {
    REQUIRE(::Catch::Detail::stringify(streamable_range{}) == "op<<(streamable_range)");
    REQUIRE(::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)");
    REQUIRE(::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }");
    REQUIRE(::Catch::Detail::stringify(disabled_range{}) == "{ !!! }");
}