2012年12月27日木曜日

Google Test ユーザーが Boost.Test を使ってみた (補足)

この記事は、C++ Advent Calendar 2012: 17日目「Google Test ユーザーが Boost.Test を使ってみた」の補足記事になります。

本編では書ききれなかったことや、訂正なども含めて補足をしたいと思います。

値をパラメータ化したテスト
Boost.Test でも使えます。リファレンスはこちらです。

(Boost.Test Example)
#include <boost/test/included/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
using namespace boost::unit_test;

//____________________________________________________________________________//

void free_test_function( int i )
{
    BOOST_CHECK( i < 4 /* test assertion */ );
}

//____________________________________________________________________________//

test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    int params[] = { 1, 2, 3, 4, 5 };

    framework::master_test_suite().
        add( BOOST_PARAM_TEST_CASE( &free_test_function, params, params+5 ) );

    return 0;
}

//____________________________________________________________________________//

こちらですが、自動でテストケースを認識・実行してくれる AUTO マクロがありません。
そのため、明示的に add する必要があります。

それはちょっと面倒臭い

ということで、簡単にマクロを用意してみました。
#include <boost/test/included/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
 
#define BOOST_AUTO_TEST_CASE_PARAM( function, begin, end )    \
BOOST_AUTO_TU_REGISTRAR( BOOST_JOIN(registrar, __LINE__)  )(  \
    BOOST_PARAM_TEST_CASE(function, begin, end) )
 
void free_test_function( int i )
{
    BOOST_CHECK( i < 4 /* test assertion */ );
}
 
int params[] = { 1, 2, 3, 4, 5 };
BOOST_AUTO_TEST_CASE_PARAM( &free_test_function, params, params+5 );
(ideone)



テストの中断
Boost.Test では REQUIRE 、Google Test では ASSERT フレーバーが失敗時にテストの中断をします。
ただ、両者でこの中断の挙動が違うので補足をしておきます。

Boost.Test
void SubFunc(void)
{
    BOOST_REQUIRE_EQUAL(0, 1);
    BOOST_REQUIRE_EQUAL(1, 2);
}

BOOST_AUTO_TEST_CASE(Sub)
{
    SubFunc();
    BOOST_REQUIRE_EQUAL(2, 3);
}
Google Test
void SubFunc(void)
{
    ASSERT_EQ(0, 1);
    ASSERT_EQ(1, 2);
}

TEST(Test, Sub)
{
    SubFunc();
    ASSERT_EQ(2, 3);
}
どちらも、アサーションを記述したサブ関数をテストから呼び出しています。
さて、ここで SubFunc(); 後のアサーションに到達するかが、ここでのお話です。
実際の出力を見てみましょう。



Boost.Test
Google Test
ご覧のように、Boost.Test は最初のアサーションの失敗で中断されていますが、Google Test は ASSERT_EQ(2,3); まで到達しています。
これは Google Test の中断が return 文を使った仕組みになっているからです。
なぜこのような実装になっているかは、こちらをご覧ください。

もし、SubFunc 内で失敗したときに、以降の処理を中断したい場合は以下のように書くことができます。
void SubFunc(void)
{
    ASSERT_EQ(0, 1);
    ASSERT_EQ(1, 2);
}

TEST(Test, Sub)
{
    ASSERT_NO_FATAL_FAILURE( SubFunc() );
    ASSERT_EQ(2, 3);
}
ASSERT_NO_FATAL_FAILURE(statement); は、statement で失敗があったかどうかを検証するアサーションです。


このように、SubFunc の失敗によりテストが中断します。

テスト以外での利用
Boost.Test や Google Test などテスティングフレームワークのアサーションは、ログに詳細な情報が出力されるので、普通の assert よりもとても有益な検証になります。
通常、テストの検証として使用するこれらアサーションを、テスト以外の場所でも使おうという話です。

Boost.Test の場合
#define BOOST_TEST_NO_MAIN
#include <boost/test/included/unit_test.hpp>

int main(int , char**)
{
    BOOST_CHECK_EQUAL(2, 3);
    BOOST_REQUIRE_EQUAL(1, 2);
    return 0;
}

特にログも出ずに死亡。うーん、残念。

Google Test の場合
#include <gtest/gtest.h>

void f(void)
{
    ASSERT_EQ(1, 2);
}

int main(int, char**)
{
    EXPECT_EQ(2, 3);
    f();
    return 0;
}


ログが出力されて正常終了しました。
また、GTEST_FLAG マクロを使って失敗時ブレークポイントを有効にすれば、より使い勝手が上がります。
int main(int, char**)
{
    ::testing::GTEST_FLAG(break_on_failure) = true; // break point を有効にする
    EXPECT_EQ(2, 3);
    f();
    return 0;
}

終わり
ちょっとしたことでしたが、補足はこれで終わりです。
また機会があればテスティングフレームワークについて書きたいと思います。

それでは、また来年!

2012年12月17日月曜日

Google Test ユーザーが Boost.Test を使ってみた

この記事は、C++ Advent Calendar 2012: 17日目の記事になります。
お題は「Google Test ユーザーが Boost.Test を使ってみた」です。
(2012/12/27: 補足記事を書きました。)

これまで、C++ の testing framework には Google Test を使ってきたのですが、
この機会に Boost.Test に挑戦したいと思います。
今年2月に行われた「Boost.勉強会 #8 大阪」の参加報告で Boost.Test 使うぜ!っと意気込んでおいて今更かという感じではありますが・・・

では、なぜ今まで使わなかったのかというと
  • boost の導入がめんどくさそう
  • 日本語情報が少ない
  • Google Test が使いやすかった
と、いう勝手なイメージがあったからです。最後のが一番大きな理由でした。
でも、他のフレームワークのことも知らずに「Google Test がいいよ!」というのもあれですよね。

それでは、そろそろ本題に入ります。
以下は、今回この記事を書くにあたって参考にさせて頂いたところです。

はじめに
実は、そもそも boost を使ったことありません。
というわけで、まずは boost のセットアップからしました。

開発環境は、Visual Studio 2012 Express 2012 for Windows Desktop です。
boost のバージョンは 1.52.0 です。

~~~略~~~

思っていた以上に簡単にできました!
(本題ではないので別記事にしました。)
include オンリーでも使えた
boost をビルドしたんですが、実はヘッダオンリーでも使えました。
ヘッダオンリーだと気軽に試せるので良いですね!

では、まずはヘッダーオンリーでテストを書いてみましょう。
簡単な例
まずは、簡単なテストから見ていきたいと思います。

#define BOOST_TEST_MAIN    // main関数を定義
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(sample)

BOOST_AUTO_TEST_CASE(hoge)
{
    BOOST_CHECK_EQUAL(2*2, 4);
}

BOOST_AUTO_TEST_CASE(fuga)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_SUITE_END()

この辺は特に問題ないですね。
BOOST_AUTO_TEST_SUITE でグループ化できて、BOOST_AUTO_TEST_CASE でテストの中身が書けます。
書いたテストは Boost.Test が自動に検出して実行してくれます。
ちなみに、BOOST_AUTO_TEST_SUITE は必須ではありません。

Google Test と比較してみました。
Google Test
//

TEST(sample, hoge)
{
    EXPECT_EQ(2*2, 4);
}

TEST(sample, fuga)
{
    EXPECT_EQ(2*3, 6);
}

//
Boost.Test
BOOST_AUTO_TEST_SUITE(sample)

BOOST_AUTO_TEST_CASE(hoge)
{
    BOOST_CHECK_EQUAL(2*2, 4);
}

BOOST_AUTO_TEST_CASE(fuga)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_SUITE_END()


テストスイートが Google Test における TestCase に、
テストケースが Test(Info) にあたる感じです。
アサーション
値が等しいか検証する BOOST_CHECK_EQUAL など基本的なアサーションはそろっています。
どんなアサーションがあるかは、Boost.Test のリファレンスを参照してください。
(BOOST_CHECK_EQUAL_COLLECTIONS は Google Test にはないアサーションでイイなぁーと思いました。)

