美文网首页
使用XcodeCoverage统计增量代码单元测试覆盖率

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

作者: ltryee | 来源:发表于2018-08-24 10:03 被阅读237次

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

    参考

    相关文章

      网友评论

          本文标题:使用XcodeCoverage统计增量代码单元测试覆盖率

          本文链接:https://www.haomeiwen.com/subject/elxbiftx.html