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

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <iostream>

Paul's avatar
Paul committed
7
#ifndef RTG_GUARD_TEST_TEST_HPP
Paul's avatar
Paul committed
8
#define RTG_GUARD_TEST_TEST_HPP
Paul's avatar
Paul committed
9

Paul's avatar
Paul committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace test {
// NOLINTNEXTLINE
#define TEST_FOREACH_OPERATOR(m) \
m(==, equal) \
m(!=, not_equal) \
m(<=, less_than_equal) \
m(>=, greater_than_equal) \
m(<, less_than) \
m(>, greater_than)

// NOLINTNEXTLINE
#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
28

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

template<class T, class U, class Operator>
struct expression
Paul's avatar
Paul committed
33
{
Paul's avatar
Paul committed
34
35
36
37
    T lhs;
    U rhs;

    friend std::ostream& operator<<(std::ostream& s, const expression& self)
Paul's avatar
Paul committed
38
    {
Paul's avatar
Paul committed
39
40
        s << " [ " << self.lhs << " " << Operator::as_string() << " " << self.rhs << " ]";
        return s;
Paul's avatar
Paul committed
41
    }
Paul's avatar
Paul committed
42

Paul's avatar
Paul committed
43
44
45
46
47
48
    decltype(auto) value() const { return Operator::call(lhs, rhs); };
};

template<class T, class U, class Operator>
expression<typename std::decay<T>::type, typename std::decay<U>::type, Operator> 
make_expression(T&& rhs, U&& lhs, Operator)
Paul's avatar
Paul committed
49
{
Paul's avatar
Paul committed
50
51
    return { std::forward<T>(rhs), std::forward<U>(lhs) };
}
Paul's avatar
Paul committed
52

Paul's avatar
Paul committed
53
54
55
56
57
58
59
template<class T>
struct lhs_expression;

template<class T>
lhs_expression<typename std::decay<T>::type> make_lhs_expression(T&& lhs)
{
    return lhs_expression<typename std::decay<T>::type>{ std::forward<T>(lhs) };
Paul's avatar
Paul committed
60
61
}

Paul's avatar
Paul committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
template<class T>
struct lhs_expression
{
    T lhs;
    explicit lhs_expression(T e) : lhs(e)
    {}

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

    T value() const
    {
        return lhs;
    }
Paul's avatar
Paul committed
79
// NOLINTNEXTLINE
Paul's avatar
Paul committed
80
81
82
#define TEST_LHS_OPERATOR(op, name) \
    template<class U> \
    auto operator op(const U& rhs) const { return make_expression(lhs, rhs, name{}); } // NOLINT
Paul's avatar
Paul committed
83

Paul's avatar
Paul committed
84
TEST_FOREACH_OPERATOR(TEST_LHS_OPERATOR)
Paul's avatar
Paul committed
85
// NOLINTNEXTLINE
Paul's avatar
Paul committed
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
#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(||)

};

struct capture 
{
    template<class T>
    auto operator->* (const T& x) { return make_lhs_expression(x); }
};

template<class T, class F>
void failed(T x, const char* msg, const char* file, int line, F f)
{
    if (!x.value()) 
    {
        std::cout << file << ":" << line << ":" << std::endl;
        std::cout << "    FAILED: " << msg << " " << x << std::endl;
        f();
    }
}
Paul's avatar
Paul committed
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

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

template <class F, class Exception>
bool throws(F f, std::string msg = "")
{
    try
    {
        f();
        return false;
    }
    catch(const Exception& ex)
    {
        return std::string(ex.what()).find(msg) != std::string::npos;
    }
}

template <class T>
void run_test()
{
    T t = {};
    t.run();
}

Paul's avatar
Paul committed
152
153
154
155
156
157
158
159
160
} // namespace test

// NOLINTNEXTLINE
#define CHECK(...) test::failed(test::capture{} ->* __VA_ARGS__, #__VA_ARGS__, __FILE__, __LINE__, []{})
// NOLINTNEXTLINE
#define EXPECT(...) test::failed(test::capture{} ->* __VA_ARGS__, #__VA_ARGS__, __FILE__, __LINE__, &std::abort)
// NOLINTNEXTLINE
#define STATUS(...) EXPECT((__VA_ARGS__) == 0)

Paul's avatar
Paul committed
161
#endif