|
|
@@ -44,23 +44,17 @@ protected: |
|
|
|
FixtureBase() : m_next(NULL), m_testcases(0), m_failcases(0) {} |
|
|
|
virtual ~FixtureBase() {} |
|
|
|
|
|
|
|
/* The FixtureBase class keeps track of all instanciated children |
|
|
|
* fixtures through this method. */ |
|
|
|
static FixtureBase *GetOrSetTest(FixtureBase *set = NULL) |
|
|
|
static void AddFixture(FixtureBase *fixture) |
|
|
|
{ |
|
|
|
static FixtureBase *head = NULL, *tail = NULL; |
|
|
|
if (set) |
|
|
|
{ |
|
|
|
if (!head) |
|
|
|
head = set; |
|
|
|
if (tail) |
|
|
|
tail->m_next = set; |
|
|
|
tail = set; |
|
|
|
} |
|
|
|
return head; |
|
|
|
/* Protect against several instances of the same Fixture subclass */ |
|
|
|
if (fixture->MarkFixture()) |
|
|
|
return; |
|
|
|
FixtureListHelper(fixture); |
|
|
|
} |
|
|
|
static FixtureBase *FixtureList() { return FixtureListHelper(NULL); } |
|
|
|
|
|
|
|
virtual void runFixture() = 0; |
|
|
|
virtual void RunFixture() = 0; |
|
|
|
virtual bool MarkFixture() = 0; |
|
|
|
|
|
|
|
/* Prevent compiler complaints about unreachable code */ |
|
|
|
static inline bool True() { return true; } |
|
|
@@ -70,23 +64,23 @@ protected: |
|
|
|
int m_asserts, m_failure; |
|
|
|
char const *m_fixturename, *m_currentname; |
|
|
|
std::stringstream m_errorlog, m_context; |
|
|
|
}; |
|
|
|
|
|
|
|
#define LOLUNIT_FIXTURE(FixtureName) \ |
|
|
|
class FixtureName; \ |
|
|
|
template<typename T> struct Make##FixtureName \ |
|
|
|
{ \ |
|
|
|
Make##FixtureName() { p = new T(); } \ |
|
|
|
~Make##FixtureName() { delete p; } \ |
|
|
|
T *p; \ |
|
|
|
}; \ |
|
|
|
Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \ |
|
|
|
static char const *LolUnitFixtureName(FixtureName *p) \ |
|
|
|
{ \ |
|
|
|
(void)p; \ |
|
|
|
return #FixtureName; \ |
|
|
|
} \ |
|
|
|
class FixtureName : public lol::Fixture<FixtureName> |
|
|
|
private: |
|
|
|
/* The FixtureBase class keeps track of all instanciated children |
|
|
|
* fixtures through this method. */ |
|
|
|
static FixtureBase *FixtureListHelper(FixtureBase *set) |
|
|
|
{ |
|
|
|
static FixtureBase *head = NULL, *tail = NULL; |
|
|
|
|
|
|
|
if (set) |
|
|
|
{ |
|
|
|
if (!head) head = set; |
|
|
|
if (tail) tail->m_next = set; |
|
|
|
tail = set; |
|
|
|
} |
|
|
|
return head; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/* |
|
|
|
* This template specialises FixtureBase and provides registration of |
|
|
@@ -97,35 +91,25 @@ template<class T> class Fixture : protected FixtureBase |
|
|
|
public: |
|
|
|
typedef T FixtureClass; |
|
|
|
|
|
|
|
class TestCase |
|
|
|
struct TestCase |
|
|
|
{ |
|
|
|
public: |
|
|
|
void (FixtureClass::* m_fun)(); |
|
|
|
char const *m_testname; |
|
|
|
TestCase *m_next; |
|
|
|
|
|
|
|
static void AddTestCase(TestCase *that, char const *name, |
|
|
|
void (FixtureClass::*fun)()) |
|
|
|
{ |
|
|
|
that->m_fun = fun; |
|
|
|
that->m_testname = name; |
|
|
|
that->m_next = NULL; |
|
|
|
GetOrSetTestCase(that); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Fixture<T>() |
|
|
|
{ |
|
|
|
GetOrSetTest(this); |
|
|
|
AddFixture(this); |
|
|
|
} |
|
|
|
|
|
|
|
/* Run all test cases in this fixture. */ |
|
|
|
virtual void runFixture() |
|
|
|
virtual void RunFixture() |
|
|
|
{ |
|
|
|
m_errorlog.str(""); |
|
|
|
m_testcases = 0; |
|
|
|
m_failcases = 0; |
|
|
|
for (TestCase *c = GetOrSetTestCase(); c; c = c->m_next) |
|
|
|
for (TestCase *c = TestCaseList(); c; c = c->m_next) |
|
|
|
{ |
|
|
|
m_testcases++; |
|
|
|
m_asserts = 0; |
|
|
@@ -139,9 +123,49 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Mark the current fixture type as already registered and return whether |
|
|
|
* it was seen before. */ |
|
|
|
virtual bool MarkFixture() |
|
|
|
{ |
|
|
|
static bool seen = false; |
|
|
|
if (seen) |
|
|
|
{ |
|
|
|
SealFixture(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
seen = true; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/* Manage Fixture sealing. Once SealFixture() has been called, we |
|
|
|
* will no longer accept TestCase registrations. */ |
|
|
|
static void SealFixture() { SealFixtureHelper(true); } |
|
|
|
static bool IsFixtureSealed() { return SealFixtureHelper(false); } |
|
|
|
|
|
|
|
/* Each Fixture class specialisation keeps track of its instanciated |
|
|
|
* test cases through this method. */ |
|
|
|
static TestCase *GetOrSetTestCase(TestCase *set = NULL) |
|
|
|
* test cases. */ |
|
|
|
static void AddTestCase(TestCase *that, char const *name, |
|
|
|
void (FixtureClass::*fun)()) |
|
|
|
{ |
|
|
|
if (IsFixtureSealed()) |
|
|
|
return; |
|
|
|
|
|
|
|
that->m_fun = fun; |
|
|
|
that->m_testname = name; |
|
|
|
that->m_next = NULL; |
|
|
|
TestCaseListHelper(that); |
|
|
|
} |
|
|
|
static TestCase *TestCaseList() { return TestCaseListHelper(NULL); } |
|
|
|
|
|
|
|
private: |
|
|
|
static bool SealFixtureHelper(bool set) |
|
|
|
{ |
|
|
|
static bool sealed = false; |
|
|
|
if (set) sealed = true; |
|
|
|
return sealed; |
|
|
|
} |
|
|
|
|
|
|
|
static TestCase *TestCaseListHelper(TestCase *set) |
|
|
|
{ |
|
|
|
static TestCase *head = NULL, *tail = NULL; |
|
|
|
if (set) |
|
|
@@ -154,19 +178,6 @@ public: |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
#define LOLUNIT_TEST(TestCaseName) \ |
|
|
|
class TestCase##TestCaseName : public TestCase \ |
|
|
|
{ \ |
|
|
|
public: \ |
|
|
|
TestCase##TestCaseName() \ |
|
|
|
{ \ |
|
|
|
AddTestCase(this, #TestCaseName, \ |
|
|
|
(void (FixtureClass::*)()) &FixtureClass::TestCaseName); \ |
|
|
|
} \ |
|
|
|
}; \ |
|
|
|
TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \ |
|
|
|
void TestCaseName() |
|
|
|
|
|
|
|
/* |
|
|
|
* This simple class runs all automatically registered tests and reports |
|
|
|
* on error and success in the standard output. |
|
|
@@ -180,10 +191,10 @@ public: |
|
|
|
std::stringstream errors(""); |
|
|
|
int failcases = 0, testcases = 0; |
|
|
|
|
|
|
|
for (FixtureBase *f = FixtureBase::GetOrSetTest(); f; f = f->m_next) |
|
|
|
for (FixtureBase *f = FixtureBase::FixtureList(); f; f = f->m_next) |
|
|
|
{ |
|
|
|
f->setUp(); |
|
|
|
f->runFixture(); |
|
|
|
f->RunFixture(); |
|
|
|
f->tearDown(); |
|
|
|
|
|
|
|
errors << f->m_errorlog.str(); |
|
|
@@ -221,7 +232,7 @@ public: |
|
|
|
{ \ |
|
|
|
m_errorlog << std::endl << std::endl; \ |
|
|
|
m_errorlog << ++m_failcases << ") test: " \ |
|
|
|
<< LolUnitFixtureName(this) << "::" << m_currentname \ |
|
|
|
<< lol_unit_helper_name(this) << "::" << m_currentname \ |
|
|
|
<< " (F) line: " << __LINE__ << " " \ |
|
|
|
<< __FILE__ << std::endl; \ |
|
|
|
m_errorlog << "assertion failed" << std::endl; \ |
|
|
@@ -230,7 +241,7 @@ public: |
|
|
|
m_failure = true; \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
} while(!True()) |
|
|
|
} while (!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_ASSERT_OP(op, modifier, opdesc, msg, a, b) \ |
|
|
|
do { \ |
|
|
@@ -239,7 +250,7 @@ public: |
|
|
|
{ \ |
|
|
|
m_errorlog << std::endl << std::endl; \ |
|
|
|
m_errorlog << ++m_failcases << ") test: " \ |
|
|
|
<< LolUnitFixtureName(this) << "::" << m_currentname \ |
|
|
|
<< lol_unit_helper_name(this) << "::" << m_currentname \ |
|
|
|
<< " (F) line: " << __LINE__ << " " \ |
|
|
|
<< __FILE__ << std::endl; \ |
|
|
|
m_errorlog << opdesc << " assertion failed" << std::endl; \ |
|
|
@@ -250,7 +261,7 @@ public: |
|
|
|
m_failure = true; \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
} while(!True()) |
|
|
|
} while (!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_MSG(msg) \ |
|
|
|
"- " << msg << std::endl |
|
|
@@ -262,7 +273,7 @@ public: |
|
|
|
{ \ |
|
|
|
m_errorlog << std::endl << std::endl; \ |
|
|
|
m_errorlog << ++m_failcases << ") test: " \ |
|
|
|
<< LolUnitFixtureName(this) << "::" << m_currentname \ |
|
|
|
<< lol_unit_helper_name(this) << "::" << m_currentname \ |
|
|
|
<< " (F) line: " << __LINE__ << " " \ |
|
|
|
<< __FILE__ << std::endl; \ |
|
|
|
m_errorlog << "double equality assertion failed" << std::endl; \ |
|
|
@@ -277,7 +288,61 @@ public: |
|
|
|
m_failure = true; \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
} while(!True()) |
|
|
|
} while (!True()) |
|
|
|
|
|
|
|
/* |
|
|
|
* Public helper macros |
|
|
|
*/ |
|
|
|
|
|
|
|
#define LOLUNIT_FIXTURE(N) \ |
|
|
|
class N; \ |
|
|
|
/* This pattern allows us to statically create a Fixture instance \ |
|
|
|
* before its exact implementation was defined. */ \ |
|
|
|
template<typename T> struct lol_unit_helper_fixture_##N \ |
|
|
|
{ \ |
|
|
|
lol_unit_helper_fixture_##N() { p = new T(); } \ |
|
|
|
~lol_unit_helper_fixture_##N() { delete p; } \ |
|
|
|
T *p; \ |
|
|
|
}; \ |
|
|
|
lol_unit_helper_fixture_##N<N> lol_unit_helper_fixture_##N##_instance; \ |
|
|
|
/* Allow to retrieve the class name without using RTTI and without \ |
|
|
|
* knowing the type of "this". */ \ |
|
|
|
static inline char const *lol_unit_helper_name(N *p) \ |
|
|
|
{ \ |
|
|
|
(void)p; \ |
|
|
|
return #N; \ |
|
|
|
} \ |
|
|
|
/* Now the user can define the implementation */ \ |
|
|
|
class N : public lol::Fixture<N> |
|
|
|
|
|
|
|
#define LOLUNIT_TEST(N) \ |
|
|
|
/* For each test in the fixture, we create an object that will \ |
|
|
|
* automatically register the test method in a list global to the \ |
|
|
|
* specialised fixture. */ \ |
|
|
|
class lol_unit_helper_test_##N : public TestCase \ |
|
|
|
{ \ |
|
|
|
public: \ |
|
|
|
lol_unit_helper_test_##N() \ |
|
|
|
{ \ |
|
|
|
AddTestCase(this, #N, \ |
|
|
|
(void (FixtureClass::*)()) &FixtureClass::N); \ |
|
|
|
} \ |
|
|
|
}; \ |
|
|
|
lol_unit_helper_test_##N lol_unit_helper_test_instance_##N; \ |
|
|
|
void N() |
|
|
|
|
|
|
|
/* |
|
|
|
* Provide context for error messages |
|
|
|
*/ |
|
|
|
|
|
|
|
#define LOLUNIT_SET_CONTEXT(n) \ |
|
|
|
do { \ |
|
|
|
m_context.str(""); \ |
|
|
|
m_context << "- Context : " << #n << " = " << (n) << std::endl; \ |
|
|
|
} while (!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_UNSET_CONTEXT(n) \ |
|
|
|
m_context.str("") |
|
|
|
|
|
|
|
/* |
|
|
|
* Public assert macros |
|
|
@@ -288,7 +353,7 @@ public: |
|
|
|
m_asserts++; \ |
|
|
|
m_errorlog << std::endl << std::endl; \ |
|
|
|
m_errorlog << ++m_failcases << ") test: " \ |
|
|
|
<< LolUnitFixtureName(this) << "::" << m_currentname \ |
|
|
|
<< lol_unit_helper_name(this) << "::" << m_currentname \ |
|
|
|
<< " (F) line: " << __LINE__ << " " \ |
|
|
|
<< __FILE__ << std::endl; \ |
|
|
|
m_errorlog << "forced failure" << std::endl; \ |
|
|
@@ -296,16 +361,7 @@ public: |
|
|
|
m_errorlog << m_context.str(); \ |
|
|
|
m_failure = true; \ |
|
|
|
return; \ |
|
|
|
} while(!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_SET_CONTEXT(n) \ |
|
|
|
do { \ |
|
|
|
m_context.str(""); \ |
|
|
|
m_context << "- Context : " << #n << " = " << (n) << std::endl; \ |
|
|
|
} while(!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_UNSET_CONTEXT(n) \ |
|
|
|
m_context.str("") |
|
|
|
} while (!True()) |
|
|
|
|
|
|
|
#define LOLUNIT_ASSERT(cond) \ |
|
|
|
LOLUNIT_ASSERT_GENERIC("", cond) |
|
|
|