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

Last change on this file since 914 was 914, checked in by sam, 11 years ago

test: get rid of the need for LOLUNIT_SETUP_FIXTURE().

File size: 8.2 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 <sstream>
21#include <cstdio>
22#include <cmath>
23
24namespace lol
25{
26
27using namespace std;
28
29class FixtureBase
30{
31    friend class TestRunner;
32
33public:
34    virtual void setUp(void) {};
35    virtual void tearDown(void) {};
36
37protected:
38    FixtureBase() : m_next(NULL), m_testcases(0), m_failcases(0) {}
39    virtual ~FixtureBase() {}
40
41    /* The FixtureBase class keeps track of all instanciated children
42     * fixtures through this method. */
43    static FixtureBase *GetOrSetTest(FixtureBase *set = NULL)
44    {
45        static FixtureBase *head = NULL, *tail = NULL;
46        if (set)
47        {
48            if (!head)
49                head = set;
50            if (tail)
51                tail->m_next = set;
52            tail = set;
53        }
54        return head;
55    }
56
57    virtual void RunFixture() = 0;
58
59    /* Prevent compiler complaints about unreachable code */
60    static inline bool True() { return true; }
61
62    FixtureBase *m_next;
63    int m_testcases, m_failcases;
64    int m_asserts, m_failure;
65    char const *m_fixturename, *m_currentname;
66    std::stringstream m_errorlog;
67};
68
69template<class T> class Fixture : protected FixtureBase
70{
71public:
72    typedef T FixtureClass;
73
74    class TestCase
75    {
76    public:
77        void (FixtureClass::* m_fun)();
78        char const *m_testname;
79        TestCase *m_next;
80
81        static void AddTestCase(TestCase *that, char const *name,
82                                void (FixtureClass::*fun)())
83        {
84            that->m_testname = name;
85            that->m_fun = fun;
86            GetOrSetTestCase(that);
87        }
88    };
89
90    Fixture<T>()
91    {
92        GetOrSetTest(this);
93    }
94
95    /* Run all test cases in this fixture. */
96    virtual void RunFixture()
97    {
98        m_errorlog.str("");
99        m_testcases = 0;
100        m_failcases = 0;
101        for (TestCase *c = GetOrSetTestCase(); c; c = c->m_next)
102        {
103            m_testcases++;
104            m_asserts = 0;
105            m_failure = false;
106            m_currentname = c->m_testname;
107            (static_cast<FixtureClass *>(this)->*c->m_fun)();
108            std::cout << (m_failure ? 'F' : '.');
109        }
110    }
111
112    /* Each Fixture class specialisation keeps track of its instanciated
113     * test cases through this method. */
114    static TestCase *GetOrSetTestCase(TestCase *set = NULL)
115    {
116        static TestCase *head = NULL, *tail = NULL;
117        if (set)
118        {
119            if (!head) head = set;
120            if (tail) tail->m_next = set;
121            tail = set;
122        }
123        return head;
124    }
125};
126
127class TestRunner
128{
129public:
130    bool Run()
131    {
132        bool ret = true;
133        std::stringstream errors("");
134        int failcases = 0, testcases = 0;
135
136        for (FixtureBase *f = FixtureBase::GetOrSetTest(); f; f = f->m_next)
137        {
138            f->setUp();
139            f->RunFixture();
140            f->tearDown();
141
142            errors << f->m_errorlog.str();
143            testcases += f->m_testcases;
144            failcases += f->m_failcases;
145        }
146        std::cout << std::endl;
147
148        std::cout << std::endl << std::endl;
149        if (failcases)
150        {
151            std::cout << "!!!FAILURES!!!" << std::endl;
152            std::cout << "Test Results:" << std::endl;
153            std::cout << "Run:  " << testcases
154                      << "  Failures: " << failcases
155                      << "  Errors: 0" << std::endl; /* TODO: handle errors */
156
157            std::cout << errors.str();
158            ret = false;
159        }
160        else
161        {
162            std::cout << "OK (" << testcases << " tests)" << std::endl;
163        }
164        std::cout << std::endl << std::endl;
165
166        return ret;
167    }
168};
169
170#define LOLUNIT_FIXTURE(FixtureName) \
171    class FixtureName; \
172    template<typename T> struct Make##FixtureName \
173    { \
174        Make##FixtureName() { new T(); } \
175    }; \
176    Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \
177    static char const *LolUnitFixtureName(FixtureName *p) \
178    { \
179        (void)p; \
180        return #FixtureName; \
181    } \
182    class FixtureName : public lol::Fixture<FixtureName>
183
184#define LOLUNIT_TEST(TestCaseName) \
185    class TestCase##TestCaseName : public TestCase \
186    { \
187    public: \
188        TestCase##TestCaseName() \
189        { \
190            AddTestCase(this, #TestCaseName, \
191                    (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
192        } \
193    }; \
194    TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
195    void TestCaseName()
196
197#define LOLUNIT_ASSERT_GENERIC(message, cond) \
198    do { \
199        m_asserts++; \
200        if (True() && !(cond)) \
201        { \
202            m_errorlog << std::endl << std::endl; \
203            m_errorlog << ++m_failcases << ") test: " \
204                       << LolUnitFixtureName(this) << "::" << m_currentname \
205                       << " (F) line: " << __LINE__ << " " \
206                       << __FILE__ << std::endl; \
207            m_errorlog << "assertion failed" << std::endl; \
208            m_errorlog << "- Expression: " << #cond << std::endl; \
209            m_errorlog << message; \
210            m_failure = true; \
211            return; \
212        } \
213    } while(!True())
214
215#define LOLUNIT_ASSERT_EQUAL_GENERIC(message, a, b) \
216    do { \
217        m_asserts++; \
218        if (True() && (a) != (b)) \
219        { \
220            m_errorlog << std::endl << std::endl; \
221            m_errorlog << ++m_failcases << ") test: " \
222                       << LolUnitFixtureName(this) << "::" << m_currentname \
223                       << " (F) line: " << __LINE__ << " " \
224                       << __FILE__ << std::endl; \
225            m_errorlog << "equality assertion failed" << std::endl; \
226            m_errorlog << "- Expected: " << (b) << std::endl; \
227            m_errorlog << "- Actual  : " << (a) << std::endl; \
228            m_errorlog << message; \
229            m_failure = true; \
230            return; \
231        } \
232    } while(!True())
233
234#define LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC(message, a, b, t) \
235    do { \
236        m_asserts++; \
237        if (True() && fabs((a) - (b)) > fabs((t))) \
238        { \
239            m_errorlog << std::endl << std::endl; \
240            m_errorlog << ++m_failcases << ") test: " \
241                       << LolUnitFixtureName(this) << "::" << m_currentname \
242                       << " (F) line: " << __LINE__ << " " \
243                       << __FILE__ << std::endl; \
244            m_errorlog << "double equality assertion failed" << std::endl; \
245            m_errorlog << "- Expected: " << (b) << std::endl; \
246            m_errorlog << "- Actual  : " << (a) << std::endl; \
247            m_errorlog << "- Delta   : " << (t) << std::endl; \
248            m_errorlog << message; \
249            m_failure = true; \
250            return; \
251        } \
252    } while(!True())
253
254#define LOLUNIT_FAIL(message) \
255    do { \
256        m_asserts++; \
257        m_errorlog << std::endl << std::endl; \
258        m_errorlog << ++m_failcases << ") test: " \
259                   << LolUnitFixtureName(this) << "::" << m_currentname \
260                   << " (F) line: " << __LINE__ << " " \
261                   << __FILE__ << std::endl; \
262        m_errorlog << "forced failure" << std::endl; \
263        m_errorlog << "- " << message << std::endl; \
264        m_failure = true; \
265        return; \
266    } while(!True())
267
268#define LOLUNIT_ASSERT(cond) \
269    LOLUNIT_ASSERT_GENERIC("", cond)
270
271#define LOLUNIT_ASSERT_MESSAGE(message, cond) \
272    LOLUNIT_ASSERT_GENERIC("- " << message << std::endl, cond)
273
274#define LOLUNIT_ASSERT_EQUAL(a, b) \
275    LOLUNIT_ASSERT_EQUAL_GENERIC("", a, b)
276
277#define LOLUNIT_ASSERT_EQUAL_MESSAGE(message, a, b) \
278    LOLUNIT_ASSERT_EQUAL_GENERIC("- " << message << std::endl, a, b)
279
280#define LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) \
281    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("", a, b, t)
282
283#define LOLUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message, a, b, t) \
284    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("- " << message << std::endl, a, b, t)
285
286} /* namespace lol */
287
288#endif // __LOL_UNIT_H__
289
Note: See TracBrowser for help on using the repository browser.