2013-07-28 5 views
10

私はC++で書かれたプログラムを持っています。サブフォルダ/ライブラリにSConscriptファイルを呼び出すトップレベルのSConscriptがあります。ライブラリcppの内部では、GTESTはGTestは別のコンパイル単位でテストを見つけません

がある:

TEST(X, just_a_passing_test) { 
EXPECT_EQ(true, true); 
} 

(メインがあります)だけでメインGTestsを呼び出し、別のGTESTを持つトップレベルのプログラムソース、中:

int main(int argc, char** argv) { 
::testing::InitGoogleTest(&argc, argv); 
return RUN_ALL_TESTS(); 
} 

TEST(Dummy, should_pass){ 
EXPECT_EQ(true, true); 
} 

今問題は、私がプログラムを実行すると、GTestはmain.cppソースでのみテストを実行することです。ライブラリ内のテストを無視する。今、main.cpp内の同じライブラリcpp内の無関係なクラスを参照すると、奇妙なことになります(例えば、 'SomeClass foo;'など)、テストが魔法のように現れます。 -O0やその他のトリックを使ってgccに呼び出されないコードを最適化しないようにしてみました。私はClangも試したことがあります。 GTestがコンパイル時にテストの検出を行う方法と関係していると思われますが、この問題に関する情報は見つかりません。私は静的な初期化を使用していると信じています。 ヘルプ/情報は大歓迎です!

更新: Visual C++に特に言及しているにもかかわらず、この問題のように聞こえるFAQのセクションが見つかりました。これには、コンパイラが参照されていなければライブラリを破棄しないように強制するトリック/ハックが含まれています。 ライブラリにテストを入れないことをお勧めしますが、ライブラリごとにどのようにテストすればいいのでしょうか?シーン設定1から https://code.google.com/p/googletest/wiki/Primer#Important_note_for_Visual_C++_users

答えて

11

は、そのgtestテストケース 行方不明になり、ライブラリを静的アプリケーションのビルドにリンクされていることを収集します。また、 GNUツールチェーンが使用されています。

問題の動作の原因は簡単です。テスト には、 TEST(X, just_a_passing_test)が含まれているライブラリの参照は含まれていません。そのため、リンカーはのいずれかをリンクして、 オブジェクトファイルをリンクしてプログラムにリンクさせる必要はありません。そうではありません。そのため、ランタイムには実行可能ファイル内にそのテストが存在しないため、 gtestが見つかりません。

GNU形式の静的ライブラリは、ハウスキーピングヘッダーブロックとグローバルシンボルテーブルで飾られたオブジェクトファイルのアーカイブ です。

ザ・OPは、問題のライブラリに にプログラム内の任意のパブリックシンボルをアドホック参照を符号化することにより、彼は「魔法」のプログラムにその テストケースを強いることができることを発見しました。

魔法はありません。そのパブリックシンボルへの参照を満たすために、リンカーは であり、オブジェクトファイルをライブラリからリンクする必要があります。これは、シンボルの定義を としています。そして、OPは、図書館が.cppから であることを伝えます。したがって、ライブラリにはオブジェクトファイルが1つしかなく、 にはテストケースの定義も含まれています。 リンケージ内のそのオブジェクトファイルでは、テストケースはプログラム内にあります。

コンパイルオプションでGCCからclangに切り替えると、OPは同じ目的を達成するためにもっと尊敬できる方法を探して無駄になりました。コンパイラは とは関係ありません。 GCCまたはclangの場合、システムリンカーによってリンクされます(ld )(ただし、それを置き換えるために異常な対策が取られていない限り)。

静的ライブラリのオブジェクトファイルをオブジェクトファイルにリンクするには、ldを入手する方がいいでしょうか?

あります。で、ld

g++ -o app -L/path/to/the/libcool/archive -lcool 

この代表団コマンドラインを:問題プログラムがappで、問題の静的ライブラリは、次にappリンク通常のGCCコマンドラインは、関連 ポイントで、これに似ている libcool.a

であると言います追加のリンカーオプション、および ライブラリは、自身が見つかったシステムのデフォルトであるとみなします。

リンカーが-lcoolと考えられるようになると、というアーカイブ/path/to/the/libcool/archive/libcool.aの要求であることがわかります。次に、 の未解決のシンボル参照がこの時点でまだlibcool.aのオブジェクトファイルでコンパイルされているかどうかを、 と判断します。 anyがある場合、それらのオブジェクトファイルはappにリンクされます。そうでなければ、 をlibcool.aから何もリンクせずに渡します。

libcool.aには、にリンクするシンボル定義がありますが、appはそれらを参照していません。その場合、 が参照されていなくても、libcool.aからオブジェクトファイルをリンクするためにリンカーに を伝えることができます。より正確には、我々は そうのように、それを行うためにリンカに指示するg++を伝えることができます。

