框架的功能:
1. 把測試類的所有接口的測試方法打包成一個epk
2. 通過啓動工具啓動測試用例的epk,運行包含的測試用例
1. 啓動epk(類似apk,啓動的時候進入onCreate)
ECode CActivityOne::OnCreate(
/* [in] */ IBundle* savedInstanceState)
{
Activity::OnCreate(savedInstanceState);
SetContentView(R::layout::main);
sem_init(&g_sem, 0, 0);
SetUp();
String strPackageName;
char szIndex[10];
Int32 index;
AutoPtr<IIntent> intent;
GetIntent((IIntent**)&intent);
intent->GetInt32Extra(String("ARG"), -1, &index);
intent->GetPackage(&strPackageName);
int nLength = sizeof(TestEntry) / sizeof(TestEntry[0]);
if (index <= 0 || index > nLength) {
printf("*ERROR* Invalid testing case number\n");
return -1;
}
sprintf(szIndex , "%d" , index);
char *argv[] = {(char*)(const char *)strPackageName, szIndex};
int argc = 2;
TPINIT;
ActivityParams* pArg = new ActivityParams;
pArg->mActivity = this;
pArg->mIndex = index;
AutoPtr<IHandlerCallback> handlerCallback = new TestcaseCallback((void*)pArg);
CHandler::New((IHandlerCallback*)handlerCallback, TRUE, (IHandler**)&mDefaultHandler);
AutoPtr<TestcaseRunnable> testcaseRunnable;
AutoPtr<IThread> thread;
testcaseRunnable = new TestcaseRunnable((void*)pArg);
CThread::New((IRunnable*)testcaseRunnable, (IThread**)&thread);
thread->Start();
return NOERROR;
}
我們來分析下上面的代碼:
開始的時候,我們初始化啓動的Activity,並且初始化一個信號量,用來處理多線程操作。
sem_init(&g_sem, 0, 0);
接着我們從android的控件通信工具Intent中取出要運行的測試用例編號--index;
AutoPtr<IIntent> intent;
GetIntent((IIntent**)&intent);
intent->GetInt32Extra(String("ARG"), -1, &index);
計算下有多少個用例要跑,並且判斷index是不是在用例號範圍內。
int nLength = sizeof(TestEntry) / sizeof(TestEntry[0]);
if (index <= 0 || index > nLength) {
printf("*ERROR* Invalid testing case number\n");
return -1;
}
上面的TestEntry是一個函數指針數組,如下:
typedef ECode (CActivityOne::*PTestEntry)();
PTestEntry TestEntry[] = {
&CActivityOne::Test1,
&CActivityOne::Test2,
&CActivityOne::Test3,
&CActivityOne::Test4,
&CActivityOne::Test5,
&CActivityOne::Test6,
&CActivityOne::Test7,
&CActivityOne::Test8,
&CActivityOne::Test9,
&CActivityOne::Test10,
};
這樣後續就可以直接通過調用函數指針來調用測試用例了。
繼續看OnCreate裏面的代碼:
AutoPtr<IHandlerCallback> handlerCallback = new TestcaseCallback((void*)pArg);
CHandler::New((IHandlerCallback*)handlerCallback, TRUE, (IHandler**)&mDefaultHandler);
這裏new了一個主線程的handler,主要想法是會測試一些view,那樣就可以在另外一個線程裏處理好耗時操作後,發個message來更新頁面,測試UI接口的正確性。
接着創建一個線程去調用測試用例:
AutoPtr<TestcaseRunnable> testcaseRunnable;
AutoPtr<IThread> thread;
testcaseRunnable = new TestcaseRunnable((void*)pArg);
CThread::New((IRunnable*)testcaseRunnable, (IThread**)&thread);
thread->Start();
我們再來看看這個線程裏做了什麼操作:
ECode CActivityOne::TestcaseRunnable::Run()
{
ActivityParams* arg = reinterpret_cast<ActivityParams*>(mArg);
CActivityOne* activity = arg->mActivity;
activity->EntryRoutine((void*)arg);
return NOERROR;
}
去掉用EntryRoutine方法,那麼在這個方法裏又幹了什麼?接着看
void* CActivityOne::EntryRoutine(void *arg)
{
ActivityParams* paramsArg = reinterpret_cast<ActivityParams*>(arg);
CActivityOne* activity = paramsArg->mActivity;
int index = paramsArg->mIndex;
do {
activity->PostcppCallback(index);
sem_wait(&g_sem);
} while (!s_beQuit);
#ifdef AutoExit
TPOK;
activity->Activity::Finish();
KillElastosProcess();
#endif
return reinterpret_cast<void*>(0);
}
這裏調用PostcppCallback去執行,在裏面會發送一個message,讓主線程去執行對應的測試用例。
同時,當前線程要進入等待狀態。這裏還有一個問題,大家會看到一個do...while,原先考慮一次性運行所有測試用例,但是由於公司需要根據每條測試用例來關聯bug,
因此此處暫時設置s_beQuit = True。
來看看PostcppCallback:
ECode CActivityOne::PostcppCallback(int index)
{
AutoPtr<IMessage> msg;
Boolean result;
mDefaultHandler->ObtainMessage(102, (IMessage**)&msg);
msg->SetWhat(index);
mDefaultHandler->SendMessage(msg.Get(), &result);
TPAssertTrue("send message failed.\n", result);
return NOERROR;
}
這裏就看到通過mDefaultHandler向主線程發送message。
那麼主線程怎麼處理這些消息呢?
ECode CActivityOne::TestcaseCallback::HandleMessage(
/* [in] */ IMessage * msg,
/* [out] */ Boolean * result)
{
Int32 index;
msg->GetWhat(&index);
ActivityParams* pArg = (ActivityParams*)mArg;
CActivityOne* activity = pArg->mActivity;
(activity->*TestEntry[index-1])();
sem_post(&g_sem);
return NOERROR;
}
這裏就會通過測試用例的函數指針去調用具體的testcase。
執行完後,信號量加1,使剛剛處於等待狀態的線程繼續運行。
#ifdef AutoExit
TPOK;
activity->Activity::Finish();
KillElastosProcess();
#endif
執行完成後,就退出當前運行的Activity。
finish後沒有結束當前進程,糾結,發現android中也沒辦法。
由於權限的問題,KillElastosProcess也沒法殺死系統進程,只好在運行腳本runbat中做處理。