31

For my header-only C++ library (lots of templates etc) I use GCov to check test coverage. However, it reports 100% coverage for all headers because the unused functions aren't generated by the compiler in the first place. Manually spotting uncovered functions is easy but defeats the purpose of continuous integration…</p>

How does one solve this automatically? Should I just use "lines hit / LOC" as my coverage metric and just never reach 100% again?

4

4 回答 4

23

除了 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
于 2013-11-05T15:46:52.157 回答
2

我还使用 GCov 来检查测试覆盖率(使用 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;
};

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 生成的代码覆盖率输出:

TemplateSample.hpp 覆盖率

警告:“函数”统计数据报告为 100%,对于未实例化的模板函数,这并不是真的。

于 2012-05-30T21:57:30.080 回答
2

我也偶然发现了这个问题,不幸的是,提到的各种标志并没有太多运气,但是,我确实发现了两种在处理仅标题函数时生成更准确的覆盖率信息的方法。

首先是添加标志-fkeep-inline-functionshttps://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fkeep-inline-functions)。

这确实给了我想要的结果,但是在尝试与其他库(甚至是普通的 C++ 标准库)集成时遇到了一些严重的问题。我最终得到了链接错误,因为链接器应该删除的某些函数没有(例如没有定义的函数声明)。

第二种方法(我最终选择的方法)是__attribute(used)__在 GCC 中使用来注释我所有的标头 API 函数。文档(https://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Function-Attributes.html)指出:

用过的

附加到函数的此属性意味着即使看起来未引用该函数,也必须为该函数发出代码。

我用 a#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编译的时候也用过)

于 2019-08-04T09:28:14.903 回答
1

因为我发现这个问题在为我的仅标头库设置测试覆盖率时非常有用,所以我学到了一些额外的东西,希望它们可以帮助其他人:

即使在这些答案中提到了所有标志,我仍然在优化未使用的类方法时遇到问题。经过大量试验,我发现基于 clang源的覆盖率(这些标志:)-fprofile-instr-generate -fcoverage-mapping包括所有类方法,并且通常是获取覆盖率数据的最可靠方法。我还使用标志:-O0 -fno-inline -fno-elide-constructors进一步降低代码被优化的风险。

对于一个大型库来说,模板实例化这件事仍然是个问题。显式实例化它们一切都很好,但如果有人忘记了,你会得到不准确的代码覆盖率指标。有关自动调整代码覆盖率数据以解决此问题的方法,请参阅我对这个问题的回答。

于 2018-06-17T03:43:27.997 回答