浏览代码

Z991239-632 #comment feature: 添加测试宏:RVC_REGISTER_TEST_CASE_CONTEXT

gifur 5 年之前
父节点
当前提交
ca2e896a77
共有 4 个文件被更改,包括 183 次插入35 次删除
  1. 141 28
      Common/SpTest.h
  2. 2 0
      TODO
  3. 31 4
      test/module/mod_sample/mod_SampleEntity.cpp
  4. 9 3
      test/module/mod_sample/mod_SampleEntity.h

+ 141 - 28
Common/SpTest.h

@@ -485,6 +485,7 @@ struct ITestRegistryHub {
 
 SPBASE_API ITestRegistryHub& GetRegistryHub();
 
+/** TODO: hide*/
 SPBASE_API TestCase MakeTestCase(IMethodTestCase* testCase,
 	std::string const& className,
 	std::string const& name,
@@ -521,9 +522,7 @@ public:
 		return m_func();
 	}
 private:
-
 	virtual ~DefaultFuncTestCase() {};
-
 	TestFunction m_func;
 };
 
@@ -534,42 +533,93 @@ struct TwoWayContextTestCase : public SpReqAnsContext<TReq, TAns>, public IMetho
 	using TestTwoWayFunc = ErrorCodeEnum(TClass::*)(Pointer ctx);
 	using TestTwoWayFuncVoid = void(TClass::*)(Pointer ctx);
 
-	TwoWayContextTestCase(TClass* testee, TestTwoWayFunc func) :m_obj(testee), m_func(func), m_voidFunc(nullptr), m_ctx(nullptr){}
-	TwoWayContextTestCase(TClass* testee, TestTwoWayFuncVoid func) :m_obj(testee), m_func(nullptr), m_voidFunc(func), m_ctx(nullptr) {}
-	TwoWayContextTestCase(TestTwoWayFunc func) :TwoWayContextTestCase(nullptr, func){}
-	TwoWayContextTestCase(TestTwoWayFuncVoid func) : TwoWayContextTestCase(nullptr, func) {}
+	TwoWayContextTestCase(TClass* testee, TestTwoWayFunc func, TestTwoWayFuncVoid funVoid)
+		:SpReqAnsContext<TReq, TAns>(CreateMockTransactionContext(nullptr))
+		,m_obj(testee), m_func(func), m_voidFunc(funVoid), m_errorCode(Error_IgnoreAll), m_dwUserCode(0) {
+	}
+
+	TwoWayContextTestCase(TClass* testee, TestTwoWayFunc func) 
+		:TwoWayContextTestCase(nullptr, func, nullptr){}
+
+	TwoWayContextTestCase(TClass* testee, TestTwoWayFuncVoid func) 
+		:TwoWayContextTestCase(nullptr, nullptr, func) {}
+
+	TwoWayContextTestCase(TestTwoWayFunc func) :TwoWayContextTestCase(nullptr, func){  }
+	TwoWayContextTestCase(TestTwoWayFuncVoid func) : TwoWayContextTestCase(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_NotConfig;
+		ErrorCodeEnum result = Error_IgnoreAll;
 		TClass obj;
 		bool flag = false;
 
 		if (m_obj == nullptr)
 			flag = !!(m_obj = &obj);
 
+		PreTest();
+
 		if (m_func != nullptr) 
 		{
-			result = (m_obj->*m_func)(m_ctx);
+			result = (m_obj->*m_func)(GetCtx());
 		}
 		else if (m_voidFunc != nullptr)
 		{
-			(m_obj->*m_voidFunc)(m_ctx);
+			(m_obj->*m_voidFunc)(GetCtx());
 			result = Error_Succeed;
 		}
 
+		result =  (result == Error_Succeed) ? m_errorCode : result;
+		
+		Dbg("TwoWayContextTestCase->errorCode: %s", SpStrError(result));
+
 		if (flag) m_obj = nullptr;
 
-		return result;
+		return (((result == Error_Succeed) ? PostTest() : result));
+	}
+
+	ErrorCodeEnum Answer(ErrorCodeEnum Error = Error_Succeed) override
+	{
+		LOG_FUNCTION();
+		m_errorCode = Error;
+		return Error_Succeed;
+	}
+
+	ErrorCodeEnum Answer(ErrorCodeEnum eSysError, DWORD dwUserError) override
+	{
+		LOG_FUNCTION();
+		m_errorCode = eSysError;
+		m_dwUserCode = dwUserError;
+		return Error_Succeed;
 	}
 
 private:
