2012年3月27日火曜日

Google Test を使ってみる - その6(Jenkins との連携、スキップ対応)

これまでテストのことばっかり書いてきましたが、ここで突然 Jenkins の登場です。
Jenkins については、Wiki でも見てください。
そして、「Google Test を使ってみる」シリーズは今回で終了したいと思います。
(基本的にマニュアルに書いてあることばかりですし、そろそろネタ切れです。)

しかも、今回も Jenkins で Google Test の結果を収集しましょうという「ありがちな内容」です。
が、最後までお付き合いいただけましたら、幸いです。

※ Google Test 関係の記事一覧はこちら
プロジェクト設定
まずは、テスト結果を Windows バッチジョブから作っていきたいと思います。
テストプログラムと出力ファイルは適当です。
test.exe --gtest_output=xml:test_result.xml

つづいて、Jenkins のプロジェクト設定にて「JUnitテスト結果の集計」にチェックをいれます。
「テスト結果XML」には先程指定した xml のパスを入力します。


以上で設定完了です。実行してみると、テスト結果のリンクができます。
これを開くと、右のようなテストの結果を閲覧できます。リンクをたどっていくとテスト毎の結果を見れます。

また、テスト履歴をクリックするとテストの推移が確認できます。
1つのテスト結果を開いた状態でテスト履歴をクリックすると、そのテストだけの履歴をみることもできます。

スキップの対応
ここが今回のメインディッシュです。

Jenkins で集計されるテストの結果は、成功・失敗の他に スキップ があります。

Google Test でフィルターなどによりテストが実行されなかったものは、"notrun" として XML に出力されるのですが、これが「スキップ」ではなく「成功」として収集されてしまいます。
これをスキップとして Jenkins に集計させたいと思います。

Jenkins でスキップとして判断されるようにするには、<skipped> ノードが必要になるので、Google Test のソースを変更します。
gtest.cc の 3261 行目(XmlUnitTestResultPrinter::OutputXmlTestInfo 関数の if (failures == 0) の手前)に
以下のコードを追加してください。2012/03/30 追記: Google Test 1.6.0 です。
if( !test_info.should_run() && failures == 0 ) {
    *stream << ">\n      <skipped />\n    </testcase>\n";
    return;
  }
あとは、普通にビルドするだけです。

すると結果がこのように表示されます。

スキップされたテストが黄色で表示されるようになりましたね。

まとめ
さて、今回で「Google Test を使ってみる」シリーズはおしまいです。
と、いっても今後も Google Test 絡みのことは書いていくと思います。

