專家筆記:關(guān)于ETestDEV測試腳本開發(fā)模式的考慮
01需求的提出
ETestDEV中對(duì)于從通道中接收?qǐng)?bào)文數(shù)據(jù)的處理中,有一種處理方式為:
on_buff_recv(channel,fun),其中的fun為接收到buff的回調(diào)函數(shù),其定義的原型應(yīng)為:
function?fun(channel,buff)
end
在這個(gè)回調(diào)函數(shù)中,buff為傳進(jìn)來的接收到的緩沖區(qū)。
在操作系統(tǒng)中,通道channel傳送過來數(shù)據(jù),會(huì)產(chǎn)生一個(gè)消息,這個(gè)消息用于給應(yīng)用進(jìn)行處理響應(yīng)。
假設(shè)被測件為U,它對(duì)外定時(shí)向外發(fā)送兩種類型的協(xié)議包,分別為ProtoA和ProtoB,ProtoA和ProtoB以不同的時(shí)鐘周期發(fā)送,每10ms向外發(fā)送一幀ProtoA,每20ms向外發(fā)送一幀ProtoB;除此之外,被測件U的部分功能還會(huì)根據(jù)不同的用戶操作隨機(jī)性地向外發(fā)送協(xié)議包ProtoC。
對(duì)于測試設(shè)備T而言,T就需要使用on_buff_recv(channel,fun)處理從被測件U發(fā)送過來的數(shù)據(jù),這些數(shù)據(jù)中夾雜著協(xié)議包ProtoA、ProtoB、ProtoC,在這些個(gè)協(xié)議包中還有可能中間存在著臟數(shù)據(jù)。
那么問題來了,如何在fun(channel,buff)處理數(shù)據(jù)的協(xié)議包解析,才能確保解析速率與編程模式都最佳呢?
02ETestDEV對(duì)解包的支持
2.1 ?默認(rèn)提供的解包函數(shù)
在ETestDEV中,提供了使用協(xié)議定義從數(shù)據(jù)緩存解析出協(xié)議報(bào)文數(shù)據(jù)的API,其原型如下:
result = unpack(prot,buff,is_auto_shift)
輸入?yún)?shù):
prot:EProtocol 類型,協(xié)議對(duì)象
buff:EBuff 類型,數(shù)據(jù)緩存
is_auto_shift:boolean 類型,解包成功后是否自動(dòng)移除使用過的字節(jié)
返回值:result:dict 類型,解析結(jié)果字典 result.value:報(bào)文數(shù)據(jù)值 result.size:解包使用的字節(jié)長度 result.prot:解析時(shí)使用的協(xié)議名稱 result.left:解包完成時(shí)還剩余未使用的字節(jié)數(shù) result.skip:開始解析之前忽略的字節(jié)數(shù) result.valid_fail_segs:自動(dòng)驗(yàn)證失敗的協(xié)議字段名稱。
這個(gè)解包函數(shù)只能從buff中解析出符合某個(gè)協(xié)議的報(bào)文。
2.2 ?解包的需求及其簡單實(shí)現(xiàn)
如果按照被測件U對(duì)外發(fā)送數(shù)據(jù)的需求,在某一個(gè)on_buff_recv(channel,fun)的響應(yīng)函數(shù)中,其buff可能是 XX XX XX XX ProtoA ProtoB XX XX ProtoC ,也可能是ProtoC XX XX ProtoA XX XX ProtoB。其中XX可以代表不屬于ProtoA ProtoB ProtoC的數(shù)據(jù),也可以是臟數(shù)據(jù)。甚至對(duì)于buff而言,有可能會(huì)在末尾的ProtoC中,ProtoC被截?cái)啵琍rotoC被截成兩段,前一段位于buff中,而后一段位于下次的buff中。
如果我們采用ETestDEV中默認(rèn)提供的解包函數(shù)來做的話,要實(shí)現(xiàn)對(duì)上述解包需求的響應(yīng),在調(diào)用unpack(prot,buff,is_auto_shift)進(jìn)行解包處理時(shí),就不得不使用is_auto_shift=false,不移除掉已經(jīng)解包的buff,否則對(duì)于:
ProtoC XX XX ProtoA XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro
其中,Pro表示只有協(xié)議的一半,這樣的buff數(shù)據(jù),先解包ProtoA后,如果采用is_auto_shift=true,則必然解包ProtoA時(shí),就把之前的ProtoC XX XX從緩沖區(qū)中移除,使得ProtoC不再能被解出,則不得不使用is_auto_shift=false調(diào)用對(duì)ProtoA的解包。
整個(gè)解包程序如下:
????--剩余的字節(jié)數(shù)

