|
@@ -0,0 +1,314 @@
|
|
|
+#include "stdafx.h"
|
|
|
+#include "SpBase.h"
|
|
|
+#include "SpTest.h"
|
|
|
+#include "modVer.h"
|
|
|
+
|
|
|
+/*Integrated test deamon*/
|
|
|
+
|
|
|
+#define OP_START 1
|
|
|
+#define OP_TEST 2
|
|
|
+#define OP_CHECK 3
|
|
|
+
|
|
|
+struct TestModeInfo {
|
|
|
+ TestModeInfo() :isTestMode(false), nLevel(0) {
|
|
|
+ }
|
|
|
+ bool isTestMode;
|
|
|
+ int nLevel;
|
|
|
+};
|
|
|
+
|
|
|
+class CTestDeamon
|
|
|
+ : public CEntityBase
|
|
|
+ , public IEntityLifeListener
|
|
|
+ , public IEntityStateListener
|
|
|
+ , public ILogListener
|
|
|
+ , public ICallbackListener
|
|
|
+ , public ITerminalStateChangedListener
|
|
|
+ , public ITimerListener
|
|
|
+{
|
|
|
+public:
|
|
|
+ CTestDeamon() : _dwStartedTestEntities(0) { _arrTestEntities.Clear(); }
|
|
|
+ virtual ~CTestDeamon() {
|
|
|
+ GetFunction()->UnsubscribeLog(m_subUUID);
|
|
|
+ GetFunction()->GetPrivilegeFunction()->UnregistLiftEvent();
|
|
|
+ GetFunction()->GetPrivilegeFunction()->UnregistEntityStateEvent(NULL);
|
|
|
+ }
|
|
|
+ virtual const char *GetEntityName() const { return "TestDeamon"; }
|
|
|
+ const char* GetEntityVersion() const { return MODULE_VERSION_FULL; }
|
|
|
+
|
|
|
+ virtual void OnPreStart(CAutoArray<CSimpleStringA> strArgs, CSmartPointer<ITransactionContext> pTransactionContext)
|
|
|
+ {
|
|
|
+ LOG_FUNCTION();
|
|
|
+ for (int i = 0; i < strArgs.GetCount(); ++i) {
|
|
|
+ auto& strParam = strArgs[i];
|
|
|
+ if (strParam.IsStartWith("TestMode", true)) {
|
|
|
+ if (strParam.IsEndWith("=ON", true)) {
|
|
|
+ _testInfo.isTestMode = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ IFFAILBREAK(GetFunction()->GetSystemRunInfo(_runInfo));
|
|
|
+
|
|
|
+ auto privilegeFunc = GetFunction()->GetPrivilegeFunction();
|
|
|
+ privilegeFunc->RegistEntityLifeEvent(this);
|
|
|
+ /*register all entity state*/
|
|
|
+ privilegeFunc->RegistEntityStateEvent(NULL, this);
|
|
|
+ Dbg("to subscribe Error_Failed type log.");
|
|
|
+ auto res = GetFunction()->SubscribeLog(m_subUUID, this, Log_Error, Severity_None, Error_Failed, -2, NULL, false);
|
|
|
+ if (IS_FAILURED(res)) {
|
|
|
+ LogError(Severity_High, res, 0, "subscribe Error_Failed type log failed!");
|
|
|
+ }
|
|
|
+ pTransactionContext->SendAnswer(Error_Succeed);
|
|
|
+ }
|
|
|
+
|
|
|
+ BOOL IsExcludeEntity(const CSimpleStringA& name)
|
|
|
+ {
|
|
|
+ if (name.Compare(GetEntityName()) == 0)
|
|
|
+ return TRUE;
|
|
|
+
|
|
|
+ for (int i = 0; i < _arrTestEntities.GetCount(); ++i) {
|
|
|
+ if (name.Compare(_arrTestEntities[i]) == 0)
|
|
|
+ return TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ void TestAllRegistedEntity()
|
|
|
+ {
|
|
|
+ CAutoArray<CSimpleStringA> arrStartEntities;
|
|
|
+ CAutoArray< DWORD> arrEntitityID;
|
|
|
+ IFFAILBREAK(GetFunction()->GetAllStartedEntity(arrStartEntities, arrEntitityID));
|
|
|
+ for (int i = 0; i < arrStartEntities.GetCount(); ++i) {
|
|
|
+ if(!IsExcludeEntity(arrStartEntities[i]))
|
|
|
+ TestEntity(arrStartEntities[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void OnStarted()
|
|
|
+ {
|
|
|
+ LOG_FUNCTION();
|
|
|
+ IFFAILBREAK(GetFunction()->RegistTerminalStateChangeEvent(this));
|
|
|
+ IFFAILBREAK(GetFunction()->SetTimer(1, this, 60 * 1000));
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void OnPreClose(EntityCloseCauseEnum eCloseCause,CSmartPointer<ITransactionContext> pTransactionContext)
|
|
|
+ {
|
|
|
+ LOG_FUNCTION();
|
|
|
+ pTransactionContext->SendAnswer(Error_Succeed);
|
|
|
+ }
|
|
|
+
|
|
|
+ //IEntityLifeListener
|
|
|
+ void OnCreated(const char* pszEntityName, ErrorCodeEnum eOnStartErrorCode, const char* pszCallerEntity)
|
|
|
+ {
|
|
|
+ LOG_TRACE("%s created by %s turns out %s",pszEntityName, pszCallerEntity, SpStrError(eOnStartErrorCode));
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnClosed(const char* pszEntityName,
|
|
|
+ EntityCloseCauseEnum eCloseCause,
|
|
|
+ ErrorCodeEnum eOnCloseErrorCode,
|
|
|
+ const char* pszCallerEntity)
|
|
|
+ {
|
|
|
+ LOG_TRACE("%s closed by %s because reason: %s turns out: %s",pszEntityName, pszCallerEntity, SpStrCloseCause(eCloseCause), SpStrError(eOnCloseErrorCode));
|
|
|
+ }
|
|
|
+ void OnException(const char* pszEntityName, const char* pszFunctionName, EntityStateEnum eState, EntityStateEnum eLastState, ErrorCodeEnum eErrorCode)
|
|
|
+ {
|
|
|
+ THROW_FATAL("OnException: %s::%s, changed state from %s to %s: ec:%s",
|
|
|
+ pszEntityName, pszFunctionName, SpStrEntityState(eLastState), SpStrEntityState(eState), SpStrError(eErrorCode));
|
|
|
+ if (eState == EntityState_Failed || eState == EntityState_Lost) {
|
|
|
+ RequireTerminalExit();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //IEntityStateListener
|
|
|
+ void OnEntityStateHook(const char* pszEntityName,
|
|
|
+ const char* pszTriggerEntity,
|
|
|
+ EntityStateEnum eState,
|
|
|
+ EntityStateEnum eLastState)
|
|
|
+ {
|
|
|
+ LOG_TRACE(">>>%s's entity state refreshed from %s to %s by %s",
|
|
|
+ pszEntityName, SpStrEntityState(eLastState), SpStrEntityState(eState), pszTriggerEntity);
|
|
|
+ if (eState == EntityState_Failed || eState == EntityState_Lost) {
|
|
|
+ RequireTerminalExit();
|
|
|
+ }
|
|
|
+ else if (eLastState == EntityState_Starting && eState == EntityState_Idle) {
|
|
|
+ /** if meet multi-entities at one mod, do test maybe failed beacause mod is busy at starting other brother entity.*/
|
|
|
+ //TestEntity(pszEntityName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnUserStateHook(const char* pszEntityName, DWORD dwState, DWORD dwLastState)
|
|
|
+ {
|
|
|
+ LOG_TRACE(">>>%s'user state refreshed to %d from %d", pszEntityName, dwState, dwLastState);
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnCeateConnection(const char* pszCallerEntity, const char* pszServiceEntity)
|
|
|
+ {
|
|
|
+ LOG_TRACE(">>>OnCeateConnection: caller: %s, service: %s", pszCallerEntity, pszServiceEntity);
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnCloseConnection(const char* pszCallerEntity, const char* pszServiceEntity)
|
|
|
+ {
|
|
|
+ LOG_TRACE(">>>OnCloseConnection: caller: %s, service: %s", pszCallerEntity, pszServiceEntity);
|
|
|
+ }
|
|
|
+
|
|
|
+ //ILogListener
|
|
|
+ void OnLog(const CAutoArray<CUUID>& SubIDs
|
|
|
+ , const CUUID nLogID
|
|
|
+ , const LogTypeEnum eLogType
|
|
|
+ , const SeverityLevelEnum eLevel
|
|
|
+ , const DWORD dwSysError, const DWORD dwUserCode
|
|
|
+ , const DWORD dwEntityInstanceID, const WORD wEntityDevelID
|
|
|
+ , const CAutoArray<DWORD>& Param
|
|
|
+ , const char* pszEntityName, const char* pszModuleName, const char* pszMessage)
|
|
|
+ {
|
|
|
+ LogEvent(eLevel, 0,
|
|
|
+ CSimpleStringA::Format("Catched Test Failed Message: [%s][0x%03X][%s] - %s",
|
|
|
+ pszModuleName, wEntityDevelID, pszEntityName, pszMessage));
|
|
|
+ switch (eLevel) {
|
|
|
+ case Severity_High:
|
|
|
+ RequireTerminalExit();
|
|
|
+ break;
|
|
|
+ case Severity_Middle:
|
|
|
+ /** do nothings*/
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnStateChanged(FrameworkStateEnum oldState, FrameworkStateEnum curState, const char* triggerEntity)
|
|
|
+ {
|
|
|
+
|
|
|
+ Dbg("OnStateChanged: from %s to %s trigged by %s",
|
|
|
+ SpStrFrameworkState(oldState), SpStrFrameworkState(curState), triggerEntity);
|
|
|
+
|
|
|
+ if (curState == FrameworkState_Running && _testInfo.isTestMode
|
|
|
+ // && !(_runInfo.dwBootOption & BootOption_Benchmark) /** when under benchmark, still need to test entity??*/
|
|
|
+ )
|
|
|
+ {
|
|
|
+ TestAllRegistedEntity();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnTimeout(DWORD dwTimerID)
|
|
|
+ {
|
|
|
+ Dbg("timer out: %d, to shake hand", dwTimerID);
|
|
|
+ CAutoArray<CSimpleStringA> arrStartEntities;
|
|
|
+ CAutoArray< DWORD> arrEntitityID;
|
|
|
+ IFFAILBREAK(GetFunction()->GetAllStartedEntity(arrStartEntities, arrEntitityID));
|
|
|
+ for (int i = 0; i < arrStartEntities.GetCount(); ++i)
|
|
|
+ {
|
|
|
+ if (arrStartEntities[i].Compare(GetEntityName()) != 0)
|
|
|
+ {
|
|
|
+ TestEntity(arrStartEntities[i], TRUE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ struct TestCallbackResult : public IReleasable
|
|
|
+ {
|
|
|
+ TestCallbackResult() :strEntityName(true), op(0) {}
|
|
|
+ virtual ~TestCallbackResult() {}
|
|
|
+ CSimpleStringA strEntityName;
|
|
|
+ int op;
|
|
|
+ };
|
|
|
+
|
|
|
+ /*ICallbackListener*/
|
|
|
+ void OnAnswer(CSmartPointer<IAsynWaitSp> pAsynWaitSp)
|
|
|
+ {
|
|
|
+ CSmartPointer<ICallbackListener> listener;
|
|
|
+ CSmartPointer<IReleasable> context;
|
|
|
+ CHECK(pAsynWaitSp->GetCallback(listener, context));
|
|
|
+ TestCallbackResult* result = static_cast<TestCallbackResult*>(context.GetRawPointer());
|
|
|
+ CHECK(result != NULL);
|
|
|
+ DWORD dwUserCode = 0;
|
|
|
+ const auto ec = pAsynWaitSp->AsyncGetAnswer(dwUserCode);
|
|
|
+ if (result->op == OP_START)
|
|
|
+ {
|
|
|
+ if (IS_FAILURED(ec))
|
|
|
+ {
|
|
|
+ THROW_ERROR("Start %s failed return %s!", result->strEntityName.GetData(), SpStrError(ec));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Dbg("Start entity %s succ.", result->strEntityName.GetData());
|
|
|
+ _arrTestEntities[_dwStartedTestEntities++] = result->strEntityName;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(result->op == OP_TEST)
|
|
|
+ { /** OnExam returned.*/
|
|
|
+ if (Error_IgnoreAll == ec || Error_NotImpl == ec)
|
|
|
+ {
|
|
|
+ LogWarn(Severity_Middle, Error_NotImpl, 0, CSimpleStringA::Format(
|
|
|
+ "Test for entity [%s] is not OK: Detect no any TestCase!!", result->strEntityName.GetData()));
|
|
|
+ }
|
|
|
+ else if (Error_Accept == ec || Error_Succeed == ec)
|
|
|
+ {
|
|
|
+ LogEvent(Severity_High, 0, CSimpleStringA::Format("Test for entity[%s] succ.", result->strEntityName.GetData()));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ TestCaseStatistics statistic;
|
|
|
+ statistic.ConvertFormUserCode(dwUserCode);
|
|
|
+ THROW_ERROR("Test for entity[%s] failed return %s: %.2lf percent tests passed, %u test failed out of %u cases",
|
|
|
+ result->strEntityName.GetData(), SpStrError(ec), statistic.FailureRate(), statistic.Failures(), statistic.Total());
|
|
|
+ }
|
|
|
+ Dbg("return usercode: 0x%08X", dwUserCode);
|
|
|
+ }
|
|
|
+ else if (result->op == OP_CHECK)
|
|
|
+ {
|
|
|
+ if (IS_FAILURED(ec))
|
|
|
+ {
|
|
|
+ THROW_ERROR("Check %s failed return %s!", result->strEntityName.GetData(), SpStrError(ec));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+
|
|
|
+ void RequireTerminalExit()
|
|
|
+ {
|
|
|
+ if (_testInfo.isTestMode)
|
|
|
+ {
|
|
|
+ Dbg("Test Failed! shutdown the terminal program...");
|
|
|
+ auto pPriviFunc = GetFunction()->GetPrivilegeFunction();
|
|
|
+ pPriviFunc->Reboot(RebootTrigger_DeadForever, RebootWay_Framework);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void SetOPCallback(CSmartPointer<IAsynWaitSp>& spWait, const char* pcszName, int op)
|
|
|
+ {
|
|
|
+ TestCallbackResult* result = new TestCallbackResult();
|
|
|
+ result->strEntityName = pcszName;
|
|
|
+ result->op = op;
|
|
|
+ spWait->SetCallback(this, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ ErrorCodeEnum TestEntity(const char* lcpszEntityName, bool bShake = false)
|
|
|
+ {
|
|
|
+ if(!bShake)
|
|
|
+ LogEvent(Severity_High, 0, CSimpleStringA::Format("Test specified entity: %s", lcpszEntityName));
|
|
|
+ auto pPriviFunc = GetFunction()->GetPrivilegeFunction();
|
|
|
+ CSmartPointer<IAsynWaitSp> waitSp;
|
|
|
+ auto ec = pPriviFunc->TestEntity(lcpszEntityName, bShake ? Test_ShakeHand : Test_Examine, waitSp);
|
|
|
+ if (IS_FAILURED(ec))
|
|
|
+ {
|
|
|
+ THROW_ERROR("Try to %s for %s failed return %s!", bShake ? "check" : "test", lcpszEntityName, SpStrError(ec));
|
|
|
+ return Error_Failed;
|
|
|
+ }
|
|
|
+ SetOPCallback(waitSp, lcpszEntityName, bShake ? OP_CHECK : OP_TEST);
|
|
|
+ return Error_Succeed;
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ TestModeInfo _testInfo;
|
|
|
+ CUUID m_subUUID;
|
|
|
+ CAutoArray<CSimpleStringA> _arrTestEntities;
|
|
|
+ DWORD _dwStartedTestEntities;
|
|
|
+ CSystemRunInfo _runInfo;
|
|
|
+};
|
|
|
+
|
|
|
+SP_BEGIN_ENTITY_MAP()
|
|
|
+ SP_ENTITY(CTestDeamon)
|
|
|
+SP_END_ENTITY_MAP()
|