SpTest.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. #ifndef _RVC_SPTEST_H__
  2. #define _RVC_SPTEST_H__
  3. #pragma once
  4. #include "SpBase.h"
  5. #include "SpHelper.h"
  6. #include "SpComm.hpp"
  7. #include "SpUtility.hpp"
  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("expression (\"" #expr "\") requires failed! at file: <%s>, line: %d", _GetFileName(__FILE__), __LINE__));\
  37. return Error_Failed; \
  38. } \
  39. } while (false)
  40. #define CHECK(expr) \
  41. do { \
  42. if (!(expr)) { \
  43. LogError(Severity_High, Error_Failed, 0,\
  44. CSimpleStringA::Format("expression (\"" #expr "\") requires failed! at file: <%s>, line: %d", _GetFileName(__FILE__), __LINE__));\
  45. return; \
  46. } \
  47. } while (false)
  48. #define THROW_FATAL(fmt, ...) \
  49. LogError(Severity_High, Error_Failed, 0, \
  50. CSimpleStringA::Format("%s at file: <%s>, line: %d", \
  51. (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
  52. #define THROW_ERROR(fmt, ...) \
  53. LogError(Severity_Middle, Error_Failed, 0, \
  54. CSimpleStringA::Format("%s at file: <%s>, line: %d", \
  55. (LPCTSTR)CSimpleStringA::Format(fmt, ##__VA_ARGS__), _GetFileName(__FILE__), __LINE__))
  56. #else
  57. #define IFFAILRET(func) ((void)0)
  58. #define IFFAILBREAK(func) ((void)0)
  59. #define REQUIRE(expr) ((void)0)
  60. #define CHECK(expr) ((void)0)
  61. #define THROW_FATAL(fmt, ...) ((void)0)
  62. #define THROW_ERROR(fmt, ...) ((void)0)
  63. #endif /*defined(TEST_MODE_SWITCH_ON) && TEST_MODE_SWITCH_ON == 1*/
  64. #define REQUIRE_FALSE(expr) \
  65. REQUIRE(!(expr))
  66. #define CHECK_FALSE(expr) \
  67. CHECK(!(expr))
  68. /** Entity's MOCK Context*/
  69. SPBASE_API CSmartPointer<ITransactionContext>
  70. CreateMockTransactionContext(CSmartPointer<IEntityFunction> entityFunction);
  71. /** Test Case */
  72. //struct TestCaseService_RunTest_Req
  73. //{
  74. // CSimpleStringA strParam;
  75. //
  76. // void Serialize(SpBuffer& Buf)
  77. // {
  78. // auto& buf = Buf & strParam;
  79. // }
  80. //
  81. //};
  82. //
  83. //struct TestCaseService_RunTest_Ans
  84. //{
  85. // DWORD dwUserCode;
  86. //
  87. // void Serialize(SpBuffer& Buf)
  88. // {
  89. // auto& buf = Buf & dwUserCode;
  90. // }
  91. //};
  92. //
  93. //void SimulateTestCastEndPoint(SpReqAnsContext< TestCaseService_RunTest_Req, TestCaseService_RunTest_Ans>::Pointer ctx)
  94. //{
  95. // ctx->Answer(Error_Failed);
  96. //}
  97. struct IMethodTestCase
  98. {
  99. virtual ErrorCodeEnum RunTest() = 0;
  100. virtual ~IMethodTestCase() {};
  101. };
  102. struct TestCaseStatistics
  103. {
  104. DWORD dwTotalTestCaseNum;
  105. DWORD dwAcceptTestCaseNum;
  106. DWORD dwFailureTestCaseNum;
  107. DWORD dwIgnoreTestCaseNum;
  108. TestCaseStatistics() { Reset(); }
  109. void Reset()
  110. {
  111. dwTotalTestCaseNum = 0;
  112. dwAcceptTestCaseNum = 0;
  113. dwFailureTestCaseNum = 0;
  114. dwIgnoreTestCaseNum = 0;
  115. }
  116. void Clear()
  117. {
  118. const DWORD dwCaseNum = dwTotalTestCaseNum;
  119. Reset();
  120. dwTotalTestCaseNum = dwCaseNum;
  121. }
  122. };
  123. template<typename T>
  124. class ITestCaseSuite
  125. {
  126. public:
  127. ITestCaseSuite() = default;
  128. virtual ~ITestCaseSuite();
  129. typedef T* TestObjectPointer;
  130. typedef void(T::* TestCaseEntry)(CSmartPointer<ITransactionContext> pTransactionContext);
  131. template<class TReq, class TAns>
  132. struct MethodTestCaseT : public SpReqAnsContext<TReq, TAns>, public IMethodTestCase
  133. {
  134. using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
  135. /*Prototype of Test Case Function*/
  136. typedef ErrorCodeEnum(T::* ToTestFuncProto)(Pointer ctx);
  137. MethodTestCaseT(CEntityBase* ent, ToTestFuncProto testFunc)
  138. :SpReqAnsContext<TReq, TAns>(m_spMockTransactionContext),m_pEntity(ent), m_testFunc(testFunc)
  139. ,m_errorCode(Error_IgnoreAll),m_dwUserCode(0)
  140. {
  141. /** empty. */
  142. }
  143. virtual void PreTest() {/* user should set context request content at Req structure.*/}
  144. virtual ErrorCodeEnum PostTest()
  145. {
  146. LOG_FUNCTION();
  147. /*User should check response content Ans's validity
  148. *if detect the value conveied by 'Ans' is not the dream one, return ErrorCode except Error_Succeed
  149. *Or*/return Error_Succeed;
  150. /*Tips: Only if the 'RunTest()' returned Error_Succeed, then this function would be invoked.*/
  151. }
  152. ErrorCodeEnum RunTest()
  153. {
  154. LOG_FUNCTION();
  155. if (m_testFunc && m_pEntity)
  156. {
  157. PreTest();
  158. TestObjectPointer that = static_cast<TestObjectPointer>(m_pEntity);
  159. (that->*m_testFunc)(GetCtx());
  160. #if 0
  161. DWORD dwUserCode = 0, dwNoUsed = 0;
  162. const ErrorCodeEnum result = m_spMockTransactionContext->GetExpireTime(dwUserCode, dwNoUsed);
  163. #else
  164. const ErrorCodeEnum result = m_errorCode;
  165. #endif
  166. Dbg("errorCode: %s", SpStrError(result));
  167. return (((result == Error_Succeed) ? PostTest() : result));
  168. }
  169. return Error_IgnoreAll;
  170. }
  171. ErrorCodeEnum Answer(ErrorCodeEnum Error = Error_Succeed)
  172. {
  173. LOG_FUNCTION();
  174. m_errorCode = Error;
  175. return Error_Succeed;
  176. }
  177. ErrorCodeEnum Answer(ErrorCodeEnum eSysError, DWORD dwUserError)
  178. {
  179. LOG_FUNCTION();
  180. m_errorCode = eSysError;
  181. m_dwUserCode = dwUserError;
  182. return Error_Succeed;
  183. }
  184. private:
  185. Pointer GetCtx()
  186. {
  187. Pointer pt = this;
  188. pt.AddRef();
  189. return pt;
  190. }
  191. private:
  192. CEntityBase* m_pEntity;
  193. ToTestFuncProto m_testFunc;
  194. ErrorCodeEnum m_errorCode;
  195. DWORD m_dwUserCode;
  196. };
  197. typedef std::vector<TestCaseEntry> TestCaseSet;
  198. void AddTestCaseEntry(TestCaseEntry entry);
  199. typedef std::vector<IMethodTestCase*> MethodTestCaseSet;
  200. void AddTestMethodEntry(IMethodTestCase* testMethod);
  201. protected:
  202. virtual ErrorCodeEnum RunTestCase();
  203. /** user can override this function to add any other test, but do not
  204. invoke 'AddTestCaseEntry' and 'AddTestMethodEntry' method at this scope!!!!*/
  205. virtual ErrorCodeEnum AdditionalTest() { return Error_Succeed; }
  206. private:
  207. TestCaseSet m_testCases;
  208. MethodTestCaseSet m_testMethods;
  209. TestCaseStatistics m_testStatistcs;
  210. static CSmartPointer<ITransactionContext> m_spMockTransactionContext;
  211. };
  212. /*!
  213. * [Gifur@2020519]
  214. * declare Transaction context as static type for some reason:
  215. * 1. when creating SpReqAnsContext class object, we must convey a CSmartPointer<ITransactionContext> type param to initialize,
  216. * 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
  217. * hook it to mock real test result without changing any functional code which I really unwill to see it!
  218. * 2. subclass MethodTestCaseT inherited from SpReqAnsContext cannot initialize earlier than SpReqAnsContext as children class, so we
  219. * cannot declare a 'CSmartPointer<ITransactionContext>' type member and initialze it first then convey it to SpReqAnsContext.
  220. * 3. multi-thead unsafe !!!
  221. */
  222. template<typename TClass>
  223. CSmartPointer<ITransactionContext> ITestCaseSuite<TClass>::m_spMockTransactionContext \
  224. = CreateMockTransactionContext(nullptr);
  225. template<typename T>
  226. ITestCaseSuite<T>::~ITestCaseSuite()
  227. {
  228. for (auto& r : m_testMethods) {
  229. if (r) {
  230. delete r;
  231. r = NULL;
  232. }
  233. }
  234. }
  235. /* 'TestCaseEntry' Prototype: void Entity::Function(CSmartPointer<ITransactionContext> pTransactionContext)
  236. * User should declare and implement it.
  237. */
  238. template<typename TClass>
  239. void ITestCaseSuite<TClass>::AddTestCaseEntry(TestCaseEntry entry)
  240. {
  241. m_testCases.push_back(entry);
  242. m_testStatistcs.dwTotalTestCaseNum++;
  243. }
  244. template<typename TClass>
  245. void ITestCaseSuite<TClass>::AddTestMethodEntry(IMethodTestCase* testMethod)
  246. {
  247. m_testMethods.push_back(testMethod);
  248. m_testStatistcs.dwTotalTestCaseNum++;
  249. }
  250. template<typename TClass>
  251. ErrorCodeEnum ITestCaseSuite<TClass>::RunTestCase()
  252. {
  253. LOG_FUNCTION();
  254. m_testStatistcs.Clear();
  255. UINT testCaseNum = 0;
  256. TestObjectPointer that = static_cast<TestObjectPointer>(this);
  257. if (!m_testCases.empty())
  258. {
  259. auto it = m_testCases.begin();
  260. while (it != m_testCases.end())
  261. {
  262. ++testCaseNum;
  263. Dbg("Run TestCase#%d...", testCaseNum);
  264. CSmartPointer<ITransactionContext> mock = CreateMockTransactionContext(that->GetFunction());
  265. (that->*(*it))(mock);
  266. DWORD dwUserCode = 0, dwNoUsed = 0;
  267. ErrorCodeEnum result = mock->GetExpireTime(dwUserCode, dwNoUsed);
  268. if (IS_FAILURED(result) && result != Error_IgnoreAll)
  269. {
  270. THROW_ERROR("TestCase#%d test failed return %s(userCode: 0x%X)", testCaseNum, SpStrError(result), dwUserCode);
  271. m_testStatistcs.dwFailureTestCaseNum++;
  272. }
  273. else if (result == Error_IgnoreAll)
  274. {
  275. m_testStatistcs.dwIgnoreTestCaseNum++;
  276. }
  277. else
  278. {
  279. m_testStatistcs.dwAcceptTestCaseNum++;
  280. }
  281. it++;
  282. }
  283. }
  284. UINT testMethodNum = 0;
  285. if (!m_testMethods.empty())
  286. {
  287. auto it = m_testMethods.begin();
  288. while (it != m_testMethods.end())
  289. {
  290. ++testMethodNum;
  291. Dbg("Run TestMethod#%d...", testMethodNum);
  292. ErrorCodeEnum result = (*it)->RunTest();
  293. if (IS_FAILURED(result) && result != Error_IgnoreAll)
  294. {
  295. THROW_ERROR("TestMethod#%d test failed return %s", testMethodNum, SpStrError(result));
  296. m_testStatistcs.dwFailureTestCaseNum++;
  297. }
  298. else if (result == Error_IgnoreAll)
  299. {
  300. m_testStatistcs.dwIgnoreTestCaseNum++;
  301. }
  302. else
  303. {
  304. m_testStatistcs.dwAcceptTestCaseNum++;
  305. }
  306. it++;
  307. }
  308. }
  309. return (m_testStatistcs.dwFailureTestCaseNum > 0) ? Error_Failed : Error_Succeed;
  310. }
  311. #define TESTCASE_OVERRIDE_ON_EXAM_AND_IMPLEMENT() \
  312. void OnExam(CSmartPointer<ITransactionContext> pTransactionContext) override \
  313. { \
  314. LOG_FUNCTION(); \
  315. ErrorCodeEnum testResult = RunTestCase(); \
  316. if (testResult == Error_Succeed || testResult == Error_IgnoreAll) { \
  317. testResult = AdditionalTest(); \
  318. pTransactionContext->SendAnswer(testResult); \
  319. } else { \
  320. pTransactionContext->SendAnswer(testResult); \
  321. } \
  322. }
  323. /** experiment below */
  324. #define TESTCASE_DECLARE_BEGIN(serviceName, testFuncName) \
  325. void Test_##testFuncName(CSmartPointer<ITransactionContext> pTransactionContext) {\
  326. SpReqAnsContext< serviceName##_##testFuncName##_Req, serviceName##_##testFuncName##_Ans>::Pointer ctx = \
  327. new SpReqAnsContext< serviceName##_##testFuncName##_Req, serviceName##_##testFuncName##_Ans>(pTransactionContext)
  328. /*user et the member value */
  329. #define TESTCASE_DECLARE_INVOKE(testFuncName) \
  330. do {\
  331. DWORD dwUserCode = 0, dwNoUsed = 0;\
  332. Test_##testFuncName(ctx);\
  333. ErrorCodeEnum result = mock->GetExpireTime(dwUserCode, dwNoUsed);\
  334. if (IS_FAILURED(result)) { return; }\
  335. } while (false)
  336. #define TESTCASE_DECLARE_END(testFuncName) \
  337. }
  338. //////////////////////////////////////////////////////////////////////////
  339. using namespace SP::Detail;
  340. class TestCase;
  341. using TestFunction = ErrorCodeEnum(*)();
  342. struct Counters {
  343. Counters() :passed(0), failed(0) {}
  344. std::size_t Total() const { return (passed + failed); }
  345. bool AllPassed() const { return (failed == 0); }
  346. std::size_t passed;
  347. std::size_t failed;
  348. };
  349. struct TestStatistics {
  350. Counters testCases;
  351. };
  352. struct ITestRunner {
  353. virtual ~ITestRunner() {};
  354. virtual TestStatistics RunTest(TestCase const& testCase) = 0;
  355. };
  356. struct TestConfig
  357. {
  358. std::string filterTag;
  359. };
  360. struct ITestCaseRegistry
  361. {
  362. virtual ~ITestCaseRegistry() {}
  363. /** TODO: relate with Entity Name*/
  364. virtual std::vector<TestCase> const& getAllTests(TestConfig const& config) const = 0;
  365. };
  366. struct ITestRegistryHub {
  367. virtual ~ITestRegistryHub() {}
  368. virtual void RegisterTest(TestCase const& testInfo) = 0;
  369. virtual ITestCaseRegistry const& GetTestCaseRegistry() const = 0;
  370. };
  371. SPBASE_API ITestRegistryHub& GetRegistryHub();
  372. /** TODO: hide*/
  373. SPBASE_API TestCase MakeTestCase(IMethodTestCase* testCase,
  374. std::string const& className,
  375. std::string const& name,
  376. std::string const& desc,
  377. SourceLineInfo const& lineInfo);
  378. SPBASE_API void RegisterTestCase(IMethodTestCase* testCase, char const* className,
  379. NameAndDesc const& nameAndDesc,
  380. SourceLineInfo const& lineInfo);
  381. SPBASE_API void RegisterTestCaseFunction(TestFunction function,
  382. NameAndDesc const& nameAndDesc,
  383. SourceLineInfo const& lineInfo
  384. );
  385. template<typename TClass>
  386. class MethodTestCase : public IMethodTestCase {
  387. public :
  388. MethodTestCase(ErrorCodeEnum(TClass::* method)()) : m_method(method) {}
  389. virtual ErrorCodeEnum RunTest() {
  390. TClass obj;
  391. return (obj.*m_method)();
  392. //return Error_Succeed;
  393. }
  394. private:
  395. virtual ~MethodTestCase() {}
  396. ErrorCodeEnum(TClass::* m_method)();
  397. };
  398. class DefaultFuncTestCase : public IMethodTestCase {
  399. public:
  400. DefaultFuncTestCase(TestFunction func) :m_func(func) {}
  401. virtual ErrorCodeEnum RunTest() {
  402. return m_func();
  403. }
  404. private:
  405. virtual ~DefaultFuncTestCase() {};
  406. TestFunction m_func;
  407. };
  408. template<typename TClass, class TReq, class TAns>
  409. struct TwoWayContextTestCase : public SpReqAnsContext<TReq, TAns>, public IMethodTestCase {
  410. using Pointer = CSmartPointer<SpReqAnsContext<TReq, TAns> >;
  411. using TestTwoWayFunc = ErrorCodeEnum(TClass::*)(Pointer ctx);
  412. using TestTwoWayFuncVoid = void(TClass::*)(Pointer ctx);
  413. TwoWayContextTestCase(TClass* testee, TestTwoWayFunc func, TestTwoWayFuncVoid funVoid)
  414. :SpReqAnsContext<TReq, TAns>(CreateMockTransactionContext(nullptr))
  415. ,m_obj(testee), m_func(func), m_voidFunc(funVoid), m_errorCode(Error_IgnoreAll), m_dwUserCode(0) {
  416. }
  417. TwoWayContextTestCase(TClass* testee, TestTwoWayFunc func)
  418. :TwoWayContextTestCase(nullptr, func, nullptr){}
  419. TwoWayContextTestCase(TClass* testee, TestTwoWayFuncVoid func)
  420. :TwoWayContextTestCase(nullptr, nullptr, func) {}
  421. TwoWayContextTestCase(TestTwoWayFunc func) :TwoWayContextTestCase(nullptr, func){ }
  422. TwoWayContextTestCase(TestTwoWayFuncVoid func) : TwoWayContextTestCase(nullptr, func) { }
  423. virtual void PreTest() {/* user should set context request content at Req structure.*/ }
  424. virtual ErrorCodeEnum PostTest()
  425. {
  426. LOG_FUNCTION();
  427. /*User should check response content Ans's validity
  428. *if detect the value conveied by 'Ans' is not the dream one, return ErrorCode except Error_Succeed
  429. *Or*/return Error_Succeed;
  430. /*Tips: Only if the 'RunTest()' returned Error_Succeed, then this function would be invoked.*/
  431. }
  432. virtual ErrorCodeEnum RunTest()
  433. {
  434. ErrorCodeEnum result = Error_IgnoreAll;
  435. TClass obj;
  436. bool flag = false;
  437. if (m_obj == nullptr)
  438. flag = !!(m_obj = &obj);
  439. PreTest();
  440. if (m_func != nullptr)
  441. {
  442. result = (m_obj->*m_func)(GetCtx());
  443. }
  444. else if (m_voidFunc != nullptr)
  445. {
  446. (m_obj->*m_voidFunc)(GetCtx());
  447. result = Error_Succeed;
  448. }
  449. result = (result == Error_Succeed) ? m_errorCode : result;
  450. Dbg("TwoWayContextTestCase->errorCode: %s", SpStrError(result));
  451. if (flag) m_obj = nullptr;
  452. return (((result == Error_Succeed) ? PostTest() : result));
  453. }
  454. ErrorCodeEnum Answer(ErrorCodeEnum Error = Error_Succeed) override
  455. {
  456. LOG_FUNCTION();
  457. m_errorCode = Error;
  458. return Error_Succeed;
  459. }
  460. ErrorCodeEnum Answer(ErrorCodeEnum eSysError, DWORD dwUserError) override
  461. {
  462. LOG_FUNCTION();
  463. m_errorCode = eSysError;
  464. m_dwUserCode = dwUserError;
  465. return Error_Succeed;
  466. }
  467. private:
  468. CSmartPointer<ITransactionContext> m_mockCtx = CreateMockTransactionContext(nullptr);
  469. Pointer GetCtx()
  470. {
  471. Pointer pt = this;
  472. pt.AddRef();
  473. return pt;
  474. }
  475. TestTwoWayFunc m_func;
  476. TestTwoWayFuncVoid m_voidFunc;
  477. TClass* m_obj;
  478. ErrorCodeEnum m_errorCode;
  479. DWORD m_dwUserCode;
  480. };
  481. struct TestAutoReg {
  482. TestAutoReg() {}
  483. ~TestAutoReg() {}
  484. /** for test simple func*/
  485. TestAutoReg(TestFunction function,
  486. SourceLineInfo const& lineInfo,
  487. NameAndDesc const& nameAndDesc
  488. )
  489. {
  490. RegisterTestCaseFunction(function, nameAndDesc, lineInfo);
  491. }
  492. /** for test Class*/
  493. template<typename TClass>
  494. TestAutoReg(
  495. ErrorCodeEnum(TClass::* method)(),
  496. char const* lpcszClassName,
  497. NameAndDesc const& nameAndDesc,
  498. SourceLineInfo const& lineInfo)
  499. {
  500. RegisterTestCase(new MethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
  501. }
  502. TestAutoReg(
  503. IMethodTestCase* testCaseImpl,
  504. char const* lpcszClassName,
  505. NameAndDesc const& nameAndDesc,
  506. SourceLineInfo const& lineInfo)
  507. {
  508. RegisterTestCase(testCaseImpl, lpcszClassName, nameAndDesc, lineInfo);
  509. }
  510. /** for test Class's context*/
  511. template<typename TClass>
  512. TestAutoReg(
  513. void(TClass::* method)(),
  514. char const* lpcszClassName,
  515. NameAndDesc const& nameAndDesc,
  516. SourceLineInfo const& lineInfo)
  517. {
  518. //RegisterTestCase(new MethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
  519. }
  520. };
  521. struct TestCaseInfo {
  522. TestCaseInfo(std::string const& name, std::string const& className, std::string const& desc, SourceLineInfo const& info)
  523. :strName(name.c_str()), strClassName(className.c_str()),strDescription(desc.c_str()), lineInfo(info) {}
  524. TestCaseInfo(TestCaseInfo const& other)
  525. :TestCaseInfo(other.strName, other.strClassName, other.strDescription, other.lineInfo)
  526. {/*delegrate directory*/}
  527. std::string strName;
  528. std::string strClassName;
  529. std::string strDescription;
  530. SourceLineInfo lineInfo;
  531. };
  532. class TestCase : public TestCaseInfo
  533. {
  534. public:
  535. TestCase(IMethodTestCase* testCase, TestCaseInfo const& info): TestCaseInfo(info), m_testCase(testCase) {}
  536. TestCase(TestCase const& other)
  537. :TestCaseInfo(other), m_testCase(other.m_testCase) {}
  538. TestCaseInfo const& GetTestInfo() const { return *this; }
  539. /** for anonymous case regist*/
  540. TestCase CloneExceptName(std::string const& newName) const {
  541. TestCase newCase(*this);
  542. newCase.strName = newName;
  543. return newCase;
  544. }
  545. ErrorCodeEnum RunTest() const
  546. {
  547. return m_testCase->RunTest();
  548. }
  549. private:
  550. CSmartPointer<IMethodTestCase> m_testCase;
  551. };
  552. class TestRunner : public ITestRunner
  553. {
  554. public:
  555. TestStatistics RunTest(TestCase const& testCase)
  556. {
  557. try {
  558. testCase.RunTest();
  559. }
  560. catch (std::exception& ex) {
  561. }
  562. return TestStatistics();
  563. }
  564. };
  565. #define RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line ) name##line
  566. #define RVC_INTERVAL_UNIQUE_NAME_LINE( name, line ) RVC_INTERVAL_UNIQUE_NAME_LINE2( name, line )
  567. #define RVC_INTERVAL_UNIQUE_NAME(name) RVC_INTERVAL_UNIQUE_NAME_LINE(name, __COUNTER__)
  568. #define RVC_INTERVAL_STRINGFY(name) #name
  569. #define INTERNAL_RVC_TEST_CASE_METHOD2(TestName, ClassName, ...) \
  570. namespace { \
  571. struct TestName : ClassName { \
  572. ErrorCodeEnum Test(); \
  573. }; \
  574. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  575. } \
  576. ErrorCodeEnum TestName::Test()
  577. #define INTERNAL_RVC_TEST_CASE_METHOD(ClassName, ...) \
  578. INTERNAL_RVC_TEST_CASE_METHOD2(RVC_INTERVAL_UNIQUE_NAME(CVRssalCtseTduS), ClassName, __VA_ARGS__)
  579. #define INTERNAL_RVC_TESTCASE2(TestName, ...) \
  580. static ErrorCodeEnum TestName(); \
  581. namespace { \
  582. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName, SP_INTERNAL_LINEINFO, SP::Detail::NameAndDesc(__VA_ARGS__)); \
  583. } \
  584. static ErrorCodeEnum TestName()
  585. #define INTERNAL_RVC_TESTCASE(...) \
  586. INTERNAL_RVC_TESTCASE2(RVC_INTERVAL_UNIQUE_NAME(CVRcnuFtseTduS), __VA_ARGS__)
  587. #define RVC_TWO_WAY_SECTION(ServiceName, ContextName) \
  588. void Test_##ServiceName_##ContextName() { \
  589. CSmartPointer<ITransactionContext> mock = CreateMockTransactionContext(this->GetFunction()); \
  590. SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer ctx = \
  591. new SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>(mock); \
  592. }
  593. #define RVC_TWO_WAY_INVOKE(TestFuncName) \
  594. testFunc = TestFuncName; \
  595. }\
  596. ErrorCodeEnum TestName::PostTest_##ContextName(ServiceName##_##ContextName##_Ans const& Ans) \
  597. #include <functional>
  598. #define SECTION_INNER(StructName, ...) \
  599. struct StructName : public TwoWayContextReqAnsImpl { \
  600. void Test(); \
  601. }; \
  602. void StructName::Test()
  603. #define SECTION(...) \
  604. SECTION_INNER(RVC_INTERVAL_UNIQUE_NAME(CVRnoitceStseTduS), __VA_ARGS__)
  605. #define TWO_WAY_CHECK_ANSWER_BEGIN(ansName) \
  606. checkAnsFunc = [](TwoWayResultType const& ansName) -> ErrorCodeEnum {
  607. #define TWO_WAY_CHECK_ANSWER_END() };
  608. #define INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY2(TestName, ClassName, ServiceName, ContextName, TestFuncName, ...) \
  609. namespace { \
  610. struct TestName : ClassName { \
  611. CSmartPointer<ITransactionContext> mMockTrans; \
  612. SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer mCtx;\
  613. using TwoWayResultType = ServiceName##_##ContextName##_Ans; \
  614. void (ClassName::*testFuncVoid)(SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer);\
  615. ErrorCodeEnum (ClassName::*testFunc)(SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer);\
  616. ErrorCodeEnum testRes; \
  617. std::function<ErrorCodeEnum(ServiceName##_##ContextName##_Ans const&)> checkAnsFunc; \
  618. TestName():mMockTrans(CreateMockTransactionContext(this->GetFunction())), \
  619. mCtx(new SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>(mMockTrans)) \
  620. , testFuncVoid(nullptr),testFunc(nullptr), testRes(Error_Succeed), checkAnsFunc(nullptr) { \
  621. testFunc = &ClassName::TestFuncName; \
  622. }\
  623. void PreTest_##ContextName(ServiceName##_##ContextName##_Req& Req); \
  624. ErrorCodeEnum PostTest_##ContextName() { \
  625. return checkAnsFunc == nullptr ? Error_Succeed : checkAnsFunc(mCtx->Ans); \
  626. } \
  627. ErrorCodeEnum Test_##ContextName() { \
  628. PreTest_##ContextName(mCtx->Req); \
  629. if(testFuncVoid != nullptr) { \
  630. (this->*testFuncVoid)(mCtx); \
  631. } \
  632. else if(testFunc != nullptr) { \
  633. testRes =(this->*testFunc)(mCtx); \
  634. } \
  635. if(testRes == Error_Succeed) { \
  636. DWORD dwUserCode = 0, dwNoUsed = 0; \
  637. testRes = mMockTrans->GetExpireTime(dwUserCode, dwNoUsed); \
  638. } \
  639. if(testRes == Error_Succeed) { \
  640. testRes = PostTest_##ContextName(); \
  641. } else if(testRes == Error_IgnoreAll) \
  642. testRes = Error_Succeed; \
  643. return testRes; \
  644. } \
  645. struct TwoWayContextReqAnsImpl : \
  646. public TwoWayContextTestCase<ClassName, ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans> \
  647. { \
  648. }; \
  649. }; \
  650. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test_##ContextName, RVC_INTERVAL_STRINGFY(ClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  651. } \
  652. void TestName::PreTest_##ContextName(ServiceName##_##ContextName##_Req& Req)
  653. #define INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(EntityClassName, ServiceName, TestFuncName, ContextName, ...) \
  654. INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), EntityClassName, ServiceName, ContextName, TestFuncName, __VA_ARGS__)
  655. #define INTERNAL_RVC_TEST_CASE_CONTEXT2(TestName, ClassName, ...) \
  656. namespace { \
  657. struct TestName : ClassName { \
  658. CSmartPointer<ITransactionContext> mMockTrans; \
  659. TestName():mMockTrans(CreateMockTransactionContext(this->GetFunction())){}; \
  660. ErrorCodeEnum Test() { \
  661. TestContext(mMockTrans); \
  662. DWORD dwUserCode = 0, dwNoUsed = 0; \
  663. ErrorCodeEnum testRes = mMockTrans->GetExpireTime(dwUserCode, dwNoUsed); \
  664. return (testRes == Error_IgnoreAll) ? Error_Succeed : testRes; \
  665. } \
  666. void TestContext(CSmartPointer<ITransactionContext> pTransactionContext); \
  667. }; \
  668. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test, RVC_INTERVAL_STRINGFY(ClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  669. } \
  670. void TestName::TestContext(CSmartPointer<ITransactionContext> pTransactionContext)
  671. #define INTERNAL_RVC_TEST_CASE_CONTEXT(EntityClassName, ...) \
  672. INTERNAL_RVC_TEST_CASE_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), EntityClassName, __VA_ARGS__)
  673. ///////////////////////////////////////////////////////////////////////////////
  674. struct Tolerate {
  675. enum Choice
  676. {
  677. Strict,
  678. Ignore
  679. };
  680. };
  681. template<typename T>
  682. struct Matcher {
  683. Tolerate::Choice level = Tolerate::Ignore;
  684. std::string strExpr;
  685. std::function<bool(T const&)> exprFunc;
  686. SourceLineInfo lineInfo;
  687. Matcher(Tolerate::Choice const& choice, std::string const& desc, std::function<bool(T const&)>&& expr, SourceLineInfo const& info)
  688. :level(choice), strExpr(desc), exprFunc(expr),lineInfo(info) {}
  689. };
  690. # define RVC_INTERNAL_STRINGIFY(expr) #expr
  691. #define INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT2(TestName, EntityClassName, ServiceName, ContextName, ...) \
  692. namespace { \
  693. struct TestName : TwoWayContextTestCase<EntityClassName, ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans> { \
  694. TestName(TestTwoWayFunc func):TwoWayContextTestCase(func){ Initial(); }\
  695. void Initial(); \
  696. using TwoWayResultType = ServiceName##_##ContextName##_Ans; \
  697. using AnsMatcher = Matcher<TwoWayResultType>; \
  698. using AnsMachers = std::vector< AnsMatcher >; \
  699. AnsMachers matchers; \
  700. ErrorCodeEnum PostTest() { \
  701. ErrorCodeEnum result = Error_Succeed; \
  702. for( AnsMachers::const_iterator it = matchers.begin(), itEnd = matchers.end(); it != itEnd;++it) { \
  703. if (((it->exprFunc)(Ans))) { \
  704. } else { \
  705. Dbg("Test \" %s \" failed %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str()); \
  706. if(it->level == Tolerate::Strict) { result = Error_MisMatched; break; } \
  707. } \
  708. } \
  709. return result; \
  710. } \
  711. }; \
  712. TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)( new TestName(&EntityClassName::ContextName) , RVC_INTERVAL_STRINGFY(EntityClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
  713. } \
  714. void TestName::Initial()
  715. #define INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT(EntityClassName, ServiceName, ContextName, ... ) \
  716. INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRtxetnoCtseTduS), EntityClassName, ServiceName, ContextName, __VA_ARGS__)
  717. #define INNER_ANSWER_CHECK(Expression, Description, TolerateLevel) \
  718. do { \
  719. auto f = [](TwoWayResultType const& Ans)->bool { return ( Expression ); }; \
  720. AnsMatcher matchInst(TolerateLevel, std::string(Description), f, SP_INTERNAL_LINEINFO); \
  721. matchers.push_back(matchInst); \
  722. } while (false)
  723. #define ANSWER_CHECK(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Ignore)
  724. #define ANSWER_REQUIRE(expr) INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Strict)
  725. /** Export for user*/
  726. #define RVC_TEST_CASE( ... ) INTERNAL_RVC_TESTCASE( __VA_ARGS__ )
  727. #define RVC_TEST_CASE_METHOD(className, ...) INTERNAL_RVC_TEST_CASE_METHOD( className, __VA_ARGS__ )
  728. #define RVC_TEST_CASE_VOID_CONTEXT(entityClassName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT( entityClassName, __VA_ARGS__ )
  729. #define RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, contextName, __VA_ARGS__)
  730. #define RVC_REGISTER_TEST_CASE_CONTEXT(entityClassName, serviceName, contextName, ... ) INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT(entityClassName, serviceName, contextName, __VA_ARGS__ )
  731. #endif //_RVC_SPTEST_H__