上述解包程序的基本思路是在一個(gè)循環(huán)中對(duì)ProtoA ProtoB ProtoC分別進(jìn)行解包,解包時(shí)切記不能移除已解出的buff,然后每一次解包成功時(shí)將留下的left字節(jié)數(shù)加入到一個(gè)table中,這個(gè)table為leftlen,然后在循環(huán)中當(dāng)ProtoA ProtoB ProtoC均不再能解出時(shí),跳出循環(huán)。
循環(huán)跳出后,然后移除掉已經(jīng)完成解包的所有數(shù)據(jù),但需要留下最后留下的len,以等待下一個(gè)on_buff_recv的到來,這樣下一個(gè)on_buff_recv到來時(shí)還能有機(jī)會(huì)把之前留下的len與新到來數(shù)據(jù)的組合形成完整的ProtoC。
2.3 ?遞歸解包的實(shí)現(xiàn)
采用2.2的方法進(jìn)行解包的一個(gè)明顯問題是:由于每次調(diào)用unpack進(jìn)行解包處理時(shí),并沒有移除掉已經(jīng)解包完成的buff,循環(huán)中的下一個(gè)調(diào)用unpack則不得不從buff的開頭重新搜索解包,這顯然降低了處理的速度和效率。對(duì)于如:
ProtoC XX XX ProtoA XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro
這樣的buff數(shù)據(jù),我們解出了ProtoA后,自然就希望在之前的ProtoC XX XX中繼續(xù)找剩余的ProtoB和ProtoC,在之后的XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro繼續(xù)找ProtoA、ProtoB、ProtoC。我們將ProtoA、ProtoB、ProtoC放入到1個(gè)table中,每次都取第1個(gè)Proto從buff中解包,然后把buff一截兩端為buff0與buff1,重新形成一個(gè)table,設(shè)為table2,從table2中移除掉第1個(gè),然后再在buff0中尋找table2中所有Proto的匹配,而在buff1中尋找所有table中的所有Proto。
根據(jù)上述思路,我們編寫出buff中具有多種類型,每種類型具有多個(gè)包的遞歸解包函數(shù),如下圖所示:? ? ? ? ? ? ? ? ? ??

2.4 ?遞歸后的測試程序開發(fā)模式
有了上述遞歸解包函數(shù)后,我們將這個(gè)遞歸解包函數(shù)放入ETestDEV公共庫中,作為ETestDEV所沉淀的測試程序資產(chǎn),不斷壯大ETestDEV的能力,為測試程序開發(fā)提供更多的支持。

進(jìn)入公共庫中的公用模塊OnRcvFromChannel可以在測試程序中進(jìn)行應(yīng)用,以簡化測試程序的開發(fā)邏輯,OnRcvFromChannel引入了請(qǐng)求解包服務(wù)注冊(cè)的機(jī)制,可以向公共庫模塊OnRcvFromChannel進(jìn)行協(xié)議以及協(xié)議處理函數(shù)的注冊(cè),原型如下:
function?OnRcvFromChannel.RegisterProto(Proto,fun)
其中Proto為在ETestDEV中定義的協(xié)議,fun是當(dāng)解析到具有該協(xié)議包的數(shù)據(jù)時(shí),協(xié)議包的處理函數(shù),可以在這個(gè)處理函數(shù)中對(duì)接收到已經(jīng)解包完成的協(xié)議包進(jìn)行各種處理,如計(jì)算、界面顯示、通信響應(yīng)等。
03在實(shí)際項(xiàng)目中的實(shí)驗(yàn)
3.1 ?被測系統(tǒng)UUT模擬
在如下的測試環(huán)境拓?fù)渲?,被測件UUT連接著兩個(gè)測試設(shè)備,當(dāng)已經(jīng)編寫完成與協(xié)議、測試邏輯相關(guān)的測試程序與測試用例后,一個(gè)問題是如何在缺少被測件UUT的情況下,對(duì)已經(jīng)編寫完成的測試程序進(jìn)行調(diào)試。

