背景

事情源于iOS (Object-C) 非单元测试状态下代码覆盖率获取尝鲜,让我恍然大悟,覆盖率可以基于手工测试统计。
很感激这篇文章的详细介绍,依据这篇文章,我手工走通了一遍,然后就开始调研怎么落地在我们项目。

难点

手工走通这篇文章之后,落实在自动化上,有两个难点:

解决方案

我在我们项目中已经通过自动化的方式,实现了 XcodeCoverage 手工覆盖率监测,并开始这个项目的内测。对于接触这篇文章前,最好先通过手工方式走通一遍iOS (Object-C) 非单元测试状态下代码覆盖率获取尝鲜

我的解决方案分三步:

下面我一一进行简单阐述。

第一步:在 linux 上运行

部署在 linux 上调用 XcodeCoverage 命令,会报错:

Out of memory!
Reading tracefile Coverage.info
lcov: ERROR: no valid records found in tracefile Coverage.info
Reading tracefile Coverage.info
lcov: ERROR: no valid records found in tracefile Coverage.info
cat: /.xcodecoverageignore: No such file or directory
Reading tracefile Coverage.info
lcov: ERROR: no valid records found in tracefile Coverage.info
Reading data file Coverage.info
genhtml: ERROR: no valid records found in tracefile Coverage.info

直接给出解决方案:

# 修改/envcov.sh为
 LCOV() {
     "${LCOV_PATH}/lcov" "$@"
 }

# 修改/getcov为
"${LCOV_PATH}/genhtml" --ignore-errors source --output-directory . "${LCOV_INFO}"

简单解释就是执行 lcov 生成测试覆盖率.info 文件的时候稍微有点改变,我还是很佩服 XcodeCoverage 这个项目的作者,只是改了一点点就支持 linux 执行。

第二步:接收并处理 *.gcda 文件

其实这里更多的是依赖客户端同学的配合,他帮我实现了,从手机端生成 *.gcda 文件以后,并打了个以当时时间戳为名的 zip 包,然后直接 post 到我写的服务接口上。

客户端在传递 *.zip 包同时,在 header 里面增加了关于这个包的版本号,构件号,userid,和固件号等信息,我在后台解析以后统一存入一个表。

第三步:执行 XcodeCoverage 等一系列命令

其实接收了*.gcda文件只是万里长征第一步,还需要将在 mac slave 上打包过程中的临时性文件,*.gcno文件转存到 linux 上,并且需要修改*.gcno的文件中的路径。这样才能执行 XcodeCoverage 命令。

在这个过程中,我踩过的坑有:

我通过 scp 的方式,先实现 linux 可以免密登录打包的 mac slave,由于我们执行打覆盖率的包是指定 slave 的,然后只需要将 linxu 和 slave 互相信任免密登录,然后再执行完打包之后,通过如下命令:

#!/bin/bash
path="=Pods/XcodeCoverage/env.sh"

while read line
do
name=`echo $line|awk -F '=' '{print $1}'`
value=`echo $line|awk -F '=' '{print $2}'`
case ${name} in
"export OBJECT_FILE_DIR_normal") path1=${value} ;;
"export CURRENT_ARCH") path2=$value;;
"export SRCROOT") path3=$value;;
*)
;;
esac
done < ${path}

git_message=$(git log -1 --pretty=format:"%an:%s")
git_commit_id=$(git rev-parse --short HEAD)

git_repo="xxxx"
appversion="xxx"
buildversion="xxxx"
url="http://x.x.x.x:5000/api/cov/repo"


curl -H "Content-Type: application/json" \
-d "{\"commit_id\":\"${git_commit_id}\", \"repo\":\"${git_repo}\",  \"message\":\"${git_message}\",\"appversion\":\"${appversion}\",  \"buildversion\":\"${buildversion}\",\"object_file_dir_normal_path\":${path1},  \"current_arch_path\":${path2}, \"gcno_path\":${path3}}" ${url}

服务端接收到打包的版本号和构建号以后,同时还存入 commid 以及 commit message 等,执行 scp 命令就很简单了。对了,还需要强调一下,这里打的 iOS 包必须为Debug 包

需要保证 *.gcno 文件内的内容路径字符数,和替换后的完全相等,并不需要在 linux 上建立和 slave 同样的路径。这里的路径可以从集成到 iOS 项目工程中Pods/XcodeCoverage/env.sh里面取 SRCROOT,意思是如果 ${SRCROOT}有四位为/opt/,则在 linux 上执行的时候,需要字符相等的进行替换,替换为 linux 上存在的路径,为/ttt/

其实 XcodeCoverage 已经考虑到了覆盖率结果的合并,只需要在执行 XcodeCoverage 之前,指定XcodeCoverage/env.sh中 OBJECT_FILE_DIR_normal 和 CURRENT_ARCH 的值为高一级的目录,比如我想合并时间戳为 11.gcda 和 22.gcda 的覆盖率结果,那么我只需要在文件夹 33 内分别建立两个文件夹 11 和 22,然后将 11.gcda 放入 11 中,22.gcda 放入 22 中,然后将 *.gcno 分别拷入 11 和 22。执行 XcodeCoverage 命令之前,将XcodeCoverage/env.shOBJECT_FILE_DIR_normal替换为 33,CURRENT_ARCH替换为/,即可。

最后

还有一些细节,比如什么时候执行 scp 命令,什么时候执行替换 *.gcno 内的路径,什么时候执行 XcodeCoverage/getcov 命令等。

当然,覆盖率报告并不能衡量 app 的质量,它只是一个定量化的报告展示。

最后放一张我简单写的展示报告。

我想等我再整理整理,在 github 上放出我写的很 low 的代码。

ToDo

参考文献

https://testerhome.com/topics/6644
https://github.com/jonreid/XcodeCoverage/blob/master/README.md
https://github.com/jonreid/XcodeCoverage/issues/48
https://github.com/DormyMo/SpiderKeeper


↙↙↙阅读原文可查看相关链接,并与作者交流