我正在使用 gcov 来测量我的 C++ 代码中的覆盖率。我想达到 100% 的覆盖率,但受到以下事实的阻碍:有些代码行在理论上是不可命中的(需要实现但从未调用的方法、switch
语句的默认分支等) .)。这些分支中的每一个都包含一个assert( false );
语句,但 gcov 仍将它们标记为未命中。
我希望能够告诉 gcov 忽略这些分支。有没有办法给 gcov 提供信息——通过注释源代码,或通过任何其他机制?
我正在使用 gcov 来测量我的 C++ 代码中的覆盖率。我想达到 100% 的覆盖率,但受到以下事实的阻碍:有些代码行在理论上是不可命中的(需要实现但从未调用的方法、switch
语句的默认分支等) .)。这些分支中的每一个都包含一个assert( false );
语句,但 gcov 仍将它们标记为未命中。
我希望能够告诉 gcov 忽略这些分支。有没有办法给 gcov 提供信息——通过注释源代码,或通过任何其他机制?
请使用lcov。它隐藏了 gcov 的复杂性,产生良好的输出,允许每个测试的详细输出,具有简单的文件过滤和 - ta-taa - 已审查行的行标记:
来自 geninfo(1):
geninfo 可以识别以下标记:
- LCOV_EXCL_LINE
- 包含此标记的行将被排除。
- LCOV_EXCL_START
- 标记排除部分的开始。当前行是本节的一部分。
- LCOV_EXCL_STOP
- 标记排除部分的结束。当前行不属于本节。
一个名为gcovr的工具可用于汇总 gcov 的输出,并且(至少从 3.4 版开始)它支持与 lcov 相同的排除标记。
从这个答案:
geninfo 可以识别以下标记:
- LCOV_EXCL_LINE
- 包含此标记的行将被排除。
- LCOV_EXCL_START
- 标记排除部分的开始。当前行是本节的一部分。
- LCOV_EXCL_STOP
- 标记排除部分的结束。当前行不属于本节。
您也可以将'LCOV'
上面替换为'GCOV'
或'GCOVR'
。他们都工作。
您能否介绍相关功能的单元测试,这些功能的存在只是为了通过直接攻击理论上不可攻击的代码路径来关闭 gcov?由于它们是单元测试,因此它们可能会忽略这种情况的“不可能”。他们可以调用从未调用过的函数,传递无效的枚举值来捕获默认分支等。
然后要么仅在使用 NDEBUG 编译的代码版本上运行这些测试,要么在测试断言被触发的工具中运行它们 - 无论你的测试框架支持什么。
尽管规范说代码必须存在,而不是包含对代码的功能要求的规范,但我觉得有点奇怪。特别是,这意味着您的测试没有测试这些需求,这是保持需求功能的一个很好的理由。就个人而言,我想修改规范说,“如果使用无效的枚举值调用,该函数将失败assert
。调用者不应在发布模式下使用无效的枚举值调用该函数”。或者一些这样的。
大概它目前所说的内容是“所有 switch 语句都必须有一个默认情况”。但这意味着编码标准通过引入死代码来干扰可观察的行为(至少,在 gcov 下可观察)。编码标准不应该这样做,因此功能规范应尽可能考虑编码标准。
如果做不到这一点,您也许可以将不可命中的代码包装在 中#if !GCOV_BUILD
,并为 gcov 的利益进行单独的构建。此构建将无法满足某些要求,但前提是您对代码的分析是否正确,它使您有信心测试套件测试其他所有内容。
编辑:你说你正在使用一个狡猾的代码生成器,但你也通过注释源代码来寻求解决方案。如果您要更改源代码,您可以在很多情况下删除死代码吗?并不是说更改生成的源是理想的,但需求必须...
我不相信这是可能的。Gcov 依赖 gcc 生成额外的代码来产生覆盖输出。GCov 本身只是解析数据。这意味着 Gcov 无法比 gcc 更好地分析代码(我假设您使用 -Wall 并删除了报告为无法访问的代码)。
请记住,可以从任何地方调用可重定位函数,甚至可能是外部 dll 或可执行文件,因此编译器无法知道哪些可重定位函数不会被调用或这些函数可能有什么输入。
您可能需要使用一些漂亮的静态分析工具来获取您想要的信息。