+
+	CSmartPointer<ITransactionContext> m_mockCtx = CreateMockTransactionContext(nullptr);
+
+	Pointer GetCtx()
+	{
+		Pointer pt = this;
+		pt.AddRef();
+		return pt;
+	}
+
 	TestTwoWayFunc m_func;
 	TestTwoWayFuncVoid m_voidFunc;
-	Pointer m_ctx;
 	TClass* m_obj;
-};
 
+	ErrorCodeEnum m_errorCode;
+	DWORD m_dwUserCode;
+};
 
 struct TestAutoReg {
 
@@ -596,6 +646,15 @@ struct TestAutoReg {
 			RegisterTestCase(new MethodTestCase<TClass>(method), lpcszClassName, nameAndDesc, lineInfo);
 		}
 
+	TestAutoReg(
+		IMethodTestCase* testCaseImpl,
+		char const* lpcszClassName,
+		NameAndDesc const& nameAndDesc,
+		SourceLineInfo const& lineInfo)
+	{
+		RegisterTestCase(testCaseImpl, lpcszClassName, nameAndDesc, lineInfo);
+	}
+
 	/** for test Class's context*/
 	template<typename TClass>
 	TestAutoReg(
@@ -701,15 +760,12 @@ public:
 
 
 #define RVC_TWO_WAY_SECTION(ServiceName, ContextName)	\
-	void Test_##ContextName_##ServiceName() {	\
+	void Test_##ServiceName_##ContextName() {	\
 		CSmartPointer<ITransactionContext> mock = CreateMockTransactionContext(this->GetFunction());	\
 		SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer ctx =	\
 		new SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>(mock); \
 	}
 
-#define RVC_TWO_WAY_VOID_INVOKE(TestFuncName)	\
-	testFuncVoid = TestFuncName
-	
 #define RVC_TWO_WAY_INVOKE(TestFuncName)	\
 	testFunc = TestFuncName;	\
 	}\
@@ -717,7 +773,6 @@ public:
 	
 #include <functional>
 
