5

小さな C++ テスト プロジェクトを立ち上げて 100% のカバレッジで実行することにより、自動テストについて詳しく学ぼうとしているときに、次の問題に遭遇しました。 、lcov はまだ 2 つの行をテストされていない (関数定義のみを含む) として報告し、「実際の」コンストラクター (これまでに定義および使用された唯一のコンストラクター) と完全に一致するにもかかわらず、テストされていないと思われる「重複した」コンストラクター メソッドを報告します。

(最小限の再現の場合は EDIT にスキップしてください)

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 (これらの関数内の行が完全にカバーされているにもかかわらず、実行されていないとマークされている 2 つの関数定義行、および関数ヒット = 関数の合計 - 1)。

lcov からのそのソース ファイルの関数統計: http://gints.dyndns.info/heap_std_lcov_func (2 つの同一のコンストラクター定義を示しています。どちらもファイル内の同じコード行を参照しており、そのうちの 1 つはヒット 5 回とマークされ、もう 1 つはマークされています)。 0回)。

中間の lcov .info ファイル: http://gints.dyndns.info/lcov_coverage_filtered.info.txtを見ると、そこにも 2 つのコンストラクター定義があることがわかります。どちらも同じ行にあるはずです: FN:8 ,_ZN4BBOS8Heap_stdC1Ev & FN:8,_ZN4BBOS8Heap_stdC2Ev.

ああ、.uic インクルード / デストラクタの周りの混乱は気にしないでくださいそれらのファイルのスナップショットを撮ったとき、たまたま試していました。

これを解決する方法について誰か提案がありますか? 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 の追加のヒットしないコンストラクターは、関数リストにまだ存在します (上部の関数をクリックします)。

関数定義からスロー宣言を削除しようとしましたが、関数宣言で実行されていない行を処理します: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 関数の定義にはまだ奇妙な点があります)。

そして、もちろん、上記の両方を実行すれば、すべてうまくいきます

しかし、これの根本的な原因が何であるかについてはまだ困惑しています...そして、メソッド(コンストラクターを含む)をクラス本体ではなく別の.cppファイルで定義したいと思っています。関数には、スローできる明確に定義された例外があります。

これで遊んでみたいと思った場合に備えて、ソースは次のとおりです。http://gints.dyndns.info/lcov_repro_src.zip

何か案は?

ありがとう!

4

1 に答える 1

4

OK、C++ の例外宣言を調べて読んだ後、何が起こっているのか理解できたと思います。

  • un-hit throw 宣言に関する限り、ここではすべてが実際に正しいようです: 関数 throw 宣言は、出力オブジェクト ファイルに余分なコードを追加することになっています。 . 私はこのようなケースをテストしていなかったので、そのコードが実行されず、それらのステートメントがヒットしなかったとマークされたことは理にかなっています。いずれにせよ、ここの状況は理想とはかけ離れていますが、少なくともこれがどこから来ているかを見ることができます.

  • 重複するコンストラクターに関する限り、これは gcc での長年の議論 (および結果として生じるオブジェクト コードの重複を解決するためのさまざまなパッチの試み) で知られているようです: http://gcc.gnu.org/bugzilla/show_bug .cgi?id=3187 - 基本的に、2 つのバージョンのコンストラクターが作成されます。1 つはこのクラスで使用し、もう 1 つは子クラスで使用します。100% のカバレッジが必要な場合は、両方を実行する必要があります。

于 2012-10-11T11:20:01.550 に答える