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