SpTest.h 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. #ifndef _RVC_SPTEST_H__
  2. #define _RVC_SPTEST_H__
  3. #pragma once
  4. #include "SpBase.h"
  5. #include "SpHelper.h"
  6. #include "SpCatch.h"
  7. #include "SpUtility.h"
  8. #include <vector>
  9. //helper macro for test temporary.
  10. #define TEST_MODE_SWITCH_ON 1
  11. #if (defined(TEST_MODE_SWITCH_ON) && TEST_MODE_SWITCH_ON == 1)
  12. #define IFFAILRET(func) \
  13. do { \
  14. ErrorCodeEnum errorCode = func; \
  15. if (errorCode != Error_Succeed) { \
  16. LogError(Severity_High, Error_Failed, 0,\
  17. CSimpleStringA::Format("Invoke \"" #func "\" failed ec=%s! at file: <%s>, line: %d",\
  18. SpStrError(errorCode), _GetFileName(__FILE__), __LINE__));\
  19. return Error_Failed; \
  20. } \
  21. } while (false)
  22. #define IFFAILBREAK(func) \
  23. do { \
  24. ErrorCodeEnum errorCode = func; \
  25. if (errorCode != Error_Succeed) { \
  26. LogError(Severity_High, Error_Failed, 0,\
  27. CSimpleStringA::Format("Invoke \"" #func "\" failed ec=%s! at file: <%s>, line: %d",\
  28. SpStrError(errorCode), _GetFileName(__FILE__), __LINE__));\
  29. return; \
  30. } \
  31. } while (false)
  32. #define REQUIRE(expr) \
  33. do { \
  34. if (!(expr)) { \
  35. LogError(Severity_High, Error_Failed, 0,\
  36. CSimpleStringA::Format("Expr (\"" #expr "\") requires failed! at file: <%s>, line: %d", \
  37. _GetFileName(__FILE__), __LINE__));\
  38. return Error_Failed; \
  39. } \
  40. } while (false)
  41. #define CHECK(expr) \
  42. do { \
  43. if (!(expr)) { \
  44. LogError(Severity_High, Error_Failed, 0,\
  45. CSimpleStringA::Format("Expr (\"" #expr "\") requires failed! at file: <%s>, line: %d", \
  46. _GetFileName(__FILE__), __LINE__));\
  47. return; \
  48. } \
  49. } while (false)
  50. #define THROW_FATAL(fmt, ...) \
  51. LogError(Severity_High, Error_Failed, 0, \
  52. CSimpleStringA::Format("%s at file: <%s>, line: %d", \
  53. (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
  54. #define THROW_ERROR(fmt, ...) \
  55. LogError(Severity_Middle, Error_Failed, 0, \
  56. CSimpleStringA::Format("%s at file: <%s>, line: %d", \
  57. (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
  58. #define THROW_FAIL(fmt, ...) \
  59. LogError(Severity_Low, Error_Failed, 0, \
  60. CSimpleStringA::Format("%s at file: <%s>, line: %d", \
  61. (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
  62. #define FALTAL(fmt, ...) LogError(Severity_High, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
  63. #define ERR(fmt, ...) LogError(Severity_Middle, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
  64. #define FAIL(fmt, ...) LogError(Severity_Low, Error_Failed, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
  65. #define WARN(fmt, ...) LogWarn(Severity_Middle, Error_Unexpect, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
  66. #define INFO(fmt, ...) LogEvent(Severity_Middle, 0, CSimpleStringA::Format(fmt, ##__VA_ARGS__))
  67. #define RVC_FUNC_TAKEUP_START(func) \
  68. do { SP::Test::Timer consumTimer; \
  69. consumTimer.Start()
  70. #define RVC_FUNC_TAKEUP_DONE(func) \
  71. const double duration = consumTimer.GetElapsedSeconds(); \
  72. LogEvent(Severity_Middle, 0, CSimpleStringA::Format("Invoking \"" #func "\" takes %s secs at file: <%s>, line: %d",\
  73. std::to_string(duration).c_str(), _GetFileName(__FILE__), __LINE__ - 1)); \
  74. } while (false)
  75. #else
  76. #define IFFAILRET(func) ((void)0)
  77. #define IFFAILBREAK(func) ((void)0)
  78. #define REQUIRE(expr) ((void)0)
  79. #define CHECK(expr) ((void)0)
  80. #define THROW_FATAL(fmt, ...) ((void)0)
  81. #define THROW_ERROR(fmt, ...) ((void)0)
  82. #define THROW_FAIL(fmt, ...) ((void)0)
  83. #define FALTAL(fmt, ...) ((void)0)
  84. #define ERR(fmt, ...) ((void)0)
  85. #define FAIL(fmt, ...) ((void)0)
  86. #define WARN(fmt, ...) ((void)0)
  87. #define INFO(fmt, ...) ((void)0)
  88. #define RVC_FUNC_TAKEUP_START(noused) ((void)0)
  89. #define RVC_FUNC_TAKEUP_DONE(noused) ((void)0)
  90. #endif /*defined(TEST_MODE_SWITCH_ON) && TEST_MODE_SWITCH_ON == 1*/
  91. #define REQUIRE_FALSE(expr) \
  92. REQUIRE(!(expr))
  93. #define CHECK_FALSE(expr) \
  94. CHECK(!(expr))
  95. /** Entity's MOCK Context*/
  96. SPBASE_API CSmartPointer<ITransactionContext>
  97. CreateMockTransactionContext(CSmartPointer<IEntityFunction> const& entityFunction);
  98. enum class SPClassType { Entity, FSM };
  99. enum class Tolerate { Strict, Ignore };
  100. #ifdef RVC_OS_WIN
  101. namespace SP {
  102. typedef unsigned long long UInt64;
  103. }
  104. #else
  105. #include <sys/time.h>
  106. #include <stdint.h>
  107. namespace SP {
  108. typedef uint64_t UInt64;
  109. }
  110. #endif // RVC_OS_WIN
  111. namespace SP {
  112. namespace Test {
  113. namespace {
  114. #ifdef RVC_OS_WIN
  115. UInt64 GetCurrentTicks()
  116. {
  117. static UInt64 hz = 0, hzo = 0;
  118. if (!hz) {
  119. QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&hz));
  120. QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&hzo));
  121. }
  122. UInt64 t;
  123. QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t));
  124. return ((t - hzo) * 1000000) / hz;
  125. }
  126. #else
  127. UInt64 GetCurrentTicks()
  128. {
  129. timeval t;
  130. gettimeofday(&t, nullptr);
  131. return static_cast<UInt64>(t.tv_sec) * 1000000ull + static_cast<UInt64>(t.tv_usec);
  132. }
  133. #endif
  134. class Timer {
  135. public:
  136. Timer() : m_ticks(0) {}
  137. void Start()
  138. {
  139. m_ticks = GetCurrentTicks();
  140. }
  141. unsigned int GetElapsedMicroseconds() const {
  142. return static_cast<unsigned int>(GetCurrentTicks() - m_ticks);
  143. }
  144. unsigned int GetElapsedMilliseconds() const {
  145. return static_cast<unsigned int>(GetElapsedMicroseconds() / 1000);
  146. }
  147. double GetElapsedSeconds() const {
  148. return GetElapsedMicroseconds() / 1000000.0;
  149. }
  150. private:
  151. UInt64 m_ticks;
  152. };
  153. }
  154. }
  155. }
  156. struct IMethodTestCase
  157. {
  158. virtual ErrorCodeEnum RunTest() = 0;
  159. virtual ~IMethodTestCase() {};
  160. void BindEntity(CEntityBase* const pEntityT) { pEntityBase = pEntityT; }
  161. CEntityBase* pEntityBase = nullptr;
  162. };
  163. template<typename TClass>
  164. class MethodTestCase : public IMethodTestCase
  165. {
  166. public:
  167. MethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
  168. virtual ErrorCodeEnum RunTest()
  169. {
  170. TClass obj;
  171. TClass* ptr = &obj;
  172. return (ptr->*m_method)();
  173. //return Error_Succeed;
  174. }
  175. private:
  176. virtual ~MethodTestCase() {}
  177. ErrorCodeEnum(TClass::* m_method)();
  178. };
  179. template<typename TClass>
  180. class EntityMethodTestCase : public IMethodTestCase
  181. {
  182. public:
  183. EntityMethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
  184. virtual ErrorCodeEnum RunTest()
  185. {
  186. TClass obj;
  187. TClass* ptr = &obj;
  188. if (pEntityBase == nullptr)
  189. return Error_NotInit;
  190. ptr->MockEntityFunction(pEntityBase->GetFunction());
  191. return (ptr->*m_method)();
  192. }
  193. private:
  194. virtual ~EntityMethodTestCase() {}
  195. ErrorCodeEnum(TClass::* m_method)();
  196. };
  197. template<typename TClass>
  198. class FSMMethodTestCase : public IMethodTestCase
  199. {
  200. public:
  201. FSMMethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
  202. virtual ErrorCodeEnum RunTest()
  203. {
  204. TClass obj;
  205. TClass* ptr = &obj;
  206. if (pEntityBase == nullptr)
  207. return Error_NotInit;
  208. ErrorCodeEnum result = Error_Succeed;
  209. ptr->SetInitState(ptr->GetInitState());
  210. ptr->SetEntityBase(pEntityBase);
  211. result = (ptr->*m_method)();
  212. return result;
  213. }
  214. private:
  215. virtual ~FSMMethodTestCase() {}
  216. ErrorCodeEnum(TClass::* m_method)();
  217. };
  218. using TestFunction = ErrorCodeEnum(*)();
  219. class DefaultFuncTestCase : public IMethodTestCase
  220. {
  221. public:
  222. DefaultFuncTestCase(TestFunction func) :m_func(func) {}
  223. virtual ErrorCodeEnum RunTest() {
  224. return m_func();
  225. }
  226. private:
  227. virtual ~DefaultFuncTestCase() {};
  228. TestFunction m_func;
  229. };
  230. template<typename TEntity, class TReq, class TAns>
  231. struct TwoWayContextTestCaseT : public SpReqAnsContext<TReq, TAns>, public IMethodTestCase
  232. {
  233. using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
  234. using TestTwoWayFunc = ErrorCodeEnum(TEntity::*)(Pointer ctx);
  235. using TestTwoWayFuncVoid = void(TEntity::*)(Pointer ctx);
  236. TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFunc func, TestTwoWayFuncVoid funVoid)
  237. :SpReqAnsContext<TReq, TAns>(CreateMockTransactionContext(nullptr))
  238. , m_pEntity(pEntityT), m_func(func), m_voidFunc(funVoid)
  239. , m_errorCode(Error_IgnoreAll), m_dwUserCode(0) {
  240. }
  241. TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFunc func)
  242. :TwoWayContextTestCaseT(nullptr, func, nullptr) {}
  243. TwoWayContextTestCaseT(TEntity* pEntityT, TestTwoWayFuncVoid func)
  244. :TwoWayContextTestCaseT(nullptr, nullptr, func) {}
  245. TwoWayContextTestCaseT(TestTwoWayFunc func) :TwoWayContextTestCaseT(nullptr, func) { }
  246. TwoWayContextTestCaseT(TestTwoWayFuncVoid func) : TwoWayContextTestCaseT(nullptr, func) { }
  247. virtual void PreTest() {/* user should set context request content at Req structure.*/ }
  248. virtual ErrorCodeEnum PostTest()
  249. {
  250. LOG_FUNCTION();
  251. /*User should check response content Ans's validity
  252. *if detect the value conveied by 'Ans' is not the dream one, return ErrorCode except Error_Succeed
  253. *Or*/return Error_Succeed;
  254. /*Tips: Only if the 'RunTest()' returned Error_Succeed, then this function would be invoked.*/
  255. }
  256. virtual ErrorCodeEnum RunTest()
  257. {
  258. ErrorCodeEnum result = Error_IgnoreAll;
  259. TEntity entityInst;
  260. bool flag = false;
  261. if (m_pEntity == nullptr)
  262. flag = !!(m_pEntity = &entityInst);
  263. if (flag && pEntityBase != nullptr) {
  264. m_pEntity->MockEntityFunction(pEntityBase->GetFunction());
  265. }
  266. PreTest();
  267. if (m_func != nullptr) {
  268. result = (m_pEntity->*m_func)(GetCtx());
  269. }
  270. else if (m_voidFunc != nullptr) {
  271. (m_pEntity->*m_voidFunc)(GetCtx());
  272. result = Error_Succeed;
  273. }
  274. result = (result == Error_Succeed) ? m_errorCode : result;
  275. DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("TwoWayContextTestCaseT->errorCode: %s", SpStrError(result));
  276. if (flag) m_pEntity = nullptr;
  277. return (((result == Error_Succeed) ? PostTest() : result));
  278. }
  279. ErrorCodeEnum Answer(ErrorCodeEnum Error = Error_Succeed) override
  280. {
  281. m_errorCode = Error;
  282. return Error_Succeed;
  283. }
  284. ErrorCodeEnum Answer(ErrorCodeEnum eSysError, DWORD dwUserError) override
  285. {
  286. m_errorCode = eSysError;
  287. m_dwUserCode = dwUserError;
  288. return Error_Succeed;
  289. }
  290. protected:
  291. Pointer GetCtx()
  292. {
  293. Pointer pt = this;
  294. pt.AddRef();
  295. return pt;
  296. }
  297. TestTwoWayFunc m_func;
  298. TestTwoWayFuncVoid m_voidFunc;
  299. TEntity* m_pEntity;
  300. ErrorCodeEnum m_errorCode;
  301. DWORD m_dwUserCode;
  302. };
  303. struct TestCaseStatistics
  304. {
  305. std::size_t totalTestCaseNum;
  306. std::size_t passedTestCaseNum;
  307. std::size_t failureTestCaseNum;
  308. std::size_t ignoreTestCaseNum;
  309. TestCaseStatistics() { Reset(); }
  310. TestCaseStatistics(std::size_t totalNum):totalTestCaseNum(totalNum){ Clear(); }
  311. void Reset()
  312. {
  313. totalTestCaseNum = 0;
  314. passedTestCaseNum = 0;
  315. failureTestCaseNum = 0;
  316. ignoreTestCaseNum = 0;
  317. }
  318. void ConvertFormUserCode(const DWORD& dwUserCode)
  319. {
  320. Reset();
  321. totalTestCaseNum = ((dwUserCode >> 16) & 0x0000FFFF);
  322. failureTestCaseNum = (dwUserCode & 0x0000FFFF);
  323. passedTestCaseNum = totalTestCaseNum - failureTestCaseNum;
  324. }
  325. double FailureRate() const
  326. {
  327. double failed = 1.0 * failureTestCaseNum;
  328. double rate = failed / (double)(totalTestCaseNum);
  329. return rate * 100;
  330. }
  331. void Clear()
  332. {
  333. const std::size_t oldCaseNum = totalTestCaseNum;
  334. Reset();
  335. totalTestCaseNum = oldCaseNum;
  336. }
  337. std::size_t Total() const { return (totalTestCaseNum); }
  338. std::size_t Failures() const { return (failureTestCaseNum); }
  339. bool AllPassed() const { return (failureTestCaseNum == 0); }
  340. };
  341. template<typename T>
  342. class ITestCaseSuite
  343. {
  344. public:
  345. ITestCaseSuite() = default;
  346. virtual ~ITestCaseSuite();
  347. typedef T* TestObjectPointer;
  348. template<class TReq, class TAns>
  349. struct TwoWayMethodTestCaseT : public TwoWayContextTestCaseT<T, TReq, TAns>
  350. {
  351. using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
  352. using TestTwoWayFunc = ErrorCodeEnum(T::*)(Pointer ctx);
  353. using TestTwoWayFuncVoid = void(T::*)(Pointer ctx);
  354. /** constructor*/
  355. TwoWayMethodTestCaseT(T* pEntityT, TestTwoWayFunc testFunc)
  356. :TwoWayContextTestCaseT<T, TReq, TAns>(pEntityT, testFunc)
  357. {/*empty*/}
  358. };
  359. typedef void(T::* TransMethodProto)(CSmartPointer<ITransactionContext> pTransactionContext);
  360. typedef std::vector<TransMethodProto> TestCaseSet;
  361. void AddTransMethod(TransMethodProto entry);
  362. typedef std::vector<IMethodTestCase*> MethodTestCaseSet;
  363. void AddMethodTestCase(IMethodTestCase* methodCastPtr);
  364. protected:
  365. virtual ErrorCodeEnum RunTestCase();
  366. /** user can override this function to add any other test, but do not
  367. invoke 'AddTransMethod' and 'AddMethodTestCase' method at this scope!!!!*/
  368. virtual ErrorCodeEnum AdditionalTest() { return Error_Succeed; }
  369. private:
  370. TestCaseSet m_testCases;
  371. MethodTestCaseSet m_testMethods;
  372. TestCaseStatistics m_testStatistcs;
  373. static CSmartPointer<ITransactionContext> m_spMockTransactionContext;
  374. };
  375. /*!
  376. * [Gifur@2020519]
  377. * declare Transaction context as static type for some reason:
  378. * 1. when creating SpReqAnsContext class object, we must convey a CSmartPointer<ITransactionContext> type param to initialize,
  379. * 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
  380. * hook it to mock real test result without changing any functional code which I really unwill to see it!
  381. * 2. subclass TwoWayMethodTestCaseT inherited from SpReqAnsContext cannot initialize earlier than SpReqAnsContext as children class, so we
  382. * cannot declare a 'CSmartPointer<ITransactionContext>' type member and initialze it first then convey it to SpReqAnsContext.
  383. * 3. multi-thead unsafe !!!
  384. */
  385. template<typename TClass>
  386. CSmartPointer<ITransactionContext> ITestCaseSuite<TClass>::m_spMockTransactionContext \
  387. = CreateMockTransactionContext(nullptr);
  388. template<typename T>
  389. ITestCaseSuite<T>::~ITestCaseSuite()
  390. {
  391. for (auto& r : m_testMethods) {
  392. if (r) {
  393. delete r;
  394. r = NULL;
  395. }
  396. }
  397. }
  398. /* 'TransMethodProto' Prototype: void Entity::Function(CSmartPointer<ITransactionContext> pTransactionContext)
  399. * User should declare and implement it.
  400. */
  401. template<typename TClass>
  402. void ITestCaseSuite<TClass>::AddTransMethod(TransMethodProto entry)
  403. {
  404. m_testCases.push_back(entry);
  405. m_testStatistcs.totalTestCaseNum++;
  406. }
  407. template<typename TClass>
  408. void ITestCaseSuite<TClass>::AddMethodTestCase(IMethodTestCase* methodCastPtr)
  409. {
  410. m_testMethods.push_back(methodCastPtr);
  411. m_testStatistcs.totalTestCaseNum++;
  412. }
  413. template<typename TClass>
  414. ErrorCodeEnum ITestCaseSuite<TClass>::RunTestCase()
  415. {
  416. LOG_FUNCTION();
  417. m_testStatistcs.Clear();
  418. std::size_t testCaseNum = 0;
  419. TestObjectPointer that = static_cast<TestObjectPointer>(this);
  420. if (!m_testCases.empty())
  421. {
  422. auto it = m_testCases.begin();
  423. while (it != m_testCases.end())
  424. {
  425. ++testCaseNum;
  426. DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("Run TestCase#%d...", testCaseNum);
  427. CSmartPointer<ITransactionContext> mock = CreateMockTransactionContext(that->GetFunction());
  428. (that->*(*it))(mock);
  429. DWORD dwUserCode = 0, dwNoUsed = 0;
  430. ErrorCodeEnum result = mock->GetExpireTime(dwUserCode, dwNoUsed);
  431. if (IS_FAILURED(result) && result != Error_IgnoreAll)
  432. {
  433. THROW_ERROR("TestCase#%d test failed return %s(userCode: 0x%X)", testCaseNum, SpStrError(result), dwUserCode);
  434. m_testStatistcs.failureTestCaseNum++;
  435. }
  436. else if (result == Error_IgnoreAll)
  437. {
  438. m_testStatistcs.ignoreTestCaseNum++;
  439. }
  440. else
  441. {
  442. m_testStatistcs.passedTestCaseNum++;
  443. }
  444. it++;
  445. }
  446. }
  447. std::size_t testMethodNum = 0;
  448. if (!m_testMethods.empty())
  449. {
  450. auto it = m_testMethods.begin();
  451. while (it != m_testMethods.end())
  452. {
  453. ++testMethodNum;
  454. DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("Run TestMethod#%d...", testMethodNum);
  455. ErrorCodeEnum result = (*it)->RunTest();
  456. if (IS_FAILURED(result) && result != Error_IgnoreAll)
  457. {
  458. THROW_ERROR("TestMethod#%d test failed return %s", testMethodNum, SpStrError(result));
  459. m_testStatistcs.failureTestCaseNum++;
  460. }
  461. else if (result == Error_IgnoreAll)
  462. {
  463. m_testStatistcs.ignoreTestCaseNum++;
  464. }
  465. else
  466. {
  467. m_testStatistcs.passedTestCaseNum++;
  468. }
  469. it++;
  470. }
  471. }
  472. return m_testStatistcs.AllPassed() ? Error_Failed : Error_Succeed;
  473. }
  474. #define INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1() \
  475. { \
  476. LOG_FUNCTION(); \
  477. ErrorCodeEnum testResult = RunTestCase(); \
  478. if (testResult == Error_Succeed || testResult == Error_IgnoreAll) { \
  479. testResult = AdditionalTest(); \
  480. pTransactionContext->SendAnswer(testResult); \
  481. } else { \
  482. pTransactionContext->SendAnswer(testResult); \
  483. } \
  484. }
  485. #define INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2() \
  486. { \
  487. LOG_FUNCTION(); \
  488. TestConfig defaultConfig{ this->GetEntityName() }; \
  489. std::vector<TestCase> const& allTestCases = GetRegistryHub().GetTestCaseRegistry().getAllTests(defaultConfig); \
  490. TestCaseStatistics testStatistcs(allTestCases.size() ); \
  491. ErrorCodeEnum testResult = Error_Succeed; \
  492. SP::Test::Timer testCaseTimer; \
  493. double duration = 0; \
  494. for (std::vector<TestCase>::const_iterator itStart = allTestCases.begin(), itEnd = allTestCases.end(); itStart != itEnd; ++itStart) { \
  495. TestCaseInfo const& testCaseInfo = itStart->GetTestInfo(); \
  496. try { \
  497. testCaseTimer.Start(); \
  498. testResult = itStart->RunTest(this); \
  499. duration = testCaseTimer.GetElapsedSeconds(); \
  500. } catch(const std::exception& ex) { \
  501. DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("exception occurs: %s", ex.what()); \
  502. testResult = Error_Exception; \
  503. } \
  504. if (testResult != Error_Succeed) { \
  505. ERR("TestCase<%s %s> %s failed the test, return %s.", \
  506. testCaseInfo.strName.c_str(), \
  507. testCaseInfo.strDescription.empty() ? "(No description)" : testCaseInfo.strDescription.c_str(), \
  508. testCaseInfo.lineInfo.ToString().c_str(), SpStrError(testResult)); \
  509. testStatistcs.failureTestCaseNum++; \
  510. } else { \
  511. INFO("TestCase<%s %s> passed test elapsed: %s secs", \
  512. testCaseInfo.strName.c_str(), \
  513. testCaseInfo.strDescription.empty() ? "(No description)" : testCaseInfo.strDescription.c_str(), \
  514. std::to_string(duration).c_str()); \
  515. testStatistcs.passedTestCaseNum++; \
  516. }\
  517. } \
  518. const DWORD dwStatistics = ((((DWORD)(WORD)testStatistcs.Total()) << 16) | (((DWORD)testStatistcs.Failures()) & 0x0000FFFF)); \
  519. if(testStatistcs.Total() == 0) \
  520. pTransactionContext->SendAnswer(Error_IgnoreAll, dwStatistics); \
  521. else if(testStatistcs.AllPassed()) \
  522. pTransactionContext->SendAnswer(Error_Accept, dwStatistics); \
  523. else { \
  524. pTransactionContext->SendAnswer(Error_Failed, dwStatistics); \
  525. } \
  526. }
  527. #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT1(EntityClassName) \
  528. void EntityClassName::OnExam(CSmartPointer<ITransactionContext> pTransactionContext) override \
  529. INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1()
  530. #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE1() \
  531. void OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
  532. INTERNAL_TEST_CAST_ON_EXAM_FUNCTION1()
  533. #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT2(EntityClassName) \
  534. void EntityClassName::OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
  535. INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2()
  536. #define INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE2() \
  537. void OnExam(CSmartPointer<ITransactionContext> pTransactionContext) \
  538. INTERNAL_TEST_CAST_ON_EXAM_FUNCTION2()
  539. #ifdef WITH_BUILD_MODULE_TEST
  540. #define TEST_CASE_OVERRIDE_ON_EXAM_DECLAER() \
  541. INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_DECLARE2()
  542. #define TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT(entityClassName) \
  543. INTERNAL_TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT2(entityClassName)
  544. #else //WITH_BUILD_MODULE_TEST
  545. #define TEST_CASE_OVERRIDE_ON_EXAM_DECLAER()
  546. #define TEST_CASE_OVERRIDE_ON_EXAM_IMPLEMENT(entityClassName)
  547. #endif // WITH_BUILD_MODULE_TEST
  548. //////////////////////////////////////////////////////////////////////////
  549. using namespace SP::Detail;
  550. struct NameAndDesc
  551. {
  552. NameAndDesc(const char* _name = "", const char* _description = "")
  553. : name(_name), description(_description)
  554. {/*empty*/
  555. }
  556. const char* name;
  557. const char* description;
  558. };
  559. class TestCase;
  560. struct TestConfig
  561. {
  562. std::string filterTag;
  563. };
  564. struct ITestCaseRegistry
  565. {
  566. virtual ~ITestCaseRegistry() {}
  567. /** TODO: relate with Entity Name*/
  568. virtual std::vector<TestCase> const& getAllTests(TestConfig const& config) const = 0;
  569. };
  570. struct ITestRegistryHub
  571. {
  572. virtual ~ITestRegistryHub() {}
  573. virtual void RegisterTest(TestCase const& testInfo) = 0;
  574. virtual ITestCaseRegistry const& GetTestCaseRegistry() const = 0;
  575. };
  576. SPBASE_API ITestRegistryHub& GetRegistryHub();
  577. /** TODO: hide*/
  578. SPBASE_API TestCase MakeTestCase(IMethodTestCase* testCase,
  579. std::string const& className,
  580. std::string const& name,
  581. std::string const& desc,
  582. SourceLineInfo const& lineInfo);
  583. SPBASE_API void RegisterTestCase(IMethodTestCase* testCase, char const* className,
  584. NameAndDesc const& nameAndDesc,
  585. SourceLineInfo const& lineInfo);
  586. SPBASE_API void RegisterTestCaseFunction(TestFunction function,
  587. NameAndDesc const& nameAndDesc,
  588. SourceLineInfo const& lineInfo
  589. );
  590. struct TestAutoReg
  591. {
  592. TestAutoReg() {}
  593. ~TestAutoReg() {}
  594. /** for test simple func*/
  595. TestAutoReg(TestFunction function,
  596. SourceLineInfo const& lineInfo,
  597. NameAndDesc const& nameAndDesc
  598. )
  599. {
  600. RegisterTestCaseFunction(function, nameAndDesc, lineInfo);
  601. }
  602. /** for test normal Class*/
  603. template<typename TClass>
  604. TestAutoReg(
  605. ErrorCodeEnum(TClass::* method)(),
  606. char const* lpcszClassName,
  607. NameAndDesc const& nameAndDesc,
  608. SourceLineInfo const& lineInfo)
  609. {
  610. RegisterTestCase(new MethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
  611. }
  612. /** for test entity Class*/
  613. template<typename TClass>
  614. TestAutoReg(
  615. ErrorCodeEnum(TClass::* method)(),
  616. char const* lpcszClassName,
  617. SourceLineInfo const& lineInfo,
  618. NameAndDesc const& nameAndDesc,
  619. SPClassType classType /*for special class from framework, */
  620. )
  621. {
  622. RegisterTestCase(new EntityMethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
  623. }
  624. /** for test FSM Class*/
  625. template<typename TClass>
  626. TestAutoReg(
  627. ErrorCodeEnum(TClass::* method)(),
  628. char const* lpcszClassName,
  629. NameAndDesc const& nameAndDesc,
  630. SourceLineInfo const& lineInfo,
  631. SPClassType classType /*for special class from framework, */
  632. )
  633. {
  634. RegisterTestCase(new FSMMethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
  635. }
  636. TestAutoReg(
  637. IMethodTestCase* testCaseImpl,
  638. char const* lpcszClassName,
  639. NameAndDesc const& nameAndDesc,
  640. SourceLineInfo const& lineInfo)
  641. {
  642. RegisterTestCase(testCaseImpl, lpcszClassName, nameAndDesc, lineInfo);
  643. }
  644. };
  645. struct TestCaseInfo {
  646. TestCaseInfo(std::string const& name, std::string const& className, std::string const& desc, SourceLineInfo const& info)
  647. :strName(name.c_str()), strClassName(className.c_str()),strDescription(desc.c_str()), lineInfo(info) {}
  648. TestCaseInfo(TestCaseInfo const& other)
  649. :TestCaseInfo(other.strName, other.strClassName, other.strDescription, other.lineInfo)
  650. {/*delegrate directory*/}
  651. std::string strName;
  652. std::string strClassName;
  653. std::string strDescription;
  654. SourceLineInfo lineInfo;
  655. };
  656. class TestCase : public TestCaseInfo
  657. {
  658. public:
  659. TestCase(IMethodTestCase* testCase, TestCaseInfo const& info): TestCaseInfo(info), m_testCase(testCase) {}
  660. TestCase(TestCase const& other)
  661. :TestCaseInfo(other), m_testCase(other.m_testCase) {}
  662. ~TestCase() { m_testCase = nullptr; }
  663. TestCaseInfo const& GetTestInfo() const { return *this; }
  664. /** for anonymous case regist*/
  665. TestCase CloneExceptName(std::string const& newName) const {
  666. TestCase newCase(*this);
  667. newCase.strName = newName;
  668. return newCase;
  669. }
  670. ErrorCodeEnum RunTest(CEntityBase* pInvoker) const
  671. {
  672. m_testCase->BindEntity(pInvoker);
  673. return m_testCase->RunTest();
  674. }
  675. private:
  676. CSmartPointer<IMethodTestCase> m_testCase;
  677. };
  678. #define RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line ) name##line
  679. #define RVC_INTERVAL_UNIQUE_NAME_LINE( name, line ) RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line )
  680. #define RVC_INTERVAL_UNIQUE_NAME(name) RVC_INTERVAL_UNIQUE_NAME_LINE(name, __COUNTER__)
  681. #define RVC_INTERVAL_STRINGFY(name) #name
  682. #define INTERNAL_TEST_CASE_NORMAL_CLASS2(TestName, ClassName, ...) \
  683. namespace { \
  684. struct TestName : ClassName { \
  685. ErrorCodeEnum Test(); \
  686. }; \
  687. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  688. } \
  689. ErrorCodeEnum TestName::Test()
  690. #define INTERNAL_TEST_CASE_NORMAL_CLASS(ClassName, ...) \
  691. INTERNAL_TEST_CASE_NORMAL_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
  692. #define INTERNAL_TEST_CASE_ENTITY_CLASS2(TestName, ClassName, ... ) \
  693. namespace { \
  694. struct TestName : public ClassName { \
  695. TestName():m_pEntityFunctionDelegrate(nullptr){}\
  696. CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
  697. void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
  698. ErrorCodeEnum Test(); \
  699. private: \
  700. IEntityFunction* m_pEntityFunctionDelegrate; \
  701. }; \
  702. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__), SPClassType::Entity); \
  703. } \
  704. ErrorCodeEnum TestName::Test()
  705. #define INTERNAL_TEST_CASE_ENTITY_CLASS(ClassName, ...) \
  706. INTERNAL_TEST_CASE_ENTITY_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
  707. #define INTERNAL_TEST_CASE_FSM_CLASS2(TestName, ClassName, ... ) \
  708. namespace { \
  709. struct TestName : public ClassName { \
  710. ErrorCodeEnum Test(); \
  711. void SetInitState(int state) { \
  712. m_iState = state; \
  713. } \
  714. void SetEntityBase(CEntityBase * const pEntityBase) { m_pEntity = pEntityBase; } \
  715. }; \
  716. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO, SPClassType::FSM); \
  717. } \
  718. ErrorCodeEnum TestName::Test()
  719. #define INTERNAL_TEST_CASE_FSM_CLASS(ClassName, ...) \
  720. INTERNAL_TEST_CASE_FSM_CLASS2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
  721. //////////////////////////////////////////////////////////////////////////
  722. #define INTERNAL_RVC_TESTCASE2(TestName, ...) \
  723. static ErrorCodeEnum TestName(); \
  724. namespace { \
  725. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName, SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__)); \
  726. } \
  727. static ErrorCodeEnum TestName()
  728. #define INTERNAL_RVC_TESTCASE(...) INTERNAL_RVC_TESTCASE2(RVC_INTERVAL_UNIQUE_NAME(CVRcnuFtseTduS), __VA_ARGS__)
  729. #include <functional>
  730. //////////////////////////////////////////////////////////////////////////
  731. #define INTERNAL_RVC_TEST_CASE_VOID_CONTEXT2(TestName, SubClassName, ClassName, ...) \
  732. namespace { \
  733. struct SubClassName : public ClassName { \
  734. SubClassName():m_pEntityFunctionDelegrate(nullptr){}\
  735. CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
  736. void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
  737. private: \
  738. IEntityFunction* m_pEntityFunctionDelegrate; \
  739. }; \
  740. struct TestName : SubClassName { \
  741. CSmartPointer<ITransactionContext> mMockTrans; \
  742. TestName():mMockTrans(CreateMockTransactionContext(nullptr)){}; \
  743. ErrorCodeEnum Test() { \
  744. TestContext(mMockTrans); \
  745. DWORD dwUserCode = 0, dwNoUsed = 0; \
  746. ErrorCodeEnum testRes = mMockTrans->GetExpireTime(dwUserCode, dwNoUsed); \
  747. return (testRes == Error_IgnoreAll) ? Error_Succeed : testRes; \
  748. } \
  749. void TestContext(CSmartPointer<ITransactionContext> pTransactionContext); \
  750. }; \
  751. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP_INTERNAL_LINEINFO, NameAndDesc(__VA_ARGS__), SPClassType::Entity); \
  752. } \
  753. void TestName::TestContext(CSmartPointer<ITransactionContext> pTransactionContext)
  754. #define INTERNAL_RVC_TEST_CASE_VOID_CONTEXT(EntityClassName, ...) \
  755. INTERNAL_RVC_TEST_CASE_VOID_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), RVC_INTERVAL_UNIQUE_NAME(CVRssalCbuSytitnE), EntityClassName, __VA_ARGS__)
  756. ///////////////////////////////////////////////////////////////////////////////
  757. template<typename T>
  758. struct Matcher {
  759. Tolerate level = Tolerate::Ignore;
  760. std::string strExpr;
  761. std::function<bool(T const&)> exprFunc;
  762. SourceLineInfo lineInfo;
  763. Matcher(Tolerate const& choice, std::string const& desc, std::function<bool(T const&)>&& expr, SourceLineInfo const& info)
  764. :level(choice), strExpr(desc), exprFunc(expr),lineInfo(info) {}
  765. };
  766. # define RVC_INTERNAL_STRINGIFY(expr) #expr
  767. #define INTERNAL_TEST_CASE_ENTITY_CONTEXT2(TestName, SubEntityClassName, EntityClassName, ServiceName, ContextName, ...) \
  768. namespace { \
  769. struct SubEntityClassName : public EntityClassName { \
  770. SubEntityClassName():m_pEntityFunctionDelegrate(nullptr){}\
  771. CSmartPointer<IEntityFunction> GetFunction() { return m_pEntityFunctionDelegrate; } \
  772. void MockEntityFunction(CSmartPointer<IEntityFunction> const& func) { m_pEntityFunctionDelegrate = func.GetRawPointer(); } \
  773. private: \
  774. IEntityFunction* m_pEntityFunctionDelegrate; \
  775. }; \
  776. struct TestName : public TwoWayContextTestCaseT<SubEntityClassName, ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans> { \
  777. TestName(TestTwoWayFunc func):TwoWayContextTestCaseT(func){ }\
  778. TestName(TestTwoWayFuncVoid func):TwoWayContextTestCaseT(func){ }\
  779. using TwoWayResultType = ServiceName##_##ContextName##_Ans; \
  780. using AnsMatcher = Matcher<TwoWayResultType>; \
  781. using AnsMachers = std::vector< AnsMatcher >; \
  782. AnsMachers matchers; \
  783. void PreTest(); \
  784. ErrorCodeEnum PostTest() { \
  785. ErrorCodeEnum result = Error_Succeed; \
  786. for( AnsMachers::const_iterator it = matchers.begin(), itEnd = matchers.end(); it != itEnd;++it) { \
  787. if (!((it->exprFunc)(Ans))) { \
  788. if(it->level == Tolerate::Strict) { \
  789. ERR("Expr (\" %s \") tests failed !! %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str()); \
  790. result = Error_MisMatched; \
  791. break; \
  792. } else { \
  793. FAIL("Expr (\" %s \") expects failed ! %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str()); \
  794. } \
  795. } \
  796. } \
  797. return result; \
  798. } \
  799. }; \
  800. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)( new TestName(&SubEntityClassName::ContextName) , RVC_INTERVAL_STRINGFY(EntityClassName), NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  801. } \
  802. void TestName::PreTest()
  803. #define INTERNAL_TEST_CASE_ENTITY_CONTEXT(EntityClassName, ServiceName, ContextName, ... ) \
  804. INTERNAL_TEST_CASE_ENTITY_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRtxetnoCtseTduS), RVC_INTERVAL_UNIQUE_NAME(CVRssalCbuSytitnE), EntityClassName, ServiceName, ContextName, __VA_ARGS__)
  805. #define INNER_ANSWER_CHECK(Expression, Description, TolerateLevel) \
  806. do { \
  807. auto f = [](TwoWayResultType const& Ans)->bool { return ( Expression ); }; \
  808. AnsMatcher matchInst(TolerateLevel, std::string(Description), f, SP_INTERNAL_LINEINFO); \
  809. matchers.push_back(matchInst); \
  810. } while (false)
  811. ////////////////////////////Export for user//////////////////////////////////////////////
  812. /** declare it at entity class scope*/
  813. #define ON_ENTITYT_TEST() TEST_CASE_OVERRIDE_ON_EXAM_DECLAER()
  814. /** for test static normal menthod */
  815. #define TEST_CASE_METHOD( ... ) INTERNAL_RVC_TESTCASE( __VA_ARGS__ )
  816. /** for test normal class except for entity and fsm*/
  817. #define TEST_CASE_NORMAL_CLASS(className, ...) INTERNAL_TEST_CASE_NORMAL_CLASS( className, __VA_ARGS__ )
  818. /** for test Entity class, which enable GetFunction() validity*/
  819. #define TEST_CASE_ENTITY_CLASS(className, ...) INTERNAL_TEST_CASE_ENTITY_CLASS( className, __VA_ARGS__ )
  820. /** for test FSM class, which enable GetEntityBase() validity*/
  821. #define TEST_CASE_FSM_CLASS(className, ...) INTERNAL_TEST_CASE_FSM_CLASS( className, __VA_ARGS__ )
  822. #define RVC_TEST_CASE_VOID_CONTEXT(entityClassName, ...) INTERNAL_RVC_TEST_CASE_VOID_CONTEXT( entityClassName, __VA_ARGS__ )
  823. /**deprecate!! please replace with TEST_CASE_ENTITY_CONTEXT*/
  824. #define RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, contextName, __VA_ARGS__)
  825. /** for test Entity's transaction context with two way*/
  826. #define TEST_CASE_ENTITY_CONTEXT(entityClassName, serviceName, contextName, ... ) INTERNAL_TEST_CASE_ENTITY_CONTEXT(entityClassName, serviceName, contextName, __VA_ARGS__ )
  827. /** for check Ans's member validity lightly, only be used at {TEST_CASE_ENTITY_CONTEXT} scope*/
  828. #define ANSWER_CHECK(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Ignore)
  829. /** for check Ans's member validity heavilier, only be used at {TEST_CASE_ENTITY_CONTEXT} scope*/
  830. #define ANSWER_REQUIRE(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Strict)
  831. #endif //_RVC_SPTEST_H__