-
 #define SECTION_INNER(StructName, ...)	\
 	struct  StructName : public TwoWayContextReqAnsImpl {	\
 		void Test();	\
@@ -727,18 +782,16 @@ public:
 #define SECTION(...)	\
 	SECTION_INNER(RVC_INTERVAL_UNIQUE_NAME(CVRnoitceStseTduS), __VA_ARGS__)
 
-#define CHECK_ANSWER_BEGIN(ansName)	\
+#define TWO_WAY_CHECK_ANSWER_BEGIN(ansName)	\
 	checkAnsFunc = [](TwoWayResultType const& ansName) -> ErrorCodeEnum	 {
 
-#define CHECK_ANSWER_END() };
+#define TWO_WAY_CHECK_ANSWER_END() };
 
 #define INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY2(TestName, ClassName, ServiceName, ContextName, TestFuncName, ...)	\
 		namespace {	\
 		struct TestName : ClassName {	\
 			CSmartPointer<ITransactionContext> mMockTrans;	\
 			SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer mCtx;\
-			ServiceName##_##ContextName##_Req& Req;	\
-			ServiceName##_##ContextName##_Ans& Ans;	\
 			using TwoWayResultType = ServiceName##_##ContextName##_Ans;	\
 			void (ClassName::*testFuncVoid)(SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer);\
 			ErrorCodeEnum (ClassName::*testFunc)(SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>::Pointer);\
@@ -746,16 +799,15 @@ public:
 			std::function<ErrorCodeEnum(ServiceName##_##ContextName##_Ans const&)> checkAnsFunc;	\
 			TestName():mMockTrans(CreateMockTransactionContext(this->GetFunction())),	\
 				mCtx(new SpReqAnsContext< ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans>(mMockTrans))	\
-				, Req(mCtx->Req), Ans(mCtx->Ans)	\
 				, testFuncVoid(nullptr),testFunc(nullptr), testRes(Error_Succeed), checkAnsFunc(nullptr) {	\
 				testFunc = &ClassName::TestFuncName;	\
 			}\
-			void PreTest_##ContextName();	\
+			void PreTest_##ContextName(ServiceName##_##ContextName##_Req& Req);	\
 			ErrorCodeEnum PostTest_##ContextName() {	\
 				return checkAnsFunc == nullptr ? Error_Succeed : checkAnsFunc(mCtx->Ans);	\
 			}	\
 			ErrorCodeEnum Test_##ContextName() {	\
-				PreTest_##ContextName();	\
+				PreTest_##ContextName(mCtx->Req);	\
 				if(testFuncVoid != nullptr) {		\
 					(this->*testFuncVoid)(mCtx);	\
 				}	\
@@ -768,7 +820,8 @@ public:
 				}	\
 				if(testRes == Error_Succeed) {	\
 					testRes = PostTest_##ContextName();	\
-				}	\
+				} else if(testRes == Error_IgnoreAll)	\
+					testRes = Error_Succeed;	\
 				return testRes;	\
 			}	\
 			struct TwoWayContextReqAnsImpl :	\
@@ -778,13 +831,12 @@ public:
 		};	\
 	TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)(&TestName::Test_##ContextName, RVC_INTERVAL_STRINGFY(ClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
 	}	\
-	void TestName::PreTest_##ContextName()
+	void TestName::PreTest_##ContextName(ServiceName##_##ContextName##_Req& Req)
 	
 
 #define INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(EntityClassName, ServiceName, TestFuncName, ContextName, ...)	\
 	INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), EntityClassName, ServiceName,  ContextName, TestFuncName, __VA_ARGS__)
 
-
 #define INTERNAL_RVC_TEST_CASE_CONTEXT2(TestName, ClassName, ...)	\
 	namespace {	\
 		struct TestName : ClassName {	\
@@ -806,12 +858,73 @@ public:
 #define INTERNAL_RVC_TEST_CASE_CONTEXT(EntityClassName, ...)	\
 	INTERNAL_RVC_TEST_CASE_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRytitnEtseTduS), EntityClassName, __VA_ARGS__)
 
