天天看點

專家筆記:關于ETestDEV測試腳本開發模式的考慮

作者:凱雲科技

1需求的提出

ETestDEV中對于從通道中接收封包資料的進行中,有一種處理方式為:

on_buff_recv(channel,fun),其中的fun為接收到buff的回調函數,其定義的原型應為:

function fun(channel,buff)

end

在這個回調函數中,buff為傳進來的接收到的緩沖區。

在作業系統中,通道channel傳送過來資料,會産生一個消息,這個消息用于給應用進行處理響應。

假設被測件為U,它對外定時向外發送兩種類型的協定包,分别為ProtoA和ProtoB,ProtoA和ProtoB以不同的時鐘周期發送,每10ms向外發送一幀ProtoA,每20ms向外發送一幀ProtoB;除此之外,被測件U的部分功能還會根據不同的使用者操作随機性地向外發送協定包ProtoC。

對于測試裝置T而言,T就需要使用On_on_buff_recv(channel,fun)處理從被測件U發送過來的資料,這些資料中夾雜着協定包ProtoA、ProtoB、ProtoC,在這些個協定包中還有可能中間存在着髒資料。

那麼問題來了,如何在fun(channel,buff)處理資料的協定包解析,才能確定解析速率與程式設計模式都最佳呢?

2ETestDEV對解包的支援

2.1 預設提供的解包函數

在ETestDEV中,提供了使用協定定義從資料緩存解析出協定封包資料的API,其原型如下:

result = unpack(prot,buff,is_auto_shift)

輸入參數:

prot:EProtocol 類型,協定對象

buff:EBuff 類型,資料緩存

is_auto_shift:boolean 類型,解包成功後是否自動移除使用過的位元組

傳回值: result:dict 類型,解析結果字典 result.value:封包資料值 result.size:解包使用的位元組長度 result.prot:解析時使用的協定名稱 result.left:解包完成時還剩餘未使用的位元組數 result.skip:開始解析之前忽略的位元組數 result.valid_fail_segs:自動驗證失敗的協定字段名稱。

這個解包函數隻能從buff中解析出符合某個協定的封包。

2.2 解包的需求及其簡單實作

如果按照被測件U對外發送資料的需求,在某一個on_buff_recv(channel,fun)的響應函數中,其buff可能是 XX XX XX XX ProtoA ProtoB XX XX ProtoC ,也可能是ProtoC XX XX ProtoA XX XX ProtoB。其中XX可以代表不屬于ProtoA ProtoB ProtoC的資料,也可以是髒資料。甚至對于buff而言,有可能會在末尾的ProtoC中,ProtoC被截斷,ProtoC被截成兩段,前一段位于buff中,而後一段位于下次的buff中。

如果我們采用ETestDEV中預設提供的解包函數來做的話,要實作對上述解包需求的響應,在調用unpack(prot,buff,is_auto_shift)進行解包處理時,就不得不使用is_auto_shift=false,不移除掉已經解包的buff,否則對于:

ProtoC XX XX ProtoA XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro

其中,Pro表示隻有協定的一半,這樣的buff資料,先解包ProtoA後,如果采用is_auto_shift=true,則必然解包ProtoA時,就把之前的ProtoC XX XX從緩沖區中移除,使得ProtoC不再能被解出,則不得不使用is_auto_shift=false調用對ProtoA的解包。

整個解包程式如下:

--剩餘的位元組數

local leftlen={}

while true do

local res1 = unpack(protocols.ProtoA, buff.value, true)

local res2 = unpack(protocols ProtoB, buff.value, true)

local res3 = unpack(protocols.ProtoC, buff.value, true)

if res1 ~= nil and res1.value ~= nil then

table.insert(leftlen,res1.left)

ProtoA =res1.value

end

if res2 ~= nil and res2.value ~= nil then

table.insert(leftlen,res2.left)

ProtoB=res2.value

end

if res3 ~= nil and res3.value ~= nil then

table.insert(leftlen,res3.left)

ProtoC=res3.value

end

if res1 == nil and res2 == nil and res3 == nil then

break

end

end

local len=min(leftlen)

if len>=0 then

