From e3dea8e38d183acb1b8d74ffcdc085df6a5e3467 Mon Sep 17 00:00:00 2001 From: lymslive <403708621@qq.com> Date: Fri, 26 Apr 2024 22:29:13 +0800 Subject: [PATCH] feat: test suite class static setupFirst and teardownLast #3 #10 --- include/classtast.hpp | 186 +++++++++++++++++++++++++++++++++++++-- include/tinytast.hpp | 5 +- utest/test-classtast.cpp | 17 +++- 3 files changed, 200 insertions(+), 8 deletions(-) diff --git a/include/classtast.hpp b/include/classtast.hpp index 4d1f9e8..44562d9 100644 --- a/include/classtast.hpp +++ b/include/classtast.hpp @@ -10,6 +10,10 @@ #include "tinytast.hpp" +#if __cplusplus >= 201103L +#include +#endif + namespace tast { @@ -17,23 +21,193 @@ namespace tast /// It is encouraged overwrite `setup()` for initial code for each test case, /// and `teardown()` for any clean up code. The default constructor and /// destructor can also be used. +/// The static `setupFirst()` will be called when the first test case of this +/// suite is executed, and such only called once. The static `teardownLast()` +/// is called after the last test case finished. +/// When compiled with C++11 or above, there is no need to derive from this +/// base class, and any of the four methods can be omitted if no useful +/// action to take. struct CTastSuite { void setup() {} void teardown() {} void run() {} + static void setupFirst() {} + static void teardownLast() {} }; -/// The test run function for that derived from a test suite class. +/// Helper class for each custome test suite class or file unit or namespace +/// to track how many test cases in it have been defined and runned. template -void tast_suite_run() +struct CTastCounter +{ + int tastCount; + int runCount; + CTastCounter() : tastCount(0), runCount(0) {} + + static CTastCounter* GetInstance() + { + static CTastCounter instance; + return &instance; + } + + struct Increase + { + Increase(bool autoRun) + { + if (autoRun) + { + CTastCounter::GetInstance()->tastCount++; + } + } + }; +}; + +template +void run_suite_setup_uncheck(suiteT* ptr) +{ + static bool run = false; + if (run) { return; } + run = true; + suiteT::setupFirst(); +} + +template +void run_suite_teardown_uncheck(suiteT* ptr) +{ + if (G_TASTMGR->GetTastRun()->m_autoRun == false) + { + return; + } + CTastCounter* pInstance = CTastCounter::GetInstance(); + pInstance->runCount++; + if (pInstance->runCount >= pInstance->tastCount) + { + suiteT::teardownLast(); + } +} + +#if __cplusplus >= 201103L + +template +struct HasSuiteSetup +{ + template + static constexpr auto check(U*) -> decltype(&U::setupFirst, std::true_type{}); + template + static constexpr std::false_type check(...); + static constexpr bool value = decltype(check(nullptr))::value; +}; + +template +struct HasSuiteTeardown +{ + template + static constexpr auto check(U*) -> decltype(&U::teardownLast, std::true_type{}); + template + static constexpr std::false_type check(...); + static constexpr bool value = decltype(check(nullptr))::value; +}; + +template +struct HasTestSetup +{ + template + static constexpr auto check(U*) -> decltype(&U::setup, std::true_type{}); + template + static constexpr std::false_type check(...); + static constexpr bool value = decltype(check(nullptr))::value; +}; + +template +struct HasTestTeardown +{ + template + static constexpr auto check(U*) -> decltype(&U::teardown, std::true_type{}); + template + static constexpr std::false_type check(...); + static constexpr bool value = decltype(check(nullptr))::value; +}; + +template +typename std::enable_if::value, void>::type +/*void*/ run_suite_setup(suiteT* ptr) +{ + run_suite_setup_uncheck(ptr); +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_suite_setup(suiteT* ptr) +{ +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_suite_teardown(suiteT* ptr) +{ + run_suite_teardown_uncheck(ptr); +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_suite_teardown(suiteT* ptr) +{ +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_test_setup(testT& self) { - suiteT self; self.setup(); - self.run(); +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_test_setup(testT& self) +{ +} + +template +typename std::enable_if::value, void>::type +/*void*/ run_test_teardown(testT& self) +{ self.teardown(); } +template +typename std::enable_if::value, void>::type +/*void*/ run_test_teardown(testT& self) +{ +} + +#endif + +/// The test run function for that derived from a test suite class. +template +void tast_suite_run() +{ +#if __cplusplus >= 201103L + run_suite_setup(nullptr); + { + testT self; + run_test_setup(self); + self.run(); + run_test_teardown(self); + } + run_suite_teardown(nullptr); +#else + run_suite_setup_uncheck(NULL); + { + testT self; + self.setup(); + self.run(); + self.teardown(); + } + run_suite_teardown_uncheck(NULL); +#endif +} + } /* tast:: */ /// Concate full test case name belong some test suite. @@ -41,12 +215,14 @@ void tast_suite_run() /// Concate full test case class identifier belong some test suite. #define STAST_CLASS(suite, name) CTast_ ## suite ## _ ## name #define STAST_BUILDER(suite, name) tast_ ## suite ## _ ## name ## _builder -#define STAST_RUNNER(suite, name) tast::tast_suite_run +#define STAST_COUNTER(suite, name) tast_ ## suite ## _ ## name ## _counter +#define STAST_RUNNER(suite, name) tast::tast_suite_run /// Define a test case belong to some suite. #define DEC_TAST_IMPL(suite, name, autoRun, ...) \ struct STAST_CLASS(suite, name) : public suite { void run(); }; \ static tast::CTastBuilder STAST_BUILDER(suite,name)(STAST_RUNNER(suite,name), STAST_NAME(suite,name), __FILE__, __LINE__, autoRun, ## __VA_ARGS__); \ + static tast::CTastCounter::Increase STAST_COUNTER(suite,name)(autoRun); \ void STAST_CLASS(suite, name)::run() /// Similar `DEF_TAST` but for custome test suite class. diff --git a/include/tinytast.hpp b/include/tinytast.hpp index 9d3e6ea..d651bd4 100644 --- a/include/tinytast.hpp +++ b/include/tinytast.hpp @@ -175,6 +175,7 @@ class CTastMgr : public CTinyCli typedef void (*PrintFun)(const char* str); PrintFun m_fnPrint; //< custome print function for output + const CTastCase* m_tastRun; //< the current running test case friend class CStatement; friend class CTastBuilder; @@ -187,12 +188,13 @@ class CTastMgr : public CTinyCli CTastMgr() : m_beginTime(0), m_endTime(0), m_currentFail(0), m_passedCase(0), m_failedCase(0), - m_coutMask(COUT_MASK_ALL), m_fnPrint(NULL) + m_coutMask(COUT_MASK_ALL), m_fnPrint(NULL), m_tastRun(NULL) { m_tastPool.reserve(128); } const std::vector& GetTastPool() const { return m_tastPool; } + const CTastCase* GetTastRun() const { return m_tastRun; } public: // output void CoutEnable (int bit) { m_coutMask = m_coutMask | bit; } @@ -306,6 +308,7 @@ class CTastMgr : public CTinyCli if (bOnlyAuto && !tast.m_autoRun) { return; } PreRunTast(tast.m_name); + m_tastRun = &tast; tast.m_run(); PostRunTast(tast.m_name); } diff --git a/utest/test-classtast.cpp b/utest/test-classtast.cpp index cd1a49f..84bb908 100644 --- a/utest/test-classtast.cpp +++ b/utest/test-classtast.cpp @@ -14,6 +14,16 @@ struct MySuite : public tast::CTastSuite std::string strValue; SComplex complexValue; + static void setupFirst() + { + DESC("MySuite setupFirst"); + } + + static void teardownLast() + { + DESC("MySuite teardownLast"); + } + MySuite() { DESC("MySuite constructor"); @@ -24,13 +34,13 @@ struct MySuite : public tast::CTastSuite DESC("MySuite destructor"); } - virtual void setup() + void setup() { DESC("MySuite::setup"); strValue = "abcdefg"; } - virtual void teardown() + void teardown() { DESC("MySuite::teardown"); } @@ -155,3 +165,6 @@ DEF_TAST(tastargv, "test argv managemant") COUT(argv.BindValue(dnum, "section.xyz"), true); COUT(dnum, 45.6); } + +#if __cplusplus >= 201103L +#endif