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