【问题标题】:lcov woes: weird duplicate constructor marked as not covered & function not marked as covered, even though its lines have been executedlcov 问题:奇怪的重复构造函数标记为未覆盖和函数未标记为已覆盖,即使它的行已被执行
【发布时间】:2012-09-30 05:25:19
【问题描述】:

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

(对于最小复制情况跳至编辑)

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

这就是我的意思:

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

使用 lcov 处理的相同 .gcda 文件:http://gints.dyndns.info/heap_std_lcov.html(两个函数定义行标记为未执行,即使这些函数中的行已被完全覆盖,以及函数 Hit = functions 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 包含/析构函数周围的混乱,这只是处理 What is the branch in the destructor reported by gcov? 的一种肮脏方式,我在拍摄这些文件快照时碰巧正在尝试。

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

编辑:在下面添加最小重现场景

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

有什么想法吗?

谢谢!

【问题讨论】:

  • 我修复了你的超链接,希望你不要介意...
  • 看起来像优化问题,您是否尝试过关闭优化?
  • 谢谢,是的,我用 -g -O0 编译所有内容 - 添加了用于原始帖子的完整标志。
  • 是不是因为构造函数被内联了?
  • 大卫,你为什么会这样想?在每次运行时,您都可以看到构造函数中的覆盖率,并且构造函数的一个副本在统计信息中被计为命中 - 只是该构造函数有一个相同的“额外”副本不被计为命中。此外,这并不能解释 Do_stuff 行没有被覆盖。而且,优化在 -O0,不应该发生这样的内联。

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


【解决方案1】:

好的,经过一番搜寻和阅读 C++ 异常声明后,我想我明白发生了什么:

  • 就未命中的 throw 声明而言,这里似乎一切实际上都是正确的:函数 throw 声明应该向输出对象文件添加额外的代码来检查非法(就声明)抛出异常。由于我没有测试发生这种情况的情况,因此该代码从未执行过,并且这些语句被标记为未命中是有道理的。尽管无论如何这里的情况远非理想,但至少可以看出这是从哪里来的。

  • 就重复的构造函数而言,这似乎是 gcc 的一个已知问题,并进行了长期的讨论(以及各种补丁尝试以解决生成的目标代码重复):http://gcc.gnu.org/bugzilla/show_bug.cgi?id=3187 - 基本上,有是创建的构造函数的两个版本 - 一个用于此类,一个用于子类,如果您想要 100% 的覆盖率,您需要同时练习这两个版本。

【讨论】:

    猜你喜欢
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    • 1970-01-01
    • 2017-07-09
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多