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

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

lolunit: reverse the expected/actual logic.

CPPUNIT_ASSERT_EQUAL(a, b) “expects” the value “a” and fails if “b” is
different. The logic seems backwards to me, but matching CppUnit’s
output is important so I’m doing the same in LolUnit.

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_fun = fun;
85            that->m_testname = name;
86            that->m_next = NULL;
87            GetOrSetTestCase(that);
88        }
89    };
90
91    Fixture<T>()
92    {
93        GetOrSetTest(this);
94    }
95
96    /* Run all test cases in this fixture. */
97    virtual void runFixture()
98    {
99        m_errorlog.str("");
100        m_testcases = 0;
101        m_failcases = 0;
102        for (TestCase *c = GetOrSetTestCase(); c; c = c->m_next)
103        {
104            m_testcases++;
105            m_asserts = 0;
106            m_failure = false;
107            m_currentname = c->m_testname;
108            (static_cast<FixtureClass *>(this)->*c->m_fun)();
109            std::cout << (m_failure ? 'F' : '.');
110        }
111    }
112
113    /* Each Fixture class specialisation keeps track of its instanciated
114     * test cases through this method. */
115    static TestCase *GetOrSetTestCase(TestCase *set = NULL)
116    {
117        static TestCase *head = NULL, *tail = NULL;
118        if (set)
119        {
120            if (!head) head = set;
121            if (tail) tail->m_next = set;
122            tail = set;
123        }
124        return head;
125    }
126};
127
128class TextTestRunner
129{
130public:
131    bool run()
132    {
133        bool ret = true;
134        std::stringstream errors("");
135        int failcases = 0, testcases = 0;
136
137        for (FixtureBase *f = FixtureBase::GetOrSetTest(); f; f = f->m_next)
138        {
139            f->setUp();
140            f->runFixture();
141            f->tearDown();
142
143            errors << f->m_errorlog.str();
144            testcases += f->m_testcases;
145            failcases += f->m_failcases;
146        }
147        std::cout << std::endl;
148
149        std::cout << std::endl << std::endl;
150        if (failcases)
151        {
152            std::cout << "!!!FAILURES!!!" << std::endl;
153            std::cout << "Test Results:" << std::endl;
154            std::cout << "Run:  " << testcases
155                      << "  Failures: " << failcases
156                      << "  Errors: 0" << std::endl; /* TODO: handle errors */
157
158            std::cout << errors.str();
159            ret = false;
160        }
161        else
162        {
163            std::cout << "OK (" << testcases << " tests)" << std::endl;
164        }
165        std::cout << std::endl << std::endl;
166
167        return ret;
168    }
169};
170
171#define LOLUNIT_FIXTURE(FixtureName) \
172    class FixtureName; \
173    template<typename T> struct Make##FixtureName \
174    { \
175        Make##FixtureName() { p = new T(); } \
176        ~Make##FixtureName() { delete p; } \
177        T *p; \
178    }; \
179    Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \
180    static char const *LolUnitFixtureName(FixtureName *p) \
181    { \
182        (void)p; \
183        return #FixtureName; \
184    } \
185    class FixtureName : public lol::Fixture<FixtureName>
186
187#define LOLUNIT_TEST(TestCaseName) \
188    class TestCase##TestCaseName : public TestCase \
189    { \
190    public: \
191        TestCase##TestCaseName() \
192        { \
193            AddTestCase(this, #TestCaseName, \
194                    (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
195        } \
196    }; \
197    TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
198    void TestCaseName()
199
200#define LOLUNIT_ASSERT_GENERIC(message, cond) \
201    do { \
202        m_asserts++; \
203        if (True() && !(cond)) \
204        { \
205            m_errorlog << std::endl << std::endl; \
206            m_errorlog << ++m_failcases << ") test: " \
207                       << LolUnitFixtureName(this) << "::" << m_currentname \
208                       << " (F) line: " << __LINE__ << " " \
209                       << __FILE__ << std::endl; \
210            m_errorlog << "assertion failed" << std::endl; \
211            m_errorlog << "- Expression: " << #cond << std::endl; \
212            m_errorlog << message; \
213            m_failure = true; \
214            return; \
215        } \
216    } while(!True())
217
218#define LOLUNIT_ASSERT_EQUAL_GENERIC(message, a, b) \
219    do { \
220        m_asserts++; \
221        if (True() && (a) != (b)) \
222        { \
223            m_errorlog << std::endl << std::endl; \
224            m_errorlog << ++m_failcases << ") test: " \
225                       << LolUnitFixtureName(this) << "::" << m_currentname \
226                       << " (F) line: " << __LINE__ << " " \
227                       << __FILE__ << std::endl; \
228            m_errorlog << "equality assertion failed" << std::endl; \
229            m_errorlog << "- Expected: " << (a) << std::endl; \
230            m_errorlog << "- Actual  : " << (b) << std::endl; \
231            m_errorlog << message; \
232            m_failure = true; \
233            return; \
234        } \
235    } while(!True())
236
237#define LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC(message, a, b, t) \
238    do { \
239        m_asserts++; \
240        if (True() && fabs((a) - (b)) > fabs((t))) \
241        { \
242            m_errorlog << std::endl << std::endl; \
243            m_errorlog << ++m_failcases << ") test: " \
244                       << LolUnitFixtureName(this) << "::" << m_currentname \
245                       << " (F) line: " << __LINE__ << " " \
246                       << __FILE__ << std::endl; \
247            m_errorlog << "double equality assertion failed" << std::endl; \
248            m_errorlog << "- Expected: " << (a) << std::endl; \
249            m_errorlog << "- Actual  : " << (b) << std::endl; \
250            m_errorlog << "- Delta   : " << (t) << std::endl; \
251            m_errorlog << message; \
252            m_failure = true; \
253            return; \
254        } \
255    } while(!True())
256
257#define LOLUNIT_FAIL(message) \
258    do { \
259        m_asserts++; \
260        m_errorlog << std::endl << std::endl; \
261        m_errorlog << ++m_failcases << ") test: " \
262                   << LolUnitFixtureName(this) << "::" << m_currentname \
263                   << " (F) line: " << __LINE__ << " " \
264                   << __FILE__ << std::endl; \
265        m_errorlog << "forced failure" << std::endl; \
266        m_errorlog << "- " << message << std::endl; \
267        m_failure = true; \
268        return; \
269    } while(!True())
270
271#define LOLUNIT_ASSERT(cond) \
272    LOLUNIT_ASSERT_GENERIC("", cond)
273
274#define LOLUNIT_ASSERT_MESSAGE(message, cond) \
275    LOLUNIT_ASSERT_GENERIC("- " << message << std::endl, cond)
276
277#define LOLUNIT_ASSERT_EQUAL(a, b) \
278    LOLUNIT_ASSERT_EQUAL_GENERIC("", a, b)
279
280#define LOLUNIT_ASSERT_EQUAL_MESSAGE(message, a, b) \
281    LOLUNIT_ASSERT_EQUAL_GENERIC("- " << message << std::endl, a, b)
282
283#define LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) \
284    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("", a, b, t)
285
286#define LOLUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message, a, b, t) \
287    LOLUNIT_ASSERT_DOUBLES_EQUAL_GENERIC("- " << message << std::endl, a, b, t)
288
289} /* namespace lol */
290
291#endif // __LOL_UNIT_H__
292
Note: See TracBrowser for help on using the repository browser.