石佳琦,陳 鵬
1(武漢郵電科學(xué)研究院,武漢 430010)
2(烽火通信科技股份有限公司,武漢 430010)
覆蓋率對(duì)于軟件測(cè)試有著非常重要的作用.通過代碼覆蓋率,可以發(fā)現(xiàn)程序的未測(cè)試部分,估計(jì)程序中哪段代碼最耗時(shí),幫助開發(fā)人員創(chuàng)建更高效、更快的運(yùn)行代碼,提高代碼質(zhì)量.
GCOV是一個(gè)GNU的本地覆蓋測(cè)試工具[1],配合GCC共同實(shí)現(xiàn)對(duì)C或者C++文件的語(yǔ)句覆蓋和分支覆蓋測(cè)試.而在Linux環(huán)境下,shell腳本的魅力大放異彩,通過帶參數(shù)的指令集,可以實(shí)現(xiàn)許多的功能.Expect腳本工具可以實(shí)現(xiàn)交互式任務(wù),而無需人的干預(yù).Cygwin 工具,更是為我們提供了一種在Windows環(huán)境下類Linux的環(huán)境.本文基于GCOV工具覆蓋率生成流程,配合上述軟件工具完成Linux平臺(tái)下代碼覆蓋率報(bào)告自動(dòng)化輸出設(shè)計(jì)[2,3].
基本塊BB(Basic Block)是程序中一個(gè)順序執(zhí)行的語(yǔ)句序列,BB中的所有語(yǔ)句的執(zhí)行次數(shù)一定相同,一般由多個(gè)順序執(zhí)行語(yǔ)句后跟一個(gè)跳轉(zhuǎn)語(yǔ)句組成.ARC分支,從一個(gè)BB到另一個(gè)BB的跳轉(zhuǎn)記為一個(gè)ARC.
如圖1所示.如果跳轉(zhuǎn)語(yǔ)句是有條件的,就產(chǎn)生了一個(gè)分支(ARC),該基本塊就有兩個(gè)基本塊作為目的地.如果把每個(gè)基本塊當(dāng)作一個(gè)節(jié)點(diǎn),那么一個(gè)函數(shù)中的所有基本塊就構(gòu)成了一個(gè)有向圖,稱之為基本塊圖.只要知道BB或ARC的執(zhí)行次數(shù)就可以推算出所有的BB和所有的ARC的執(zhí)行次數(shù).GCOV根據(jù)BB和ARC的統(tǒng)計(jì)情況來統(tǒng)計(jì)各BB內(nèi)各行代碼執(zhí)行情況,從而計(jì)算整個(gè)程序的覆蓋率情況[4].
圖1 基本塊圖
主要工作流程見圖2.
1)編譯前,在編譯器中加入編譯器參數(shù)-fprofilearcs-ftest-coverage.
2)源碼經(jīng)過編譯預(yù)處理,在生成匯編文件的階段完成插樁.生成可執(zhí)行文件,并且生成關(guān)聯(lián)BB和跳轉(zhuǎn)次數(shù)ARC的*.gcno文件.
3)執(zhí)行可執(zhí)行文件,在運(yùn)行過程中“樁點(diǎn)”負(fù)責(zé)收集程序的執(zhí)行信息[5].
4)生成具有BB和ARC的執(zhí)行統(tǒng)計(jì)次數(shù)等數(shù)據(jù)信的*.gcda文件.
5)通過lcov和genhtml可將*.gcno、*.gcda中的統(tǒng)計(jì)信息圖形化,生成具體的報(bào)告文檔.
由于實(shí)際運(yùn)行環(huán)境為嵌入式分布式設(shè)備,且編譯環(huán)境和測(cè)試環(huán)境分別在不同的服務(wù)器上,我們采用模塊化設(shè)計(jì),將各功能的實(shí)現(xiàn)模塊化,便于后續(xù)的管理優(yōu)化,降低各階段執(zhí)行的耦合率,提高覆蓋率自動(dòng)化測(cè)試的時(shí)效性.
圖2 GCOV收集覆蓋率信息流程圖
整體環(huán)境分為四部分: 編譯環(huán)境、運(yùn)行環(huán)境、服務(wù)器端、用戶端.
編譯環(huán)境: 主要實(shí)現(xiàn)不同模塊軟件源代碼編譯打包,完成覆蓋率報(bào)告的生成.
運(yùn)行環(huán)境: 軟件運(yùn)行的實(shí)際環(huán)境.通過更新軟件實(shí)際運(yùn)行,實(shí)現(xiàn)代碼在具體目標(biāo)終端設(shè)備機(jī)上 運(yùn)行的代碼覆蓋率實(shí)際情況.
服務(wù)器端: 工程測(cè)試人員操作平臺(tái),主要完成源代碼編譯的操作的控制、更新代碼在目標(biāo)終端設(shè)備機(jī)上運(yùn)行情況的控制以及代碼自動(dòng)化覆蓋率測(cè)試及輸出的.
用戶端: 工程測(cè)試人員個(gè)人PC覆蓋率報(bào)告文檔的目標(biāo)用戶,可以設(shè)置多個(gè).
圖3為系統(tǒng)結(jié)構(gòu)拓?fù)鋱D.
Cygwin是一個(gè)在Windows平臺(tái)上運(yùn)行的類Linux模擬環(huán)境,它提供一個(gè)UNIX模擬 DLL 以及在其上上層構(gòu)建的多種可以在 Linux 系統(tǒng)中找到的軟件包.
Expect是一種免費(fèi)的編程工具語(yǔ)言,用來實(shí)現(xiàn)自動(dòng)交互式任務(wù)進(jìn)行通信,而無需人工值守.它是一個(gè)用來實(shí)現(xiàn)自動(dòng)交互功能的軟件套件.
在當(dāng)前Windows工作服務(wù)器安裝Cygwin 軟件,在安裝時(shí)勾選除Linux環(huán)境一些常見的軟件包外,還需勾選基于TCL 的Expect 軟件包,配置支持遠(yuǎn)程遠(yuǎn)程登錄的ssh等服務(wù)[2].
圖3 系統(tǒng)結(jié)構(gòu)拓?fù)鋱D
2.4.1 服務(wù)器端設(shè)計(jì)目的與實(shí)現(xiàn)的功能
由于程序需要在終端設(shè)備上運(yùn)行,且測(cè)試環(huán)境和編譯環(huán)境在不同的服務(wù)器上,這就需要我們?cè)O(shè)置一個(gè)中心節(jié)點(diǎn)可以有效的將各模塊和環(huán)境聯(lián)系起來,以實(shí)現(xiàn)設(shè)計(jì)的完整性和操作連貫性.
不同用戶可以遠(yuǎn)程到服務(wù)器端,依據(jù)GCOV覆蓋率報(bào)告生成的步驟,通過調(diào)用封裝好腳本工具實(shí)現(xiàn)如下功能:
自動(dòng)登錄編譯環(huán)境完成編譯,拷貝可執(zhí)行文至服務(wù)器端的用戶目錄;
拷貝可執(zhí)行文件至測(cè)試環(huán)境,并將生成的*.gcda文件拷貝至用戶目錄;
自動(dòng)檢查用戶目錄存在*.gcda文件時(shí),拷貝*.gcda文件至編譯環(huán)境對(duì)應(yīng)目錄,執(zhí)行l(wèi)cov 和 genhtml完成可視化覆蓋率報(bào)告的生成
自動(dòng)檢查在編譯環(huán)境對(duì)應(yīng)目錄下是否存在生成的覆蓋率報(bào)告,并拷貝至服務(wù)端用戶目錄.
2.4.2 編譯環(huán)境設(shè)計(jì)目的與實(shí)現(xiàn)的功能
大型的C工程的編譯都有各自的makefile和編譯腳本.編譯時(shí)工程開發(fā)人員通過鍵入不同的參數(shù)來編譯外鏈庫(kù)文件或者可執(zhí)行文件.
該模塊設(shè)計(jì)主要目的是生成覆蓋率數(shù)據(jù)文件*.gcno.通過修改編譯腳本實(shí)現(xiàn)插樁編譯以及外鏈庫(kù)和可執(zhí)行文件的單一編譯或混合編譯.通過判斷是否帶GCOV參數(shù)來控制是否進(jìn)行插樁編譯,即使在不進(jìn)行覆蓋率編譯的情況下,也可以實(shí)現(xiàn)普通編譯及文件拷貝,減少了開發(fā)人員與編譯服務(wù)器的交互.該模塊主要響應(yīng)來自“服務(wù)器端”開發(fā)人員鍵入命令,對(duì)編譯環(huán)境下常用編譯文件、腳本的解析執(zhí)行,完成軟件的編譯拷貝工作.將參數(shù)提取,根據(jù)工程開發(fā)人員在編譯模塊腳本鍵入不同的參數(shù)組合實(shí)現(xiàn)單一庫(kù)文件或可執(zhí)行文件的編譯,以及庫(kù)文件可執(zhí)行文件的混合編譯.服務(wù)器端不編譯服務(wù)器交互過程見圖4.
2.4.3 運(yùn)行環(huán)境設(shè)計(jì)目的與實(shí)現(xiàn)的功能
運(yùn)行環(huán)境設(shè)計(jì)的主要目的是生成覆蓋率數(shù)據(jù)文件*.gcda.運(yùn)行可執(zhí)行文件,在捕捉到外部信號(hào)時(shí),對(duì)程序進(jìn)行exit.檢查是否存在*.gcda文件并通過服務(wù)器端轉(zhuǎn)存到編譯環(huán)境.
2.4.4 用戶側(cè)設(shè)計(jì)目的與實(shí)現(xiàn)的功能
用戶側(cè)為遠(yuǎn)程到服務(wù)器端的客戶端.是開發(fā)人員工作的物理環(huán)境.
圖4 服務(wù)器端不編譯服務(wù)器交互過程
3.1.1 流程提示工具
main.sh 參數(shù):(0~9 的數(shù)字 或無)
功能說明:
主要通過echo 命令對(duì)整個(gè)流程進(jìn)行索引,指導(dǎo)流程.參數(shù): 一個(gè)數(shù)字.不帶參數(shù)時(shí)輸出整個(gè)流程提示,已數(shù)字為流程序號(hào)索引;帶參數(shù)時(shí),輸出該數(shù)字對(duì)應(yīng)流程調(diào)用的腳本及示例.
腳本示例:
main.sh 1
3.1.2 拷貝工具
copy.sh 參數(shù): 1)拷貝環(huán)境;2)需要拷貝的文件;3)目標(biāo)環(huán)境;4)目標(biāo)路徑.
功能說明:
完成對(duì)本地文件拷貝到目標(biāo)環(huán)境,或從目標(biāo)環(huán)境拷貝文件到本地.其中目標(biāo)環(huán)境可以是運(yùn)行環(huán)境或用戶端.
功能實(shí)現(xiàn):
遠(yuǎn)程拷貝時(shí)需要輸入用戶名和密碼,以及路徑.由于我們拷貝文件的目錄比較固定,可以將這些靜態(tài)數(shù)據(jù)寫成配置文件,而動(dòng)態(tài)的拷貝環(huán)境,文件名等作為腳本工具的參數(shù).
local_to_dst 主要完成了本地到目標(biāo)環(huán)境的遠(yuǎn)程拷貝.將 scp 命令“嵌入”到Expect腳本中,對(duì)登錄用戶寫靜態(tài)配置,當(dāng)目標(biāo)環(huán)境提示輸入密碼時(shí),將提示語(yǔ)“password”作為Expect響應(yīng)關(guān)鍵字,此時(shí)將配置好的靜態(tài)密碼通過 Expect 中 send 命令發(fā)送,則實(shí)現(xiàn)了文件自動(dòng)拷貝.
同理dst_to_local 實(shí)現(xiàn)了反向的功能.
腳本示例:
copy.sh local shell_shell_gcov dst board1
3.1.3 校驗(yàn)工具
check.sh 參數(shù): 1)校驗(yàn)環(huán)境;2)工作目錄;3)文件類型;4)文件名稱;5)user_name;6)date.
功能說明:
完成對(duì)目標(biāo)環(huán)境下目標(biāo)文檔是否存在進(jìn)行校驗(yàn),完成不同拷貝動(dòng)作.
功能實(shí)現(xiàn):
調(diào)用本地或拷貝到運(yùn)行環(huán)境和編譯環(huán)境 shell_for_gcov 下的find_the_file.sh ,將目標(biāo)文件校驗(yàn)結(jié)果寫入校驗(yàn)文件,并拷貝到服務(wù)器端建立的用戶目錄下.我們對(duì)不同的目標(biāo)文件都設(shè)有一個(gè)以文件類型結(jié)尾的校驗(yàn)結(jié)果文件,通過這個(gè)文件寫入的內(nèi)容判斷是否存在目標(biāo)文件,是否可以進(jìn)行下一步的拷貝動(dòng)作.
腳本示例:
check.sh root gcov gcda gcda-0506 sjq 20180506
3.1.4 編譯工具
complex_compile.sh
參數(shù): 1)編譯類別;2)編譯目錄;3)工作路徑;4)版本信息;5)user_name;6)date.
功能說明:
通過編譯類別遠(yuǎn)程不同的編譯服務(wù)器,通過編譯目錄執(zhí)行不同的編譯腳本,加版本信息對(duì)每次編譯進(jìn)行區(qū)分.
功能實(shí)現(xiàn):
首先對(duì)編譯makefile修改增加和覆蓋率相關(guān)的編譯和鏈接選項(xiàng).
其次在當(dāng)前前Windows 服務(wù)器選定一個(gè)工作路徑,以以此路徑為基路徑,調(diào)用mkdir.sh 腳本,兩個(gè)參數(shù) 1)、人名,2)日期 ,以u(píng)ser_name為基路徑下一級(jí)目錄,日期作為一級(jí)目錄下二級(jí)目錄,用于存放相關(guān)文件.檢查基路徑下所有一級(jí)目錄名,若不存在則新建,若存在則進(jìn)入該目錄,檢查一級(jí)目錄下二級(jí)目錄名,如不存在則新建,若存在則輸出提示,新建的目錄名后加bak后綴以作區(qū)分.
通過Expect交互腳本完成對(duì)可執(zhí)行文件以及可執(zhí)行文件依賴的外鏈庫(kù)進(jìn)行自動(dòng)化編譯.軟件包內(nèi)的不同可執(zhí)行文件是否需要編譯可以通過修改配置文件完成,編譯時(shí)將編譯配置文件內(nèi)的參數(shù)傳入到編譯腳本.如果外鏈的庫(kù)需要編譯,則先編譯外鏈庫(kù)再將庫(kù)文件拷貝到編譯可執(zhí)行文件的lib庫(kù)目錄下編譯[6,7].
編譯完成后拷貝到服務(wù)器端的建立用戶目錄下,完成軟件包的解壓,將可執(zhí)行文件拷貝出.
腳本示例:
complex_compile.sh ccu " " 686214_V2R5_new 128 sjq 20171207
3.1.5 備份工具
backup.sh 參數(shù): 1)備份環(huán)境;2)備份文件類型;3)是否恢復(fù)操作標(biāo)志;4)user_name;5)date.
功能說明:
完成不同環(huán)境上容易變動(dòng)的文件的備份,以便環(huán)境恢復(fù)出錯(cuò)時(shí)可以降回到以前版本.
功能實(shí)現(xiàn):
主要在備份環(huán)境上不同類型的文件備份目錄下以u(píng)ser_name為基路徑下一級(jí)目錄,日期作為一級(jí)目錄下二級(jí)目錄.將文件拷貝只該目錄下.需要恢復(fù)環(huán)境時(shí),將該目錄下的文件拷回原目錄下.
腳本示例:
Backup.sh dst exe 0 sjq 20180506
功能說明:
完成對(duì)本地文件拷貝到目標(biāo)環(huán)境,或從目標(biāo)環(huán)境拷貝文件到本地.其中目標(biāo)環(huán)境可以是運(yùn)行環(huán)境或用戶端.
圖5為本系統(tǒng)工作的主要流程.
流程簡(jiǎn)述:
1)調(diào)用mkdir.sh 輸入 user_name 和 date 信息,在服務(wù)器端建立用戶目錄用于存放文件;
2)調(diào)用compile.sh 完成外鏈庫(kù)或軟件的編譯,編譯成功通過控制臺(tái)會(huì)輸出關(guān)鍵字“success”;
3)通過expect交互腳本語(yǔ)言做出匹配該關(guān)鍵字的操作,即調(diào)用服務(wù)器上shell_for_gcov 下的find_the_file.sh查找是否存在編譯好的文件并寫校驗(yàn)文件,然后將校驗(yàn)文件拷貝到服務(wù)器端.此時(shí),expect腳本會(huì)自動(dòng)調(diào)用check.sh 讀取用戶目錄下的校驗(yàn)文件,如果讀取可以拷貝,后將帶有覆蓋率編譯的軟件拷貝至用戶目錄下,解壓軟件將可執(zhí)行文件取出;
4)將運(yùn)行環(huán)境上的可執(zhí)行文件備份,將用戶目錄的新文件拷貝運(yùn)行環(huán)境運(yùn)行;
5)登錄運(yùn)行環(huán)境系統(tǒng)設(shè)置覆蓋率相關(guān)的環(huán)境變量,運(yùn)行程序;
6)觸發(fā)指令操作,覆蓋代碼執(zhí)行,待程序退出此時(shí)會(huì)在環(huán)境變量設(shè)置的目錄下生成存放同名C文件的GCDA 文件目錄;
7)校驗(yàn)運(yùn)行GCDA文件是否生成,并拷貝至編譯服務(wù)器上的編譯目錄;
8)在編譯環(huán)境執(zhí)行覆蓋率命令,此時(shí)會(huì)將覆蓋率的信息以html格式的文件輸出;
9)校驗(yàn)是否生成覆蓋率報(bào)告,并通過mail命令拷貝到用戶目錄.
本設(shè)計(jì)通過作者本人實(shí)際工作中進(jìn)行覆蓋率測(cè)試的經(jīng)驗(yàn)和經(jīng)歷提煉而來.在進(jìn)行覆蓋率統(tǒng)計(jì)時(shí),很多準(zhǔn)備工作都是是單調(diào)重復(fù)的操作[8].由最開始shell腳本設(shè)計(jì)的批處理拷貝,在實(shí)踐中不斷完善,參考覆蓋率生成的步驟,將其設(shè)計(jì)成腳本工具,在實(shí)際中的確減少了人機(jī)的頻繁交互,節(jié)省了時(shí)間,提高了工作效率.
1)該設(shè)計(jì)并不是完全自動(dòng)的,部分修改還需人工操作,如:
需要在測(cè)試main函數(shù)中增加信號(hào)捕捉函數(shù),以便后臺(tái)程序可以exit;
需要在運(yùn)行環(huán)境中手動(dòng)設(shè)置環(huán)境變量,該步驟可以通過腳本實(shí)現(xiàn);
程序的運(yùn)行需要手動(dòng)操作,該步驟也可以通過腳本實(shí)現(xiàn);
2)本設(shè)計(jì)前提是在運(yùn)行環(huán)境和編譯環(huán)境可以登錄的情況下進(jìn)行的,沒有對(duì)環(huán)境的可用性進(jìn)行檢查,可以通ping命令配合過腳本實(shí)現(xiàn)對(duì)環(huán)境的可用性檢查;
3)日志記錄并沒有保存文件,可以通過重定向的方式將控制臺(tái)打印按一定格式存文件;
4)本設(shè)計(jì)的分支測(cè)試需要外部命令觸發(fā),可以通gtest工具對(duì)增加測(cè)試用例,配合GCOV實(shí)現(xiàn)對(duì)代碼的全覆蓋,給出更全面的報(bào)告;
5)目前的覆蓋率是全量的覆蓋率,可以優(yōu)化實(shí)現(xiàn)增量代碼的覆蓋率.
還有很多不足之處和待優(yōu)化的地方需要改進(jìn).
圖5 代碼覆蓋率報(bào)告自動(dòng)輸出流程圖
1)如圖6,首先進(jìn)入服務(wù)器端工作路徑,調(diào)用提示工具提示操作步驟;
2)調(diào)用目錄創(chuàng)建工具,完成用戶目錄的創(chuàng)建(見圖7);
3)調(diào)用編譯工具完成,軟件的編譯(見圖8);
4)在運(yùn)行環(huán)境完成*.gcda文件生成,檢驗(yàn)后拷貝到服務(wù)器端,此時(shí)調(diào)用覆蓋率自動(dòng)輸出工具,會(huì)完成覆蓋率報(bào)告的自動(dòng)輸出(見圖9),并拷貝的服務(wù)器端用戶目錄下,見圖10.
圖6 提示工具使用樣圖
圖7 目錄創(chuàng)建工具使用樣圖
圖8 編譯工具使用樣圖
圖9 覆蓋率報(bào)告自動(dòng)輸出工具使用樣圖
圖10 拷貝到用戶目錄下的覆蓋率報(bào)告及各種校驗(yàn)結(jié)果文件
如圖11所示,我們可以看到整個(gè)源碼的覆蓋率情況,和函數(shù)覆蓋百分比.點(diǎn)擊某個(gè)C文件我們可以看到每一行代碼執(zhí)行的頻率,還有各函數(shù)的覆蓋情況以及內(nèi)部分支執(zhí)行的情況.
圖11 生成的覆蓋率報(bào)告
本文介紹了一種Linux平臺(tái)下代碼覆蓋率自動(dòng)化輸出設(shè)計(jì).首先對(duì)本設(shè)計(jì)依據(jù)原理進(jìn)行了介紹,然后對(duì)系統(tǒng)整體的架構(gòu)進(jìn)行了介紹,以及各模塊的設(shè)計(jì)目的及實(shí)現(xiàn)功能進(jìn)行了描述,最后依據(jù)該設(shè)計(jì)進(jìn)行測(cè)試,并結(jié)合實(shí)際操作所遇到的問題,指出了本設(shè)計(jì)的不足之處和優(yōu)化建議.
本設(shè)計(jì)是作者在進(jìn)行Linux下覆蓋率測(cè)試時(shí),發(fā)現(xiàn)基于GCOV的覆蓋率測(cè)試具有操作繁瑣單調(diào)、耗時(shí)的特點(diǎn),為解決這一問題對(duì)覆蓋率輸出做出了改進(jìn)優(yōu)化.與原有GCOV覆蓋率輸出相比,本設(shè)計(jì)具有搭建方便、操作簡(jiǎn)單,節(jié)省時(shí)間的的優(yōu)點(diǎn).同時(shí)通過修改編譯腳本和相關(guān)路徑及IP的配置文件,可實(shí)現(xiàn)移植.在現(xiàn)有的條件下極大的提供高了覆蓋率報(bào)告的輸出效率,為工程開發(fā)人員提供了有效依據(jù),節(jié)約了大量時(shí)間.