g++ -o app -L/path/to/the/libcool/archive -Wl,--whole-archive -lcool -Wl,-no-whole-archive 

もの-Wl,...オプションが...ldにオプションを渡すg++を教えてください。 --whole-archive オプションを指定すると、 が参照されるかどうかに関わらず、その後のアーカイブからのすべてのオブジェクトファイルを後で通知するまでリンクするように指示されます。 -no-whole-archiveは、 ldにそれをやめ、通常どおりビジネスを再開するよう指示します。

g++コマンドラインの最後にあるように、-Wl,-no-whole-archiveが重複しているように見えることがあります。しかし、そうではありません。 g++は、ldに渡す前に、システムのデフォルトライブラリ をコマンドラインに追加することを覚えておいてください。あなたは間違いなく --whole-archiveがデフォルトのライブラリがリンクされているときに有効にしたくないです。 (複数の定義エラーでリンケージが失敗します)。

は、問題の場合には、この溶液を適用し、TEST(X, just_a_passing_test) は、そのテストを定義するオブジェクトファイルに、いくつかのノーオペレーション 参照を行うためのプログラムを強制のハックすることなく、実行されます。

一般的なケースでは、この解決策には明らかな欠点があります。何か参照されていないオブジェクトファイルのリンクを強制したい のライブラリがある場合は、という実際に以外の参照されていないオブジェクトファイルの束が 含まれています。 --whole-archiveもそれらのすべてをリンクしており、プログラム内で膨らんでいるだけです。

--whole-archiveソリューションは、ノーオペレーション参照 ハックより立派かもしれないが、それは立派ではありません。それも見て尊敬していません。

実際の解決策は、合理的なことを行うことです。 リンカでプログラム内の何かの定義をリンクさせる場合は、は、 のリンカーからの秘密を保持しません。少なくともはの定義が使用されることを期待している各コンパイル単位内のものをと宣言します。

class X_just_a_passing_test_Test : public ::testing::Test { 
public: 
    X_just_a_passing_test_Test() {} 
private: 
    virtual void TestBody(); 
    static ::testing::TestInfo* const test_info_ __attribute__ ((unused)); 
    X_just_a_passing_test_Test(X_just_a_passing_test_Test const &); 
    void operator=(X_just_a_passing_test_Test const &); 
}; 

(プラスtest_info_の静的初期化子と:gtestテストケースとの合理的なことをやって

TEST(X, just_a_passing_test)のようなgtestマクロは、この場合にクラス定義、 に展開 という理解を必要としますTestBody()の定義)。

TEST_F,TEST_P変異体についても同様である。したがって、これらの マクロを、コードに がクラス定義に適用されるのと同じ制約と期待で展開することができます。この光で

あなたはcool.hで定義されたライブラリlibcoolcool.cpp に実装し、そのためのgtestユニットテストをしたい、tests.cppに実装されたテストプログラムtests によって実行されるようにしている場合、合理的なものは次のとおりです。 -

  • はそれでcool_test.h
  • #include "cool.h"、ヘッダファイルを書く
  • #include <gtest/gtest.h>がその中にあります。
  • は、コンパイル
  • tests.cppでそれに
  • #include "cool_test.h"をごlibcoolテストケースを定義し、libcoollibgtest

とのリンクtests.cppそして、あなたはOPが何をしたかしないだろう、なぜそれは明らかです。あなたはtests.cppcool.cpp としない範囲内、tests.cppによって必要とされている クラス、およびcool.cppで必要ないを定義しないでしょう。すぐに彼らに痛みを実行すること

他にどのようにあなたは一人一人のための実行可能ファイルをせずに、ライブラリをテストするだろう、 :ので

OPは、ライブラリ でテストケースを定義に対するアドバイスを嫌うました。経験則として

私はユニットテスト済みであることをライブラリごとgtest実行 を維持するための練習をお勧めします:すぐにそれらを実行すると、当たり前の自動化ツール などmakeと無痛であり、それは取得するはるかに良いです図書館ごとの合否判定は よりも多くの図書館の評決に過ぎません。しかし、あなたはまだ 異議には何もありませんことを行うにはしたくない場合:詳細な情報についてlibcoollibcoolerlibcoolestlibgtest

+0

おかげで

// tests.cpp #include "cool_test.h" #include "cooler_test.h" #include "coolest_test.h" int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } 

コンパイルとリンク。これは私の疑惑を確認するのに役立ちます。最終的に私は単体テストを実行するために各ライブラリの実行可能なターゲットを使用しました。 – NitrousUK

+0

+50良い例で素晴らしい答え! –

+0

私の場合は、StaticライブラリからMach-OタイプのリロケータブルオブジェクトファイルへのXcodeの変更を手助けしました –

関連する問題