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

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

lolunit: free all the memory we allocated to avoid a minor memory leak,
and slightly tweak the API syntax.

File size: 8.3 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 TextTestRunner;
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 TextTestRunner
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() { p = new T(); } \
175        ~Make##FixtureName() { delete p; } \
176        T *p; \
177    }; \
178    Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \
179    static char const *LolUnitFixtureName(FixtureName *p) \
180    { \
181        (void)p; \
182        return #FixtureName; \
183    } \
184    class FixtureName : public lol::Fixture<FixtureName>
185
186#define LOLUNIT_TEST(TestCaseName) \
187    class TestCase##TestCaseName : public TestCase \
188    { \
189    public: \
190        TestCase##TestCaseName() \
191        { \
192            AddTestCase(this, #TestCaseName, \
193                    (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
194        } \
195    }; \
196    TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
197    void TestCaseName()
198
199#define LOLUNIT_ASSERT_GENERIC(message, cond) \
200    do { \
201        m_asserts++; \
202        if (True() && !(cond)) \
203        { \
204            m_errorlog << std::endl << std::endl; \
205            m_errorlog << ++m_failcases << ") test: " \
206                       << LolUnitFixtureName(this) << "::" << m_currentname \
207                       << " (F) line: " << __LINE__ << " " \
208                       << __FILE__ << std::endl; \
209            m_errorlog << "assertion failed" << std::endl; \
210            m_errorlog << "- Expression: " << #cond << std::endl; \
211            m_errorlog << message; \
212            m_failure = true; \
213            return; \
214        } \
215    } while(!True())
216
217#define LOLUNIT_ASSERT_EQUAL_GENERIC(message, a, b) \
218    do { \
219        m_asserts++; \
220        if (True() && (a) != (b)) \
221        { \
222            m_errorlog << std::endl << std::endl; \
223            m_errorlog << ++m_failcases << ") test: " \
224                       << LolUnitFixtureName(this) << "::" << m_currentname \
225                       << " (F) line: " << __LINE__ << " " \
226                       << __FILE__ << std::endl; \
227            m_errorlog << "equality assertion failed" << std::endl; \
228            m_errorlog << "- Expected: " << (b) << std::endl; \
229            m_errorlog << "- Actual  : " << (a) << std::endl; \
230            m_errorlog << message; \
231            m_failure = true; \
232            return; \
233        } \
234    } while(!True())
235
236#define LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC(message, a, b, t) \
237    do { \
238        m_asserts++; \
239        if (True() && fabs((a) - (b)) > fabs((t))) \
240        { \
241            m_errorlog << std::endl << std::endl; \
242            m_errorlog << ++m_failcases << ") test: " \
243                       << LolUnitFixtureName(this) << "::" << m_currentname \
244                       << " (F) line: " << __LINE__ << " " \
245                       << __FILE__ << std::endl; \
246            m_errorlog << "double equality assertion failed" << std::endl; \
247            m_errorlog << "- Expected: " << (b) << std::endl; \
248            m_errorlog << "- Actual  : " << (a) << std::endl; \
249            m_errorlog << "- Delta   : " << (t) << std::endl; \
250            m_errorlog << message; \
251            m_failure = true; \
252            return; \
253        } \
254    } while(!True())
255
256#define LOLUNIT_FAIL(message) \
257    do { \
258        m_asserts++; \
259        m_errorlog << std::endl << std::endl; \
260        m_errorlog << ++m_failcases << ") test: " \
261                   << LolUnitFixtureName(this) << "::" << m_currentname \
262                   << " (F) line: " << __LINE__ << " " \
263                   << __FILE__ << std::endl; \
264        m_errorlog << "forced failure" << std::endl; \
265        m_errorlog << "- " << message << std::endl; \
266        m_failure = true; \
267        return; \
268    } while(!True())
269
270#define LOLUNIT_ASSERT(cond) \
271    LOLUNIT_ASSERT_GENERIC("", cond)
272
273#define LOLUNIT_ASSERT_MESSAGE(message, cond) \
274    LOLUNIT_ASSERT_GENERIC("- " << message << std::endl, cond)
275
276#define LOLUNIT_ASSERT_EQUAL(a, b) \
277    LOLUNIT_ASSERT_EQUAL_GENERIC("", a, b)
278
279#define LOLUNIT_ASSERT_EQUAL_MESSAGE(message, a, b) \
280    LOLUNIT_ASSERT_EQUAL_GENERIC("- " << message << std::endl, a, b)
281
282#define LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) \
283    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("", a, b, t)
284
285#define LOLUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message, a, b, t) \
286    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("- " << message << std::endl, a, b, t)
287
288} /* namespace lol */
289
290#endif // __LOL_UNIT_H__
291
Note: See TracBrowser for help on using the repository browser.