框架的功能:
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中做处理。