在ETest中不僅提供了測試程序的開發(fā)能力,也提供了模擬被測件的能力,我們只需要設(shè)置某個(gè)測試程序?yàn)槟M腳本,就可以執(zhí)行這個(gè)模擬腳本作為被測件的模擬,從而驅(qū)動(dòng)對(duì)測試程序的調(diào)試工作。
模擬被測件以一個(gè)單獨(dú)控制臺(tái)的形式運(yùn)行,其運(yùn)行在模擬的ETestDEV執(zhí)行器上,為了確保模擬被測件根據(jù)測試程序進(jìn)行響應(yīng),模擬被測件在接到測試程序發(fā)送的數(shù)據(jù)后,向測試程序發(fā)送數(shù)據(jù)包,以檢驗(yàn)測試程序在打包解包處理中的正確性。

3.2 ?被測件UUT發(fā)包情況
在被測件UUT接收到測試程序發(fā)送的數(shù)據(jù)后,其一次性地向外輸出多包帶有不同協(xié)議的數(shù)據(jù)包,其中還夾雜著一些隨機(jī)的干擾數(shù)據(jù)。如下圖所示:

被測件發(fā)送出這樣的數(shù)據(jù),不同種類協(xié)議出現(xiàn)的順序有可能是不同的,解包程序需要從buffer中解包出所有已在OnRcvFromChannel中注冊(cè)的協(xié)議包數(shù)據(jù)。被測件對(duì)外發(fā)送的數(shù)據(jù)包模擬程序如下圖。


被測件發(fā)送數(shù)據(jù)包協(xié)議的不確定性大大增加了從buffer中解包處理的難度,不僅要求邏輯正確,還必須保證性能。
3.3 ?實(shí)驗(yàn)結(jié)果
在測試程序中,我們首先向引入的公共庫OnRcvFromChannel注冊(cè)三種類型的協(xié)議幀格式及其響應(yīng)處理函數(shù),然后向被測件UUT發(fā)送一個(gè)“開始處理”的字符串,被測件就會(huì)從模擬程序中接收到。在ETestDEV中對(duì)于所有的類型的通道均可以使用虛擬的方式模擬各種類型接口通道的通信。


下圖展示出了被測件UUT發(fā)送數(shù)據(jù)的情況和測試程序解包處理的情況,從圖中可以看出,被測件發(fā)送出的1A 1A 1B 1C,ETestDEV的框架機(jī)制產(chǎn)生了on_buff_recv回調(diào),在這個(gè)回調(diào)中,顯然找不到在OnRcvFromChannel中注冊(cè)的任何協(xié)議,所以很快跳出了遞歸。


緊接著被測件UUT又連續(xù)發(fā)送了多包具有協(xié)議內(nèi)容的數(shù)據(jù),注意ETestDEV里對(duì)于被測件UUT多包發(fā)送數(shù)據(jù),只產(chǎn)生了一個(gè)回調(diào),這大幅減輕了ETestDEV框架中協(xié)議處理的效率,在框架所提供的回調(diào)中,我們采用了多包解析的方式進(jìn)行處理。從圖中可以看到,解包時(shí)間可以在300us內(nèi)完成,說明ETestDEV對(duì)于1ms周期內(nèi)解包響應(yīng)是滿足要求的。
04啟示
ETestDEV在協(xié)議包的定義、解析處理、打包等功能上,提供了校驗(yàn)函數(shù)擴(kuò)展、字段打包解包函數(shù)擴(kuò)展等功能,ETestDEV為我們提供的測試系統(tǒng)開發(fā)的基礎(chǔ)框架,解決了協(xié)議、通道、測試環(huán)境、腳本執(zhí)行等一系列問題。在這個(gè)框架中,我們還可以不斷為其賦予新的功能,不斷增長ETestDEV的能力,使得ETestDEV成為真正的測試系統(tǒng)生產(chǎn)力工具。