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

Last change on this file since 913 was 913, checked in by sam, 8 years ago

test: make LolUnit's output match CppUnit's more closely.

File size: 6.9 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    static char const *LolUnitFixtureName(FixtureName *p) \
173    { \
174        (void)p; \
175        return #FixtureName; \
176    } \
177    class FixtureName : public lol::Fixture<FixtureName>
178
179#define LOLUNIT_TEST(TestCaseName) \
180    class TestCase##TestCaseName : public TestCase \
181    { \
182    public: \
183        TestCase##TestCaseName() \
184        { \
185            AddTestCase(this, #TestCaseName, \
186                    (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
187        } \
188    }; \
189    TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
190    void TestCaseName()
191
192#define LOLUNIT_SETUP_FIXTURE(ClassName) \
193    ClassName ClassName##Test_Instance;
194
195#define LOLUNIT_ASSERT(cond) \
196    do { \
197        m_asserts++; \
198        if (True() && !(cond)) \
199        { \
200            m_errorlog << std::endl << std::endl; \
201            m_errorlog << ++m_failcases << ") test: " \
202                       << LolUnitFixtureName(this) << "::" << m_currentname \
203                       << " (F) line: " << __LINE__ << " " \
204                       << __FILE__ << std::endl; \
205            m_errorlog << "assertion failed" << std::endl; \
206            m_errorlog << "- Expression: " << #cond << std::endl; \
207            m_failure = true; \
208            return; \
209        } \
210    } while(!True())
211
212#define LOLUNIT_ASSERT_EQUAL(a, b) \
213    do { \
214        m_asserts++; \
215        if (True() && (a) != (b)) \
216        { \
217            m_errorlog << std::endl << std::endl; \
218            m_errorlog << ++m_failcases << ") test: " \
219                       << LolUnitFixtureName(this) << "::" << m_currentname \
220                       << " (F) line: " << __LINE__ << " " \
221                       << __FILE__ << std::endl; \
222            m_errorlog << "equality assertion failed" << std::endl; \
223            m_errorlog << "- Expected: " << (b) << std::endl; \
224            m_errorlog << "- Actual  : " << (a) << std::endl; \
225            m_errorlog << message; \
226            m_failure = true; \
227            return; \
228        } \
229    } while(!True())
230
231#define LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) \
232    do { \
233        m_asserts++; \
234        if (True() && fabs((a) - (b)) > fabs((t))) \
235        { \
236            m_errorlog << std::endl << std::endl; \
237            m_errorlog << ++m_failcases << ") test: " \
238                       << LolUnitFixtureName(this) << "::" << m_currentname \
239                       << " (F) line: " << __LINE__ << " " \
240                       << __FILE__ << std::endl; \
241            m_errorlog << "double equality assertion failed" << std::endl; \
242            m_errorlog << "- Expected: " << (b) << std::endl; \
243            m_errorlog << "- Actual  : " << (a) << std::endl; \
244            m_errorlog << "- Delta   : " << (t) << std::endl; \
245            m_failure = true; \
246            return; \
247        } \
248    } while(!True())
249
250} /* namespace lol */
251
252#endif // __LOL_UNIT_H__
253
Note: See TracBrowser for help on using the repository browser.