作为我的单元测试的一部分,我想确保测试的代码覆盖率。目的是在代码中的某处放置REQUIRE_TEST
宏,并检查是否所有这些都被调用。
void foo(bool b) {
if (b) {
REQUIRE_TEST
...
} else {
REQUIRE_TEST
...
}
}
void main() {
foo(true);
output_all_missed_REQUIRE_macros();
}
理想情况下,输出将包括源文件和宏的行。
我最初的想法是让宏创建静态对象,将自己注册到某个地图中,然后检查是否所有这些都被调用
#define REQUIRE_TEST \
do { \
static ___RequiredTest requiredTest(__func__, __FILE__, __LINE__);\
(void)requiredTest;\
___RequiredTest::increaseCounter(__func__, __FILE__, __LINE__);\
} while(false)
但静态对象仅在第一次调用代码时创建。因此,映射仅包含在下一行中计算的函数 - 找不到丢失的REQUIRE_TEST宏。在这种情况下忽略__attribute__((used))
。
gcc有一个很好的属性__attribute__((constructor))
,但显然选择在放在这里时忽略它(代码而不是静态对象)
struct teststruct { \
__attribute__((constructor)) static void bla() {\
___RequiredTest::register(__func__, __FILE__, __LINE__); \
} \
};\
以及
[]() __attribute__((constructor)) { \
___RequiredTest::register(__func__, __FILE__, __LINE__); \
};\
我能想到的唯一出路是a)手动(或通过脚本)分析常规编译之外的代码(uargh)或b)使用__COUNTER__
宏来计算宏 - 但是我不知道哪些特定的REQUIRE_TEST宏没有被调用...(如果其他人决定使用__COUNTER__
宏,一切都会中断...)
这个问题有没有合适的解决方案?我错过了什么?它 有一个附加当前行和文件的宏会很高兴 所以每当调用它时都会有一些预处理器变量 - 但事实并非如此 可能,对吗?是否还有其他方式可以注册 在
main()
之前执行,可以在函数体内完成吗?
答案 0 :(得分:2)
这个怎么样:
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
retrieveMQTTConnAssets();
}
return true;
}
结果:
#include <iostream>
static size_t cover() { return 1; }
#define COV() do { static size_t cov[2] __attribute__((section("cov"))) = { __LINE__, cover() }; } while(0)
static void dump_cov()
{
extern size_t __start_cov, __stop_cov;
for (size_t* p = &__start_cov; p < &__stop_cov; p += 2)
{
std::cout << p[0] << ": " << p[1] << "\n";
}
}
int main(int argc, char* argv[])
{
COV();
if (argc > 1)
COV();
if (argc > 2)
COV();
dump_cov();
return 0;
}
和
$ ./cov_test
19: 1
22: 0
25: 0
和
$ ./cov_test x
19: 1
22: 1
25: 0
基本上,我们在命名的内存部分设置了一个coverage数组(显然我们已经使用了GCC特定的机制),我们在执行后转储它。
我们依赖于在启动时执行的局部静态的不断初始化 - 它将行号放入覆盖数组中,覆盖标志设置为零 - 并且通过在第一次执行时执行的函数调用$ ./cov_test x y
19: 1
22: 1
25: 1
进行初始化语句的设置,将执行行的coverage标志设置为1。我不是100%确定所有这些都是由标准保证,也不是由标准的哪个版本保证(我使用cover()
编译)。
最后,使用'-O3'构建也会产生正确的结果(尽管以不同的顺序分配):
--std=c++11
和
$ ./a
25: 0
22: 0
19: 1
和
$ ./a x
25: 0
22: 1
19: 1
答案 1 :(得分:1)
一种丑陋但简单的方法是REQUIRE_TEST
使用__LINE__
或__COUNTER__
来构造它所引用的唯一文件范围静态对象的名称,这将导致如果没有声明,编译器错误。然后,您需要预先手动声明所有此类对象,每个REQUIRE_TEST
一个 - 但至少如果您没有这样做,则会出现编译错误。
答案 2 :(得分:1)
杰里米给了我一个正确的想法,仔细看看这些部分。他的回答有效 - 但只有inline
函数。通过一些更多的研究,我能够找到以下解决方案,它同样依赖于gcc(由于部分的名称),但更灵活(并且在内联函数中工作)。宏现在如下:
#define REQUIRE_TEST \
do { \
struct ___a{ static void rt() {\
___RequiredTest::register_test(__FILE__, __LINE__);\
} };\
static auto ___rtp __attribute__((section(".init_array"))) = &___a::rt; \
(void) ___rtp; \
___RequiredTest::increase_counter(__FILE__, __LINE__); \
} while(false)
将函数指针放在.init_array
部分实际上将它放在main之前调用的初始化函数列表中。这样可以确保在main之前调用本地定义的函数。