c++ - 一些 googletest 宏的覆盖分析显示分布在多行时覆盖不完整 - 为什么?

标签 c++ macros code-coverage googletest gcov

我正在使用 gcov/lcov 进行 googletest 单元测试的覆盖率分析。

一个反复出现的问题是,当某些 googletest 宏分布在多行时,覆盖率报告会在测试代码中显示未覆盖的行。

我知道 gov/lcov 不能比单行更准确,但我对我看到的行为感到困惑。有人可以解释一下吗?最小示例:

#include <gtest/gtest.h>

TEST(coverage,incomplete)
{
  // Every second line in every invocation here will show up as uncovered:
  EXPECT_NO_THROW(40 +
                  2);
  EXPECT_NO_THROW(40 + 2
                  );
  EXPECT_NO_THROW(40 + 2)
    ;
}

TEST(coverage,complete)
{
  // This test does not show uncovered lines
  EXPECT_NO_THROW(40 + 2);
  EXPECT_EQ(40
            +
            2
            , // even though this is spread over several lines
            42
            )
    ;
}

覆盖率分析是如何完成的:

g++-4.8 -Igtest/googletest/include/ --coverage -o coverage_macropp coverage_macropp.cpp gtest/googletest/make/gtest_main.a -pthread
./coverage_macropp
lcov --capture --directory . --output-file coverage.info
genhtml --demangle-cpp coverage.info --output-directory coverage

Web 浏览器中的覆盖率分析将显示第 7、9 和 11 行未被覆盖:

      Line data    Source code

   1             : #include <gtest/gtest.h>
   2             : 
   3           5 : TEST(coverage,incomplete)
   4             : {
   5             :   // Every second line in every invocation here will show up as uncovered:
   6           1 :   EXPECT_NO_THROW(40 +
   7           0 :                   2);
   8           1 :   EXPECT_NO_THROW(40 + 2
   9           0 :                   );
  10           1 :   EXPECT_NO_THROW(40 + 2)
  11           0 :     ;
  12           1 : }
  13             : 
  14           5 : TEST(coverage,complete)
  15             : {
  16             :   // This test does not show uncovered lines
  17           1 :   EXPECT_NO_THROW(40 + 2);
  18           1 :   EXPECT_EQ(40
  19             :             +
  20             :             2
  21             :             , // even though this is spread over several lines
  22             :             42
  23             :             )
  24           1 :     ;
  25           4 : }

为什么?为什么 EXPECT_EQ 宏不受影响?

最佳答案

这是一项引人入胜的调查。看着这些宏,我学到了关于 switch/case 和 goto 的知识,我不知道这些是可能的。

但行为差异的原因来自 if/else 结构,它们的共同点是永远不会执行 else 分支,但不同之处在于编译器是否已经知道这些 else 分支永远不会被执行。

首先,将宏分布到源代码中的多行中,预处理器会为编译器及其覆盖分析器生成(就此问题而言)以下代码:

if (condition) statement1; else statement2


  ;

显然,覆盖分析器 gcov 将带有单独分号的行计为代码行,并认为它在执行 else 分支的 statement2 时执行。

现在,要重现问题中观察到的覆盖率分析差异,请考虑以下示例程序:

#include <stdio.h>
#include <time.h>

int main(int, char*[]) {
  const bool true_1 = true;
  const bool true_2 = time(NULL) != 0;

  if (true_1) 42; else printf("hello\n")
                    ;
  if (true_2) 42; else printf("hello\n")
                    ;
  return 0;
}

true_1true_2 始终为真(在我的一生中,如果我不弄乱计算机的时钟),但在 true_1< 的情况下,编译器知道,而对于 true_2,它无法知道。 (是的,我可能会为 true_2 找到一个更安全的初始值设定项。现在就可以了。)

另请注意,if 分支中的语句什么都不做,但 else 分支中的语句会产生副作用。

该程序的 gcov/lcov 覆盖分析如下所示:

      Line data    Source code

   1             : #include <stdio.h>
   2             : #include <time.h>
   3             : 
   4           1 : int main(int, char*[]) {
   5           1 :   const bool true_1 = true;
   6           1 :   const bool true_2 = time(NULL) != 0;
   7             :   
   8             :   if (true_1) 42; else printf("hello\n")
   9             :                     ;
  10           1 :   if (true_2) 42; else printf("hello\n")
  11           0 :                     ;
  12           1 :   return 0;
  13             : }

因为编译器已经知道 true_1 为真,else 分支,因此,第 9 行,不被考虑用于覆盖分析。在使用 -O0 编译时甚至是这样。

然而,该程序中的第 11 行被考虑用于覆盖率分析,因为 true_2 仅在运行时已知。

作为旁注:如果我也在 else 分支中使用了另一个虚拟语句,或者甚至是已知没有副作用的函数调用,例如 sin(7) 而不是 printf("hello"),那么这一行也不会计入 gcov 覆盖率分析。

到目前为止,为了完整的正式覆盖,我必须将 googletest 宏限制在单个源代码行中,如果测试结果在编译时未知的话。

关于c++ - 一些 googletest 宏的覆盖分析显示分布在多行时覆盖不完整 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43495370/

相关文章:

javascript - Sonarqube 不从 LCOV 检索我的 JavaScript 覆盖率

c++ - 使用 Bazel 交叉编译时系统头文件为 "Missing dependency declarations"

c++ - 两个模板类互相用作模板参数

variables - 如何在clojure宏中绑定(bind)var的名称和值?

c++ - 可以将宏重新定义应用于单个 cpp 文件吗?

.net - .NET 中用于个人项目的免费代码覆盖工具

unit-testing - 从 Azure devops 管道中的代码覆盖率分析中排除文件

c++ - QT Creator - 在我自己的库中使用外部库

C++ Vector 遍历抽象类

macros - 定义一个宏来获取输入流