ebuff.shift_len(buff.value,#buff.value-len)

end

上述解包程式的基本思路是在一個循環中對ProtoA ProtoB ProtoC分别進行解包,解包時切記不能移除已解出的buff,然後每一次解包成功時将留下的left位元組數加入到一個table中,這個table為leftlen,然後在循環中當ProtoA ProtoB ProtoC均不再能解出時,跳出循環。

循環跳出後,然後移除掉已經完成解包的所有資料,但需要留下最後留下的len,以等待下一個on_buff_recv的到來,這樣下一個on_buff_recv到來時還能有機會把之前留下的len與新到來資料的組合形成完整的ProtoC。

2.3 遞歸解包的實作

采用2.2的方法進行解包的一個明顯問題是:由于每次調用unpack進行解包處理時,并沒有移除掉已經解包完成的buff,循環中的下一個調用unpack則不得不從buff的開頭重新搜尋解包,這顯然降低了處理的速度和效率。對于如:

ProtoC XX XX ProtoA XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro

這樣的buff資料,我們解出了ProtoA後,自然就希望在之前的ProtoC XX XX中繼續找剩餘的ProtoB和ProtoC,在之後的XX XX ProtoB ProtoA XX XX ProtoB XX XX Pro繼續找ProtoA、ProtoB、ProtoC。我們将ProtoA、ProtoB、ProtoC放入到1個table中,每次都取第1個Proto從buff中解包,然後把buff一截兩端為buff0與buff1,重新形成一個table,設為table2,從table2中移除掉第1個,然後再在buff0中尋找table2中所有Proto的比對,而在buff1中尋找所有table中的所有Proto。

根據上述思路,我們編寫出buff中具有多種類型,每種類型具有多個包的遞歸解包函數,如下圖所示:

專家筆記:關于ETestDEV測試腳本開發模式的考慮

2.4 遞歸後的測試程式開發模式

有了上述遞歸解包函數後,我們将這個遞歸解包函數放入ETestDEV公共庫中,作為ETestDEV所沉澱的測試程式資産,不斷壯大ETestDEV的能力,為測試程式開發提供更多的支援。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

進入公共庫中的公用子產品OnRcvFromChannel可以在測試程式中進行應用,以簡化測試程式的開發邏輯,OnRcvFromChannel引入了請求解包服務注冊的機制,可以向公共庫子產品OnRcvFromChannel進行協定以及協定處理函數的注冊,原型如下:

function OnRcvFromChannel.RegisterProto(Proto,fun)

其中Proto為在ETestDEV中定義的協定,fun是當解析到具有該協定包的資料時,協定包的處理函數,可以在這個處理函數中對接收到已經解包完成的協定包進行各種處理,如計算、界面顯示、通信響應等。

3在實際項目中的實驗

3.1 被測系統UUT模拟

在如下的測試環境拓撲中,被測件UUT連接配接着兩個測試裝置,當已經編寫完成與協定、測試邏輯相關的測試程式與測試用例後,一個問題是如何在缺少被測件UUT的情況下,對已經編寫完成的測試程式進行調試。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

在ETest中不僅提供了測試程式的開發能力,也提供了模拟被測件的能力,我們隻需要設定某個測試程式為模拟腳本,就可以執行這個模拟腳本作為被測件的模拟,進而驅動對測試程式的調試工作。

模拟被測件以一個單獨控制台的形式運作,其運作在模拟的ETestDEV執行器上,為了確定模拟被測件根據測試程式進行響應,模拟被測件在接到測試程式發送的資料後,向測試程式發送資料包,以檢驗測試程式在打包解包進行中的正确性。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

3.2 被測件UUT發包情況

在被測件UUT接收到測試程式發送的資料後,其一次性地向外輸出多包帶有不同協定的資料包,其中還夾雜着一些随機的幹擾資料。如下圖所示:

專家筆記:關于ETestDEV測試腳本開發模式的考慮

被測件發送出這樣的資料,不同種類協定出現的順序有可能是不同的,解包程式需要從buffer中解包出所有已在OnRcvFromChannel中注冊的協定包資料。被測件對外發送的資料包模拟程式如下圖。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

被測件發送資料包協定的不确定性大大增加了從buffer中解包處理的難度,不僅要求邏輯正确,還必須保證性能。

3.3 實驗結果

在測試程式中,我們首先向引入的公共庫OnRcvFromChannel注冊三種類型的協定幀格式及其響應處理函數,然後向被測件UUT發送一個“開始處理”的字元串,被測件就會從模拟程式中接收到。在ETestDEV中對于所有的類型的通道均可以使用虛拟的方式模拟各種類型接口通道的通信。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

下圖展示出了被測件UUT發送資料的情況和測試程式解包處理的情況,從圖中可以看出,被測件發送出的1A 1A 1B 1C,ETestDEV的架構機制産生了on_buff_recv回調,在這個回調中,顯然找不到在OnRcvFromChannel中注冊的任何協定,是以很快跳出了遞歸。

專家筆記:關于ETestDEV測試腳本開發模式的考慮

緊接着被測件UUT又連續發送了多包具有協定内容的資料,注意ETestDEV裡對于被測件UUT多包發送資料,隻産生了一個回調,這大幅減輕了ETestDEV架構中協定處理的效率,在架構所提供的回調中,我們采用了多包解析的方式進行處理。從圖中可以看到,解包時間可以在300us内完成,說明ETestDEV對于1ms周期内解包響應是滿足要求的。

4啟示

ETestDEV在協定包的定義、解析處理、打包等功能上,提供了校驗函數擴充、字段打包解包函數擴充等功能,ETestDEV為我們提供的測試系統開發的基礎架構,解決了協定、通道、測試環境、腳本執行等一系列問題。在這個架構中,我們還可以不斷為其賦予新的功能,不斷增長ETestDEV的能力,使得ETestDEV成為真正的測試系統生産力工具。

還想了解更多内容,歡迎關注微信公衆号【ETest】

繼續閱讀