1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042 |
- #ifndef _RVC_SPTEST_H__
- #define _RVC_SPTEST_H__
- #pragma once
- #include "SpBase.h"
- #include "SpHelper.h"
- #include "SpCatch.h"
- #include "SpUtility.h"
- #include <vector>
- //helper macro for test temporary.
- #define TEST_MODE_SWITCH_ON 1
- #if (defined(TEST_MODE_SWITCH_ON) && TEST_MODE_SWITCH_ON == 1)
- #define IFFAILRET(func) \
- do { \
- ErrorCodeEnum errorCode = func; \
- if (errorCode != Error_Succeed) { \
- LogError(Severity_High, Error_Failed, 0,\
- CSimpleStringA::Format("Invoke \"" #func "\" failed ec=%s! at file: <%s>, line: %d",\
- SpStrError(errorCode), _GetFileName(__FILE__), __LINE__));\
- return Error_Failed; \
- } \
- } while (false)
- #define IFFAILBREAK(func) \
- do { \
- ErrorCodeEnum errorCode = func; \
- if (errorCode != Error_Succeed) { \
- LogError(Severity_High, Error_Failed, 0,\
- CSimpleStringA::Format("Invoke \"" #func "\" failed ec=%s! at file: <%s>, line: %d",\
- SpStrError(errorCode), _GetFileName(__FILE__), __LINE__));\
- return; \
- } \
- } while (false)
- #define REQUIRE(expr) \
- do { \
- if (!(expr)) { \
- LogError(Severity_High, Error_Failed, 0,\
- CSimpleStringA::Format("Expr (\"" #expr "\") requires failed! at file: <%s>, line: %d", \
- _GetFileName(__FILE__), __LINE__));\
- return Error_Failed; \
- } \
- } while (false)
- #define CHECK(expr) \
- do { \
- if (!(expr)) { \
- LogError(Severity_High, Error_Failed, 0,\
- CSimpleStringA::Format("Expr (\"" #expr "\") requires failed! at file: <%s>, line: %d", \
- _GetFileName(__FILE__), __LINE__));\
- return; \
- } \
- } while (false)
- #define THROW_FATAL(fmt, ...) \
- LogError(Severity_High, Error_Failed, 0, \
- CSimpleStringA::Format("%s at file: <%s>, line: %d", \
- (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
- #define THROW_ERROR(fmt, ...) \
- LogError(Severity_Middle, Error_Failed, 0, \
- CSimpleStringA::Format("%s at file: <%s>, line: %d", \
- (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
- #define THROW_FAIL(fmt, ...) \
- LogError(Severity_Low, Error_Failed, 0, \
- CSimpleStringA::Format("%s at file: <%s>, line: %d", \
- (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
- #define FALTAL(fmt, ...) LogError(Severity_High, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
- #define ERR(fmt, ...) LogError(Severity_Middle, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
- #define FAIL(fmt, ...) LogError(Severity_Low, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
- #define WARN(fmt, ...) LogWarn(Severity_Middle, Error_Unexpect, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
- #define INFO(fmt, ...) LogEvent(Severity_Middle, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
- #define RVC_FUNC_TAKEUP_START(func) \
- do { SP::Test::Timer consumTimer; \
- consumTimer.Start()
- #define RVC_FUNC_TAKEUP_DONE(func) \
- const double duration = consumTimer.GetElapsedSeconds(); \
- LogEvent(Severity_Middle, 0, CSimpleStringA::Format("Invoking \"" #func "\" takes %s secs at file: <%s>, line: %d",\
- std::to_string(duration).c_str(), _GetFileName(__FILE__), __LINE__ - 1)); \
- } while (false)
- #else
- #define IFFAILRET(func) ((void)0)
- #define IFFAILBREAK(func) ((void)0)
- #define REQUIRE(expr) ((void)0)
- #define CHECK(expr) ((void)0)
- #define THROW_FATAL(fmt, ...) ((void)0)
- #define THROW_ERROR(fmt, ...) ((void)0)
- #define THROW_FAIL(fmt, ...) ((void)0)
- #define FALTAL(fmt, ...) ((void)0)
- #define ERR(fmt, ...) ((void)0)
- #define FAIL(fmt, ...) ((void)0)
- #define WARN(fmt, ...) ((void)0)
- #define INFO(fmt, ...) ((void)0)
- #define RVC_FUNC_TAKEUP_START(noused) ((void)0)
- #define RVC_FUNC_TAKEUP_DONE(noused) ((void)0)
- #endif /*defined(TEST_MODE_SWITCH_ON) && TEST_MODE_SWITCH_ON == 1*/
- #define REQUIRE_FALSE(expr) \
- REQUIRE(!(expr))
- #define CHECK_FALSE(expr) \
- CHECK(!(expr))
- /** Entity's MOCK Context*/
- SPBASE_API CSmartPointer<ITransactionContext>
- CreateMockTransactionContext(CSmartPointer<IEntityFunction> const& entityFunction);
- enum class SPClassType { Entity, FSM };
- enum class Tolerate { Strict, Ignore };
- #ifdef RVC_OS_WIN
- namespace SP {
- typedef unsigned long long UInt64;
- }
- #else
- #include <sys/time.h>
- #include <stdint.h>
- namespace SP {
- typedef uint64_t UInt64;
- }
- #endif // RVC_OS_WIN
- namespace SP {
- namespace Test {
- namespace {
- #ifdef RVC_OS_WIN
- UInt64 GetCurrentTicks()
- {
- static UInt64 hz = 0, hzo = 0;
- if (!hz) {
- QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&hz));
- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&hzo));
- }
- UInt64 t;
- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t));
- return ((t - hzo) * 1000000) / hz;
- }
- #else
- UInt64 GetCurrentTicks()
- {
- timeval t;
- gettimeofday(&t, nullptr);
- return static_cast<UInt64>(t.tv_sec) * 1000000ull + static_cast<UInt64>(t.tv_usec);
- }
- #endif
- class Timer {
- public:
- Timer() : m_ticks(0) {}
- void Start()
- {
- m_ticks = GetCurrentTicks();
- }
- unsigned int GetElapsedMicroseconds() const {
- return static_cast<unsigned int>(GetCurrentTicks() - m_ticks);
- }
- unsigned int GetElapsedMilliseconds() const {
- return static_cast<unsigned int>(GetElapsedMicroseconds() / 1000);
- }
- double GetElapsedSeconds() const {
- return GetElapsedMicroseconds() / 1000000.0;
- }
- private:
- UInt64 m_ticks;
- };
- }
- }
- }
- struct IMethodTestCase
- {
- virtual ErrorCodeEnum RunTest() = 0;
- virtual ~IMethodTestCase() {};
- void BindEntity(CEntityBase* const pEntityT) { pEntityBase = pEntityT; }
- CEntityBase* pEntityBase = nullptr;
- };
- template<typename TClass>
- class MethodTestCase : public IMethodTestCase
- {
- public:
- MethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
- virtual ErrorCodeEnum RunTest()
- {
- TClass obj;
- TClass* ptr = &obj;
- return (ptr->*m_method)();
- //return Error_Succeed;
- }
- private:
- virtual ~MethodTestCase() {}
- ErrorCodeEnum(TClass::* m_method)();
- };
- template<typename TClass>
- class EntityMethodTestCase : public IMethodTestCase
- {
- public:
- EntityMethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
- virtual ErrorCodeEnum RunTest()
- {
- TClass obj;
- TClass* ptr = &obj;
- if (pEntityBase == nullptr)
- return Error_NotInit;
- ptr->MockEntityFunction(pEntityBase->GetFunction());
- return (ptr->*m_method)();
- }
- private:
- virtual ~EntityMethodTestCase() {}
- ErrorCodeEnum(TClass::* m_method)();
- };
- template<typename TClass>
- class FSMMethodTestCase : public IMethodTestCase
- {
- public:
- FSMMethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
- virtual ErrorCodeEnum RunTest()
- {
- TClass obj;
- TClass* ptr = &obj;
- if (pEntityBase == nullptr)
- return Error_NotInit;
- ErrorCodeEnum result = Error_Succeed;
-
- ptr->SetInitState(ptr->GetInitState());
- ptr->SetEntityBase(pEntityBase);
- result = (ptr->*m_method)();
- return result;
- }
- private:
- virtual ~FSMMethodTestCase() {}
- ErrorCodeEnum(TClass::* m_method)();
- };
- using TestFunction = ErrorCodeEnum(*)();
- class DefaultFuncTestCase : public IMethodTestCase
- {
- public:
- DefaultFuncTestCase(TestFunction func) :m_func(func) {}
- virtual ErrorCodeEnum RunTest() {
- return m_func();
- }
- private:
- virtual ~DefaultFuncTestCase() {};
- TestFunction m_func;
- };
- template<typename TEntity, class TReq, class TAns>
- struct TwoWayContextTestCaseT : public SpReqAnsContext<TReq, TAns>, public IMethodTestCase
- {
- using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
- using TestTwoWayFunc = ErrorCodeEnum(TEntity::*)(Pointer ctx);
- using TestTwoWayFuncVoid = void(TEntity::*)(Pointer ctx);
- TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFunc func, TestTwoWayFuncVoid funVoid)
- :SpReqAnsContext<TReq, TAns>(CreateMockTransactionContext(nullptr))
- , m_pEntity(pEntityT), m_func(func), m_voidFunc(funVoid)
- , m_errorCode(Error_IgnoreAll), m_dwUserCode(0) {
- }
- TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFunc func)
- :TwoWayContextTestCaseT(nullptr, func, nullptr) {}
- TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFuncVoid func)
- :TwoWayContextTestCaseT(nullptr, nullptr, func) {}
- TwoWayContextTestCaseT(TestTwoWayFunc func) :TwoWayContextTestCaseT(nullptr, func) { }
- TwoWayContextTestCaseT(TestTwoWayFuncVoid func) : TwoWayContextTestCaseT(nullptr, func) { }
- virtual void PreTest() {/* user should set context request content at Req structure.*/ }
- virtual ErrorCodeEnum PostTest()
- {
- LOG_FUNCTION();
- /*User should check response content Ans's validity
- *if detect the value conveied by 'Ans' is not the dream one, return ErrorCode except Error_Succeed
- *Or*/return Error_Succeed;
- /*Tips: Only if the 'RunTest()' returned Error_Succeed, then this function would be invoked.*/
- }
- virtual ErrorCodeEnum RunTest()
- {
- ErrorCodeEnum result = Error_IgnoreAll;
- TEntity entityInst;
- bool flag = false;
- if (m_pEntity == nullptr)
- flag = !!(m_pEntity = &entityInst);
- if (flag && pEntityBase != nullptr) {
- m_pEntity->MockEntityFunction(pEntityBase->GetFunction());
- }
- PreTest();
- if (m_func != nullptr) {
- result = (m_pEntity->*m_func)(GetCtx());
- }
- else if (m_voidFunc != nullptr) {
- (m_pEntity->*m_voidFunc)(GetCtx());
- result = Error_Succeed;
- }
- result = (result == Error_Succeed) ? m_errorCode : result;
- DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("TwoWayContextTestCaseT->errorCode: %s", SpStrError(result));
- if (flag) m_pEntity = nullptr;
- return (((result == Error_Succeed) ? PostTest() : result));
- }
- ErrorCodeEnum Answer(ErrorCodeEnum Error = Error_Succeed) override
- {
- m_errorCode = Error;
- return Error_Succeed;
- }
- ErrorCodeEnum Answer(ErrorCodeEnum eSysError, DWORD dwUserError) override
- {
- m_errorCode = eSysError;
- m_dwUserCode = dwUserError;
- return Error_Succeed;
- }
- protected:
- Pointer GetCtx()
- {
- Pointer pt = this;
- pt.AddRef();
- return pt;
- }
- TestTwoWayFunc m_func;
- TestTwoWayFuncVoid m_voidFunc;
- TEntity* m_pEntity;
- ErrorCodeEnum m_errorCode;
- DWORD m_dwUserCode;
- };
- struct TestCaseStatistics
- {
- std::size_t totalTestCaseNum;
- std::size_t passedTestCaseNum;
- std::size_t failureTestCaseNum;
- std::size_t ignoreTestCaseNum;
- TestCaseStatistics() { Reset(); }
- TestCaseStatistics(std::size_t totalNum):totalTestCaseNum(totalNum){ Clear(); }
- void Reset()
- {
- totalTestCaseNum = 0;
- passedTestCaseNum = 0;
- failureTestCaseNum = 0;
- ignoreTestCaseNum = 0;
- }
- void ConvertFormUserCode(const DWORD& dwUserCode)
- {
- Reset();
- totalTestCaseNum = ((dwUserCode >> 16) & 0x0000FFFF);
- failureTestCaseNum = (dwUserCode & 0x0000FFFF);
- passedTestCaseNum = totalTestCaseNum - failureTestCaseNum;
- }
- double FailureRate() const
- {
- double failed = 1.0 * failureTestCaseNum;
- double rate = failed / (double)(totalTestCaseNum);
- return rate * 100;
- }
- void Clear()
- {
- const std::size_t oldCaseNum = totalTestCaseNum;
- Reset();
- totalTestCaseNum = oldCaseNum;
- }
- std::size_t Total() const { return (totalTestCaseNum); }
- std::size_t Failures() const { return (failureTestCaseNum); }
- bool AllPassed() const { return (failureTestCaseNum == 0); }
- };
- template<typename T>
- class ITestCaseSuite
- {
- public:
- ITestCaseSuite() = default;
- virtual ~ITestCaseSuite();
- typedef T* TestObjectPointer;
- template<class TReq, class TAns>
- struct TwoWayMethodTestCaseT : public TwoWayContextTestCaseT<T, TReq, TAns>
- {
- using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
- using TestTwoWayFunc = ErrorCodeEnum(T::*)(Pointer ctx);
- using TestTwoWayFuncVoid = void(T::*)(Pointer ctx);
- /** constructor*/
- TwoWayMethodTestCaseT(T* pEntityT, TestTwoWayFunc testFunc)
- :TwoWayContextTestCaseT<T, TReq, TAns>(pEntityT, testFunc)
- {/*empty*/}
- };
- typedef void(T::* TransMethodProto)(CSmartPointer<ITransactionContext> pTransactionContext);
- typedef std::vector<TransMethodProto> TestCaseSet;
- void AddTransMethod(TransMethodProto entry);
- typedef std::vector<IMethodTestCase*> MethodTestCaseSet;
- void AddMethodTestCase(IMethodTestCase* methodCastPtr);
- protected:
- virtual ErrorCodeEnum RunTestCase();
- /** user can override this function to add any other test, but do not
- invoke 'AddTransMethod' and 'AddMethodTestCase' method at this scope!!!!*/
- virtual ErrorCodeEnum AdditionalTest() { return Error_Succeed; }
- private:
- TestCaseSet m_testCases;
- MethodTestCaseSet m_testMethods;
- TestCaseStatistics m_testStatistcs;
- static CSmartPointer<ITransactionContext> m_spMockTransactionContext;
- };
- /*!
- * [Gifur@2020519]
- * declare Transaction context as static type for some reason:
- * 1. when creating SpReqAnsContext class object, we must convey a CSmartPointer<ITransactionContext> type param to initialize,
- * if we create it at the same time, we cannot get it anymore because it has been declared as private at SpReqAnsContext, but we must
- * hook it to mock real test result without changing any functional code which I really unwill to see it!
- * 2. subclass TwoWayMethodTestCaseT inherited from SpReqAnsContext cannot initialize earlier than SpReqAnsContext as children class, so we
- * cannot declare a 'CSmartPointer<ITransactionContext>' type member and initialze it first then convey it to SpReqAnsContext.
- * 3. multi-thead unsafe !!!
- */
- template<typename TClass>
- CSmartPointer<ITransactionContext> ITestCaseSuite<TClass>::m_spMockTransactionContext \
- = CreateMockTransactionContext(nullptr);
- template<typename T>
- ITestCaseSuite<T>::~ITestCaseSuite()
- {
- for (auto& r : m_testMethods) {
- if (r) {
- delete r;
- r = NULL;
- }
- }
- }
- /* 'TransMethodProto' Prototype: void Entity::Function(CSmartPointer<ITransactionContext> pTransactionContext)
- * User should declare and implement it.
- */
- template<typename TClass>
- void ITestCaseSuite<TClass>::AddTransMethod(TransMethodProto entry)
- {
- m_testCases.push_back(entry);
- m_testStatistcs.totalTestCaseNum++;
- }
- template<typename TClass>
- void ITestCaseSuite<TClass>::AddMethodTestCase(IMethodTestCase* methodCastPtr)
- {
- m_testMethods.push_back(methodCastPtr);
- m_testStatistcs.totalTestCaseNum++;
- }
- template<typename TClass>
- ErrorCodeEnum ITestCaseSuite<TClass>::RunTestCase()
- {
- LOG_FUNCTION();
- m_testStatistcs.Clear();
- std::size_t testCaseNum = 0;
- TestObjectPointer that = static_cast<TestObjectPointer>(this);
- if (!m_testCases.empty())
- {
- auto it = m_testCases.begin();
- while (it != m_testCases.end())
- {
- ++testCaseNum;
- DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("Run TestCase#%d...", testCaseNum);
- CSmartPointer<ITransactionContext> mock = CreateMockTransactionContext(that->GetFunction());
- (that->*(*it))(mock);
- DWORD dwUserCode = 0, dwNoUsed = 0;
- ErrorCodeEnum result = mock->GetExpireTime(dwUserCode, dwNoUsed);
- if (IS_FAILURED(result) && result != Error_IgnoreAll)
- {
- THROW_ERROR("TestCase#%d test failed return %s(userCode: 0x%X)", testCaseNum, SpStrError(result), dwUserCode);
- m_testStatistcs.failureTestCaseNum++;
- }
- else if (result == Error_IgnoreAll)
- {
- m_testStatistcs.ignoreTestCaseNum++;
- }
- else
- {
- m_testStatistcs.passedTestCaseNum++;
- }
- it++;
- }
- }
- std::size_t testMethodNum = 0;
- if (!m_testMethods.empty())
- {
- auto it = m_testMethods.begin();
- while (it != m_testMethods.end())
- {
- ++testMethodNum;
- DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("Run TestMethod#%d...", testMethodNum);
- ErrorCodeEnum result = (*it)->RunTest();
- if (IS_FAILURED(result) && result != Error_IgnoreAll)
- {
- THROW_ERROR("TestMethod#%d test failed return %s", testMethodNum, SpStrError(result));
- m_testStatistcs.failureTestCaseNum++;
- }
- else if (result == Error_IgnoreAll)
- {
- m_testStatistcs.ignoreTestCaseNum++;
- }
- else
- {
- m_testStatistcs.passedTestCaseNum++;
- }
- it++;
- }
- }
- return m_testStatistcs.AllPassed() ? Error_Failed : Error_Succeed;
- }
- #define INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1() \
- { \
- LOG_FUNCTION(); \
- ErrorCodeEnum testResult = RunTestCase(); \
- if (testResult == Error_Succeed || testResult == Error_IgnoreAll) { \
- testResult = AdditionalTest(); \
- pTransactionContext->SendAnswer(testResult); \
- } else { \
- pTransactionContext->SendAnswer(testResult); \
- } \
- }
- #define INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2() \
- { \
- LOG_FUNCTION(); \
- TestConfig defaultConfig{ this->GetEntityName() }; \
- std::vector<TestCase> const& allTestCases = GetRegistryHub().GetTestCaseRegistry().getAllTests(defaultConfig); \
- TestCaseStatistics testStatistcs(allTestCases.size() ); \
- ErrorCodeEnum testResult = Error_Succeed; \
- SP::Test::Timer testCaseTimer; \
- double duration = 0; \
- for (std::vector<TestCase>::const_iterator itStart = allTestCases.begin(), itEnd = allTestCases.end(); itStart != itEnd; ++itStart) { \
- TestCaseInfo const& testCaseInfo = itStart->GetTestInfo(); \
- try { \
- testCaseTimer.Start(); \
- testResult = itStart->RunTest(this); \
- duration = testCaseTimer.GetElapsedSeconds(); \
- } catch(const std::exception& ex) { \
- DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("exception occurs: %s", ex.what()); \
- testResult = Error_Exception; \
- } \
- if (testResult != Error_Succeed) { \
- ERR("TestCase<%s %s> %s failed the test, return %s.", \
- testCaseInfo.strName.c_str(), \
- testCaseInfo.strDescription.empty() ? "(No description)" : testCaseInfo.strDescription.c_str(), \
- testCaseInfo.lineInfo.ToString().c_str(), SpStrError(testResult)); \
- testStatistcs.failureTestCaseNum++; \
- } else { \
- INFO("TestCase<%s %s> passed test elapsed: %s secs", \
- testCaseInfo.strName.c_str(), \
- testCaseInfo.strDescription.empty() ? "(No description)" : testCaseInfo.strDescription.c_str(), \
- std::to_string(duration).c_str()); \
- testStatistcs.passedTestCaseNum++; \
- }\
- } \
- const DWORD dwStatistics = ((((DWORD)(WORD)testStatistcs.Total()) << 16) | (((DWORD)testStatistcs.Failures()) & 0x0000FFFF)); \
- if(testStatistcs.Total() == 0) \
- pTransactionContext->SendAnswer(Error_IgnoreAll, dwStatistics); \
- else if(testStatistcs.AllPassed()) \
- pTransactionContext->SendAnswer(Error_Accept, dwStatistics); \
- else { \
- pTransactionContext->SendAnswer(Error_Failed, dwStatistics); \
- } \
- }
- #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT1(EntityClassName) \
- void EntityClassName::OnExam(CSmartPointer<ITransactionContext> pTransactionContext) override \
- INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1()
- #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE1() \
- void OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
- INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1()
- #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT2(EntityClassName) \
- void EntityClassName::OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
- INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2()
- #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE2() \
- void OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
- INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2()
- #ifdef WITH_BUILD_MODULE_TEST
- #define TEST_CASE_OVERRIDE_ON_EXAM_DECLAER() \
- INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE2()
- #define TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT(entityClassName) \
- INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT2(entityClassName)
- #else //WITH_BUILD_MODULE_TEST
- #define TEST_CASE_OVERRIDE_ON_EXAM_DECLAER()
- #define TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT(entityClassName)
- #endif // WITH_BUILD_MODULE_TEST
- //////////////////////////////////////////////////////////////////////////
- using namespace SP::Detail;
- struct NameAndDesc
- {
- NameAndDesc(const char* _name = "", const char* _description = "")
- : name(_name), description(_description)
- {/*empty*/
- }
- const char* name;
- const char* description;
- };
- class TestCase;
- struct TestConfig
- {
- std::string filterTag;
- };
- struct ITestCaseRegistry
- {
- virtual ~ITestCaseRegistry() {}
- /** TODO: relate with Entity Name*/
- virtual std::vector<TestCase> const& getAllTests(TestConfig const& config) const = 0;
- };
- struct ITestRegistryHub
- {
- virtual ~ITestRegistryHub() {}
- virtual void RegisterTest(TestCase const& testInfo) = 0;
- virtual ITestCaseRegistry const& GetTestCaseRegistry() const = 0;
- };
- SPBASE_API ITestRegistryHub& GetRegistryHub();
- /** TODO: hide*/
- SPBASE_API TestCase MakeTestCase(IMethodTestCase* testCase,
- std::string const& className,
- std::string const& name,
- std::string const& desc,
- SourceLineInfo const& lineInfo);
- SPBASE_API void RegisterTestCase(IMethodTestCase* testCase, char const* className,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo);
- SPBASE_API void RegisterTestCaseFunction(TestFunction function,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo
- );
- struct TestAutoReg
- {
- TestAutoReg() {}
- ~TestAutoReg() {}
- /** for test simple func*/
- TestAutoReg(TestFunction function,
- SourceLineInfo const& lineInfo,
- NameAndDesc const& nameAndDesc
- )
- {
- RegisterTestCaseFunction(function, nameAndDesc, lineInfo);
- }
- /** for test normal Class*/
- template<typename TClass>
- TestAutoReg(
- ErrorCodeEnum(TClass::* method)(),
- char const* lpcszClassName,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo)
- {
- RegisterTestCase(new MethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
- }
- /** for test entity Class*/
- template<typename TClass>
- TestAutoReg(
- ErrorCodeEnum(TClass::* method)(),
- char const* lpcszClassName,
- SourceLineInfo const& lineInfo,
- NameAndDesc const& nameAndDesc,
- SPClassType classType /*for special class from framework, */
- )
- {
- RegisterTestCase(new EntityMethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
- }
- /** for test FSM Class*/
- template<typename TClass>
- TestAutoReg(
- ErrorCodeEnum(TClass::* method)(),
- char const* lpcszClassName,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo,
- SPClassType classType /*for special class from framework, */
- )
- {
- RegisterTestCase(new FSMMethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
- }
- TestAutoReg(
- IMethodTestCase* testCaseImpl,
- char const* lpcszClassName,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo)
- {
- RegisterTestCase(testCaseImpl, lpcszClassName, nameAndDesc, lineInfo);
- }
- };
- struct TestCaseInfo {
- TestCaseInfo(std::string const& name, std::string const& className, std::string const& desc, SourceLineInfo const& info)
- :strName(name.c_str()), strClassName(className.c_str()),strDescription(desc.c_str()), lineInfo(info) {}
- TestCaseInfo(TestCaseInfo const& other)
- :TestCaseInfo(other.strName, other.strClassName, other.strDescription, other.lineInfo)
- {/*delegrate directory*/}
- std::string strName;
- std::string strClassName;
- std::string strDescription;
- SourceLineInfo lineInfo;
- };
- class TestCase : public TestCaseInfo
- {
- public:
- TestCase(IMethodTestCase* testCase, TestCaseInfo const& info): TestCaseInfo(info), m_testCase(testCase) {}
- TestCase(TestCase const& other)
- :TestCaseInfo(other), m_testCase(other.m_testCase) {}
- ~TestCase() { m_testCase = nullptr; }
- TestCaseInfo const& GetTestInfo() const { return *this; }
- /** for anonymous case regist*/
- TestCase CloneExceptName(std::string const& newName) const {
- TestCase newCase(*this);
- newCase.strName = newName;
- return newCase;
- }
- ErrorCodeEnum RunTest(CEntityBase* pInvoker) const
- {
- m_testCase->BindEntity(pInvoker);
- return m_testCase->RunTest();
- }
- private:
- CSmartPointer<IMethodTestCase> m_testCase;
- };
- #define RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line ) name##line
- #define RVC_INTERVAL_UNIQUE_NAME_LINE( name, line ) RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line )
- #define RVC_INTERVAL_UNIQUE_NAME(name) RVC_INTERVAL_UNIQUE_NAME_LINE(name, __COUNTER__)
- #define RVC_INTERVAL_STRINGFY(name) #name
- #define INTERNAL_TEST_CASE_NORMAL_CLASS2(TestName, ClassName, ...) \
- namespace { \
- struct TestName : ClassName { \
- ErrorCodeEnum Test(); \
- }; \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
- } \
- ErrorCodeEnum TestName::Test()
- #define INTERNAL_TEST_CASE_NORMAL_CLASS(ClassName, ...) \
- INTERNAL_TEST_CASE_NORMAL_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
- #define INTERNAL_TEST_CASE_ENTITY_CLASS2(TestName, ClassName, ... ) \
- namespace { \
- struct TestName : public ClassName { \
- TestName():m_pEntityFunctionDelegrate(nullptr){}\
- CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
- void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
- ErrorCodeEnum Test(); \
- private: \
- IEntityFunction* m_pEntityFunctionDelegrate; \
- }; \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__), SPClassType::Entity); \
- } \
- ErrorCodeEnum TestName::Test()
- #define INTERNAL_TEST_CASE_ENTITY_CLASS(ClassName, ...) \
- INTERNAL_TEST_CASE_ENTITY_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
- #define INTERNAL_TEST_CASE_FSM_CLASS2(TestName, ClassName, ... ) \
- namespace { \
- struct TestName : public ClassName { \
- ErrorCodeEnum Test(); \
- void SetInitState(int state) { \
- m_iState = state; \
- } \
- void SetEntityBase(CEntityBase * const pEntityBase) { m_pEntity = pEntityBase; } \
- }; \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO, SPClassType::FSM); \
- } \
- ErrorCodeEnum TestName::Test()
- #define INTERNAL_TEST_CASE_FSM_CLASS(ClassName, ...) \
- INTERNAL_TEST_CASE_FSM_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
- //////////////////////////////////////////////////////////////////////////
- #define INTERNAL_RVC_TESTCASE2(TestName, ...) \
- static ErrorCodeEnum TestName(); \
- namespace { \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName, SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__)); \
- } \
- static ErrorCodeEnum TestName()
- #define INTERNAL_RVC_TESTCASE(...) INTERNAL_RVC_TESTCASE2(RVC_INTERVAL_UNIQUE_NAME(CVRcnuFtseTduS), __VA_ARGS__)
- #include <functional>
- //////////////////////////////////////////////////////////////////////////
- #define INTERNAL_RVC_TEST_CASE_VOID_CONTEXT2(TestName, SubClassName, ClassName, ...) \
- namespace { \
- struct SubClassName : public ClassName { \
- SubClassName():m_pEntityFunctionDelegrate(nullptr){}\
- CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
- void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
- private: \
- IEntityFunction* m_pEntityFunctionDelegrate; \
- }; \
- struct TestName : SubClassName { \
- CSmartPointer<ITransactionContext> mMockTrans; \
- TestName():mMockTrans(CreateMockTransactionContext(nullptr)){}; \
- ErrorCodeEnum Test() { \
- TestContext(mMockTrans); \
- DWORD dwUserCode = 0, dwNoUsed = 0; \
- ErrorCodeEnum testRes = mMockTrans->GetExpireTime(dwUserCode, dwNoUsed); \
- return (testRes == Error_IgnoreAll) ? Error_Succeed : testRes; \
- } \
- void TestContext(CSmartPointer<ITransactionContext> pTransactionContext); \
- }; \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__), SPClassType::Entity); \
- } \
- void TestName::TestContext(CSmartPointer<ITransactionContext> pTransactionContext)
-
- #define INTERNAL_RVC_TEST_CASE_VOID_CONTEXT(EntityClassName, ...) \
- INTERNAL_RVC_TEST_CASE_VOID_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), RVC_INTERVAL_UNIQUE_NAME(CVRssalCbuSytitnE), EntityClassName, __VA_ARGS__)
- ///////////////////////////////////////////////////////////////////////////////
- template<typename T>
- struct Matcher {
- Tolerate level = Tolerate::Ignore;
- std::string strExpr;
- std::function<bool(T const&)> exprFunc;
- SourceLineInfo lineInfo;
- Matcher(Tolerate const& choice, std::string const& desc, std::function<bool(T const&)>&& expr, SourceLineInfo const& info)
- :level(choice), strExpr(desc), exprFunc(expr),lineInfo(info) {}
- };
- # define RVC_INTERNAL_STRINGIFY(expr) #expr
- #define INTERNAL_TEST_CASE_ENTITY_CONTEXT2(TestName, SubEntityClassName, EntityClassName, ServiceName, ContextName, ...) \
- namespace { \
- struct SubEntityClassName : public EntityClassName { \
- SubEntityClassName():m_pEntityFunctionDelegrate(nullptr){}\
- CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
- void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
- private: \
- IEntityFunction* m_pEntityFunctionDelegrate; \
- }; \
- struct TestName : public TwoWayContextTestCaseT<SubEntityClassName, ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans> { \
- TestName(TestTwoWayFunc func):TwoWayContextTestCaseT(func){ }\
- TestName(TestTwoWayFuncVoid func):TwoWayContextTestCaseT(func){ }\
- using TwoWayResultType = ServiceName##_##ContextName##_Ans; \
- using AnsMatcher = Matcher<TwoWayResultType>; \
- using AnsMachers = std::vector< AnsMatcher >; \
- AnsMachers matchers; \
- void PreTest(); \
- ErrorCodeEnum PostTest() { \
- ErrorCodeEnum result = Error_Succeed; \
- for( AnsMachers::const_iterator it = matchers.begin(), itEnd = matchers.end(); it != itEnd;++it) { \
- if (!((it->exprFunc)(Ans))) { \
- if(it->level == Tolerate::Strict) { \
- ERR("Expr (\" %s \") tests failed !! %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str()); \
- result = Error_MisMatched; \
- break; \
- } else { \
- FAIL("Expr (\" %s \") expects failed ! %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str()); \
- } \
- } \
- } \
- return result; \
- } \
- }; \
- TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)( new TestName(&SubEntityClassName::ContextName) , RVC_INTERVAL_STRINGFY(EntityClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
- } \
- void TestName::PreTest()
-
- #define INTERNAL_TEST_CASE_ENTITY_CONTEXT(EntityClassName, ServiceName, ContextName, ... ) \
- INTERNAL_TEST_CASE_ENTITY_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRtxetnoCtseTduS), RVC_INTERVAL_UNIQUE_NAME(CVRssalCbuSytitnE), EntityClassName, ServiceName, ContextName, __VA_ARGS__)
- #define INNER_ANSWER_CHECK(Expression, Description, TolerateLevel) \
- do { \
- auto f = [](TwoWayResultType const& Ans)->bool { return ( Expression ); }; \
- AnsMatcher matchInst(TolerateLevel, std::string(Description), f, SP_INTERNAL_LINEINFO); \
- matchers.push_back(matchInst); \
- } while (false)
- ////////////////////////////Export for user//////////////////////////////////////////////
- /** declare it at entity class scope*/
- #define ON_ENTITYT_TEST() TEST_CASE_OVERRIDE_ON_EXAM_DECLAER()
- /** for test static normal menthod */
- #define TEST_CASE_METHOD( ... ) INTERNAL_RVC_TESTCASE( __VA_ARGS__ )
- /** for test normal class except for entity and fsm*/
- #define TEST_CASE_NORMAL_CLASS(className, ...) INTERNAL_TEST_CASE_NORMAL_CLASS( className, __VA_ARGS__ )
- /** for test Entity class, which enable GetFunction() validity*/
- #define TEST_CASE_ENTITY_CLASS(className, ...) INTERNAL_TEST_CASE_ENTITY_CLASS( className, __VA_ARGS__ )
- /** for test FSM class, which enable GetEntityBase() validity*/
- #define TEST_CASE_FSM_CLASS(className, ...) INTERNAL_TEST_CASE_FSM_CLASS( className, __VA_ARGS__ )
- #define RVC_TEST_CASE_VOID_CONTEXT(entityClassName, ...) INTERNAL_RVC_TEST_CASE_VOID_CONTEXT( entityClassName, __VA_ARGS__ )
- /**deprecate!! please replace with TEST_CASE_ENTITY_CONTEXT*/
- #define RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, contextName, __VA_ARGS__)
- /** for test Entity's transaction context with two way*/
- #define TEST_CASE_ENTITY_CONTEXT(entityClassName, serviceName, contextName, ... ) INTERNAL_TEST_CASE_ENTITY_CONTEXT(entityClassName, serviceName, contextName, __VA_ARGS__ )
- /** for check Ans's member validity lightly, only be used at {TEST_CASE_ENTITY_CONTEXT} scope*/
- #define ANSWER_CHECK(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Ignore)
- /** for check Ans's member validity heavilier, only be used at {TEST_CASE_ENTITY_CONTEXT} scope*/
- #define ANSWER_REQUIRE(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Strict)
- #endif //_RVC_SPTEST_H__
|