使用XcodeCoverage統(tǒng)計增量代碼單元測試覆蓋率

XcodeCoverage 是一個基于lcov的統(tǒng)計工具,用于計算Xcode項目的單元測試覆蓋率绞惦,且能生成html格式的統(tǒng)計報表。
現(xiàn)在需要統(tǒng)計在一個版本周期中增量代碼的覆蓋率拘哨,而XcodeCoverage只能統(tǒng)計全量的覆蓋率疤苹,因此需要借助XcodeCoverage生成的數(shù)據(jù),手動計算版本周期中修改過的文件的覆蓋率踪栋。問題可以分解為三個子問題:

  1. 獲取一個版本周期內(nèi)存在修改的代碼文件列表
  2. 獲取Xcode單元測試生成的每個代碼文件的覆蓋率數(shù)據(jù)
  3. 篩選并計算

獲取一個版本周期內(nèi)存在修改的代碼文件列表

在版本庫中焙格,只要確定當(dāng)某個本周期的起始結(jié)束commit,就可以利用git diff命令篩選出我們想要的文件列表夷都。
結(jié)束commit容易確定眷唉,如果統(tǒng)計當(dāng)前正在開發(fā)的版本,那么結(jié)束commit對應(yīng)的就是版本庫的HEAD
而起始commit的確定依賴于手動標(biāo)記冬阳,本項目會對每個發(fā)布版本打一個tag蛤虐,所以最新的一個tag對應(yīng)的commit即為上個版本的發(fā)布commit,亦即當(dāng)前版本的起始commit肝陪。

# get_modified_file_list.sh

#!/bin/bash

tag=`git --no-pager tag | sort -V | tail -1` #1
beginCommit=`git --no-pager show $tag --pretty=raw | head -1 | awk '{print $2}'` #2
endCommit=`git --no-pager log --max-count=1 --no-decorate | head -1 | awk '{print $2}'` #3

# echo Calculation will start from $beginCommit to $endCommit, since $tag

git --no-pager diff $beginCommit $endCommit --name-status \ #4
| awk '$2 ~ /\.m$/ {print $2}' \ #5
| awk -F '/' '{print $NF}' #6
  1. 獲取最新的tag驳庭,這里需注意,tag名是符合SemVer規(guī)則描述的版本號氯窍,因此可以使用sort命令排序
  2. 根據(jù)上個版本的tag獲取起始commit
  3. 獲取結(jié)束commit
  4. 打印起始/結(jié)束commit之間存在修改的文件列表
  5. 提取文件路徑
  6. 提取文件名

獲取Xcode單元測試生成的每個代碼文件的覆蓋率數(shù)據(jù)

通過分析XcodeCoverage的腳本可以知道饲常,執(zhí)行Xcode單元測試之后生成的覆蓋率數(shù)據(jù)文件在

~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/ProfileData/F76FA0C5-258D-4233-BE5A-C672666F0D1C/Coverage.profdata

生成的二進(jìn)制包在

~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/Products/Debug-iphonesimulator/Demo.app/Demo

其中~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/這個路徑,在腳本的執(zhí)行過程中存儲在環(huán)境變量BUILD_ROOT中狼讨,而F76FA0C5-258D-4233-BE5A-C672666F0D1C代表測試設(shè)備的UUID贝淤,存儲在環(huán)境變量TARGET_DEVICE_IDENTIFIER中。因此只需要仿照XcodeCoverage導(dǎo)入環(huán)境變量的方式熊楼,自己實現(xiàn)一個exportsnv.sh霹娄,在單元測試運行時將我們需要的路徑注入到env.sh,待計算覆蓋率時使用source命令導(dǎo)入即可鲫骗。

Xcode執(zhí)行單元測試時提取環(huán)境變量

exportsnv.sh加到Project相應(yīng)的Scheme的Run Scripts中犬耻,Xcode執(zhí)行單元測試時即可將所需的環(huán)境變量導(dǎo)入到env.sh

# exportsnv.sh

#!/bin/bash

scripts="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export | egrep '(TARGET_DEVICE_IDENTIFIER)|(BUILD_ROOT)|(TARGET_NAME)' > "${scripts}/env.sh"
# env.sh

declare -x BUILD_ROOT="~/Library/Developer/Xcode/DerivedData/Demo-bxynohvelscfkufcyzjsxmqxonmn/Build/Products"
declare -x TARGET_DEVICE_IDENTIFIER="6C2F1A5C-31E0-4495-9802-B870196E0399"
declare -x TARGET_NAME="Demo"

