1 //-----------------------------------------------------------------------------
2 /** @file libboardgame_test/Test.h
3     Provides functionality similar to Boost.Test.
4     @author Markus Enzenberger
5     @copyright GNU General Public License version 3 or later */
6 //-----------------------------------------------------------------------------
7 
8 #ifndef LIBBOARDGAME_TEST_TEST_H
9 #define LIBBOARDGAME_TEST_TEST_H
10 
11 #include <cmath>
12 #include <functional>
13 #include <sstream>
14 #include <stdexcept>
15 #include <string>
16 
17 namespace libboardgame_test {
18 
19 using namespace std;
20 
21 //-----------------------------------------------------------------------------
22 
23 using TestFunction = function<void()>;
24 
25 //-----------------------------------------------------------------------------
26 
27 class TestFail
28     : public logic_error
29 {
30 public:
31     TestFail(const char* file, int line, const string& s);
32 };
33 
34 //-----------------------------------------------------------------------------
35 
36 void add_test(const string& name, const TestFunction& function);
37 
38 bool run_all_tests();
39 
40 bool run_test(const string& name);
41 
42 //-----------------------------------------------------------------------------
43 
44 /** Helper class that automatically adds a test when an instance is
45     declared. */
46 struct TestRegistrar
47 {
TestRegistrarTestRegistrar48     TestRegistrar(const string& name, const TestFunction& function)
49     {
50         add_test(name, function);
51     }
52 };
53 
54 //-----------------------------------------------------------------------------
55 
56 } // namespace libboardgame_test
57 
58 //-----------------------------------------------------------------------------
59 
60 #define LIBBOARDGAME_TEST_CASE(name)                                       \
61     static void name();                                                    \
62     static libboardgame_test::TestRegistrar name##_registrar(#name, name); \
63     void name()
64 
65 
66 #define LIBBOARDGAME_CHECK(expr)                                        \
67     if (! (expr))                                                       \
68         throw libboardgame_test::TestFail(__FILE__, __LINE__, "check failed")
69 
70 #define LIBBOARDGAME_CHECK_EQUAL(expr1, expr2)                          \
71     {                                                                   \
72         using libboardgame_test::TestFail;                              \
73         const auto& result1 = (expr1);                                         \
74         const auto& result2 = (expr2);                                         \
75         if (result1 != result2)                                         \
76         {                                                               \
77             ostringstream msg;                                          \
78             msg << "'" << result1 << "' != '" << result2 << "'";   \
79             throw TestFail(__FILE__, __LINE__, msg.str());              \
80         }                                                               \
81     }
82 
83 #define LIBBOARDGAME_CHECK_THROW(expr, exception)                       \
84     {                                                                   \
85         using libboardgame_test::TestFail;                              \
86         bool was_thrown = false;                                        \
87         try                                                             \
88         {                                                               \
89             expr;                                                       \
90         }                                                               \
91         catch (const exception&)                                        \
92         {                                                               \
93             was_thrown = true;                                          \
94         }                                                               \
95         if (! was_thrown)                                               \
96         {                                                               \
97             ostringstream msg;                                          \
98             msg << "Exception '" << #exception << "' was not thrown";   \
99             throw TestFail(__FILE__, __LINE__, msg.str());              \
100         }                                                               \
101     }
102 
103 #define LIBBOARDGAME_CHECK_NO_THROW(expr)                               \
104     {                                                                   \
105         using libboardgame_test::TestFail;                              \
106         try                                                             \
107         {                                                               \
108             expr;                                                       \
109         }                                                               \
110         catch (...)                                                     \
111         {                                                               \
112             throw TestFail(__FILE__, __LINE__,                          \
113                            "Unexpected exception was thrown");         \
114         }                                                               \
115     }
116 
117 /** Compare floating points using a tolerance in percent. */
118 #define LIBBOARDGAME_CHECK_CLOSE(expr1, expr2, tolerance)               \
119     {                                                                   \
120         using libboardgame_test::TestFail;                              \
121         auto result1 = (expr1);                                         \
122         auto result2 = (expr2);                                         \
123         if (fabs(result1 - result2) > (tolerance) * result1 / 100)      \
124         {                                                               \
125             ostringstream msg;                                          \
126             msg << "Difference between " << result1 << " and "          \
127                 << result2 << " exceeds " << ((tolerance) / 100 )       \
128                 << " percent";                                          \
129             throw TestFail(__FILE__, __LINE__, msg.str());              \
130         }                                                               \
131     }
132 
133 /** Compare floating points using an epsilon. */
134 #define LIBBOARDGAME_CHECK_CLOSE_EPS(expr1, expr2, epsilon)             \
135     {                                                                   \
136         using libboardgame_test::TestFail;                              \
137         auto result1 = (expr1);                                         \
138         auto result2 = (expr2);                                         \
139         if (fabs(result1 - result2) > (epsilon))                        \
140         {                                                               \
141             ostringstream msg;                                          \
142             msg << "Difference between " << result1 << " and "          \
143                 << result2 << " exceeds " << (epsilon);                 \
144             throw TestFail(__FILE__, __LINE__, msg.str());              \
145         }                                                               \
146     }
147 
148 //-----------------------------------------------------------------------------
149 
150 #endif // LIBBOARDGAME_TEST_TEST_H
151