使用XcodeCoverage统计增量代码单元测试覆盖率

XcodeCoverage 是一个基于lcov的统计工具,用于计算Xcode项目的单元测试覆盖率,且能生成html格式的统计报表。
现在需要统计在一个版本周期中增量代码的覆盖率,而XcodeCoverage只能统计全量的覆盖率,因此需要借助XcodeCoverage生成的数据,手动计算版本周期中修改过的文件的覆盖率。问题可以分解为三个子问题:

  1. 获取一个版本周期内存在修改的代码文件列表
  2. 获取Xcode单元测试生成的每个代码文件的覆盖率数据
  3. 筛选并计算

获取一个版本周期内存在修改的代码文件列表

在版本库中,只要确定当某个本周期的起始结束commit,就可以利用git diff命令筛选出我们想要的文件列表。
结束commit容易确定,如果统计当前正在开发的版本,那么结束commit对应的就是版本库的HEAD
而起始commit的确定依赖于手动标记,本项目会对每个发布版本打一个tag,所以最新的一个tag对应的commit即为上个版本的发布commit,亦即当前版本的起始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规则描述的版本号,因此可以使用sort命令排序
  2. 根据上个版本的tag获取起始commit
  3. 获取结束commit
  4. 打印起始/结束commit之间存在修改的文件列表
  5. 提取文件路径
  6. 提取文件名

获取Xcode单元测试生成的每个代码文件的覆盖率数据

通过分析XcodeCoverage的脚本可以知道,执行Xcode单元测试之后生成的覆盖率数据文件在

~/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

其中~/Library/Developer/Xcode/DerivedData/Demo-axxzxbxzjokinpghkkhgihkbcrgo/Build/这个路径,在脚本的执行过程中存储在环境变量BUILD_ROOT中,而F76FA0C5-258D-4233-BE5A-C672666F0D1C代表测试设备的UUID,存储在环境变量TARGET_DEVICE_IDENTIFIER中。因此只需要仿照XcodeCoverage导入环境变量的方式,自己实现一个exportsnv.sh,在单元测试运行时将我们需要的路径注入到env.sh,待计算覆盖率时使用source命令导入即可。

Xcode执行单元测试时提取环境变量

exportsnv.sh加到Project相应的Scheme的Run Scripts中,Xcode执行单元测试时即可将所需的环境变量导入到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"

提取覆盖率数据

覆盖率数据通过xcrun llvm-cov report命令导出。

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. 导入XcodeCoverage生成的环境变量

  2. 导入自己生成的环境变量

  3. -enableCodeCoverage设置为YES,才能生成Coverage.profdata文件

  4. 导入文件名列表,并修改成awk命令中正则表达式的格式

  5. 导入所有文件的单元测试覆盖率

  6. 筛选出$fileList中相应文件的覆盖率数据

  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 )用于输出当前执行的脚本所在目录

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容