source: trunk/src/lol/unit.h @ 930

Last change on this file since 930 was 930, checked in by sam, 9 years ago

lolunit: add several helper macros for various comparison checks.

File size: 11.4 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11//
12// The Unit test framework
13// -----------------------
14//
15
16#if !defined __LOL_UNIT_H__
17#define __LOL_UNIT_H__
18
19#include <iostream>
20#include <iomanip>
21#include <sstream>
22#include <cstdio>
23#include <cmath>
24
25namespace lol
26{
27
28using namespace std;
29
30class FixtureBase
31{
32    friend class TextTestRunner;
33
34public:
35    virtual void setUp(void) {};
36    virtual void tearDown(void) {};
37
38protected:
39    FixtureBase() : m_next(NULL), m_testcases(0), m_failcases(0) {}
40    virtual ~FixtureBase() {}
41
42    /* The FixtureBase class keeps track of all instanciated children
43     * fixtures through this method. */
44    static FixtureBase *GetOrSetTest(FixtureBase *set = NULL)
45    {
46        static FixtureBase *head = NULL, *tail = NULL;
47        if (set)
48        {
49            if (!head)
50                head = set;
51            if (tail)
52                tail->m_next = set;
53            tail = set;
54        }
55        return head;
56    }
57
58    virtual void runFixture() = 0;
59
60    /* Prevent compiler complaints about unreachable code */
61    static inline bool True() { return true; }
62
63    FixtureBase *m_next;
64    int m_testcases, m_failcases;
65    int m_asserts, m_failure;
66    char const *m_fixturename, *m_currentname;
67    std::stringstream m_errorlog, m_context;
68};
69
70template<class T> class Fixture : protected FixtureBase
71{
72public:
73    typedef T FixtureClass;
74
75    class TestCase
76    {
77    public:
78        void (FixtureClass::* m_fun)();
79        char const *m_testname;
80        TestCase *m_next;
81
82        static void AddTestCase(TestCase *that, char const *name,
83                                void (FixtureClass::*fun)())
84        {
85            that->m_fun = fun;
86            that->m_testname = name;
87            that->m_next = NULL;
88            GetOrSetTestCase(that);
89        }
90    };
91
92    Fixture<T>()
93    {
94        GetOrSetTest(this);
95    }
96
97    /* Run all test cases in this fixture. */
98    virtual void runFixture()
99    {
100        m_errorlog.str("");
101        m_testcases = 0;
102        m_failcases = 0;
103        for (TestCase *c = GetOrSetTestCase(); c; c = c->m_next)
104        {
105            m_testcases++;
106            m_asserts = 0;
107            m_failure = false;
108            m_currentname = c->m_testname;
109            m_context.str("");
110
111            std::cout << '.';
112            (static_cast<FixtureClass *>(this)->*c->m_fun)();
113            if (m_failure) std::cout << 'F';
114        }
115    }
116
117    /* Each Fixture class specialisation keeps track of its instanciated
118     * test cases through this method. */
119    static TestCase *GetOrSetTestCase(TestCase *set = NULL)
120    {
121        static TestCase *head = NULL, *tail = NULL;
122        if (set)
123        {
124            if (!head) head = set;
125            if (tail) tail->m_next = set;
126            tail = set;
127        }
128        return head;
129    }
130};
131
132class TextTestRunner
133{
134public:
135    bool run()
136    {
137        bool ret = true;
138        std::stringstream errors("");
139        int failcases = 0, testcases = 0;
140
141        for (FixtureBase *f = FixtureBase::GetOrSetTest(); f; f = f->m_next)
142        {
143            f->setUp();
144            f->runFixture();
145            f->tearDown();
146
147            errors << f->m_errorlog.str();
148            testcases += f->m_testcases;
149            failcases += f->m_failcases;
150        }
151        std::cout << std::endl;
152
153        std::cout << std::endl << std::endl;
154        if (failcases)
155        {
156            std::cout << "!!!FAILURES!!!" << std::endl;
157            std::cout << "Test Results:" << std::endl;
158            std::cout << "Run:  " << testcases
159                      << "  Failures: " << failcases
160                      << "  Errors: 0" << std::endl; /* TODO: handle errors */
161
162            std::cout << errors.str();
163            ret = false;
164        }
165        else
166        {
167            std::cout << "OK (" << testcases << " tests)" << std::endl;
168        }
169        std::cout << std::endl << std::endl;
170
171        return ret;
172    }
173};
174
175#define LOLUNIT_FIXTURE(FixtureName) \
176    class FixtureName; \
177    template<typename T> struct Make##FixtureName \
178    { \
179        Make##FixtureName() { p = new T(); } \
180        ~Make##FixtureName() { delete p; } \
181        T *p; \
182    }; \
183    Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \
184    static char const *LolUnitFixtureName(FixtureName *p) \
185    { \
186        (void)p; \
187        return #FixtureName; \
188    } \
189    class FixtureName : public lol::Fixture<FixtureName>
190
191#define LOLUNIT_TEST(TestCaseName) \
192    class TestCase##TestCaseName : public TestCase \
193    { \
194    public: \
195        TestCase##TestCaseName() \
196        { \
197            AddTestCase(this, #TestCaseName, \
198                    (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
199        } \
200    }; \
201    TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
202    void TestCaseName()
203
204#define LOLUNIT_ASSERT_GENERIC(msg, cond) \
205    do { \
206        m_asserts++; \
207        if (True() && !(cond)) \
208        { \
209            m_errorlog << std::endl << std::endl; \
210            m_errorlog << ++m_failcases << ") test: " \
211                       << LolUnitFixtureName(this) << "::" << m_currentname \
212                       << " (F) line: " << __LINE__ << " " \
213                       << __FILE__ << std::endl; \
214            m_errorlog << "assertion failed" << std::endl; \
215            m_errorlog << "- Expression: " << #cond << std::endl; \
216            m_errorlog << msg; \
217            m_failure = true; \
218            return; \
219        } \
220    } while(!True())
221
222#define LOLUNIT_ASSERT_OP(op, modifier, opdesc, msg, a, b) \
223    do { \
224        m_asserts++; \
225        if (True() && !modifier((a) op (b))) \
226        { \
227            m_errorlog << std::endl << std::endl; \
228            m_errorlog << ++m_failcases << ") test: " \
229                       << LolUnitFixtureName(this) << "::" << m_currentname \
230                       << " (F) line: " << __LINE__ << " " \
231                       << __FILE__ << std::endl; \
232            m_errorlog << opdesc << " assertion failed" << std::endl; \
233            m_errorlog << "- Expected: " << #a << " = " << (a) << std::endl; \
234            m_errorlog << "- Actual  : " << #b << " = " << (b) << std::endl; \
235            m_errorlog << msg; \
236            m_errorlog << m_context.str(); \
237            m_failure = true; \
238            return; \
239        } \
240    } while(!True())
241
242#define LOLUNIT_MSG(msg) \
243    "- " << msg << std::endl
244
245#define LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC(msg, a, b, t) \
246    do { \
247        m_asserts++; \
248        if (True() && fabs((a) - (b)) > fabs((t))) \
249        { \
250            m_errorlog << std::endl << std::endl; \
251            m_errorlog << ++m_failcases << ") test: " \
252                       << LolUnitFixtureName(this) << "::" << m_currentname \
253                       << " (F) line: " << __LINE__ << " " \
254                       << __FILE__ << std::endl; \
255            m_errorlog << "double equality assertion failed" << std::endl; \
256            std::streamsize old_prec = m_errorlog.precision(); \
257            m_errorlog << std::setprecision(16); \
258            m_errorlog << "- Expected: " << #a << " = " << (a) << std::endl; \
259            m_errorlog << "- Actual  : " << #b << " = " << (b) << std::endl; \
260            m_errorlog << "- Delta   : " << (t) << std::endl; \
261            m_errorlog << std::setprecision(old_prec); \
262            m_errorlog << msg; \
263            m_errorlog << m_context.str(); \
264            m_failure = true; \
265            return; \
266        } \
267    } while(!True())
268
269#define LOLUNIT_FAIL(msg) \
270    do { \
271        m_asserts++; \
272        m_errorlog << std::endl << std::endl; \
273        m_errorlog << ++m_failcases << ") test: " \
274                   << LolUnitFixtureName(this) << "::" << m_currentname \
275                   << " (F) line: " << __LINE__ << " " \
276                   << __FILE__ << std::endl; \
277        m_errorlog << "forced failure" << std::endl; \
278        m_errorlog << "- " << msg << std::endl; \
279        m_errorlog << m_context.str(); \
280        m_failure = true; \
281        return; \
282    } while(!True())
283
284#define LOLUNIT_SET_CONTEXT(n) \
285    do { \
286        m_context.str(""); \
287        m_context << "- Context : " << #n << " = " << (n) << std::endl; \
288    } while(!True())
289#define LOLUNIT_UNSET_CONTEXT(n) \
290    m_context.str("")
291
292#define LOLUNIT_ASSERT(cond) \
293    LOLUNIT_ASSERT_GENERIC("", cond)
294#define LOLUNIT_ASSERT_MESSAGE(m, cond) \
295    LOLUNIT_ASSERT_GENERIC(LOLUNIT_MSG(m), cond)
296
297
298#define LOLUNIT_ASSERT_EQUAL(a, b) \
299    LOLUNIT_ASSERT_OP(==, !!, "equality", "", a, b)
300#define LOLUNIT_ASSERT_EQUAL_MESSAGE(m, a, b) \
301    LOLUNIT_ASSERT_OP(==, !!, "equality", LOLUNIT_MSG(m), a, b)
302#define LOLUNIT_ASSERT_DIFFERENT(a, b) \
303    LOLUNIT_ASSERT_OP(!=, !!, "inequality", "", a, b)
304#define LOLUNIT_ASSERT_DIFFERENT_MESSAGE(m, a, b) \
305    LOLUNIT_ASSERT_OP(!=, !!, "inequality", LOLUNIT_MSG(m), a, b)
306#define LOLUNIT_ASSERT_LESS(a, b) \
307    LOLUNIT_ASSERT_OP(<, !!, "less than", "", a, b)
308#define LOLUNIT_ASSERT_LESS_MESSAGE(m, a, b) \
309    LOLUNIT_ASSERT_OP(<, !!, "less than", LOLUNIT_MSG(m), a, b)
310#define LOLUNIT_ASSERT_LEQUAL(a, b) \
311    LOLUNIT_ASSERT_OP(<=, !!, "less than or equal", "", a, b)
312#define LOLUNIT_ASSERT_LEQUAL_MESSAGE(m, a, b) \
313    LOLUNIT_ASSERT_OP(<=, !!, "less than or equal", LOLUNIT_MSG(m), a, b)
314#define LOLUNIT_ASSERT_GREATER(a, b) \
315    LOLUNIT_ASSERT_OP(>, !!, "greater than", "", a, b)
316#define LOLUNIT_ASSERT_GREATER_MESSAGE(m, a, b) \
317    LOLUNIT_ASSERT_OP(>, !!, "greater than", LOLUNIT_MSG(m), a, b)
318#define LOLUNIT_ASSERT_GEQUAL(a, b) \
319    LOLUNIT_ASSERT_OP(>=, !!, "greater than or equal", "", a, b)
320#define LOLUNIT_ASSERT_GEQUAL_MESSAGE(m, a, b) \
321    LOLUNIT_ASSERT_OP(>=, !!, "greater than or equal", LOLUNIT_MSG(m), a, b)
322
323
324#define LOLUNIT_ASSERT_NOT_EQUAL(a, b) \
325    LOLUNIT_ASSERT_OP(==, !, "not equality", "", a, b)
326#define LOLUNIT_ASSERT_NOT_EQUAL_MESSAGE(m, a, b) \
327    LOLUNIT_ASSERT_OP(==, !, "not equality", LOLUNIT_MSG(m), a, b)
328#define LOLUNIT_ASSERT_NOT_DIFFERENT(a, b) \
329    LOLUNIT_ASSERT_OP(!=, !, "not inequality", "", a, b)
330#define LOLUNIT_ASSERT_NOT_DIFFERENT_MESSAGE(m, a, b) \
331    LOLUNIT_ASSERT_OP(!=, !, "not inequality", LOLUNIT_MSG(m), a, b)
332#define LOLUNIT_ASSERT_NOT_LESS(a, b) \
333    LOLUNIT_ASSERT_OP(<, !, "not less than", "", a, b)
334#define LOLUNIT_ASSERT_NOT_LESS_MESSAGE(m, a, b) \
335    LOLUNIT_ASSERT_OP(<, !, "not less than", LOLUNIT_MSG(m), a, b)
336#define LOLUNIT_ASSERT_NOT_LEQUAL(a, b) \
337    LOLUNIT_ASSERT_OP(<=, !, "not less than or equal", "", a, b)
338#define LOLUNIT_ASSERT_NOT_LEQUAL_MESSAGE(m, a, b) \
339    LOLUNIT_ASSERT_OP(<=, !, "not less than or equal", LOLUNIT_MSG(m), a, b)
340#define LOLUNIT_ASSERT_NOT_GREATER(a, b) \
341    LOLUNIT_ASSERT_OP(>, !, "not greater than", "", a, b)
342#define LOLUNIT_ASSERT_NOT_GREATER_MESSAGE(m, a, b) \
343    LOLUNIT_ASSERT_OP(>, !, "not greater than", LOLUNIT_MSG(m), a, b)
344#define LOLUNIT_ASSERT_NOT_GEQUAL(a, b) \
345    LOLUNIT_ASSERT_OP(>=, !, "not greater than or equal", "", a, b)
346#define LOLUNIT_ASSERT_NOT_GEQUAL_MESSAGE(m, a, b) \
347    LOLUNIT_ASSERT_OP(>=, !, "not greater than or equal", LOLUNIT_MSG(m), a, b)
348
349#define LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) \
350    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("", a, b, t)
351#define LOLUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(msg, a, b, t) \
352    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("- " << msg << std::endl, a, b, t)
353
354} /* namespace lol */
355
356#endif // __LOL_UNIT_H__
357
Note: See TracBrowser for help on using the repository browser.