実はもう次の記事の下書きは書いてたりしますが(^^

2012年3月24日土曜日

Cppcheck + Visual Studio

Cppcheck + Visual Studio で検索されているようなので、小ネタを。
てか、前の記事で書くのを忘れてました。

外部ツール
Visual Studio の外部ツールに Cppcheck を登録します。

[ツール(T)] -> [外部ツール(E)...] をクリック。
外部ツールのダイアログが開くので、そこで追加ボタンを押します。

タイトルは適当に「Cppcheck」とでもしておきましょう。
コマンドに「cppcheck.exe」(パスは通しておいてください。)
引数は「--enable=all --template vs $(ItemPath)」とします。
--template vs 
としておくと、 Visual Studio ライクな出力フォーマットになります。
出力ウィンドウのログからエラーのソース行にジャンプできるので、必須だと思います。

その他の引数はお好みで変えてください。
私の場合は、無視指定を追加してたりします。
-f --enable=all --template vs --inline-suppr --suppress=syntaxError $(ItemPath)

あとは、[出力ウィンドウを使用]にチェックを入れてください。

これで、[OK] すると [ツール(T)] メニューに Cppcheck のメニューが追加されます。
これをクリックすると Cppcheck が実行されます。

キー割り当て
もう少し便利にしたい、ということでキーの割当をしたいと思います。
[ツール(T)] -> [オプション(O)...] からオプションダイアログを開き、[環境] -> [キーボード] で「ツール.外部コマンド」を選択(番号は変更したい外部ツールのインデックス)し、ショートカットキーを割り当てます。

これで、設定したキーから Cppcheck を呼び出せるようになりました。
ビルドをする時のように、気軽に Cppcheck が使えますね。

2012年3月23日金曜日

Cppcheck を使ってみた

正しくは、「使ってみてたんだけど最近ようやく使えるようになった」です。

Cppcheck 1.53 にしてから、ずーと悩んでいたことが解決しちゃいました。
excluding 指定がうまくいかなくて悩んでいたのですが、
バージョンが上がったのを機に再チャレンジしたら、あっけなくできちゃいました。

前はダメで今回できたのはなぜだろうと調べてみたところ、こんな記事を見つけました。
CppCheckでフォルダを除外する
末尾に区切り文字が必要だったのですね。。。

とにかく、一歩前進できたのでブログにまとめておこうと思います。
※この記事では、Cppcheck 1.53 + Windows7 を想定しています。

基本的な使い方
詳しいことは、マニュアル見ましょう。
ですが、まずは簡単に解析をしてみたいと思います。

1つのファイルを解析
cppcheck --enable=all main.cpp
フォルダ以下を解析
cppcheck --enable=all folder

--enable=all ですべての警告を報告するように指定しています。
これが無いと何も教えてくれません。(詳細はマニュアル参照)
解析対象はフォルダ・ファイルどちらも OK です。
標準入力からの指定も可能なので、このようなこともできます。

ファルダ内の特定ファイルを解析
dir /s /b Gx*.cpp | cppcheck --enable=all --file-list=-
これで、カレントディレクトリ以下の Gx*.cpp にマッチするファイルが解析されます。

特定のフォルダ/ファイルを無視する
冒頭でも触れましたが、cppcheck --enable=all myproject の用にプロジェクトフォルダ以下すべてを解析するように設定していると、テストフォルダやメンテナンスされていないフォルダなど解析してほしくないところがでてきます。
その場合、-i オプションを使用します。
cppcheck --enable=all -imyproject\test\ myproject
末尾に区切り文字が必要です!

これで、myproject\test\ フォルダを除く myproject フォルダを解析できます。

期待する結果が得られない場合
この時点で、期待する結果が得られない場合 --force オプションを使用してみてください。
デフォルトでは --max-configs が 12 となっているため、#ifdef などを多用していると目的のコードに達する前に解析が終わってしまいます。

Jenkins との連携
Cppcheck のプラグインがあるので使ってみました。
Cppcheck の出力xmlを利用するので、xml を出力させます。
cppcheck --enable=all --xml myproject 2> result.xml
--xml を指定すると標準エラーにxmlが吐き出されるので、ファイルにリダイレクトさせてます。

実際に実行してみた画像がこちら。
Cppcheck のアイコンが追加されてますね。
エラー内容はもちろん、エラー数の推移が見れたりします。
収集結果やグラフなどのサンプルがあるので、それらは Cppcheck Plugin の Wiki をご覧下さい。


さて、こちらの画面ですが、実は所要時間が2時間超となってます。
解析対象の iutest は、コード量は少なめです。
これを仕事のプロジェクトでやったらどんだけかかってしまうのか?!という状態です。

そうなると、解析時間の短縮をしたくなりますよね。

並列処理
マニュアルを見てみると [2.7. Multithreaded checking] という項目があります。
これで時間短縮になるんじゃないかと思ったのです。が、
$ cppcheck -j4 --enable=style main.cpp
No thread support yet implemented for this platform.
Windows じゃ使えないっぽいです。残念。。。
2013/01/17 追記:バージョン 1.58 から使えるようになりました。

ちなみに、並列オプションを使うと未使用関数の検出ができないようです。
$ cppcheck -j4 --enable=all main.cpp
cppcheck: unusedFunction check can't be used with '-j' option, so it's disabled.
No thread support yet implemented for this platform.
ま、どのみち Windows ではサポートされてませんが。。。

Configuration
解析時間の短縮のために、解析したい箇所を限定します。
方法としては、 -D や -U オプションを使います。
-D オプションは #define 定義をするオプションです。
-D によって定義されたプリプロセッサディレクティブを解析するようになります。
-U オプションは、-D オプションの逆で #undef のようなものです。
-U で指定したマクロ名(ID)のディレクティブを解析から除外します。※1

例えば、Windows 向けに開発している場合、
cppcheck --enable=all -f -D_WIN32 -D_MSC_VER=1500 myproject
のように限定できます。

ここで注意が必要なのは -D オプションを使う場合、目的のコードに必要な定義をしっかりしないと解析されないことがあることです。
もし、-D オプションを使ってみて目的の警告が検出されなくなってしまったら、定義を見なおしてみると良いかもしれません。

では、実際に試してみた画面がこちら。


所要時間はなんと3分ちょっと
2時間が3分!
これなら、ビルド毎に Cppcheck を走らせても問題なさそうですよね。

アップしている画像だとわかりにくいですが、警告も変わらずです。
エラーIDから無視
再度、エラー無視の方法です。今度はエラーID、ファイル、行番号から設定します。
それには --suppress または、--suppressions-list オプションを使います。
cppcheck --enable=all --suppress=<error id>:[filename]:[line] myproject
<error id > にはエラーIDを指定します。(uninitVar など)
[filename],[line] はオプションで、指定した場合指定ファイルの指定行のエラーを無視します。

実は [filename] の記述の仕方がイマイチわかってません。。。
ファイル名や絶対パス、相対パスと試してみたのですが、うまくいかず…
しかたがないので、今はワイルドカードを使って指定しています。
cppcheck --enable=all --suppress=uninitVar:*test.cpp myproject

--suppressions-list の場合、上記のエラー無視指定を1行ずつ書いたテキストファイルを指定できます。

日本語対応
日本語を含むファイル名がダメらしいが、それとは違う話。
Visual C++ では、C# の #region のように使える #pragma region があります。
#pragma region 名前
void func()
{
   // ながーいコード
}
#pragma endregion
このコードを解析すると以下のようにエラーになってしまいます。
(error) The code contains characters that are unhandled. Neither unicode nor extended ASCII are supported. 
region名 に日本語があるとエラーになります。

日本語使うな!という話ではありますが、ライブラリ側でこれがあったりすると面倒です。
なので、これは無視しちゃいます。
上記のエラーの種類は syntaxError なのでこれを無視するように設定します。
cppcheck --enable=all --suppress=syntaxError myproject
他の syntaxError も無視することになってしまいますが、そもそも syntaxError ならコンパイルエラーになるはずなので Cppcheck で検出できなくても問題ないと思います。

まとめ
有償の静的解析ツールは高機能ですが、みんながみんな導入できるとは限らないでしょう。(お金がね…)
そんな時は、Cppcheck を使ってみてはどうでしょうか?
Cppcheck でも十分間違いを指摘してくれますし、Jenkins のプラグインを使えば結果も一目瞭然です。

なにもやらないよりはやった方が絶対イイので、やりましょう!Cppcheck。



※1. きちんと調べたわけではないが、#ifdef ID の場合は除外されるが、#if defined(ID) とか #ifndef ID 場合は意図通りになってくれない気がする。


追記
Visual Studio との連携 > Cppcheck + Visual Studio

2012年3月20日火曜日

Variadic Templates わからない

Variadic Template 始めましたに続き、連投です。

Variadic Template Templates を書いていたら、内部コンパイルエラーになってしまいました。
コンパイラは gcc version 4.7.0 20120128 (experimental) (GCC) です。
そもそも、書いたコードが規格に沿っていないのかもしれませんが。。。

まずは、こちらの Variadic Templates のコード
struct TypeList0 {};

template<typename T, typename ...Args>
struct VariadicTypeList
{
    typedef T   Head;
    typedef VariadicTypeList<Args...>   Tail;
};
template<typename T>
struct VariadicTypeList<T>
{
    typedef T   Head;
    typedef TypeList0   Tail;
};

template<typename ...Args>
struct Types
{
    typedef VariadicTypeList<Args...>   type;
};

int main(void)
{
    VariadicTypeList<int, float>::Head c;
    Types<int, float>::type::Head d;
    return 0;
}
http://ideone.com/23bQ6
こちらは、問題なくコンパイルできます。

つづいて、問題の Variadic Template Templates のコード
struct TemplateTypeList0 {};

template<template<typename> class U>
struct TemplateTypeSel
{
    template<typename T>
    struct bind
    {
        typedef U<T>    type;
    };
};

template<template<typename T> class T1, template<typename T> class ...Args>
struct VariadicTemplateTypeList
{
    typedef TemplateTypeSel<T1> Head;
    typedef VariadicTemplateTypeList<Args...>   Tail;
};
template<template<typename T> class T1>
struct VariadicTemplateTypeList<T1>
{
    typedef TemplateTypeSel<T1> Head;
    typedef TemplateTypeList0   Tail;
};

template<template<typename T> class ...Args>
struct Templates
{
    typedef VariadicTemplateTypeList<Args...> type;
};

template<typename T>
struct Y
{
    T v;
};

int main(void)
{
    VariadicTemplateTypeList<Y, Y>::Head::bind<int>::type a;
    Templates<Y>::type::Head::bind<int>::type b;
    return 0;
}
http://ideone.com/q5lgt
このコードがコンパイルできないんですよね。。。

$ g++ -std=c++11 test1.cpp
test1.cpp: In instantiation of 'struct VariadicTemplateTypeList<Y, Y>':
test1.cpp:43:32:   required from here
test1.cpp:13:71: internal compiler error: Segmentation fault
Please submit a full bug report,
with preprocessed source if appropriate.
See  for instructions.

これってバグ報告した方がいいんでしょうかね?
やりたいことは、最新の iutest で、
-DIUTEST_HAS_VARIADIC_TEMPLATE_TEMPLATES=1 としていただければわかると思います。

なんでも良いので、なにかあればジャンジャン指摘してください。お願いします。

追記
gcc 4.7.0 がリリースされたので、こちら(Fortran, C and C++ for Windows)のものを使用して試してみたました。が、ダメでした。

Variadic Templates 始めました

C++11 の Variadic Templates をはじめました。
早速、テストフレームワーク(iutest v0.22.0.0)に組み込んでみました。

※ここでは、テストフレームワークに組み込んだ時のことを書いています。Variadic Templates 自体の説明は省かせて頂きます。

型付けテストや値をパラメータ化したテストでは、任意の個数のパラメータを設定できます。
たとえば、以下のような使い方をします。
// 値をパラメータ化したテスト
INSTANTIATE_TEST_CASE_P(TestPInstance, TestP, testing::Values(
    "い", "ろ", "は", "に", "ほ", "へ", "と"));

// 型付けテスト
TYPED_TEST_CASE(TypedTest, testing::Types<int, short, float>);

しかし、これには制限があります。
Values や Tepes は50個までしか使えません。

template<typename T1 = detail::None, typename T2 = detail::None, typename T3 = detail::None, typename T4 = detail::None
, typename T5 = detail::None, typename T6 = detail::None, typename T7 = detail::None, typename T8 = detail::None, typename T9 = detail::None
, typename T10 = detail::None, typename T11 = detail::None, typename T12 = detail::None, typename T13 = detail::None, typename T14 = detail::None
, typename T15 = detail::None, typename T16 = detail::None, typename T17 = detail::None, typename T18 = detail::None, typename T19 = detail::None
, typename T20 = detail::None, typename T21 = detail::None, typename T22 = detail::None, typename T23 = detail::None, typename T24 = detail::None
, typename T25 = detail::None, typename T26 = detail::None, typename T27 = detail::None, typename T28 = detail::None, typename T29 = detail::None
, typename T30 = detail::None, typename T31 = detail::None, typename T32 = detail::None, typename T33 = detail::None, typename T34 = detail::None
, typename T35 = detail::None, typename T36 = detail::None, typename T37 = detail::None, typename T38 = detail::None, typename T39 = detail::None
, typename T40 = detail::None, typename T41 = detail::None, typename T42 = detail::None, typename T43 = detail::None, typename T44 = detail::None
, typename T45 = detail::None, typename T46 = detail::None, typename T47 = detail::None, typename T48 = detail::None, typename T49 = detail::None
, typename T50 = detail::None
>struct Types
{
    typedef detail::TypeList50<T1, T2, T3, T4, T5, T6, T7, T8, T9
        , T10, T11, T12, T13, T14, T15, T16, T17, T18, T19
        , T20, T21, T22, T23, T24, T25, T26, T27, T28, T29
        , T30, T31, T32, T33, T34, T35, T36, T37, T38, T39
        , T40, T41, T42, T43, T44, T45, T46, T47, T48, T49
        , T50
    > type;
};

このようなコードを何個も何個も書いてます。
まぁ50個あれば十分かもしれませんが、これを50個も書くのは大変です・・・
しかし、Variadic Templates があればこれだけです。

template<typename ...Args>
struct Types
{
    typedef detail::VariadicTypeList<Args...> type;
};

とっても便利ですね!
細かな実装は、iutest のソースを見てください。
今後も C++11 の機能を組み込んでいきたいと思います。

2012年3月11日日曜日

Visual Studio 11 Beta で Google Test を使う

Visual Studio 11 Beta が公開されましたね。
そこで Visual Studio 11 Beta でも Google Test を使ってみようと思います。

今回の元ネタは、Google グループの Google C++ Testing Framework のこちらの投稿です。
備忘録としてブログにまとめておこうと思います。

プロジェクトの準備
まずは、ダウンロードページより取得した Google Test を解凍します。
解凍したフォルダの中に msvc フォルダがあり、そこの gtest.sln を Visual Studio 11 で開きます。
(Visual Studio 11 Beta のインストールは済ませておいてください。)

プロジェクトのアップグレード(変換)ダイアログが出るので、
アップグレードします。
プロジェクトが正常に読み込まれたら、準備完了です。

ビルド
F7 キーを押して終わり。とはいきません・・・
error C2977: 'std::tuple' : テンプレート 引数の数が多すぎます
error C3203: 'tuple' : 非特殊クラス テンプレート は、テンプレート 引数として テンプレート パラメーター 'T' に使用できません。実際の型を指定してください
とか
error C2955: 'testing::internal::ParamIteratorInterface' : クラス テンプレート を使用するには テンプレート 引数リストが必要です
というエラーがでます。

一番簡単な対処方法は GTEST_HAS_TR1_TUPLE=0 としてしまうことです。
ただし、こうすると値をパラメータ化したテストで Combine が使えなくなってしまいます。

gtest-port.h
Combine を使えるようにするために、gtest/internal/gtest-port.h を編集します。
まずは、MSVC11 の <tuple> は名前空間が異なっているため、対応します。
gtest-port.h の 506 行目に以下を付け足します。
#  if defined(_MSC_VER) && _MSC_VER >= 1700
namespace std { namespace tr1 { using std::tuple; } }
#  endif

次に、Variadic Templates の上限を増やします。
えっ、と思う方もいるかもしれませんが、そういうもんだと思ってください。

gtest-port.h の 38 行目あたりに以下を付け足します。
#if defined(_MSC_VER) && _MSC_VER == 1700
#  ifndef _VARIADIC_MAX
#    define _VARIADIC_MAX 10
#  elif _VARIADIC_MAX < 9
#    define GTEST_HAS_TR1_TUPLE 0
#  endif
#endif
インクルード順の関係で、_VARIADIC_MAX がすでに定義されていたら、残念ながら使えないようにしています。

もし、確実に使いたい場合は
プロジェクトのプロパティからプリプロセッサの定義に _VARIADIC_MAX=10 を追加してください。

以上の対処でビルドエラーは解消されたはずです。
あとは今まで通りに使えると思います。

今回はここまでです。それでは。

2012年3月7日水曜日

NaCl で iutest を使ってみた

iutest を Google Native Client(以下、NaCl) で使用してみました。

ちょっと本題に入る前に iutest 自体のことを。
iutest 現在 v0.21.0.0 です。
今見返してみるとバグ修正が多いですね。。。テストフレームワーク自体がバグっていてはダメですよね。。。
バグ修正以外だと、NaCl の対応を強化したり、C++11 の機能を使ってみたりしました。
それと、WARN flavor で紹介した INFORM マクロですね。


さて、本題です。
公開の仕方があっているのかわかりませんが、サンプルを用意しました。
NaCl サンプル
このサンプルも動作が怪しいですが…

ただ、NaCl でも使えることを確認しただけです。
NaCl での printf 出力が上手くできなくて、出力をメッセージに流すようにしています。

今回は、これだけです。ご意見など頂けたら嬉しいです。
それでは。

2012年3月2日金曜日

Google Test を使ってみる - その5(関数内の失敗)

まずは、下記のコードをご覧下さい。
void SubTest(int** pp)
{
    *pp = NULL;
    ASSERT_TRUE( NULL != *pp );
}

TEST(Sample, Test)
{
    int* p=NULL;

    SubTest(&p);

    ASSERT_TRUE( NULL != p );
}
さて、このテストは失敗します。失敗しますが、あなたはどのような結果を望みますか?

[----------] 1 test from Sample
[ RUN      ] Sample.Test
main.cpp(4): error: Value of: NULL != *pp
  Actual: false
Expected: true
main.cpp(11): error: Value of: NULL != p
  Actual: false
Expected: true
[  FAILED  ] Sample.Test (1 ms)
[----------] 1 test from Sample (2 ms total)

実際に実行してみると、このような結果になります。
期待通りでしたか?


期待通り?いや、期待通りではない?ですよね??
そうなんです。
SubTest 関数で ASSERT_TRUE が失敗しても Test は続行されてしまうのです。
実は、ASSERT_* マクロはテストを中断するために return するだけなのです。

では、どのようにしてテストを中断したらよいのでしょう。

例外を使う
幸いなことに Google Test には例外を使うオプションが用意されています。
--gtest_throw_on_failure を使うと、失敗時に例外を投げるようになります。

結果がこちら。
[ RUN      ] Sample.Test
main.cpp(7): error: Value of: NULL != *pp
  Actual: false
Expected: true
続行するには何かキーを押してください . . .

テストが中断するようになりました。が、テスト全体が中断するようになってしまいました。
これは、意図している結果と少し違います。

ここでは、SubTest で失敗が発生したら Sample.Test のみを中断させたいのです。

ASSERT_NO_FATAL_FAILURE を使う
ASSERT_NO_FATAL_FAILURE という、このためだけにあると言っても過言ではないマクロがあります。
ASSERT_NO_FATAL_FAILURE(statement) マクロでは、 statement で失敗が発生した場合に失敗します。

では、早速組み込んでみましょう。

TEST(Sample, Test)
{
    int* p=NULL;

    ASSERT_NO_FATAL_FAILURE( SubTest(&p) );

    ASSERT_TRUE( NULL != p );
}

SubTest の呼び出しを、ASSERT_NO_FATAL_FAILURE で囲うだけです。
続いて、出力結果です。

[----------] 1 test from Sample
[ RUN      ] Sample.Test
main.cpp(4): error: Value of: NULL != *pp
  Actual: false
Expected: true
main.cpp(11): error: Expected: SubTest(&p) doesn't generate new fatal failures in the current thread.
  Actual: it does.
[  FAILED  ] Sample.Test (16 ms)
[----------] 1 test from Sample (16 ms total)

期待通り、SubTest 内部で発生した失敗を検知し Sample.Test が中断するようになりました!
これで、テストの記述の仕方の幅が広がりましたね!

今回はここまで。

※ Google Test 関係の記事一覧はこちら