提取覆蓋率數(shù)據(jù)

覆蓋率數(shù)據(jù)通過xcrun llvm-cov report命令導(dǎo)出执泰。

xcrun llvm-cov report -instr-profile \
    ~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/ProfileData/F76FA0C5-258D-4233-BE5A-C672666F0D1C/Coverage.profdata \
    ~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/Products/Debug-iphonesimulator/Demo.app/Demo \
    > file_level_coverage.txt

篩選并計算

# analize_coverage.sh

#!/bin/bash

ScriptsPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

XcodeCoveragePath="${ScriptsPath}/../Pods/XcodeCoverage"
source "${XcodeCoveragePath}/envcov.sh" #1

source "./env.sh" #2

CoverageDataName="Coverage.profdata"
CoverageDataPath="${BUILD_ROOT}/../ProfileData/${TARGET_DEVICE_IDENTIFIER}/${CoverageDataName}"
BinPackagePath="${BUILT_PRODUCTS_DIR}/${TARGET_NAME}.app/${TARGET_NAME}"

# test
xcodebuild test \
    -workspace ../Demo.xcworkspace \
    -scheme ${TARGET_NAME} \
    -destination 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (2nd generation)' \
    -only-testing:DemoUnitTests \
    -enableCodeCoverage YES #3

# get modified files during current app version from repo
echo Fetching modified files...
fileList="$(./get_modified_file_list.sh | tr '\n' '|')"
fileList=${fileList%?} #4

TotalLines=11
MissLines=12

# convert coverage data to humanity-readable format
echo Calculating...
CoverageDataName="file_level_coverage.txt"
xcrun llvm-cov report -instr-profile ${CoverageDataPath} ${BinPackagePath} \ #5
| awk -v total=$TotalLines -v miss=$MissLines 'NR>=3 && $1 ~ /'"$fileList"'/ {print $1,$total,$miss}' \ #6
| awk -f cal_coverage.awk #7

echo Done.
# cal_coverage.awk

#!/bin/awk -f

BEGIN {
    totalsum = 0
    misssum = 0
}
{
    totalsum += $2
    misssum += $3
}
END {
    printf "Coverage rate: %.2f%%\n", (totalsum - misssum) / totalsum * 100
}
  1. 導(dǎo)入XcodeCoverage生成的環(huán)境變量

  2. 導(dǎo)入自己生成的環(huán)境變量

  3. -enableCodeCoverage設(shè)置為YES枕磁,才能生成Coverage.profdata文件

  4. 導(dǎo)入文件名列表,并修改成awk命令中正則表達(dá)式的格式

  5. 導(dǎo)入所有文件的單元測試覆蓋率

  6. 篩選出$fileList中相應(yīng)文件的覆蓋率數(shù)據(jù)

  7. 計算增量代碼覆蓋率

    ?  scripts git:(develop) ? ./analize_coverage.sh
    Feching modified files...
    Calculating...
    Coverage rate: 10.42%
    Done.
    

Tips

  • --no-pager: Do not pipe Git output into a pager
  • $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )用于輸出當(dāng)前執(zhí)行的腳本所在目錄

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末术吝,一起剝皮案震驚了整個濱河市计济,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌排苍,老刑警劉巖沦寂,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異淘衙,居然都是意外死亡传藏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門彤守,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毯侦,“玉大人,你說我怎么就攤上這事具垫〖” “怎么了皆尔?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晤郑。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮康震,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宾濒。我一直安慰自己腿短,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布绘梦。 她就那樣靜靜地躺著橘忱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卸奉。 梳的紋絲不亂的頭發(fā)上钝诚,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音榄棵,去河邊找鬼凝颇。 笑死,一個胖子當(dāng)著我的面吹牛疹鳄,可吹牛的內(nèi)容都是我干的拧略。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘪弓,長吁一口氣:“原來是場噩夢啊……” “哼垫蛆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腺怯,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袱饭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呛占,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虑乖,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年晾虑,在試婚紗的時候發(fā)現(xiàn)自己被綠了疹味。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡走贪,死狀恐怖佛猛,靈堂內(nèi)的尸體忽然破棺而出惑芭,到底是詐尸還是另有隱情坠狡,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布遂跟,位于F島的核電站逃沿,受9級特大地震影響婴渡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凯亮,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一边臼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧假消,春花似錦柠并、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啃沪,卻和暖如春粘拾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背创千。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工缰雇, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人追驴。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓械哟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親殿雪。 傳聞我的和親對象是個殘疾皇子戒良,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容