+///////////////////////////////////////////////////////////////////////////////
+
+struct Tolerate {
+	enum Choice
+	{
+		Strict,
+		Ignore
+	};
+};
+
+template<typename T>
+struct Matcher {
+	Tolerate::Choice level = Tolerate::Ignore;
+	std::string strExpr;
+	std::function<bool(T const&)> exprFunc;
+	SourceLineInfo lineInfo;
+	Matcher(Tolerate::Choice 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_RVC_REGISTER_TEST_CASE_CONTEXT2(TestName, EntityClassName, ServiceName, ContextName, ...)	\
+	namespace {	\
+		struct TestName : TwoWayContextTestCase<EntityClassName, ServiceName##_##ContextName##_Req, ServiceName##_##ContextName##_Ans> {	\
+			TestName(TestTwoWayFunc func):TwoWayContextTestCase(func){ Initial(); }\
+			void Initial();	\
+			using TwoWayResultType = ServiceName##_##ContextName##_Ans;	\
+			using AnsMatcher = Matcher<TwoWayResultType>;	\
+			using AnsMachers = std::vector< AnsMatcher >;	\
+			AnsMachers matchers;	\
+			ErrorCodeEnum PostTest() {	\
+				ErrorCodeEnum result = Error_Succeed;	\
+				for( AnsMachers::const_iterator it = matchers.begin(), itEnd = matchers.end(); it != itEnd;++it) {	\
+					if (((it->exprFunc)(Ans))) {	\
+					}	else	{	\
+						Dbg("Test \" %s \" failed %s", it->strExpr.c_str(), it->lineInfo.ToString().c_str());	\
+						if(it->level == Tolerate::Strict) { result = Error_MisMatched; break; }	\
+					}	\
+				}	\
+				return result;	\
+			}	\
+		};	\
+		TestAutoReg RVC_INTERVAL_UNIQUE_NAME(testAutoReg)( new TestName(&EntityClassName::ContextName) , RVC_INTERVAL_STRINGFY(EntityClassName), SP::Detail::NameAndDesc(__VA_ARGS__), SP_INTERNAL_LINEINFO); \
+	}	\
+	void TestName::Initial()
+	
+#define INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT(EntityClassName, ServiceName, ContextName, ... ) \
+		INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT2(RVC_INTERVAL_UNIQUE_NAME(CVRtxetnoCtseTduS), 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)
+
+#define ANSWER_CHECK(expr)	INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Ignore)
+#define ANSWER_REQUIRE(expr)	INNER_ANSWER_CHECK(expr, RVC_INTERNAL_STRINGIFY(expr), Tolerate::Strict)		
 
 /** Export for user*/
 #define RVC_TEST_CASE( ... ) INTERNAL_RVC_TESTCASE( __VA_ARGS__ )
 #define RVC_TEST_CASE_METHOD(className, ...) INTERNAL_RVC_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define RVC_TEST_CASE_CONTEXT(entityClassName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT( entityClassName, __VA_ARGS__ )
+#define RVC_TEST_CASE_VOID_CONTEXT(entityClassName, ...) INTERNAL_RVC_TEST_CASE_CONTEXT( entityClassName, __VA_ARGS__ )
 #define RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, ...)	INTERNAL_RVC_TEST_CASE_CONTEXT_TWO_WAY(entityClassName, serviceName, contextName, contextName, __VA_ARGS__)
 
+#define RVC_REGISTER_TEST_CASE_CONTEXT(entityClassName, serviceName, contextName, ... ) INTERNAL_RVC_REGISTER_TEST_CASE_CONTEXT(entityClassName, serviceName, contextName, __VA_ARGS__ )
 
 #endif //_RVC_SPTEST_H__

+ 2 - 0
TODO

@@ -0,0 +1,2 @@
+2020年7月7日
+* CSimpleString 在未进行赋值的情况下,直接调用 Compare 成员函数会崩溃

+ 31 - 4
test/module/mod_sample/mod_SampleEntity.cpp

@@ -24,7 +24,7 @@ RVC_TEST_CASE("TestForCSampleEntity3")
 	return Error_Unexpect;
 }
 
