test.hpp 6.65 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
4
5

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <iostream>
Paul's avatar
Paul committed
6
7
#include <unordered_map>
#include <vector>
Paul's avatar
Paul committed
8

Paul's avatar
Paul committed
9
10
#ifndef MIGRAPH_GUARD_TEST_TEST_HPP
#define MIGRAPH_GUARD_TEST_TEST_HPP
Paul's avatar
Paul committed
11

Paul's avatar
Paul committed
12
13
namespace test {
// NOLINTNEXTLINE
Paul's avatar
Paul committed
14
15
16
#define TEST_FOREACH_OPERATOR(m)                                                                   \
    m(==, equal) m(!=, not_equal) m(<=, less_than_equal) m(>=, greater_than_equal) m(<, less_than) \
        m(>, greater_than)
Paul's avatar
Paul committed
17
18

// NOLINTNEXTLINE
Paul's avatar
Paul committed
19
20
21
22
23
24
25
26
27
28
#define TEST_EACH_OPERATOR_OBJECT(op, name)            \
    struct name                                        \
    {                                                  \
        static std::string as_string() { return #op; } \
        template <class T, class U>                    \
        static decltype(auto) call(T&& x, U&& y)       \
        {                                              \
            return x op y;                             \
        }                                              \
    };
Paul's avatar
Paul committed
29

Paul's avatar
Paul committed
30
31
TEST_FOREACH_OPERATOR(TEST_EACH_OPERATOR_OBJECT)

Paul's avatar
Paul committed
32
33
34
35
36
37
inline std::ostream& operator<<(std::ostream& s, std::nullptr_t)
{
    s << "nullptr";
    return s;
}

Paul's avatar
Paul committed
38
template <class T, class U, class Operator>
Paul's avatar
Paul committed
39
struct expression
Paul's avatar
Paul committed
40
{
Paul's avatar
Paul committed
41
42
43
44
    T lhs;
    U rhs;

    friend std::ostream& operator<<(std::ostream& s, const expression& self)
Paul's avatar
Paul committed
45
    {
Paul's avatar
Paul committed
46
47
        s << " [ " << self.lhs << " " << Operator::as_string() << " " << self.rhs << " ]";
        return s;
Paul's avatar
Paul committed
48
    }
Paul's avatar
Paul committed
49

Paul's avatar
Paul committed
50
51
52
    decltype(auto) value() const { return Operator::call(lhs, rhs); };
};

Paul's avatar
Paul committed
53
// TODO: Remove rvalue references
Paul's avatar
Paul committed
54
template <class T, class U, class Operator>
Paul's avatar
Paul committed
55
expression<T, U, Operator> make_expression(T&& rhs, U&& lhs, Operator)
Paul's avatar
Paul committed
56
{
Paul's avatar
Paul committed
57
    return {std::forward<T>(rhs), std::forward<U>(lhs)};
Paul's avatar
Paul committed
58
}
Paul's avatar
Paul committed
59

Paul's avatar
Paul committed
60
template <class T>
Paul's avatar
Paul committed
61
62
struct lhs_expression;

Paul's avatar
Paul committed
63
// TODO: Remove rvalue reference
Paul's avatar
Paul committed
64
template <class T>
Paul's avatar
Paul committed
65
lhs_expression<T> make_lhs_expression(T&& lhs)
Paul's avatar
Paul committed
66
{
Paul's avatar
Paul committed
67
    return lhs_expression<T>{std::forward<T>(lhs)};
Paul's avatar
Paul committed
68
69
}

Paul's avatar
Paul committed
70
template <class T>
Paul's avatar
Paul committed
71
72
73
struct lhs_expression
{
    T lhs;
Paul's avatar
Paul committed
74
    explicit lhs_expression(T e) : lhs(e) {}
Paul's avatar
Paul committed
75
76
77
78
79
80
81

    friend std::ostream& operator<<(std::ostream& s, const lhs_expression& self)
    {
        s << self.lhs;
        return s;
    }

Paul's avatar
Paul committed
82
    T value() const { return lhs; }
Paul's avatar
Paul committed
83
// NOLINTNEXTLINE
Paul's avatar
Paul committed
84
85
86
87
#define TEST_LHS_OPERATOR(op, name)                            \
    template <class U>                                         \
    auto operator op(const U& rhs) const                       \
    {                                                          \
Paul's avatar
Paul committed
88
        return make_expression(lhs, rhs, name{}); /* NOLINT */ \
Paul's avatar
Paul committed
89
    }
Paul's avatar
Paul committed
90
91

