对于我的仅限标题的C ++库(许多模板等),我使用GCov来检查测试覆盖率。但是,它会报告所有标头的100%覆盖率,因为编译器首先不会生成未使用的函数。手动发现未覆盖的功能很容易,但却无法实现持续集成......
如何自动解决这个问题?我应该只使用“line hit / LOC”作为我的覆盖率指标,再也不会达到100%吗?
答案 0 :(得分:15)
除了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
答案 1 :(得分:2)
我还使用GCov检查测试覆盖率(使用Google Test框架编写的测试),另外我使用Eclipse GCov集成插件或LCov工具生成易于检查测试覆盖率结果的视图。原始GCov输出太难使用: - (。
如果您只有标题模板库,您还需要检测(使用G ++标志--coverage)您的测试类,它们实例化模板类和模板成员函数,以查看合理的GCov输出。
使用上面提到的工具,很容易发现没有用测试用例实例化的模板代码,因为它没有注释。
我已经设置了一个示例并将LCov输出复制到您可以检查的DropBox链接。
示例代码(使用g ++ --coverage
选项检测TemplateSampleTest.cpp):
<强> TemplateSample.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;
};
<强> TemplateSampleTest.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生成的代码覆盖率输出:
警告:'函数'统计信息报告为100%,对于未实例化的模板函数,这不是真的。
答案 2 :(得分:0)
由于我发现这个问题对于为我的仅限图书馆库设置测试覆盖率非常有用,所以我学到了一些额外的东西,希望他们可以帮助其他人:
即使使用这些答案中提到的所有标志,我仍然遇到了未使用的类方法被优化的问题。经过多次实验,我发现clang source based coverage(这些标志:-fprofile-instr-generate -fcoverage-mapping
)包含所有类方法,并且通常是获取覆盖数据的最可靠方法。我还使用了标志:-O0 -fno-inline -fno-elide-constructors
来进一步降低代码优化的风险。
对于大型库,模板实例化仍然存在问题。明确地实例化它们一切都很好,但如果有人忘记了,你将获得不准确的代码覆盖率指标。请参阅我对this question的回答,了解自动调整代码覆盖率数据以解决此问题的方法。
答案 3 :(得分:0)
我也偶然发现了这个问题,不幸的是,提到的各种标志并没有带来多少运气,但是,我确实发现了两种方法,可以处理仅用于标头的函数,从而生成更准确的覆盖率信息。
首先是添加标志-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
)