为仅标头库获取有用的 GCov 结果
Getting useful GCov results for header-only libraries
对于我的仅标头C++库(大量模板等(,我使用 GCov 来检查测试覆盖率。但是,它报告所有标头的 100% 覆盖率,因为编译器首先不会生成未使用的函数。手动发现未覆盖的功能很容易,但违背了持续集成的目的......
如何自动解决此问题?我是否应该只使用"命中线/LOC"作为我的覆盖率指标,然后再也不会达到100%?
除了通常的标志到 GCC 控制内联;
--coverage -fno-inline -fno-inline-small-functions -fno-default-inline
您可以在单元测试文件的顶部实例化模板类;
template class std::map<std::string, std::string>;
这将为该模板类中的每个方法生成代码,使覆盖率工具完美运行。
另外,请确保初始化 *.gcno 文件(对于 lcov(
lcov -c -i -b ${ROOT} -d . -o Coverage.baseline
<run your tests here>
lcov -c -d . -b ${ROOT} -o Coverage.out
lcov -a Coverage.baseline -a Coverage.out -o Coverage.combined
genhtml Coverage.combined -o HTML
率(使用 Google 测试框架编写的测试(,此外,我还使用 Eclipse GCov 集成插件或 LCov 工具来生成易于检查的测试覆盖率结果视图。原始 GCov 输出太难使用 :-(。
如果您有仅标头模板库,则还需要检测(使用 G++ 标志 --coverage(实例化模板类和模板成员函数的测试类,以查看这些类的合理 GCov 输出。
使用上述工具,很容易发现根本没有使用测试用例实例化的模板代码,因为它没有注释。
我已经设置了一个示例并将 LCov 输出复制到您可以检查的 DropBox 链接。
示例代码(TemplateSampleTest.cpp使用 g++ --coverage
选项进行检测(:
模板样本.hpp
template<typename T>
class TemplateSample
{
public:
enum CodePath
{
Path1 ,
Path2 ,
Path3 ,
};
TemplateSample(const T& value)
: data(value)
{
}
int doSomething(CodePath path)
{
switch(path)
{
case Path1:
return 1;
case Path2:
return 2;
case Path3:
return 3;
default:
return 0;
}
return -1;
}
template<typename U>
U& returnRefParam(U& refParam)
{
instantiatedCode();
return refParam;
}
template<typename U, typename R>
R doSomethingElse(const U& param)
{
return static_cast<R>(data);
}
private:
void instantiatedCode()
{
int x = 5;
x = x * 10;
}
void neverInstantiatedCode()
{
int x = 5;
x = x * 10;
}
T data;
};
模板样本测试.cpp
#include <string>
#include "gtest/gtest.h"
#include "TemplateSample.hpp"
class TemplateSampleTest : public ::testing::Test
{
public:
TemplateSampleTest()
: templateSample(5)
{
}
protected:
TemplateSample<int> templateSample;
private:
};
TEST_F(TemplateSampleTest,doSomethingPath1)
{
EXPECT_EQ(1,templateSample.doSomething(TemplateSample<int>::Path1));
}
TEST_F(TemplateSampleTest,doSomethingPath2)
{
EXPECT_EQ(2,templateSample.doSomething(TemplateSample<int>::Path2));
}
TEST_F(TemplateSampleTest,returnRefParam)
{
std::string stringValue = "Hello";
EXPECT_EQ(stringValue,templateSample.returnRefParam(stringValue));
}
TEST_F(TemplateSampleTest,doSomethingElse)
{
std::string stringValue = "Hello";
long value = templateSample.doSomethingElse<std::string,long>(stringValue);
EXPECT_EQ(5,value);
}
在此处查看从 lcov 生成的代码覆盖率输出:
模板样本.hpp 覆盖率
警告:"函数"统计信息报告为 100%,对于未实例化的模板函数,这并不真实。
我也偶然发现了这个问题,不幸的是,提到的各种标志没有太多运气,但是,我确实发现了两种在处理仅标头函数时生成更准确覆盖信息的方法。
第一种是添加标志-fkeep-inline-functions
(https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fkeep-inline-functions(。
这确实给了我想要的结果,但在尝试与其他库(甚至是普通的C++标准库(集成时遇到了一些严重的问题。我最终收到了链接错误,因为链接器应该删除的某些函数没有(例如,没有定义的函数声明(。
第二种方法(我最终选择的方法(是在 GCC 中使用 __attribute(used)__
来注释我所有的标头 API 函数。文档 (https://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Function-Attributes.html( 指出:
使用
此属性附加到函数,这意味着即使看起来未引用该函数,也必须为该函数发出代码。
我使用了一个#define
来包装它,所以我只在使用 GCC 并启用覆盖范围时才打开它:
#ifdef _MSC_VER
#define MY_API
#elif defined __GNUC__ && defined COVERAGE
#define MY_API __attribute__((__used__))
#endif // _MSC_VER ? __GNUC__ && COVERAGE
然后用法如下所示:
MY_API void some_inline_function() {}
我将尝试写下我如何在某个时候让一切工作,如果我能解决这个问题,我将在未来链接到这里
(注:编译时我也用了-coverage -g -O0 -fno-inline
(
由于我发现这个问题在为我的仅标头库设置测试覆盖率时非常有用,因此我学到了一些额外的东西,希望它们可以帮助其他人:
即使这些答案中提到了所有标志,我仍然遇到未使用的类方法被优化的问题。经过大量实验,我发现基于clang源的覆盖率(这些标志:-fprofile-instr-generate -fcoverage-mapping
(包括所有类方法,并且通常是获取覆盖率数据的最可靠方法。我还使用标志:-O0 -fno-inline -fno-elide-constructors
来进一步降低代码被优化的风险。
对于一个大型库来说,模板实例化的事情仍然是一个问题。显式实例化它们很好,但如果有人忘记了,你会得到不准确的代码覆盖率指标。请参阅我对此问题的回答,了解自动调整代码覆盖率数据以解决此问题的方法。
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 其中降频广播实际上是有用的
- 既然我们有内联变量,extern const 还有用吗?
- 函子还有更有用的用例吗?
- `__declspec(novtable)`没有用吗
- P1008("prohibit aggregates with user-declared constructors")在实践中什么时候有用?
- 当迭代器(输入参数)通常不是constexpr时,constexpr算法真的有用吗
- 如何使用带有"--gcov tool"标志的lcov
- 将指向数据成员的指针传递给 std::invoke 时有哪些用例和有用性?
- std::bit_cast 和 std::start_lifetime_as 之间有什么有用的区别吗?
- GCOV-工具合并:"not a gcov data file"
- 在现代C++中,临时寿命延长何时有用?
- 在不编写显式 setter 的情况下修改私有类数据成员的便捷方法是什么?模板有用吗?
- 如何正确设置 SonarQube cfamil.gcov?
- 是否有任何情况下,具有已删除CTOR的课程可能有用
- 使用 CXX 社区插件在 Sonarqube-5.6.6(LTS) 中导入 Gcov 报告
- KeyEqual 在 std::unordered_set/std::unordered_map 中的有用性
- std::make_pair 和 std::make_tuple 在 C++17 中的有用性
- 如果我在 Xbox UWP 应用程序上收到激活错误并且错误消息中没有任何有用的内容,该怎么办
- 为仅标头库获取有用的 GCov 结果