lcov woes:标记为未覆盖的怪异重复构造函数&功能未标记为已覆盖,即使其行已执行

时间:2012-10-09 19:57:48

标签: c++ constructor code-coverage definition lcov

通过获得一个小型C ++测试项目来了解有关自动化测试的更多信息。运行100%覆盖率,我遇到了以下问题 - 即使我的所有实际代码行和所有执行分支都被测试覆盖,lcov仍然报告两行未经测试(它们只包含函数定义),以及作为一个“重复”的构造函数方法,即使它与我的“真实”构造函数(唯一一个曾经定义和使用过的)完全匹配,也应该是未经测试的。

(跳至编辑最小的复制案例)

如果我使用gcovr python脚本生成相同的覆盖率统计信息(来自相同的源,.​​gcno& .gcda文件)并将结果传递给Jenkins Cobertura插件,它会在所有计数上给出100% - 行,条件和&方法

这就是我的意思:

Jenkins Cobertura Coverage页面:http://gints.dyndns.info/heap_std_gcovr_jenkins_cobertura.html(一切都是100%)。

使用lcov:http://gints.dyndns.info/heap_std_lcov.html处理的相同.gcda文件(标记为未执行的两个函数定义行,即使这些函数中的行被完全覆盖,以及函数Hit =函数Total - 1)。

来自lcov的源文件的函数统计信息:http:// gints.dyndns.info/heap_std_lcov_func(显示两个相同的构造函数定义,两者都引用文件中的相同代码行,其中一行标记为命中5次,另外0次)。

如果我查看中间的lcov .info文件:http://gints.dyndns.info/lcov_coverage_filtered.info.txt我看到有两个构造函数定义,两者都应该在同一行:FN:8,_ZN4BBOS8Heap_stdC1Ev& FN:8,_ZN4BBOS8Heap_stdC2Ev

哦,不要介意.uic include /析构函数周围的混乱,这只是处理What is the branch in the destructor reported by gcov?的一种肮脏方式。当我拍摄这些文件快照时,我碰巧尝试了。

有人建议如何解决这个问题? C ++编译器在这里有一些“幕后”的魔力吗? (特殊目的的构造函数的额外副本,我应该确保从我的测试中调用,也许?)常规函数定义怎么样 - 即使主体已经过全面测试,定义行怎么能不被测试?这只是lcov的一个问题吗?欢迎任何建议 - 我想了解为什么这种情况正在发生,如果真的有一些功能我的测试没有发现,Cobertura没有抱怨......或者如果没有,我该怎么做lcov明白了吗?

编辑:在

下面添加最少的repro场景

lcov_repro_one_bad.cpp:

#include <stdexcept>
class Parent {
public:
    Parent() throw() { }
    virtual void * Do_stuff(const unsigned m) throw(std::runtime_error) =0;
};

class Child : public Parent {
public:
    Child() throw();
    virtual void * Do_stuff(const unsigned m)
        throw(std::runtime_error);
};

Child::Child()
    throw()
    : Parent()
{
}

void * Child::Do_stuff(const unsigned m)
    throw(std::runtime_error)
{
    const int a = m;
    if ( a > 10 ) {
        throw std::runtime_error("oops!");
    }
    return NULL;
}

int main()
{
    Child c;
    c.Do_stuff(5);
    try {
        c.Do_stuff(11);
    }
    catch ( const std::runtime_error & ) { }
    return 0;
}

生成文件:

GPP_FLAGS:=-fprofile-arcs -ftest-coverage -pedantic -pedantic-errors -W -Wall -Wextra -Werror -g -O0

all:
    g++ ${GPP_FLAGS} lcov_repro_one_bad.cpp -o lcov_repro_one_bad
    ./lcov_repro_one_bad
    lcov --capture --directory ${PWD} --output-file lcov_coverage_all.info --base-directory ${PWD}
    lcov --output-file lcov_coverage_filtered.info --extract lcov_coverage_all.info ${PWD}/*.*
    genhtml --output-directory lcov_coverage_html lcov_coverage_filtered.info --demangle-cpp --sort --legend --highlight

这是我从中得到的报道:http://gints.dyndns.info/lcov_repro_bin/lcov_coverage_html/gints/lcov_repro/lcov_repro_one_bad.cpp.gcov.html

正如您所看到的,所谓的未命中行是函数可能抛出的异常的定义,而Child的额外非命中构造函数仍然存在于函数列表中(单击顶部的函数)。

我已经尝试从函数定义中删除throw声明,并在函数声明中处理未执行的行:http://gints.dyndns.info/lcov_repro_bin/lcov_coverage_html/gints/lcov_repro/lcov_repro_one_v1.cpp.gcov.html(额外的构造函数仍然存在,如您所见)。

我已经尝试将函数定义移动到类体中,而不是稍后定义它们,并且摆脱了额外的构造函数:http://gints.dyndns.info/lcov_repro_bin/lcov_coverage_html/gints/lcov_repro/lcov_repro_one_v2.cpp.gcov.html(尽管如此,围绕Do_stuff函数定义仍有一些奇怪之处,你可以看到。)

然后,当然,如果我同时做到这两点,一切都很好:http://gints.dyndns.info/lcov_repro_bin/lcov_coverage_html/gints/lcov_repro/lcov_repro_one_ok.cpp.gcov.html

但我仍然难以理解其根本原因是什么......我仍然想让我的方法(包括构造函数)在一个单独的.cpp文件中定义,而不是在类体中定义,而我我希望我的函数有明确定义的例外,它们可以抛出!

以下是来源,如果你想玩这个:http://gints.dyndns.info/lcov_repro_src.zip

有什么想法吗?

谢谢!

1 个答案:

答案 0 :(得分:4)

好的,经过一些狩猎和阅读C ++异常声明,我想我明白了发生了什么:

  • 就未命中的throw声明而言,似乎这里的一切都是正确的:函数throw声明应该在输出对象文件中添加额外的代码来检查非法(就throw而言)声明涉及)抛出异常。由于我没有测试这种情况发生的情况,因此从未执行该代码是有意义的,并且这些语句被标记为未命中。虽然这里的情况远非理想,但至少有一个人可以看到它的来源。

  • 就重复构造函数而言,这似乎是gcc的一个已知问题,经过长时间的讨论(以及解决结果对象代码重复的补丁的各种尝试):http://gcc.gnu.org/bugzilla/show_bug.cgi?id=3187 - 基本上,创建了两个版本的构造函数 - 一个用于此类,另一个用于子类,如果你想要100%覆盖,你需要同时执行这两个版本。