-RVC_TEST_CASE_CONTEXT(CSampleEntity, "SimpleContextTest") {
+RVC_TEST_CASE_VOID_CONTEXT(CSampleEntity, "SimpleContextTest") {
 
 	SpReqAnsContext<SampleService_TwoWayFuncNotOverlap_Req, SampleService_TwoWayFuncNotOverlap_Ans>::Pointer ctx =
 		new SpReqAnsContext<SampleService_TwoWayFuncNotOverlap_Req, SampleService_TwoWayFuncNotOverlap_Ans>(pTransactionContext);
@@ -39,18 +39,45 @@ RVC_TEST_CASE_CONTEXT(CSampleEntity, "SimpleContextTest") {
 	}
 }
 
-
+/** The aim test function must be member method of {entityClassName} and its signature should be like that
+	ErrorCodeEnum --////teturn type must be ErrorCodeEnum
+	{contextName} -- ////the function name is the same as the struct name generated by spgen tool.
+	(SpReqAnsContext<{serviceName}_{contextName}_Req, serviceName}_{contextName}_Ans>::Pointer ctx)
+*/
 RVC_TEST_CASE_CONTEXT_TWO_WAY(CSampleEntity, SampleService, TwoWayFuncOverlap, "TwoWayFuncOverlapName")
 {
 	Req.req_context = "hello";
-	CHECK_ANSWER_BEGIN(Ans)
+	TWO_WAY_CHECK_ANSWER_BEGIN(Ans)
 		if (Ans.ans_context.Compare("world", true) != 0) {
 			LOG_TRACE("Check failed, return except Error_Succeed");
 			return Error_Unexpect;
 		}
 		Dbg("TwoWayFuncOverlap two way enter");
 		return Error_Succeed;
-	CHECK_ANSWER_END()
+	TWO_WAY_CHECK_ANSWER_END()
+}
+
+RVC_REGISTER_TEST_CASE_CONTEXT(CSampleEntity, SampleService, TwoWayFuncNormal, "TwoWayFuncNormal", "XXXXX")
+{
+	Req.tbool = true;
+	Req.tchar = 'I';
+	Req.tint = 123;
+	Req.tuint = 123;
+	Req.tshort = 123;
+	Req.tushort = 123;
+	Req.tuchar = 'I';
+	Req.tstring = "SampleService_TwoWayFuncNormal_Req::tstring::xclvmsdiofjweoij3r283908u238925j4ioijn;klgnfakjsfsfhffjeoiwjhoi";
+	Req.twstring = "SampleService_TwoWayFuncNormal_Req::twstring::xclvmsdiofjweoij3r283908u238925j4ioijn;klgnfakjsfsfhffjeoiwjhoi";
+	Req.tfloat = 123.456f;
+	Req.tdouble = 123.456;
+	Req.tblob.Clear();
+	Req.tint64 = 123;
+	Req.tuint64 = 123;
+
+	ANSWER_CHECK(Ans.sarray_string.GetCount() == 2); /*this would pass*/
+	ANSWER_REQUIRE(Ans.sarray_string[0] == "String1"); /*this would pass*/
+	ANSWER_REQUIRE(Ans.sarray_string[1] == "String3"); /*this would be failed, abort*/
+	ANSWER_CHECK(Ans.sarray_string[2] == "String3"); /*this would not invoke*/
 }
 
 

+ 9 - 3
test/module/mod_sample/mod_SampleEntity.h

@@ -236,10 +236,10 @@ public:
 			ErrorCodeEnum testResult = itStart->RunTest();
 			if (testResult != Error_Succeed) {
 				LogError(Severity_Middle, Error_Failed, 0, CSimpleStringA::Format(
-					"Test: %s %s file: {%s} ,line: {%d} failed, return %s.",
+					"Test: %s %s %s failed, return %s.",
 					testCaseInfo.strName.c_str(), 
 					testCaseInfo.strDescription.empty() ? "(No description)": testCaseInfo.strDescription.c_str(),
-					_GetFileName(testCaseInfo.lineInfo.file), testCaseInfo.lineInfo.line, SpStrError(testResult)));
+					testCaseInfo.lineInfo.ToString().c_str() , SpStrError(testResult)));
 				if (result != Error_Failed)
 					result = Error_Failed;
 			}
@@ -289,7 +289,8 @@ public:
 	{
 		LOG_FUNCTION();
 		ErrorCodeEnum ec = Error_Succeed;
-		if (ctx->Req.req_context.Compare("hello") != 0) {
+		LOG_ASSERT(ctx != NULL);
+		if (ctx->Req.req_context.IsNullOrEmpty() || ctx->Req.req_context.Compare("hello") != 0) {
 			return Error_NotIntegrated;
 		}
 		// TODO: 
@@ -339,6 +340,11 @@ public:
 		REQUIRE(ctx->Req.tint64 == 123);
 		REQUIRE(ctx->Req.tuint64 == 123);
 
+		ctx->Ans.sarray_string.Init(3);
+		ctx->Ans.sarray_string[0] = "String1";
+		ctx->Ans.sarray_string[1] = "String2";
+		ctx->Ans.sarray_string[2] = "String3";
+
 		return Error_Succeed;
 	}