Changeset 936


Ignore:
Timestamp:
Sep 9, 2011, 4:39:00 PM (8 years ago)
Author:
sam
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/lol/unit.h

    r934 r936  
    4545    virtual ~FixtureBase() {}
    4646
    47     /* The FixtureBase class keeps track of all instanciated children
    48      * fixtures through this method. */
    49     static FixtureBase *GetOrSetTest(FixtureBase *set = NULL)
    50     {
    51         static FixtureBase *head = NULL, *tail = NULL;
    52         if (set)
    53         {
    54             if (!head)
    55                 head = set;
    56             if (tail)
    57                 tail->m_next = set;
    58             tail = set;
    59         }
    60         return head;
    61     }
    62 
    63     virtual void runFixture() = 0;
     47    static void AddFixture(FixtureBase *fixture)
     48    {
     49        /* Protect against several instances of the same Fixture subclass */
     50        if (fixture->MarkFixture())
     51            return;
     52        FixtureListHelper(fixture);
     53    }
     54    static FixtureBase *FixtureList() { return FixtureListHelper(NULL); }
     55
     56    virtual void RunFixture() = 0;
     57    virtual bool MarkFixture() = 0;
    6458
    6559    /* Prevent compiler complaints about unreachable code */
     
    7165    char const *m_fixturename, *m_currentname;
    7266    std::stringstream m_errorlog, m_context;
     67
     68private:
     69    /* The FixtureBase class keeps track of all instanciated children
     70     * fixtures through this method. */
     71    static FixtureBase *FixtureListHelper(FixtureBase *set)
     72    {
     73        static FixtureBase *head = NULL, *tail = NULL;
     74
     75        if (set)
     76        {
     77            if (!head) head = set;
     78            if (tail) tail->m_next = set;
     79            tail = set;
     80        }
     81        return head;
     82    }
    7383};
    74 
    75 #define LOLUNIT_FIXTURE(FixtureName) \
    76     class FixtureName; \
    77     template<typename T> struct Make##FixtureName \
    78     { \
    79         Make##FixtureName() { p = new T(); } \
    80         ~Make##FixtureName() { delete p; } \
    81         T *p; \
    82     }; \
    83     Make##FixtureName<FixtureName> lol_unit_fixture_##FixtureName; \
    84     static char const *LolUnitFixtureName(FixtureName *p) \
    85     { \
    86         (void)p; \
    87         return #FixtureName; \
    88     } \
    89     class FixtureName : public lol::Fixture<FixtureName>
    9084
    9185/*
     
    9892    typedef T FixtureClass;
    9993
    100     class TestCase
    101     {
    102     public:
     94    struct TestCase
     95    {
    10396        void (FixtureClass::* m_fun)();
    10497        char const *m_testname;
    10598        TestCase *m_next;
    106 
    107         static void AddTestCase(TestCase *that, char const *name,
    108                                 void (FixtureClass::*fun)())
    109         {
    110             that->m_fun = fun;
    111             that->m_testname = name;
    112             that->m_next = NULL;
    113             GetOrSetTestCase(that);
    114         }
    11599    };
    116100
    117101    Fixture<T>()
    118102    {
    119         GetOrSetTest(this);
     103        AddFixture(this);
    120104    }
    121105
    122106    /* Run all test cases in this fixture. */
    123     virtual void runFixture()
     107    virtual void RunFixture()
    124108    {
    125109        m_errorlog.str("");
    126110        m_testcases = 0;
    127111        m_failcases = 0;
    128         for (TestCase *c = GetOrSetTestCase(); c; c = c->m_next)
     112        for (TestCase *c = TestCaseList(); c; c = c->m_next)
    129113        {
    130114            m_testcases++;
     
    140124    }
    141125
     126    /* Mark the current fixture type as already registered and return whether
     127     * it was seen before. */
     128    virtual bool MarkFixture()
     129    {
     130        static bool seen = false;
     131        if (seen)
     132        {
     133            SealFixture();
     134            return true;
     135        }
     136        seen = true;
     137        return false;
     138    }
     139
     140    /* Manage Fixture sealing. Once SealFixture() has been called, we
     141     * will no longer accept TestCase registrations. */
     142    static void SealFixture() { SealFixtureHelper(true); }
     143    static bool IsFixtureSealed() { return SealFixtureHelper(false); }
     144
    142145    /* Each Fixture class specialisation keeps track of its instanciated
    143      * test cases through this method. */
    144     static TestCase *GetOrSetTestCase(TestCase *set = NULL)
     146     * test cases. */
     147    static void AddTestCase(TestCase *that, char const *name,
     148                            void (FixtureClass::*fun)())
     149    {
     150        if (IsFixtureSealed())
     151            return;
     152
     153        that->m_fun = fun;
     154        that->m_testname = name;
     155        that->m_next = NULL;
     156        TestCaseListHelper(that);
     157    }
     158    static TestCase *TestCaseList() { return TestCaseListHelper(NULL); }
     159
     160private:
     161    static bool SealFixtureHelper(bool set)
     162    {
     163        static bool sealed = false;
     164        if (set) sealed = true;
     165        return sealed;
     166    }
     167
     168    static TestCase *TestCaseListHelper(TestCase *set)
    145169    {
    146170        static TestCase *head = NULL, *tail = NULL;
     
    154178    }
    155179};
    156 
    157 #define LOLUNIT_TEST(TestCaseName) \
    158     class TestCase##TestCaseName : public TestCase \
    159     { \
    160     public: \
    161         TestCase##TestCaseName() \
    162         { \
    163             AddTestCase(this, #TestCaseName, \
    164                     (void (FixtureClass::*)()) &FixtureClass::TestCaseName); \
    165         } \
    166     }; \
    167     TestCase##TestCaseName lol_unit_test_case_##TestCaseName; \
    168     void TestCaseName()
    169180
    170181/*
     
    181192        int failcases = 0, testcases = 0;
    182193
    183         for (FixtureBase *f = FixtureBase::GetOrSetTest(); f; f = f->m_next)
     194        for (FixtureBase *f = FixtureBase::FixtureList(); f; f = f->m_next)
    184195        {
    185196            f->setUp();
    186             f->runFixture();
     197            f->RunFixture();
    187198            f->tearDown();
    188199
     
    222233            m_errorlog << std::endl << std::endl; \
    223234            m_errorlog << ++m_failcases << ") test: " \
    224                        << LolUnitFixtureName(this) << "::" << m_currentname \
     235                       << lol_unit_helper_name(this) << "::" << m_currentname \
    225236                       << " (F) line: " << __LINE__ << " " \
    226237                       << __FILE__ << std::endl; \
     
    231242            return; \
    232243        } \
    233     } while(!True())
     244    } while (!True())
    234245
    235246#define LOLUNIT_ASSERT_OP(op, modifier, opdesc, msg, a, b) \
     
    240251            m_errorlog << std::endl << std::endl; \
    241252            m_errorlog << ++m_failcases << ") test: " \
    242                        << LolUnitFixtureName(this) << "::" << m_currentname \
     253                       << lol_unit_helper_name(this) << "::" << m_currentname \
    243254                       << " (F) line: " << __LINE__ << " " \
    244255                       << __FILE__ << std::endl; \
     
    251262            return; \
    252263        } \
    253     } while(!True())
     264    } while (!True())
    254265
    255266#define LOLUNIT_MSG(msg) \
     
    263274            m_errorlog << std::endl << std::endl; \
    264275            m_errorlog << ++m_failcases << ") test: " \
    265                        << LolUnitFixtureName(this) << "::" << m_currentname \
     276                       << lol_unit_helper_name(this) << "::" << m_currentname \
    266277                       << " (F) line: " << __LINE__ << " " \
    267278                       << __FILE__ << std::endl; \
     
    278289            return; \
    279290        } \
    280     } while(!True())
     291    } while (!True())
     292
     293/*
     294 * Public helper macros
     295 */
     296
     297#define LOLUNIT_FIXTURE(N) \
     298    class N; \
     299    /* This pattern allows us to statically create a Fixture instance \
     300     * before its exact implementation was defined. */ \
     301    template<typename T> struct lol_unit_helper_fixture_##N \
     302    { \
     303        lol_unit_helper_fixture_##N() { p = new T(); } \
     304        ~lol_unit_helper_fixture_##N() { delete p; } \
     305        T *p; \
     306    }; \
     307    lol_unit_helper_fixture_##N<N> lol_unit_helper_fixture_##N##_instance; \
     308    /* Allow to retrieve the class name without using RTTI and without \
     309     * knowing the type of "this". */ \
     310    static inline char const *lol_unit_helper_name(N *p) \
     311    { \
     312        (void)p; \
     313        return #N; \
     314    } \
     315    /* Now the user can define the implementation */ \
     316    class N : public lol::Fixture<N>
     317
     318#define LOLUNIT_TEST(N) \
     319    /* For each test in the fixture, we create an object that will \
     320     * automatically register the test method in a list global to the \
     321     * specialised fixture. */ \
     322    class lol_unit_helper_test_##N : public TestCase \
     323    { \
     324    public: \
     325        lol_unit_helper_test_##N() \
     326        { \
     327            AddTestCase(this, #N, \
     328                        (void (FixtureClass::*)()) &FixtureClass::N); \
     329        } \
     330    }; \
     331    lol_unit_helper_test_##N lol_unit_helper_test_instance_##N; \
     332    void N()
     333
     334/*
     335 * Provide context for error messages
     336 */
     337
     338#define LOLUNIT_SET_CONTEXT(n) \
     339    do { \
     340        m_context.str(""); \
     341        m_context << "- Context : " << #n << " = " << (n) << std::endl; \
     342    } while (!True())
     343
     344#define LOLUNIT_UNSET_CONTEXT(n) \
     345    m_context.str("")
    281346
    282347/*
     
    289354        m_errorlog << std::endl << std::endl; \
    290355        m_errorlog << ++m_failcases << ") test: " \
    291                    << LolUnitFixtureName(this) << "::" << m_currentname \
     356                   << lol_unit_helper_name(this) << "::" << m_currentname \
    292357                   << " (F) line: " << __LINE__ << " " \
    293358                   << __FILE__ << std::endl; \
     
    297362        m_failure = true; \
    298363        return; \
    299     } while(!True())
    300 
    301 #define LOLUNIT_SET_CONTEXT(n) \
    302     do { \
    303         m_context.str(""); \
    304         m_context << "- Context : " << #n << " = " << (n) << std::endl; \
    305     } while(!True())
    306 
    307 #define LOLUNIT_UNSET_CONTEXT(n) \
    308     m_context.str("")
     364    } while (!True())
    309365
    310366#define LOLUNIT_ASSERT(cond) \
Note: See TracChangeset for help on using the changeset viewer.