一些 googletest 宏的覆盖分析显示分布在多行时覆盖不完整 - 为什么?
Coverage analysis of some googletest macros shows incomplete coverage when spread over several lines - why?
我正在使用 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 分支的语句 2 时执行。
现在,要重现问题中观察到的覆盖率分析差异,请考虑以下示例程序:
#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_1
和 true_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 宏限制在单个源代码行中,如果测试结果在编译时未知的话。
我正在使用 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 分支的语句 2 时执行。
现在,要重现问题中观察到的覆盖率分析差异,请考虑以下示例程序:
#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_1
和 true_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 宏限制在单个源代码行中,如果测试结果在编译时未知的话。