Browse Source

lolunit: protect against multiple registration of the same fixture type.

legacy
Sam Hocevar sam 13 years ago
parent
commit
2e314ddc2f
1 changed files with 134 additions and 78 deletions
  1. +134
    -78
      src/lol/unit.h

+ 134
- 78
src/lol/unit.h View File

@@ -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)


Loading…
Cancel
Save