また、 Boost.Test のアサーションにはレベルが3種類あって、REQUIRE,CHECK,WARN が使えます。これらのレベルのことを Boost.Test ではフレーバー(flavor)といいます。

Google TestBoost.Testエラーカウントテストの実行
ASSERTREQUIRE増加中断
EXPECTCHECK増加継続
---WARNそのまま継続
(※ Boost.Test の中断は例外を使ったテストの中断、Google Test は return 文でのテスト関数の中断)

Google Test には WARN フレーバーがありませんが、コードを加えることで対応可能です。
詳しくはこちらを参考にしてください。
ブログズミ - Google Test で Boost.Test の WARN flavor を実現する

あと、大事な点を注意書きで済ませてしまってますが、まぁそこは気をつけてくださいってことで。

グループ化
先の説明で Boost.Test のテストスイートが Google Test の TestCase にあたると書きましたが、Boost.Test では namespace のように BOOST_AUTO_TEST_SUITE を複数ネストして使えます。
これは Google Test ではできないことです。

BOOST_AUTO_TEST_SUITE(sample)

BOOST_AUTO_TEST_SUITE(group1)

BOOST_AUTO_TEST_CASE(hoge)
{
    BOOST_CHECK_EQUAL(2*2, 4);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

テストフィクスチャ
Boost.Test でもテストフィクスチャが使用できます。
テストフィクスチャはグローバル、テストスイート、テストケースの3箇所で設置できます。
テストフィクスチャの役割は、主に
  • テスト開始前のセットアップ
  • テストに関する特定の状態の提供
  • テスト終了後の後片付け
の3つです。

この辺は Google Test と変わりませんが、テストフィクスチャの構成や挙動に違いがあるので下記サンプルで説明していきます。
struct Fixture1 {
    Fixture1() : fix1(1) { ::std::cout << "F1 ->" << ::std::endl; }
    ~Fixture1() { ::std::cout << "<- F1" << ::std::endl; }
    int fix1;
};

struct Fixture2 {
    Fixture2() : fix2(2) { ::std::cout << "F2 ->" << ::std::endl; }
    ~Fixture2() { ::std::cout << "<- F2" << ::std::endl; }
    int fix2;
};

struct Fixture3 {
    Fixture3() : fix3(3) { ::std::cout << "F3 ->" << ::std::endl; }
    ~Fixture3() { ::std::cout << "<- F3" << ::std::endl; }
    int fix3;
};

BOOST_GLOBAL_FIXTURE(Fixture1);

BOOST_FIXTURE_TEST_SUITE(sample, Fixture2)

BOOST_FIXTURE_TEST_CASE(hoge, Fixture3)
{
//  BOOST_CHECK_EQUAL(1, fix1); // グローバルはできない?
//  BOOST_CHECK_EQUAL(2, fix2); // Fixture2 は適応されないみたい
    BOOST_CHECK_EQUAL(3, fix3); // フィクスチャメンバーにアクセスできる
}

BOOST_AUTO_TEST_CASE(fuga)
{
//  BOOST_CHECK_EQUAL(1, fix1);
    BOOST_CHECK_EQUAL(2, fix2);
    ++fix2;
}

BOOST_AUTO_TEST_CASE(bar)
{
//  BOOST_CHECK_EQUAL(1, fix1);
    BOOST_CHECK_EQUAL(2, fix2);
    ++fix2;
}

BOOST_AUTO_TEST_SUITE_END()

まず、挙動に関してです。

テストケースでは、テストフィクスチャの状態(メンバー)にアクセスすることができます。
しかし、複数のテストフィクスチャは適応されないようで、BOOST_FIXTURE_TEST_CASE(hoge, Fixture3) では Fixture2 が実行されないようです。
当然、Fixture2 の状態にもアクセスできません。

また、fuga, bar のテストフィクスチャである Fixture2 もテストケース毎に生成/削除されるようです。

Google Test ユーザーとしては、最初「?」となりました。
この辺は実際の出力をご覧ください。

(※ --log_level=test_suite で実行)

続いて、構成です。

Google TestBoost.Test
グローバル::testing::EnvironmentBOOST_GLOBAL_FIXTURE
テストスイート毎
(テストケース毎)
::testing::Test::SetUpTestCase,
::testing::Test::TearDownTestCase
テストケース毎
(テスト毎)
::testing::Test::SetUp,
::testing::Test::TearDown
BOOST_FIXTURE_TEST_SUITE
BOOST_FIXTURE_TEST_CASE
(※ ()書きは Google Test の場合)
(※ SetUp/TearDown に着目した表です。)

Boost.Test の場合、どこでも同じテストフィクスチャを使えますが、
Google Test の場合は、それぞれ書き方が違います。

同じテストフィクスチャでも Google Test と Boost.Test で大分異なりますね。

型をパラメータ化したテスト
複数の型に対して、同様のテストを書きたい場合の方法です。

(Boost.Test Example)
typedef boost::mpl::list<int,long,unsigned char> test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE( my_test, T, test_types )
{
    BOOST_CHECK_EQUAL( sizeof(T), (unsigned)4 );
}

型リストには boost::mpl::list を使います。
この型リストの分だけテストが実行され、型は第二引数に指定した名前で参照します。

実行結果がこちら。

(※ --log_level=test_suite で実行)

型リストの分だけテストが実行されていることがわかると思います。
このように template 関数やクラスのテストを書くにあたって、型のパラメータ化テストが使えるとテストを非常に簡潔に書くことができます。
値をパラメータ化したテスト
Boost.Test に値のパラメータ化テストはない?
あって不思議じゃないんですが・・・リファレンス眺めた感じだと見あたらなかったです。

2012/12/20 追記:私の目が節穴だったようです。すみません。m(__)m

値のパラメータ化ありました。
リファレンスはこちらです。

実行時オプション
Boost.Test でもコマンドライン引数でテストの挙動を実行時に変更できます。
(※ 環境変数でも可能。詳しくはリファレンスを見てください。)

Google TestBoost.Test
テストのリスト表示gtest_list_tests
テストの選択gtest_filter=[filter]run_test=[filter]
無効テストの実行gtest_also_run_disabled_tests
シャッフルテストgtest_shufflerandom=[0|seed]
乱数シードgtest_random_seed=[seed]random=[seed]
繰り返しテストgtest_repeat=[count]
失敗時ブレークポイント停止gtest_break_on_failure
失敗時例外throwgtest_throw_on_failure
ログレベルlog_level=[level]
レポートレベルreport_level=[no|confirm|short|detailed]
ログフォーマット(gtest_output=xml[:path])output_format=[HRF|XML]
report_format=[HRF|XML]
色付き出力gtest_color=[yes|no|auto]
テスト時間出力gtest_print_time=[0|1|auto]
プログレス表示show_progress=[yes|no]
auto_start_dbg=[yes|no]
ビルド情報の出力build_info=[yes|no]
catch_system_errors=[yes|no]
framework での例外キャッチgtest_catch_exceptions=[0|1]
メモリリーク検出detect_memory_leak=[0|1|value > 1]
detect_fp_exceptions=[yes|no]
use_alt_stack=[yes|no]
終了コードresult_code=[yes|no]
ヘルプhelphelp
(※ 各オプションの前に "--" が必要です。)

ぱっと見ただけでも、Google Test と Boost.Test でできることが違うのがわかります。
テストの選択シャッフルテストはどちらも使えます。これは欠かせない機能ですね。
使い方がよくわからなかったオプションについては「?」としています。これは Boost.Test をもっと使っていけば分かるようになるんでしょう(きっと)。

Boost.Test ではログの出力レベルを細かく設定できます。その詳細です。
--log_level=test_suite はテストスイートの情報が表示されます。↑のサンプル画像を撮る際に使いました。
Value内容
allすべて表示
successall と同じ
test_suiteTestSuite のメッセージを表示
messageユーザーメッセージを表示
warning警告レポートを表示
errorエラーレポートを表示
cpp_exceptionキャッチされなかった例外を表示
system_error致命的でないシステムエラーの表示
fatal_error致命的なエラーの表示
nothingなにも表示しない
(※ 太字がデフォルト設定になります。)

Google Test の場合はログレベルの概念はありませんが、イベントリスナーをユーザーが定義することで独自の出力をさせることができます。
カスタムイベントリスナーの例はこちらを参照してください。
ブログズミ - Google Test で成功時のメッセージを非表示にする方法

テストレポート(XML)
Boost.Test の結果も Jenkins に集計させることができます!
個人的にこれがあるかないかで評価が大分変わってきます。

Google Test とは少し設定方法が違うので簡単に紹介をします。
Boost.Test で XML を出力するには、「--output_format=XML」オプションでできました。
また、Jenkins でのレポートの集計には、xUnit Plugin を使用します。
Jenkins の設定は Wiki を参考にしてください。

1点だけ注意。
--output_format=XML --log_level=all --report_level=no
Jenkins で集計してもらうためには、ログレベルなどのオプションも必要になります。

集計した結果はこんな感じで見れます。

イイ感じ!

ライブラリ版を使ってみる
ここまでヘッダーオンリーでテストを書いてきましたが、実運用で Boost.Test を使用する場合は Dynamic Library か Static Library を使うことをオススメします。ヘッダーオンリーでは、ソースファイルの分割ができないのと、コンパイルに時間がかかるからです。

ヘッダーオンリーからライブラリを使うように変更するのは簡単です。
まず、インクルードするヘッダーファイルを、
boost/test/included/unit_test.hpp から
boost/test/unit_test.hpp に変更します。
次に、[ライブラリディレクトリ]にビルドした boost の lib ファイルのあるパスを追加します。
リンクは unit_test.hpp でしてくれるので、あとは普通にビルドするだけです。

せっかくなので、ビルド時間を比較してみました。
struct Fixture1 {
 Fixture1() : fix1(1) { ::std::cout << "F1 ->" << ::std::endl; }
 ~Fixture1() { ::std::cout << "<- F1" << ::std::endl; }
 int fix1;
};

BOOST_AUTO_TEST_SUITE(GROUPNAME)

BOOST_AUTO_TEST_SUITE(a)

BOOST_AUTO_TEST_CASE(X)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_CASE(Y)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_FIXTURE_TEST_CASE(Z, Fixture1)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(b, Fixture1)

BOOST_AUTO_TEST_CASE(X)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_CASE(Y)
{
    BOOST_CHECK_EQUAL(2*3, 6);
}

BOOST_AUTO_TEST_SUITE_END()


typedef boost::mpl::list<int,long,unsigned char> test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE( my_test, T, test_types )
{
    BOOST_CHECK_EQUAL( sizeof(T), (unsigned)4 );
}

BOOST_AUTO_TEST_SUITE_END()
上記コード x N個 (プリプロセッサで増やした) ものを
Windows7, メモリ: 4GB, CPU: Intel Core i3 2.53GHz, Visual Studio 2012 Express 2012 for Windows Desktop
でビルドしました。

結果
Nインクルードライブラリ
1000:00:16.8800:00:05.44
10000:00:20.3300:00:11.00
100000:04:04.3100:03:52.23
5000error C1060:
ヒープの領域を使い果たしました。
error C1001:
コンパイラで内部エラーが発生しました。
02:04:23.22
10000error C1060:
ヒープの領域を使い果たしました。
error C1001:
コンパイラで内部エラーが発生しました。
(※ /bigobj オプション付き)

テスト数に関係なくライブラリを使った方が、10秒ちょっと早い結果になりました。
もっと差が出るものかと思ったのですが…検証コードが良くなかったのか?

まとめ
まだまだ Boost.Test を説明しきれていないですが、
冒頭の Boost.Test を使わなかった理由がどのように変わったかで、まとめにしたいと思います。
  • boost の導入がめんどくさそう
    とても簡単に導入できました。
    ヘッダオンリーでも使えるので、試しに使いたい人はダウンロードするだけですぐに使えます。
  • 日本語情報が少ない
    この記事を書いているときに検索した感じでは、少なくはなかったです。
    ただ、もっと「ここがスゴイ!便利だ!」って感じの記事があるといいかもしれませんね。
  • Google Test が使いやすかった
    個人的にはやっぱり Google Test がいいなと思いました。
    理由としては値のパラメータ化テストとテストの繰り返し実行失敗時ブレークポイント停止がないのが大きいです。特にテストデータをランダム生成して --gtest_repeat=-1 --gtest_break_on_failure で失敗するまでエイジングする使い方をよくするので、それができないのが残念です。

最後に
なんだかまとまりのないブログになってしまいましたが、最後までお付き合い頂きありがとうございました。
内容に間違いなどありましたら、コメント下さい(^^ゞ

最後に宣伝。
オレオレテスティングフレームワーク開発中です!
Boost.Test を使ってみていいなと思った機能は自作テスティングフレームワークの iutest に取り込んでいきたいと思います(^^
そして本日、iutest v1.2.0 をリリースしました。


C++ Advent Calendar 2012: 次は、krustf さんで「Cer に知って欲しい C++」です。 →「krustf の雑記
See ya!

iutest v1.2.0 をリリースしました

ダウンロードはこちらからできます。

今回の目玉アップデートは、「パッケージ機能」の追加です。
パッケージ機能については以前にブログに書いたので、そちらを見てください。

また、EQ_COLLECTIONS も追加しました。
各要素ごとに検証してログ出力してくれます。
IUTEST(Test, EqCollection)
{
    int  a[] = { 0, 1, 1, 3, 3 };
    int  b[] = { 0, 1, 2, 3, 4, 5 };
    ::std::vector c;
    for( int i=0; i < (sizeof(a)/sizeof(a[0])); ++i )
    {
        c.push_back(i);
    }
        
    IUTEST_EXPECT_EQ_COLLECTIONS(a, a+(sizeof(a)/sizeof(a[0])), b, b+(sizeof(b)/sizeof(b[0])));

    IUTEST_EXPECT_EQ_COLLECTIONS(a, a+(sizeof(a)/sizeof(a[0])), c.begin(), c.end());
}

あとはプログレス表示するイベントリスナーを追加しました。

さて、次は iutest_c v1.1 かなぁ

Visual Studio Express 2012 for Windows Desktop で boost をビルド

自分用備忘録です。

ビルド済みバイナリという選択
BoostPro Binary Installer for Visual C++
こちらからビルド済みバイナリが取得できるので、まずはこちらを確認してもいいのかも。

ダウンロード
Boost 公式からダウンロードします。
今回は、boost 1.52.0 をダウンロードしました。

ダウンロードしたら任意の場所に展開します。

ビルド
  1. [スタートメニュー]->[Microsoft Visual Studio 2012]->[Visual Studio Tools]->[VS2012 の開発者コマンド プロンプト] を実行する
  2. 展開したフォルダに移動する
  3. 「bootstrap.bat && b2.exe」と入力し実行
  4. 待つ
  5. 完了

リンク
ビルドしたライブラリは、 /stage/lib/ 以下に出力されます。
プロジェクトのプロパティのから、 /stage/lib を[追加のライブラリ ディレクトリ]に追加します。
また、[追加のインクルード ディレクトリ]に boost のパスを追加しておきます。

あとは、普通に使うだけです。


boostjp に書いてあった
ここまで書いて気づいたけど、boostjp に書いてありました f(^^;

2012年12月13日木曜日

Jenkins + CCTray(ジョブ監視ツール)

Jenkins の失敗通知をメールでしていると、
そのうちメールを読まなくなって失敗を放置、なんてことがあったりします。
(本当はちゃんと読まないとダメなんだけど・・・)

そこで私は、CCTray というタスクトレイ常駐型の通知ツールを使っています。
CCTray は、去年の Advent Calender でも取り上げられていました。
Jenkinsのモニタリング - Jenkins Advent Calendar 2011 - Day 12

とっても便利でずっと使ってたのですが、
最近 Anonymous の権限を変更したらジョブが見れなくなってしまいました。

できれば解決方法をここに書きたかったところですが、残念ながら未解決でございますm(_ _)m


でも、せっかくなので CCTray の紹介でもしておきたいと思います。
まずは、簡単にインストールから設定までを説明します。
インストール
CCTray は、こちらからダウンロードしました。(CruiseControl.NET-CCTray-1.8.2.0-Setup.exe)
http://en.sourceforge.jp/projects/sfnet_ccnet/releases/

ダウンロードしたら、インストーラーを起動し指示に従ってインストールするだけです。

設定
CCTray を起動したら [Settings...] を選択して設定ダイアログを開きます。
[Build Projects] タブを選択し、[Add...] ボタンを押します。

[AddServer] ボタンを押します。

[Supply a custom HTTP URL] を選択し、https://jenkins:port/cc.xml と入力します。
このアドレスは 「 jenkins のビューの URL + /cc.xml 」になります。
これはユーザーが作成したビューでも OK です。
URL を入力したら OK ボタンを押します。

[Available Projects]にプロジェクトがリストアップされるので、
この中から監視したいものを選択して、OK します。
(サーバーURL に指定した Jenkins のビューにあるプロジェクトがリストアップされます。)


これで設定は終わりです。

ビルドが失敗するとこのように表示されます。



read 権限がないと・・・
冒頭で書いた問題についてです。

Anonymous に read 権限がないとプロジェクトが見えません。


CCTray 設定の [Build Server] の [Configure] で、
[Server is secure] にチェックを入れて [User name/password authentication] を設定してみましたが、
こんな感じでうまくいきませんでした。





うーん。もうちょっと調べよう。。。

2012年11月30日金曜日

iutest_c v1.0.0 をリリースしました

iutest_c は C言語で書かれたテスティングフレームワークです。

iutest と同様にヘッダオンリーで使用することができます。
C++ のテスティングフレームと比較して機能的には大分劣ります。
そもそも C言語のテスティングフレームが必要とされているのか疑問ではあります。
別にテストは C++ で書いてもいいわけですから。
(組み込みだとそうでもないのかなぁ)

ともあれ、 iutest_c の特徴的な機能を紹介したいと思います。

書き方
iutest_c は iutest から派生したプログラムです。
テストの書き方はほとんど同じです。
また、iutest は Google Test を踏襲してますので、 Google Test を使っている人には分かりやすいと思います。

もっとも簡単なテストを紹介します。

IUTEST(CaseName, TestName)
{
    int x = 0;
    IUTEST_ASSERT_EQ(0, x);
}

テストを自動検出して実行
iutest_c でもテストを自動で検出して実行します。
テストの登録用変数を定義したり、登録関数を呼んだり、面倒な手続きが不要です。
(※非対応コンパイラあり。その場合は、IUTEST_REIGSTER_TEST マクロで明示的な登録が必要です。)

テストフィクスチャ
iutest_c でもテストフィクスチャが使えます。

void TestF_SetUp(void)
{
  /* ここに SetUp コード */
}
static const iuTestFixture TestF = { NULL /* SetUpTestCase */
                                  , NULL /* TearDownTestCase */
                                  , TestF_SetUp /* SetUp */
                                  , NULL /* TearDwon */
                                  };
IUTEST_F(TestF, Hoge)
{
   IUTEST_ASSERT_TRUE(TRUE);
}

iuTestFixture 構造体に TestCaseSetUp/TearDown,SetUp/TearDown 関数を設定します。
テストケース名にこの変数名を指定して下さい。

簡易的なパラメタライズテスト
関数の引数に値を渡すテストを簡単に記述できます。
void TestFunction(int x, int y)
{
    IUTEST_ASSERT_EQ(x, y);
}
IUTEST_PMZ(ParamMethodTest, EQ, TestFunction, 0, 0);
IUTEST_PMZ(ParamMethodTest, EQ, TestFunction, 1, 1);
IUTEST_PMZ(ParamMethodTest, EQ, TestFunction, 2, 2);


今後の予定
値のパラメータ化テストに対応できるような気がしないでもないので、
v1.1 はそれを目標に開発を続けたいと思います。

2012年11月26日月曜日

autest 1.0.0 α をリリースしました

AutoIt 用テスティングフレームワーク autest をアルファリリースしました。
ダウンロードはこちらから

AutoIt は Windows 用のプログラム言語です。主に Windows の GUI 操作自動化に使用します。
詳しいことは、ここでは省きます。


今回、このテスティングフレームワークを作成したのは、
「AutoIt で GUI の自動テストを組んだとき、テストが失敗したときのログ出力を
しっかり書いておかないと、原因がわかりにくい」と感じたからです。

まずはアサーションの提供から
今回リリースした autest では、他のテスティングフレームワークに似たアサーションに対応しています。

サンプルを紹介します。
#include "../../include/autest.au3"

$NotepadClassName = "[CLASS:Notepad]"
$strSendTest = "hello world."

AUTEST_ASSERT_TRUE( Run("notepad.exe") )
AUTEST_EXPECT_TRUE( WinWaitActive($NotepadClassName, "", 5) )

AUTEST_EXPECT_TRUE( ControlSend($NotepadClassName, "", "Edit1", $strSendTest) )
AUTEST_EXPECT_STREQ( $strSendTest, ControlGetText($NotepadClassName, "", "Edit1") )

AUTEST_ASSERT_TRUE( WinClose($NotepadClassName) )

; 保存しますかダイアログが出るので、ここでタイムアウト失敗する
AUTEST_ASSERT_TRUE( WinWaitClose($NotepadClassName, "", 5) )

こちらのサンプルは
  • メモ帳を起動
  • 「Hello World.」と入力し
  • 入力内容を検証したのち
  • メモ帳を終了します(保存ダイアログが出るので、この処理はタイムアウトし失敗します)

実行した画面がこちらになります。



コンソールにはこのようなメッセージが出力されます。
sample.au3:30
error: Value of WinWaitClose($NotepadClassName, "", 5)
  Actual: 0
Expected: True

このメッセージから、
sample.au3 ファイルの 30行目 で WinWaitClose($NotepadClassName, "", 5) が 0 を返して失敗したということがひと目でわかります。

ビルド方法
autest のアサーション関数は通常の AutoIt のコンパイル方法でもエラーにならないよう設計されていますが、ファイルや行、コードの詳細を出力するには専用のビルド手順が必要になります。

こちらが先ほどのサンプルの Makefile になります。
SOURCES = sample.au3

build: DEFAULT_TARGETS

include $(AUTEST_ROOT)/toolchain/commondefs

Make 用の定義などはすべて commondefs ファイルに記述されています。
ユーザーが記述するのは以上になります。

あとは普通に Make するだけです。

どういう仕組みか
Make で何をしているかというと、
  1. SOURCES で定義されたファイルを '#' を "__sharp__" に置換して .obj/*.c ファイルに出力
  2. .c ファイルをプリプロセッサで処理し "__sharp__" を '#' に戻して *.pau3 ファイルに出力
    (その他 AutoIt の構文に合わせて置換しています)
  3. .pau3 ファイルを AutoIt で実行ファイルにコンパイル
というようなことをしています。
C プリプロセッサを使用して、関数引数にファイル名や行、引数の文字列を付加することで、
先に示した出力を得られるようになります。

まとめ
プリプロセッサ便利!

2012年11月21日水曜日

Visual Studio 2012 でプロジェクト依存関係を設定してもリンクされない

Visual Studio 2012 に移行していてハマったので備忘録として。

Visual Studio 2008 では
VS2008 では、プロジェクト依存関係を設定し、
[プロジェクト]->[構成プロパティ]->[リンカ]->[全般] の
[リンク ライブラリの依存関係] を [はい] (デフォルト)
にしておけば、依存先プロジェクトの出力ライブラリに自動でリンクしてくれていました。


ビルドコンフィグごとにビルドしたライブラリをリンクしてくれるので、
Debug なら hoged.lib
Release なら hoge.lib
のような、面倒な設定をしなくて済むので便利でした。

また、プロジェクト依存関係はソリューションファイル(.sln)に記録されていたので、
ソリューションごとに別のライブラリにリンクする、なんてこともできました。

Visual Studio 2012 では
VS2012 でもプロジェクト依存関係があるのですが、
それを設定して自動でリンクしてくれません。
(VS2010 でもそうみたい)

[リンク ライブラリの依存関係]を設定するには、
[プロジェクト]->[共通プロパティ]->>[Framework と参照] ページの
[新しい参照の追加] ボタンからプロジェクトを追加します。
参照のプロパティで、[リンク ライブラリの依存関係] など細かな設定ができるようです。


ちなみに、こちらの設定はプロジェクトファイル(.vcxproj)に保存されます。
したがって、ソリューションごとにリンク先を変える手法は使えません。

うーん。困ったもんだ・・・




2012年11月17日土曜日

Cppcheck 1.57 がリリースされました

Cppcheck 1.57 がリリースされました。
ダウンロードはこちらからできます。
SourceForge.Jp - cppcheck

1.56 から 1.57 になって検査してくれる部分が増えた分、誤検知も増えたかなぁという印象です。
(※あくまでも印象です)

特に大きな問題があるわけではないので、更新してしまって問題ないと思います。

1点だけ注意が・・・
何もなければ、こんなブログは書かないわけでw

Cppcheck 1.57 になって、Visual C++ 2010 のランタイムが必要になりました。
(MSVCR100.DLL,MSVCP100.DLL)


というわけで、VC2010 がインストールされていないマシンには、ランタイム コンポーネントをインストールしましょう。
ダウンロードはこちらから > Microsoft Visual C++ 2010 再頒布可能パッケージ (x86)


Jenkins などで Cppcheck を使っている人は気をつけてください。(←自分)

2012年11月12日月曜日

Visual Studio 2012 で Google Test を使う

Visual Studio 2012 が正式リリースされ、
さらには新しいコンパイラ(Microsoft Visual C++ Compiler Nov 2012 CTP)も公開されたので、
追記したいと思います。(Google Test のバージョンは 1.6.0 です)

Visual Studio 2012 の場合
Beta 版の時と一緒です。こちらを参照してください。
Visual Studio 11 Beta で Google Test を使う

Microsoft Visual C++ Compiler Nov 2012 CTP の場合
まずは、コンパイラを使えるようにします。
やり方はこちらを参照してください。
C++でゲームプログラミング - [C++]Visual Studio 2012 に C++11 の機能が追加されるらしい

インストールしたら、早速 Google Test のライブラリをビルドします。

Variadic Templates が使えるようになったので、
_VARIADIC_MAX=10
を使わなくて済むかと思っていたのですが、
どうやら、まだ STL は対応されていないようです。残念。。。

というわけで、11 Beta の時と変わりません。こちらを参照してください。
Visual Studio 11 Beta で Google Test を使う

次に期待
またツールセットが更新されたときには、このブログを更新したいと思います。

2012年11月6日火曜日

iutest にパッケージ(名前空間)機能を追加しました

iutest は私が自作している(Google Test みたいな)C++ テスティングフレームワークです。

今回、Google Test にはない機能として名前空間のサポートを追加したので、紹介します。

IUTEST_PACKAGE
今回追加されたのは IUTEST_PACKAGE マクロです。
このマクロを使うことで、テスティングフレームワークに名前空間を伝えることができます。

具体的な例を示します。
IUTEST_PACKAGE(pkg)
{
    IUTEST(Sample, Test)
    {
        IUTEST_ASSERT_TRUE(true);
    }
}
IUTEST_PACKAGE は namespace のように使います。引数には名前空間名を指定します。
namespace ではなく、IUTEST_PACKAGE を使うとどうなるか・・・
実行してみた結果がこちらです。


このように名前空間名がテストケース名に追加されています。
そして、Jenkins のテスト結果にもパッケージとして表示されます。


ネストにも対応しています。
IUTEST_PACKAGE(pkg)
{
    IUTEST_PACKAGE(pkg2)
    {
        IUTEST_PACKAGE(pkg3)
        {
            IUTEST(Sample, Test)
            {
                IUTEST_ASSERT_TRUE(true);
            }
        }
    }
}



iutest のコードは SourceForge もしくは github から取得できます。
次回パッケージリリースは年内を予定しています。

2012年10月29日月曜日

CCCC + Jenkins + HTML Publisher Plugin

CCCC + Jenkins」 で紹介した Publish CCCC report Plugin はちょっと残念な感じでした。
そこで、今回は HTML Publisher Plugin を使いたいと思います。

設定
まずは、HTML Publisher Plugin をインストールします。
次に、プロジェクトの設定ページを開いて「ビルド後の処理の追加」から「Publish HTML reports」を選択します。


追加ボタンを押して
「HTML directory to archive」に、CCCC の結果の出力パスを設定、
「Index page[s]」 は cccc.html (CCCC のデフォルト)に変更します。
「Report title」は好きな名前をつけてください。今回は「CCCC Report」としました。



以上で設定は完了です。

実行
実行すると、「CCCC Report」が追加されます。

ここから、CCCC の HTML を閲覧できます。


もちろん、モジュールごとの結果も閲覧できます。


これで CCCC の結果がより確認しやすくなったのではないでしょうか。

2012年10月28日日曜日

Visual Studio でよく使うショートカット

こういう記事はよくありますが、自分がよく使うショートカットをまとめてみました。

検索
Ctrl + F3選択語句を検索
F3次を検索
Shift + F3前を検索
Ctrl + Fクイック検索
Ctrl + Shift + Fフォルダを指定して検索

「フォルダを指定して検索」では、検索対象に
  • 現在のドキュメント
  • すべての開かれているドキュメント
  • ソリューション全体
  • 現在のプロジェクト
  • Visual C++ インクルード ディレクトリ
を指定できます。
検索結果は、「検索結果 1 ウィンドウ」または「検索結果 2 ウィンドウ」に出力されます。
どちらに出力するかは、ダイアログの検索結果オプションで変更できます。

移動
F12定義へ移動
Ctrl + G指定行へジャンプ
Ctrl + -戻る
Ctrl + Shift + -次に進む

戻るで以前にジャンプした場所に戻れます。かなり便利です。

コーディング
Ctrl + Spaceインテリセンスの表示
Ctrl + Jメンバの一覧
Ctrl + K, Ctrl + C選択範囲のコメント
Ctrl + K, Ctrl + U選択範囲のコメント解除
Ctrl + K, Ctrl + F選択範囲のフォーマット
Ctrl + M, Ctrl + Mアウトラインの展開/折りたたみ


※この記事は、Visual Studio 2008 を対象に書かれています。
他のバージョンでは異なる恐れがあります。

2012年10月24日水曜日

エディタ=Visual Studio な人のためのメイクファイルプロジェクト

Visual Studio にはメイクファイルプロジェクトというものがあります。

メイクファイルプロジェクトでは、ビルド/クリーン/リビルドのコマンドを設定します。
このコマンドにはユーザーが自由に処理を書けるので。
Visual Studio にはないコンパイラを使うこともでき、インテリセンスの恩恵を受けることができます。
makefile を使った例
makefile でビルドするプロジェクトを Visual Studio を使って開発する例です。

  1. まずは、プロジェクトを作成します。
    メイクファイルプロジェクトを選択し、コマンドを設定します。


    (今回は、make でビルド、make clean でクリーンできるものとします。)
  2. この状態でもすでにビルドできます。
    F7 でビルドしてみましょう。

  3. 次に、インテリセンスの恩恵を受けるためにコードをプロジェクトに追加します。
    インクルードパスが通っていないと、インテリセンスが動作しないのでパスも通します。
    (プリプロセッサの定義などもあれば設定します。)
    これでインテリセンスが機能します。

  4. ログをカスタマイズする。
    ビルドログは出力ウィンドウに表示されますが、メッセージは makefile の出したそのままのメッセージです。
    このままでは、エラーウィンドウにエラー情報が表示されませんし、ログから該当コードにジャンプもできません。
    これに対応するために、Visual Studio が認識できるフォーマットでログを出力するようにします。
    build.bat ファイルを新規作成し、以下のようにログを加工します。(一例です。)
    @echo off
    
    SET LANG=ja_JP.SJIS
    bash -c "make --no-print-directory %*" 2> build_log.txt
    if errorlevel 1 (
        sed -e "s/:\([0-9]*\):[0-9]*:/(\1):/g" build_log.txt | sed -e "s/\/cygdrive\/\([a-z]\)/\1:/g"
        del /F build_log.txt
        exit /b 1
    ) else (
        sed -e "s/:\([0-9]*\):[0-9]*:/(\1):/g" build_log.txt | sed -e "s/\/cygdrive\/\([a-z]\)/\1:/g"
        del /F build_log.txt
        exit /b 0
    )
    
    ビルドコマンドを build.bat に変更し、ビルドを実行します。
    これでエラー一覧にエラーが表示されるようになりました。


HTML でも Visual Studio
Visual Studio で HTML ファイルを開くと色付きで表示してくれます。
また、閉じてないタグや存在しないパスなど警告を表示してくれます。


これだけではつまらないので、メイクファイルプロジェクトを利用して htmllint をかけたいと思います。
(htmllint はこちらからダウンロードしました)

iutest での使用を例に説明します。
まずは、Makefile と build.bat を用意します。

Makefile:
#!/usr/bin/make 

LANG=ja_JP.SJIS
HTMLLINT_PATH=$(subst \,/, $(HTMLLINT_ROOT))

default: target

all: target lint

target: Doxyfile
    doxygen Doxyfile

lint:
    (find ../ -maxdepth 1 -type f -name "*.html" -print0 | xargs -0 perl $(HTMLLINT_PATH)/htmllint)


build.bat:
@echo off

SET PATH=c:\cygwin\bin;%PATH%
SET LANG=ja_JP.SJIS
bash -c "make --no-print-directory %*" > build_log.txt
if errorlevel 1 (
    sed -e "s/: [0-9]*:/: error:/g" build_log.txt | sed -e "s/\/cygdrive\/\([a-z]\)/\1:/g"
    del /F build_log.txt
    exit /b 1
) else (
    sed -e "s/: [0-9]*:/: error:/g" build_log.txt | sed -e "s/\/cygdrive\/\([a-z]\)/\1:/g"
    del /F build_log.txt
    exit /b 0
)

make lint
で、htmllint が実行されます。
build.bat はその出力を Visual Studio が対応している書式に変更します。

Visual Studio の設定は、ビルドコマンドに "build.bat lint" とするだけです。



実行すると以下のように結果が表示されます。
一覧から該当ファイルの行にジャンプできるので、修正がはかどります。


まとめ
このように、メイクファイルプロジェクトを使えば通常 Visual Studio では扱えないプロジェクトも管理でき、
Visual Studio IDE の機能の恩恵を受けることができます。

ここまで高価な専門ツールである Visual Studio のことを紹介してきましたが、
メイクファイルプロジェクトは Express Edition でも使用出来るのでご安心ください。


2012年10月23日火曜日

意外と知らない?Visual Studio の「必ずインクルード」プロパティ

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
 return 0;
}
Visual Studio でプリコンパイル済みヘッダーを有効にすると、
stdafx.h を最初にインクルードしないといけないのですが、
ソースファイルに書きたくない(書けない)場合の対処方法を紹介します。

設定は簡単で、
[プロジェクトのプロパティ] - [構成プロパティ] - [C/C++] - [詳細設定] - [必ず使用されるインクルードファイル]に、 stdafx.h を追加するだけです。(Visual Studio 2012)



これで、ソースには書かなくて済みます。


2012年10月18日木曜日

[Jenkins] スレーブマシンの再起動をするジョブ

Groovy postbuild plugin はとりあえずインストールしとけ
で、Groovy postbuild plugin を紹介しました。

今回は、Groovy postbuild plugin を使ってスレーブマシンの再起動する例を見つけたので紹介します。

ネタ元はこちら > https://bugreports.qt-project.org/browse/QTQAINFRA-558

以下に紹介されているコードを示します。
import hudson.slaves.OfflineCause.SimpleOfflineCause
import hudson.util.RemotingDiagnostics

/* FIXME: why cannot we import jenkins.util.NonLocalizable ? */
class OfflineMessage extends org.jvnet.localizer.Localizable {
  def message
  OfflineMessage() {
    super(null, null, [])
    def timestr = new Date().format("HH:mm dd/MM/yy z", TimeZone.getTimeZone("UTC"))
    this.message = "automated reboot at end of test at " + timestr
  }
  String toString() {
    this.message
  }
  String toString(java.util.Locale l) {
    toString()
  }
}

def computer = manager.build.getBuiltOn().toComputer()
def channel = computer.getChannel()
computer.setTemporarilyOffline(true, SimpleOfflineCause.create(new OfflineMessage()))
RemotingDiagnostics.executeGroovy( """

    if (Functions.isWindows()) {
      'shutdown /r /t 10 /c "Restarting after Jenkins test completed"'.execute()
    } else {
      "sudo reboot".execute()
    }

""", channel )

何が嬉しいか
クリーンな環境でテストをしたいと思った場合、
仮想マシンや Windows SteadyState(古い?) を使う方法があります。

そういった場合、クリーンな状態に戻すときに再起動する必要があるので、
このスクリプトを使って自動化できると助かるのではないでしょうか。
(Windows の場合 shutdown コマンドに /f オプションも付けたほうがいいかも)

本当に、Groovy postbuild plugin って便利ですね!

2012年10月12日金曜日

[Jenkins] Groovy postbuild plugin はとりあえずインストールしとけ

なぜ今まで使わなかったのか。もっと早く知っていれば、
チームメンバーからの要望に応えられてたというのに・・・
Groovy Postbuild Plugin

何が便利か
Jenkins が持っている情報の取得・変更がし放題なのです!
Jenkins をもうちょこーっとだけ便利に使いたいという場合に使えます。

例えば、このようなことに使えます。
使い方
「ビルド後の処理の追加」から Groovy postbuild を選択、
「Groovy script」に処理を書きます。

例:テストの説明にビルド時刻を設定
def tr = manager.build.getTestResultAction().getResult()
def st = tr.getSuites()
st.each { suite ->
  def cr = suite.getCases()
  cr.each { res ->
    res.description = "${manager.build.getTime()}"
  }
}

Jenkins のオブジェクトについては、
Jenkins Javadoc を参照するとわかると思います。

設定は以上です。試しに実行した結果がこちら。





wiki に説明と使用例が載ってるのでそちらも参考にしてください。

最後に
とても万能な plugin なので、今は不要でもとりあえずインストールしておくといいと思います。
Groovy postbuild plugin 便利!

2012年10月5日金曜日

Cppcheck + Jenkins + メール通知

今回は Cppcheck の結果をメールで通知したいと思います。
※まだ試行錯誤中の記事なので間違っているとこがあるかもしれません。

email-ext plugin
まずは、下記サイトを参考に email-ext plugin を使ってみることにしました。

Jenkinsから送信されるメールをカスタマイズする
Email-ext plugin - Jenkins - Jenkins Wiki

${SCRIPT} を使用します。template は
$JENKINS_HOME\plugins\email-ext\WEB-INF\classes\hudson\plugins\emailext\templates\groovy-text.template
を参考にして書きました。

最終版がこちら
Cppcheck Result
<% 
def cppcheck = new XmlParser().parse(build.getWorkspace().getRemote() + "/cppcheck-result.xml")
cppcheck.error.each() { res -> %>
ERROR:
  <%= "${res.attribute("file")}" %> : <%= "${res.attribute("line")}" %> : <%= "${res.attribute("id")}" %> : <%= "${res.attribute("msg")}" %>
<%
} %>

<% %> で囲んだとこにスクリプトが書けるようです。ここの内容はメールには含まれないようです。
メールに表示するには <%= %> で囲むようです。

template ファイルは $JENKINS_HOME/email-templates フォルダに置く決まりになってます。
Jenkins の設定は以下のようにしました。


設定は以上で終わりです。試しに実行してみた結果がこちら。



ちゃんと警告が通知されました。

スレーブで実行したときに問題発生
ここで問題が発生しました。
上記スクリプトはマスター上で走るようで、スレーブマシンにある xml にアクセス出来ませんでした。
※解決方法募集中

仕方がないので、ビルドログに出すようにします。

ビルドログに出す
せっかく Groovy に触れたので Groovy で出力させます。意地です。

まず、Groovy 環境がないマシンで実行するためには groovy-all.jar が必要なので、
$JENKINS_HOME\war\WEB-INF\lib にあるものをコピーして使うことにします。
(この記事を書いている時点では groovy-all-1.8.5.jar がありました。)

スレーブマシンの任意の場所に、
groovy-all-1.8.5.jar と xml 解析をする cppcheck.groovy ファイルを置きます。
cppcheck.groovy は以下のように書きました。
println "*** Cppcheck Result Begin ***"
args.each {
  def cppcheck = new XmlParser().parse(it)
  cppcheck.error.each{
    println "${it.attribute("id")}: ${it.attribute("msg")}"
    println "    file = ${it.attribute("file")} "
    println "    line = ${it.attribute("line")} "
    println "----------------------------------------------"
    println " "
  }
}
println "*** Cppcheck Result End ***"

コマンドライン引数に解析対象の xml ファイルパスを受け取るようにしています。
そして、ビルドジョブの最後に cppcheck.groovy を呼び出します。
java -jar groovy-all-1.8.5.jar cppcheck.groovy cppcheck-result.xml

これでログに結果が出力されます。




再び email-ext plugin
最後に email-ext plugin で整形します。
デフォルトコンテンツを以下のように変更します。
${PROJECT_NAME}
STATUS: ${BUILD_STATUS}

*** Cppcheck Result ***
${BUILD_LOG_EXCERPT,start=".*Cppcheck Result Begin.*", end=".*Cppcheck Result End.*"}
BUILD_LOG_EXCERPT の部分以外はお好みでどうぞ。

送られてきたメールがこちらです。


email-ext plugin の ${SCRIPT} で行うのはうまくできませんでしたが、
イイ感じになったのではないでしょうか。

2012年10月3日水曜日

10000 ページビュー越え

10000 ページビュー突破しました。
自己満足のために始めたブログですが、見てくれる方がいるのはありがたいことです。

ちなみに、こちらがここ一ヶ月のページビューの統計

やはり平日のアクセスが多いです。
そして、こちらが記事のページビューの数(全期間)

Cppcheck のアクセスがダントツです。
Jenkins のこととかもっと書いていきたいですね。


今後も(役に立つかはわからないけれども)情報を発信していきたいと思います。

2012年9月30日日曜日

iutest v1.1.0 をリリースしました

ダウンロードはこちらから。
SourceForge もしくは github からソースコードの取得もできます。

v1.0 からの変更点
  • 追加機能
    • 例外の値を検査するアサーション
    • Pairwise
    • IUTEST_SKIP
    • Logger
    • ファイルパス操作補助クラス
  • 修正
    • 初期化関数が呼ばれていない場合に警告表示
    • CRT セキュリティ強化関数の失敗時ハンドラに対応
    • RTTI がない場合の型表示
    • IUTEST_*_NO_FATAL_FAILURE に空の statement を与えた場合の警告抑制
    • その他バグを修正

追加機能だけここで説明したいと思います。

例外の値を検査するアサーション
Google Test では、
*_THROW期待する例外が throw されることをテスト
*_ANY_THROWなんらかの例外が throw されることをテスト
*_NO_THROW例外が throw されないことをテスト
の3つのテスト方法があります。
今回、これに加えて throw された値の比較テストをできるようにしました。

static void ExceptionFunction(int i)
{
    switch( i )
    {
    case 1:
        throw 2;
        break;
    case 2:
        throw "error";
    case 3:
        throw ::std::string("error");
    default:
        break;
    }
}

IUTEST(AssertionTest, Exception)
{
    IUTEST_EXPECT_THROW_VALUE_EQ(ExceptionFunction(1), int, 2);
    IUTEST_EXPECT_THROW_VALUE_NE(ExceptionFunction(1), int, 0);

    IUTEST_ASSERT_THROW_VALUE_STREQ(ExceptionFunction(2), const char *, "error");
    IUTEST_ASSERT_THROW_VALUE_STREQ(ExceptionFunction(3), ::std::string, "error");
    IUTEST_ASSERT_THROW_VALUE_STRCASEEQ(ExceptionFunction(2), const char *, "Error");
}

対応しているのは、EQ/NE/STREQ/STRCASEEQ です。
本来ならばユーザーが例外をキャッチして値を検査するところを、簡単に記述できるようになりました。
こちらは gtest 実装を利用するモードでも使用できます。

Pairwise
複数のパラメータの組み合わせを生成する Combine に加えて、
オールペア法による組み合わせを生成する Pairwise を追加しました。
Pairwise を使用することで Combine よりも格段に少ないテスト回数で済むようになります。
オールペア法についての詳しい説明はここでは省きます。
(※ Pairwise は gtest 利用モードでは使用できません。)

IUTEST_SKIP
これまでは実行されるべきテストが実行されなかった場合に、
テスト結果を「成功」ではなく「スキップ」として出力するようにしていました。
(gtest はスキップ非対応。そのへんはGoogle Test を使ってみる - その6(Jenkins との連携、スキップ対応)で説明してます。)

今回、新たにユーザーがスキップできるように IUTEST_SKIP を追加しました。
実行環境に依存しているテストの実行/不実行の選択に使用することを想定しています。
すでにテストが失敗している場合はスキップになりません。
IUTEST(Foo, Bar)
{
    IUTEST_SKIP(); // この時点でテストをスキップして終了
    IUTEST_ASSERT_EQ(2, 3); // ここは実行されない
}

IUTEST(Foo, Baz)
{
    IUTEST_EXPECT_EQ(2, 3);
    IUTEST_SKIP(); // すでにテストが失敗しているのでスキップにはせず、終了
    IUTEST_EXPECT_EQ(2, 3); // ここは実行されない
}
(※ IUTEST_SKIP は gtest 利用モードではスキップせずに return するだけです。)

Logger
ログ出力のカスタマイズポイントとして用意しました。
Visual Studio Unit Test Framework 対応のために追加しましたが、他のケースでも利用できると思います。

ファイルパス操作補助クラス
カレントディレクトリの取得などをするクラスを追加しました。
gtest には同様なクラス(::testing::internal::FilePath)があります。

2012年9月26日水曜日

Persona Plugin があると「再起動せずにインストール」ができない

2013/05/13 追記
Version 2.3 (May 12, 2013) で修正されました。

以前、Persona Plugin について書きました。
が、どうやらこちらをインストールしていると、
プラグインを「再起動せずにインストール」ができないみたいです。

こちらがログ

hudson.util.IOException2: Failed to dynamically deploy this plugin
 at hudson.model.UpdateCenter$InstallationJob._run(UpdateCenter.java:1209)
 at hudson.model.UpdateCenter$DownloadJob.run(UpdateCenter.java:1019)
 at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
 at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
 at java.util.concurrent.FutureTask.run(Unknown Source)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at java.lang.Thread.run(Unknown Source)
Caused by: hudson.util.IOException2: Failed to install selection-tasks-plugin plugin
 at hudson.PluginManager.dynamicLoad(PluginManager.java:397)
 at hudson.model.UpdateCenter$InstallationJob._run(UpdateCenter.java:1205)
 ... 7 more
Caused by: jenkins.ExtensionRefreshException: hudson.plugins.persona.random.RandomPersonaFinder@148410b doesn't support refresh
 at jenkins.model.Jenkins.refreshExtensions(Jenkins.java:2056)
 at hudson.PluginManager.dynamicLoad(PluginManager.java:390)
 ... 8 more

同様の報告がこちらにありますが、進展はないもよう。
[#JENKINS-12803] Installing plugin (without restarting) is broken with other non-refreshable extension.

Ruby-runtime Plugin でも同じ問題があったようですが、こちらは修正済み。
[#JENKINS-12091] Installing plugin (without restarting) is broken with ruby-runtime.


ダウンロードして再起動後にインストールは可能なので、
私個人としてはこの問題はあまり気にしていませんが。。。

jenkins をこれからドンドン使っていこう!って人(開発チーム)で、
気軽に jenkins の再起動ができないような場合は、
Persona は使わない方がよいのかもしれません。

2012年9月20日木曜日

Google Test の出力XMLに日本語を出力する

Google Test で日本語を出力したり、日本語テスト名(VC)を使うと XML に文字化けした状態で出力されてしまいます。
#ifdef _MSC_VER
TEST(Japanese, テスト)
{
    FAIL() << "テスト";
}
#endif

出力されたXML
<testsuite name="Japanese" tests="1" failures="1" disabled="0" errors="0" time="0.001">
    <testcase name="eXg" status="run" time="0" classname="Japanese">
      <failure message="Failed" type=""><![CDATA[samples.cpp:16
Failed]]></failure>
    </testcase>
  </testsuite>
テスト名「テスト」が「eXg」になってしまっています。(先行バイトが抜けてる)
FAIL() で出力した「テスト」メッセージも無き者にされてしまっています。

今回は日本語出力ができるようにソースコードを変更します。
改変

文字コード変換
まずは、MBS を UTF-8 に変換します。(XML が UTF-8 で出力しているので)
幸い、ワイド文字列から UTF-8 への変換関数はあるのでそちらを利用します。

gtest.cc
--- src.orig/gtest.cc 2011-04-15 12:49:12.000000000 +0900
+++ src/gtest.cc 2012-09-18 21:50:15.185407700 +0900
@@ -1525,6 +1525,27 @@
   return StringStreamToString(&stream);
 }
 
+String MultiByteStringToUtf8(const char* str, int num_chars) {
+  if (num_chars == -1)
+    num_chars = static_cast<int>(strlen(str));
+
+  ::std::stringstream stream;
+  for (int i = 0; i < num_chars; ++i) {
+    wchar_t wc=0;
+    int len = mbtowc(&wc, str+i, MB_CUR_MAX);
+    if( len > 1 )
+    {
+      stream << WideStringToUtf8(&wc, 1);
+      i += len-1;
+    }
+    else
+    {
+      stream << str[i];
+    }
+  }
+  return StringStreamToString(&stream);
+}
+ 
 // Converts a wide C string to a String using the UTF-8 encoding.
 // NULL will be converted to "(null)".
 String String::ShowWideCString(const wchar_t * wide_c_str) {
gtest-internal-inl.h
--- src.orig/gtest-internal-inl.h 2011-04-15 12:49:12.000000000 +0900
+++ src/gtest-internal-inl.h 2012-09-18 21:48:43.489163000 +0900
@@ -221,6 +221,7 @@
 // (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output
 // as '(Invalid Unicode 0xXXXXXXXX)'.
 GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str);
+GTEST_API_ String MultiByteStringToUtf8(const char* str, int num_chars);
 
 // Converts a wide string to a narrow string in UTF-8 encoding.
 // The wide string is assumed to have the following encoding:
ヘッダに宣言を追加しておきます。


XML の処理を修正
XML に出力するか判断する前に UTF-8 に変換しておきます。
@@ -3124,6 +3145,9 @@
 String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) {
   Message m;
 
+  String tmp = MultiByteStringToUtf8(str, -1);
+  str = tmp.c_str();
+
   if (str != NULL) {
     for (const char* src = str; *src; ++src) {
       switch (*src) {
@@ -3255,7 +3279,8 @@
               << "\" type=\"\">";
       const string location = internal::FormatCompilerIndependentFileLocation(
           part.file_name(), part.line_number());
-      const string message = location + "\n" + part.message();
+      const string message_raw = location + "\n" + part.message();
+      const string message = MultiByteStringToUtf8(message_raw.c_str(), -1);
       OutputXmlCDataSection(stream,
                             RemoveInvalidXmlCharacters(message).c_str());
       *stream << "</failure>\n";

先行バイトを許可する
最後に先行バイトを許可します。
(厳密にやるなら本当に先行バイトかどうか、後続バイトも調べるべきかも)
@@ -3022,7 +3043,7 @@
 
   // May c appear in a well-formed XML document?
   static bool IsValidXmlCharacter(char c) {
-    return IsNormalizableWhitespace(c) || c >= 0x20;
+    return IsNormalizableWhitespace(c) || c >= 0x20 || c < 0x00;
   }
 
   // Returns an XML-escaped copy of the input string str.  If

テスト側の修正
テストを実行する前に、setlocale( LC_ALL, "" ); をしてください。
Visual Studio だと、起動時に setlocale( LC_ALL, "C" ); を実行するので、
mbtowc が期待どおりに動作しません。

以上で、日本語も XML に出力できるようになるはずです。

ソース/パッチ

github で公開してます。






2012年9月17日月曜日

[Cppcheck] ソースにエラー無視指定コメントを書く

Cppcheck を使ってみた
や、
Cppcheck + Visual Studio
で、書いてなかったのですが(忘れてた)、
自分でもちょこちょこ忘れて検索しているので、備忘録としてブログに書いておきます。

inline-suppr
以下のようなコメントをエラー報告のある行の前に記述します。
// cppcheck-suppress <error ID>
(error ID は syntaxError や uninitMemberVar などです。)

そして、cppcheck のコマンドライン引数に "--inline-suppr" を追加します。
以上で、コメントで書いた箇所の警告が無視されるようになります。


2012年9月13日木曜日

引数の値を集計してデフォルト引数に反映させる(動的デフォルト引数)

[C++]デフォルト引数の値を動的に変える

動的デフォルト引数で何か面白いことできないかな、と思って作ってみました。

github
ideone

#include <iostream>
#include "arggregate.hpp"

// 集計カテゴリの定義(現状は行ごとにカテゴリ化される。)
typedef ::arggregate::ArggregateCategory(test)  Arggregate1;
typedef ::arggregate::ArggregateCategory(test)  Arggregate2;

typedef ::arggregate::Min<int, ::arggregate::Max<int> > MinMax;

void f1(Arggregate1::Decl< int, ::arggregate::Min<int> >::Type x=Arggregate1::Value< int, ::arggregate::Min<int> >(1000) )
{
    if( x.is_default() ) ::std::cout << x << ::std::endl;
}

void f2(Arggregate2::Decl< int, MinMax >::Type x1=Arggregate2::Value< int, ::arggregate::Min<int> >(-1)
    , Arggregate2::Decl< int, MinMax >::Type x2=Arggregate2::Value< int, ::arggregate::Max<int> >(1000) )
{
    ::std::cout << x1 << ":" << x2 << ::std::endl;
}

int main(void)
{
    {
        ::std::cout << "----- f1 ----- " << ::std::endl;

        f1();   // 集計情報 0 のため 通常デフォルト引数が渡される
        for( int i=10; i > 0; --i )
        {
            f1(i);  // 通常使用
        }
        f1();   // 集計した結果の最小値がデフォルト引数となる
    }
    
    {
        ::std::cout << "----- f2 ----- " << ::std::endl;
        
        f2();   // 集計情報 0 のため 通常デフォルト引数が渡される
        for( int i=10; i > 0; --i )
        {
            f2(i);  // 第一引数のみ指定、第二引数にも集計結果が反映される
        }
        f2();   // 集計した結果の最小、最大値がデフォルト引数となる
    }

    return 0;
}




カテゴリ
typedef ::arggregate::ArggregateCategory(test)  Arggregate1;
カテゴリの定義をします。こちらのカテゴリごとに情報が集計されます。
現時点では、__LINE__ ごとにカテゴリが分けられます。
(※とりあえずの実装です。将来的に変更する予定です。)

引数の型
Arggregate1::Decl< int, ::arggregate::Min<int> >::Type
集計用の引数の型です。

Decl template の1つ目に本来の型を指定します。
2つ目に集計を行うクラスを指定します。
上の例では、Min と Min, Max を組み合わせた MinMax を使用しています。
(※現時点では、Min,Max のみです。今後増やす予定)

デフォルト引数の値
Arggregate1::Value< int, ::arggregate::Min<int> >(1000)
デフォルト引数の値を指定します。

2つ目の template に指定するのは単一の集計クラスです。(Min と Max を同時指定はできない)
Min の場合、今までに渡された値の最小値をデフォルト引数として使用します。

Value 関数の引数には、デフォルトのデフォルト引数の値を指定します。
サンプリングされた値がない場合に、使用されます。


最後に
なんとなく使えそうな気もするが、どういうときに使えばいいかサッパリ。