    TEST_FOREACH_OPERATOR(TEST_LHS_OPERATOR)
Paul's avatar
Paul committed
92
// NOLINTNEXTLINE
Paul's avatar
Paul committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#define TEST_LHS_REOPERATOR(op)                 \
    template <class U>                          \
    auto operator op(const U& rhs) const        \
    {                                           \
        return make_lhs_expression(lhs op rhs); \
    }
    TEST_LHS_REOPERATOR(+)
    TEST_LHS_REOPERATOR(-)
    TEST_LHS_REOPERATOR(*)
    TEST_LHS_REOPERATOR(/)
    TEST_LHS_REOPERATOR(%)
    TEST_LHS_REOPERATOR(&)
    TEST_LHS_REOPERATOR(|)
    TEST_LHS_REOPERATOR(&&)
    TEST_LHS_REOPERATOR(||)
Paul's avatar
Paul committed
108
109
};

Paul's avatar
Paul committed
110
struct capture
Paul's avatar
Paul committed
111
{
Paul's avatar
Paul committed
112
113
114
115
116
    template <class T>
    auto operator->*(const T& x)
    {
        return make_lhs_expression(x);
    }
Paul's avatar
Paul committed
117
118
};

Paul's avatar
Paul committed
119
template <class T, class F>
Paul's avatar
Paul committed
120
void failed(T x, const char* msg, const char* func, const char* file, int line, F f)
Paul's avatar
Paul committed
121
{
Paul's avatar
Paul committed
122
    if(!x.value())
Paul's avatar
Paul committed
123
    {
Paul's avatar
Paul committed
124
        std::cout << func << std::endl;
Paul's avatar
Paul committed
125
126
127
128
129
        std::cout << file << ":" << line << ":" << std::endl;
        std::cout << "    FAILED: " << msg << " " << x << std::endl;
        f();
    }
}
Paul's avatar
Paul committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

template <class F>
bool throws(F f)
{
    try
    {
        f();
        return false;
    }
    catch(...)
    {
        return true;
    }
}

Khalique's avatar
Khalique committed
145
template <class Exception, class F>
Paul's avatar
Paul committed
146
bool throws(F f, const std::string& msg = "")
Paul's avatar
Paul committed
147
148
149
150
151
152
153
154
155
156
157
158
{
    try
    {
        f();
        return false;
    }
    catch(const Exception& ex)
    {
        return std::string(ex.what()).find(msg) != std::string::npos;
    }
}

Paul's avatar
Paul committed
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
using string_map = std::unordered_map<std::string, std::vector<std::string>>;

template <class Keyword>
string_map parse(std::vector<std::string> as, Keyword keyword)
{
    string_map result;

    std::string flag;
    for(auto&& x : as)
    {
        auto f = keyword(x);
        if(f.empty())
        {
            result[flag].push_back(x);
        }
        else
        {
            flag = f.front();
            result[flag]; // Ensure the flag exists
        }
    }
    return result;
}

inline auto& get_test_cases()
{
    static std::vector<std::pair<std::string, std::function<void()>>> cases;
    return cases;
}

inline void add_test_case(std::string name, std::function<void()> f)
{
    get_test_cases().emplace_back(name, f);
}

struct auto_register
{
Paul's avatar
Paul committed
196
    auto_register(std::string name, std::function<void()> f) { add_test_case(name, f); }
Paul's avatar
Paul committed
197
198
199
};

inline void run_test_case(std::string name, std::function<void()> f)
Paul's avatar
Paul committed
200
{
Paul's avatar
Paul committed
201
202
203
204
205
206
207
208
209
    std::cout << "[   RUN    ] " << name << std::endl;
    f();
    std::cout << "[ COMPLETE ] " << name << std::endl;
}

inline void run(int argc, const char* argv[])
{
    std::vector<std::string> as(argv + 1, argv + argc);

Paul's avatar
Paul committed
210
    auto args  = parse(as, [](auto &&) -> std::vector<std::string> { return {}; });
Paul's avatar
Paul committed
211
212
213
    auto cases = args[""];
    if(cases.empty())
    {
Paul's avatar
Paul committed
214
        for(auto&& tc : get_test_cases())
Paul's avatar
Paul committed
215
216
217
218
            run_test_case(tc.first, tc.second);
    }
    else
    {
Paul's avatar
Paul committed
219
220
221
        std::unordered_map<std::string, std::function<void()>> m(get_test_cases().begin(),
                                                                 get_test_cases().end());
        for(auto&& name : cases)
Paul's avatar
Paul committed
222
223
            run_test_case(name, m[name]);
    }
Paul's avatar
Paul committed
224
225
}

Paul's avatar
Paul committed
226
227
228
} // namespace test

// NOLINTNEXTLINE
Paul's avatar
Paul committed
229
230
231
232
#define CHECK(...)                                                                                 \
    test::failed(                                                                                  \
        test::capture{}->*__VA_ARGS__, #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__, [] { \
        })
Paul's avatar
Paul committed
233
// NOLINTNEXTLINE
Paul's avatar
Paul committed
234
235
236
237
238
239
240
#define EXPECT(...)                             \
    test::failed(test::capture{}->*__VA_ARGS__, \
                 #__VA_ARGS__,                  \
                 __PRETTY_FUNCTION__,           \
                 __FILE__,                      \
                 __LINE__,                      \
                 &std::abort)
Paul's avatar
Paul committed
241
242
243
// NOLINTNEXTLINE
#define STATUS(...) EXPECT((__VA_ARGS__) == 0)

Paul's avatar
Paul committed
244
// NOLINTNEXTLINE
Paul's avatar
Paul committed
245
246
247
248
249
#define TEST_CASE(...)                                   \
    void __VA_ARGS__();                                  \
    static test::auto_register __VA_ARGS__##_register =  \
        test::auto_register(#__VA_ARGS__, &__VA_ARGS__); \
    void __VA_ARGS__()
Paul's avatar
Paul committed
250
251
252
253
254
255

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif

Paul's avatar
Paul committed
256
#endif