SSブログ

Raspbianをインストールしてみる [Raspberry Pi]

前回は,Raspbmcをインストールしたが,今回はRaspbianをインストールしてみる.

前回と同様,作業はMac上で行っている.
では,まずは適当なSDカードを用意しておく.

そしたら,ダウンロード.
ダウンロードサイトは,ここ.Raw ImagesのRaspbian “wheezy”をDirect downloadしてやる.
ダウンロードはちょっと時間がかかる.

SDカードへの書き込みは前回と同じでOK.SDカードのデバイス名が/dev/disk1なら,
$ diskutil unmountDisk /dev/disk1
Unmount of all volumes on disk1 was successful
$ sudo dd bs=1m if=2013-05-25-wheezy-raspbian.img of=/dev/rdisk1

で,書き込めたら,
$ diskutil eject /dev/rdisk1
Disk /dev/rdisk1 ejected

でOK.

あとはSDカードをRaspberry Piにセットして電源ONすれば立ち上がる.
初回起動時には,Raspi-configというのが立ち上がってくる.ここで諸々の初期設定をしてやる.設定の詳細は解説本「Raspberry Piで遊ぼう!」に詳しく書かれてるし,ネット上で検索すればいろいろ出てくるので分かると思う.ただ,書籍の説明ではraspbianが2013-02-09版だが最新(そして上記でインストールしたバージョン)は2013-05-25版なのでメニュー構成が変わってる.まぁおおよそ推測しながら調べてみれば分かると思うが,一応対応関係が書籍の著者のブログに掲載されているので,これを参考にするといいかもしれない.

で,設定が完了すれば,これで本当にあっけなく起動する.簡単.
お試しあれ.

タグ:Raspbian wheezy
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Raspberry Piをメディアセンターにしてみる [Raspberry Pi]

前回,Raspberry Piを購入した.今回は,これをメディアセンターにしてみる.
で,ちょっと検索すれば分かると思うけど,Raspberry Piでメディアセンターと言えばXBMCだ.Raspberry Piには,Raspbmcという専用ディストリビューションがあるので,これをインストールしてみる.

インストールはとても簡単だ.なお,以下の作業はMac上で行っている.
では,まずは適当なSDカードを用意しておく.

そしたら,XBMCのダウンロードサイトからStandalone Imageをダウンロードしてくる.ダウンロードしたファイルは展開しておく.うちでは,展開されたらsd.imgとなった.

そしたら,あとはMac上のターミナルで,SDカードのデバイス名を調べる.
$ diskutil list

うちの場合は/dev/disk1がSDカードだった.
そしたら,アンマウントして,そのあとddコマンドでダウンロードしたイメージを書き込んでやる.こいつはちょっと時間がかかる.
$ diskutil unmountDisk /dev/disk1
Unmount of all volumes on disk1 was successful

$ sudo dd bs=1m if=sd.img of=/dev/rdisk1

終わったら,Ejectして完了.
$ diskutil eject /dev/rdisk1


これでMac上の作業は終了.そしたらSDカードをRaspberry Piにセットして電源ONしてやる.そうするとあとは自動的にXBMCが立ち上がってくる.細かい設定についての話は省くが,まぁそんなに難しい設定は無いはずだ.

ちなみにうちではHDMIケーブルでTVと接続してる.で,TVのリモコンでXBMCの操作ができる.便利だ.

さて,XBMCをインストールした本来の目的は,AirPlayを使ってみることだ.AirPlayってのはiTunesとかiPhoneとかiPodなんかから音楽や動画をTVにストリーミング再生できるって機能だ.AirPlayを有効にするには,XBMCの「システム」メニューの「サーバー」にAirPlayという項目があるので,それで「XBMC の AirPlay コンテンツ受信を許可する」をチェックしてやる.

これで,iTunesからAirPlayで音楽をストリーミング再生できるはずだ・・・.

はずだ・・・ったのだが,実際はうまくいかない.

こんなエラーが出てしまう.
スクリーンショット 2013-06-22 13.58.20.png
「AirPlay デバイス"XBMC (raspbmc)"への接続中にエラーが起きました。不明なエラーが発生しました (-15000) 。」
なぜだ???

いろいろ試してみたところ,Windows XP上のiTunesからは問題なく再生できる.だけどMacからはうまくいかない.

で,調べてみると,IPv6が怪しいという情報を見つけた.詳細は,「RaspbmcのAirPlayでハマったでござる - もぐてっく」が分かりやすい.

どうやら,raspbmcとしてはIPv6対応だがXBMCがIPv4しか受け入れてくれなくて,で,結果としてMacからはつながらない(iTunesがエラーになる)ようだ.

で,解決策なんだけど,raspbmcのIPv6を無効にしてやる.

作業内容は以下の通り.
まずはSSHでMacからログイン.
$ ssh pi@raspbmc.local
パスワードは,デフォルトなら「raspberry」だ.
そしたらIPv6を無効にするためにipv6.confに「net.ipv6.conf.all.disable_ipv6 = 1」を書き込んでやる.
$ sudo vi /etc/sysctl.d/ipv6.conf
net.ipv6.conf.all.disable_ipv6 = 1

書いたら保存して,で,再起動.

これでMacからもエラー無くつながるようになる.やったー.

お試しあれ.

nice!(2)  トラックバック(0) 
共通テーマ:日記・雑感

Raspberry Piを買ってみる [Raspberry Pi]

以前から,Raspberry Piには興味があったんだけど,BeagleBoard-xMもあるしなぁと思って,手を出してなかった.
だけど,たまたまネットで,解説本とのセットを見かけて,つい買ってしまった.

買ったのは,これ
108459aa.jpg

残念ながら今はもう売り切れちゃったみたいだけど,解説本「Raspberry Piで遊ぼう!」と,Raspberry Pi (model B)本体にケースが付いて,6,800円+送料だった.

Raspberry Piで遊ぼう!

Raspberry Piで遊ぼう!

  • 作者: 林 和孝
  • 出版社/メーカー: ラトルズ
  • 発売日: 2013/05/20
  • メディア: 単行本(ソフトカバー)


追記(2013.06.19)
===ここから===>
当初,数量限定だったのが,再販&通常販売するようになったとのことです.気になる人はリンク先をチェックしてみてくださいな.
<===ここまで===

あと,同様のセット販売だと,日経Linux「Raspberry Pi」特別セット販売ってのもあったみたいだけど,こっちももう完売らしい.

さて,BeagleBoard-xMは結局今は冬眠中で全然活用できていないんだけど,Raspberry Piはどうなることやら...
とりあえず本を読みながら,いろいろ試してみよう.
タグ:Raspberry Pi
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google Testで「値をパラメータ化したテスト」を試してみるの続きを書いてみる [Google Test]

前回,Google Testで「値をパラメータ化したテスト」を試してみたが,その説明が途中で終わっていた.
ずいぶん間があいてしまったが,続きを書いてみる.

で,前回の続きだが,テストするときにパラメータとして入力値と期待値のペアを複数用意しておいて,それを使ってテストを実行したい場合がよくあると思う.
今回の例で言えば,奇数ならfalseで偶数ならtrueになるわけだが,テストのパラメータとして,入力値としていくつかの奇数と偶数と,その期待値であるbool値を具体的な値として用意しておく.
例えば,
---------------
input  expect
---------------
  0     true
  1     false
  2     true
  3     false
  4     true
---------------
といった具合だ.

で,そんな場合には,tupleが使える環境ならtupleで,もし使えないなら適当な構造体を定義してやって,テストパラメータとして与えてやればいい.
今回のケースなら,intとboolの要素を持つようにしておいて,それぞれ入力値と期待値として使う.
::testing::Values()を使って,tupleを列挙してもいいし,あらかじめ配列にしておいて::testing::ValuesIn()でもいい.TEST_P内での使い方は,コードを見てみれば分かると思う.

これだと,テストパラメータとして入力値と期待値をコード中に列挙してやれるので,テスト対象のコードの挙動が分かりやすくなると思う.

最後に,::testing::Combine()について書いておく.
テストするときに,多くのパラメータの組み合わせに対してテストをしておきたい場合がある.
たとえば今回の例で,mul()に対して,少なくとも九九の計算が正しいことをテストしておきたいとする.そうすると,2つの入力値をそれぞれ1〜9まで変化させてテストする必要があり,組み合わせとしては81通りが必要になる.
こんな場合に,::testing::Combine()が使える.これの引数にテストしたい2つの入力値(今回の場合なら1〜9を2つ)を書いておくと,::testing::Combine()が全組み合わせ,すなわち81通りに展開してくれる.これが無ければ自分でループ文を書くとかをしなければならないので,とても楽ちん.

では,お試しあれ.
タグ:tuple combine
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google Testで「値をパラメータ化したテスト」を試してみる [Google Test]

Google Testでは値をいろいろと変えてテストをしたいときのために,「値をパラメータ化したテスト」というのが用意されている.詳細は,いつもの通りドキュメントを参照してほしいんだけど,要は値を変えてテストするときに自分でいちいち値ごとにテストをコピーしたりしなくても,たとえば値の組み合わせを用意してやれば,Google Testがそれにあわせて値を変えながらテストしてくれるという仕組みがあるということだ.

では,具体的にやってみよう.
例によってディレクトリ構成.
gtest_advanced_param_proj/
├── CMakeLists.txt
├── compiler_settings.cmake
└── test
    └── advanced_unittest.cpp

compiler_settings.cmakeは以前から変更が無いので省略して,それ以外は以下のようになる.

CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(gtest_advanced_test)

include (compiler_settings.cmake)

enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
message(STATUS GTEST_DIR=$ENV{GTEST_DIR})

include_directories($ENV{GTEST_DIR}/include)
link_directories($ENV{GTEST_DIR}/lib)

cxx_test(gtest_advanced_test "" test/advanced_unittest.cpp)


test/advanced_unittest.cpp
#include <gtest/gtest.h>
#include <tr1/tuple>
#include <iostream>


bool IsEven(int n)
{
    return (n % 2) == 0;
}

class ParamTestInt : public ::testing::TestWithParam<int>
{};

TEST_P(ParamTestInt, IsEvenTest)
{
    EXPECT_TRUE(IsEven(GetParam()));
}

INSTANTIATE_TEST_CASE_P(
    TestDataIntRange,
    ParamTestInt,
    ::testing::Range(0, 10, 2));

INSTANTIATE_TEST_CASE_P(
    TestDataIntValues,
    ParamTestInt,
    ::testing::Values(0, 2, 4, 6, 8));

class ParamTestTuple : public ::testing::TestWithParam<
    std::tr1::tuple<int, bool> >
{};

TEST_P(ParamTestTuple, IsEvenTest)
{
    const int n = std::tr1::get<0>(GetParam());
    const bool expected = std::tr1::get<1>(GetParam());

    EXPECT_EQ(expected, IsEven(n));
}

INSTANTIATE_TEST_CASE_P(
    TestDataTuple,
    ParamTestTuple,
    ::testing::Values(
        std::tr1::make_tuple( 1,   false),
        std::tr1::make_tuple( 2,   true),
        std::tr1::make_tuple( 3,   false),
        std::tr1::make_tuple( 0,   true),
        std::tr1::make_tuple( 100, true)
    )
);

std::tr1::tuple<int, bool> TestDataArray[] = {
    std::tr1::make_tuple(11,   false),
    std::tr1::make_tuple(12,   true),
};

INSTANTIATE_TEST_CASE_P(
    TestDataTuple2,
    ParamTestTuple,
    ::testing::ValuesIn(TestDataArray)
);

struct TestData {
    int n;
    bool expected;
} test_data[] = {
    { 21, false, },
    { 22, true,  },
};

class ParamTestStruct : public ::testing::TestWithParam<TestData>
{};

TEST_P(ParamTestStruct, IsEvenTest)
{
    const int n = GetParam().n;
    const bool expected = GetParam().expected;

    EXPECT_EQ(expected, IsEven(n));
}

INSTANTIATE_TEST_CASE_P(
    TestDataSturct,
    ParamTestStruct,
    ::testing::ValuesIn(test_data)
);

int mul(int x, int y)
{
    return x * y;
}

class ParamTestCombine : public ::testing::TestWithParam< ::std::tr1::tuple<int, int> >
{};

TEST_P(ParamTestCombine, MulTest)
{
    int x = std::tr1::get<0>(GetParam());
    int y = std::tr1::get<1>(GetParam());
    std::cout << x << " * " << y << " = " << mul(x, y) << std::endl;
    EXPECT_EQ(x * y, mul(x, y));
}

INSTANTIATE_TEST_CASE_P(
        TestDataCombine, 
        ParamTestCombine,
        ::testing::Combine(::testing::Range(1,10),
                           ::testing::Range(1,10))
);


CMakeLists.txtは特に変わったことはしていないので,test/advanced_unittest.cppだけ説明しよう.
では,上の方から順番に...
まず,IsEven()というのがテスト対象で,これについて引数をいくつか値を変えてテストしたいとする.例えば,0以上10未満かつ2刻みの値を使ってテストしたいとする.
で,値をパラメータ化したテストなので,フィクスチャクラスとして,::testing::TestWithParam<T>から派生させたParamTestIntクラスを定義してやる必要がある.今はint型の値をパラメータ化したテストをしたいのでTはintだ.
そしたらTEST_Pを使って,テストを定義してやる.普段TEST_Fでやってるように定義すればいい.で,ここでGetParam()ってのを呼び出すと,テストパラメータを呼び出せるようになる.
じゃあテストパラメータってどう定義するのかってことなんだけど,INSTANTIATE_TEST_CASE_Pってのを使う.引数は,順に,インスタンス化するときの名前,フィクスチャクラス名,そしてテストパラメータだ.上記の例で言えば,TestDataIntRangeという名前でテストをインスタンス化し,そのときのテストフィクスチャクラスはParamTestIntで,パラメータは,::testing::Range(0, 10, 2)だ.
ここで,::testing::Range(0, 10, 2)ってのはGoogle Testが用意してくれてる便利なやつで,0以上10未満step2を意味する.つまり,0,2,4,6,8がテストパラメータとなる.で,TEST_P内のGetParam()ではこの値が取得できるという訳だ.
要するに,ここまでの記述で,IsEven()に対して引数が0,2,4,6,8の場合のテストができるということになる.
楽ちん.

なお,パラメータは::testing::Range()以外にもいろいろあって,たとえば::testing::Values(0, 2, 4, 6, 8)というのも使える.こっちはまさに0,2,4,6,8をパラメータとして使うって言う場合の書き方だ.他にもいくつかあるけど,それはまずはドキュメントを参照してくださいな.

ちょっと長くなってきたので,まだ最初しか説明してないけど,とりあえず今回はここまで.続きはまた次回...
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google TestでDeathテストを使ってみる [Google Test]

Google TestにはDeathテストというのがある.
詳細は例によって,ドキュメントを参照してほしいんだけど,要はコード中のアサーションが正しく動作しているかどうかを確認するためのテストで,しかもアサーションが失敗してプログラムが処理を中断し終了するかどうかをテストするものだ.
ちなみにアサーションというのは日本語だと「表明」とかって訳されていて,そこには実行時に絶対に満たされているべき条件を記載してやる.つまり,条件に書かれてる内容が満たされてることを期待してますよ,と表明しているわけだ.なお,その条件が満たされないと以後の処理は続けられない(続けると回復できない重大な問題が発生ししてしまう)場合に使う.その場合ってのは,つまり致命的なエラーが起きたってことだ.

では,試してみよう.
例によってディレクトリ構成.
gtest_advanced_death_proj/
├── CMakeLists.txt
├── assertion_func.cpp
├── assertion_func.h
├── compiler_settings.cmake
├── main.cpp
└── test
    └── advanced_unittest.cpp


compiler_settings.cmakeは以前から変更が無いので省略して,それ以外は以下のようになる.

CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(gtest_advanced_test)

include (compiler_settings.cmake)

enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
message(STATUS GTEST_DIR=$ENV{GTEST_DIR})

include_directories($ENV{GTEST_DIR}/include)
link_directories($ENV{GTEST_DIR}/lib)

cxx_executable(assert_main "" main.cpp assertion_func.cpp)
cxx_test(gtest_advanced_test "" assertion_func.cpp test/advanced_unittest.cpp)


assertion_func.cpp
#include <cassert>
#include "assertion_func.h"

void Func(bool is_true)
{
    assert(is_true);
}


assertion_func.h
#ifndef ASSERTION_FUNC_H
#define ASSERTION_FUNC_H

void Func(bool is_true);

#endif // ASSERTION_FUNC_H


main.cpp
#include <iostream>
#include "assertion_func.h"

int main()
{
    bool test = false;
    Func(test);

    return 0;
}


test/advanced_unittest.cpp
#include <gtest/gtest.h>
#include <cassert>
#include "assertion_func.h"

TEST(MyDeathTest, FuncDeath)
{
    ASSERT_DEATH({
            bool test = false;
            Func(test);
            }, "");
}


では,ざっくり説明.
とりあえず,ビルドして,assertion_mainを実行してみると,以下のような表示が出てプログラムは終了する.
gtest_advanced_death_proj/assertion_func.cpp:6: void Func(bool): Assertion `is_true' failed.

これはassertion_func.cpp:6にis_trueが「true」であることを期待してますよと表明しておいたにもかかわらず,実際は「false」だったため,アサーションが失敗しました,ということだ.
で,これをテストするために,Google Testでは例えばASSERT_DEATHというマクロを用意してくれてる.書式は,
ASSERT_DEATH(statement, regex);
となっていて,statementにはプログラムが「クラッシュ」する文を書く.ちなみにここで言うクラッシュとは,ドキュメントによれば「プロセスが exit() または _exit() を0ではない引数で呼び出した場合. あるいは,シグナルによって殺された場合」を指す.だから正常終了した場合なんかは当てはまらない.で,ここには式だけじゃなくて例にあるように文も書ける.
あと,regexには標準エラー出力とマッチする正規表現を書ける.

アサーション自体はとても重要だ.だからアサーションが正しく機能するかをテストすることもとても重要だ.
Deathテストを自分で作るとすると多分とてもめんどくさいはず.それをGoogle Testが用意してくれてるのはとても助かる.しかも簡単に使える.
ぜひお試しあれ.
タグ:Deathテスト
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google Testの上級ガイドにある「その他のアサーション」を試してみる [Google Test]

Google Testには本当にいろいろなアサーションが用意されている.
詳細は,「上級ガイド — Google Test ドキュメント日本語訳」に記載されてるので,そっちを参照してもらうとして,とりあえずその上級ガイドにある,「その他のアサーション」についていくつか試してみた.

で,詳細を説明しようと思ったけど,まぁ上級ガイドに書かれてることを試そうなんて人は,上記のドキュメントを読めば内容は理解できると思うので,とりあえず試したコードとメモ程度の内容だけ示しておくにとどめとく.ってまぁ説明するのが面倒なのと,実際のところ説明って言っても結局ドキュメントに書かれてることそのままになっちゃうからなんだけど.

というわけで,まずはディレクトリ構成.まぁいつもと同じなんだけど...
gtest_advanced_proj/
├── CMakeLists.txt
├── compiler_settings.cmake
└── test
    └── advanced_unittest.cpp


compiler_settings.cmakeは以前から変更が無いので省略して,それ以外のファイルを示すと,

CMakeLists.txt.
cmake_minimum_required(VERSION 2.6)
project(gtest_advanced_test)

include (compiler_settings.cmake)

enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
message(STATUS GTEST_DIR=$ENV{GTEST_DIR})

include_directories($ENV{GTEST_DIR}/include)
link_directories($ENV{GTEST_DIR}/lib)

cxx_test(gtest_advanced_test "" test/advanced_unittest.cpp)


test/advanced_unittest.cpp
#include <gtest/gtest.h>

TEST(AdvancedTest, Succeed)
{
    SUCCEED();
}

TEST(AdvancedTest, Fail)
{
    FAIL() << "FAIL() is always fail.";   // 常に失敗
}

bool do_something(int x, int y)
{
    return x == y;
}

TEST(AdvancedTest, Predicate)
{
    int a = 3;
    int b = 5;
    EXPECT_PRED2(do_something, a, b);
    EXPECT_TRUE(do_something(a, b));
}

namespace testing {
AssertionResult AssertionSuccess();
AssertionResult AssertionFailure();
}

::testing::AssertionResult IsEven(int n)
{
    if ((n % 2) == 0) {
        return ::testing::AssertionSuccess();
    } else {
        return ::testing::AssertionFailure() << n << " is odd";
    }
}

bool IsEvenSimple(int n)
{
    return (n % 2) == 0;
}

int add(int x, int y)
{
    return x + y;
}

TEST(AdvancedTest, AssertionResult)
{
    EXPECT_TRUE(IsEven(add(3, 2)));
    EXPECT_TRUE(IsEvenSimple(add(3, 2)));
}

::testing::AssertionResult AssertDoSomething(const char* m_expr,
                                             const char* n_expr,
                                             int m,
                                             int n)
{
    if (do_something(m, n)) {
        return ::testing::AssertionSuccess();
    } else {
        return ::testing::AssertionFailure()
            << m_expr << " and " << n_expr << " ( " << m << " and " << n << ")"
            << " are not same.";
    }
}

TEST(AdvancedTest, PredicateFormat)
{
    int a = 5;
    int b = 8;
    EXPECT_PRED_FORMAT2(AssertDoSomething, a, b);
}

TEST(AdvancedTest, Float)
{
    float x = 0.1;
    float y = 0.01;
    EXPECT_EQ(x, y * 10) << "using EXPECT_EQ()";
    EXPECT_FLOAT_EQ(x, y * 10) << "using EXPECT_FLOAT_EQ()";
}

template <typename T> class Foo {
    public:
        void Bar() {
            ::testing::StaticAssertTypeEq<int, T>();
        }
};

void Test1()
{
    // 以下は::testing::StaticAssertTypeEqによりコンパイルエラーを発生させてくれる
    // コンパイルできなくなり,他のテストを試せなくなるのでコメントアウト
    /*
     * Foo<bool> foo;
     * foo.Bar();
     */
}


で,内容だけど,前半は主にテスト結果をいかに分かりやすくするかって感じのいろいろな機能で,エラーのときの表示を指定する方法なんかが書かれてる.これらは分かりやすいテストを書くには必要だし,あると便利なんだろうけど,まぁ面倒であまり使わない気もする...
あとは浮動小数点のテストと型の一致テストがちょろっと書かれてる.こっちはそこそこ使うかも.

んー,手抜きだな,今回は特に... まぁ,お試しあれ.
タグ:gtest
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google MockのExpectationをいろいろ使ってみる [Google Test]

前回,Google Mockを使ってみた.
で,今回はさらにいろいろなExpectationを使ってみる.
という訳で,Google Mock ドキュメント日本語訳の超入門編に出てくるExpectationをいろいろ試してみる.

ファイル構成は前回と同じ.というか前回のファイルに追記してる.
変更したのは,painter.h,painter.cpp,test/mock_unittest.cppの3つだけだ.まずはこれを示そう.

painter.h
#ifndef PAINTER_H
#define PAINTER_H

#include "turtle.h"

class Painter {
public:
    Painter(Turtle* turtle)
        : turtle_(turtle) {}
    ~Painter() {}

    bool DrawCircle(int x, int y, int r);
    int GetPositionX();
    void Move(int steps);

private:
    Turtle* turtle_;
};

#endif // PAINTER_H


painter.cpp
#include "painter.h"

bool Painter::DrawCircle(int x, int y, int r)
{
    (void)x;
    (void)y;
    (void)r;

    turtle_->PenDown();
    return true;
}

int Painter::GetPositionX()
{
    return turtle_->GetX();
}

void Painter::Move(int steps)
{
    turtle_->Forward(steps * 10);
}


test/mock_unittest.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "painter.h"
#include "mock_turtle.h"

TEST(PainterTest, CanDrawSomething)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, PenDown())
        .Times(::testing::AtLeast(1));
  
    Painter painter(&turtle);

    EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}

TEST(PointerTest, ReturnOfGetX)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, GetX())
        .Times(5)
        .WillOnce(::testing::Return(100))
        .WillOnce(::testing::Return(150))
        .WillRepeatedly(::testing::Return(200));
  
    Painter painter(&turtle);

    EXPECT_EQ(100, painter.GetPositionX());
    EXPECT_EQ(150, painter.GetPositionX());
    EXPECT_EQ(200, painter.GetPositionX());
    EXPECT_EQ(200, painter.GetPositionX());
    EXPECT_EQ(200, painter.GetPositionX());
}

TEST(PointerTest, CheckForwardArgs)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, Forward(100));
  
    Painter painter(&turtle);
    painter.Move(10);
}

TEST(PointerTest, CheckOnlyForwardCall)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, Forward(::testing::_));
  
    Painter painter(&turtle);
    painter.Move(3);
}

TEST(PointerTest, CheckForwardArgsGe)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, Forward(::testing::Ge(200)));
  
    Painter painter(&turtle);
    painter.Move(20);

    EXPECT_CALL(turtle, Forward(::testing::Ge(200)));
    painter.Move(30);
}

TEST(PointerTest, CardinalityCheck)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, GetX())
        .WillOnce(::testing::Return(100))
        .WillOnce(::testing::Return(150));
  
    Painter painter(&turtle);
    painter.GetPositionX();
    painter.GetPositionX();
}

TEST(PainterTest, MultipleExpectation)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, Forward(::testing::_));
    EXPECT_CALL(turtle, Forward(10))
        .Times(2);
  
    Painter painter(&turtle);
    painter.Move(1);
    painter.Move(3);
    painter.Move(1);
}

TEST(PainterTest, NoSequence)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, Forward(::testing::_));
    EXPECT_CALL(turtle, PenDown());
    EXPECT_CALL(turtle, GetX());
    
    Painter painter(&turtle);

    painter.GetPositionX();         // GetX()呼び出し
    painter.Move(3);                // Forward()呼び出し
    painter.DrawCircle(0, 0, 10);   // PenDown()呼び出し
}

TEST(PainterTest, SequenceRequired)
{
    MockTurtle turtle;

    {
        ::testing::InSequence dummy;

        EXPECT_CALL(turtle, Forward(::testing::_));
        EXPECT_CALL(turtle, PenDown());
        EXPECT_CALL(turtle, GetX());
    }
    
    Painter painter(&turtle);

    painter.Move(3);                // Forward()呼び出し
    painter.DrawCircle(0, 0, 10);   // PenDown()呼び出し
    painter.GetPositionX();         // GetX()呼び出し
}

TEST(PainterTest, SaturationNoSequence)
{
    MockTurtle turtle;

    for (int i = 3; i > 0; i--) {
        EXPECT_CALL(turtle, GetX())
            .WillOnce(::testing::Return(10 * i))
            .RetiresOnSaturation();
    }

    Painter painter(&turtle);
    EXPECT_EQ(10, painter.GetPositionX());
    EXPECT_EQ(20, painter.GetPositionX());
    EXPECT_EQ(30, painter.GetPositionX());
}

TEST(PainterTest, SaturationInSequence)
{
    MockTurtle turtle;

    ::testing::InSequence s;    // これがないとEXPECT_CALLを逆順にセットする必要がある

    for (int i = 1; i <= 3; i++) {
        EXPECT_CALL(turtle, GetX())
            .WillOnce(::testing::Return(10 * i))
            .RetiresOnSaturation();
    }

    Painter painter(&turtle);
    EXPECT_EQ(10, painter.GetPositionX());
    EXPECT_EQ(20, painter.GetPositionX());
    EXPECT_EQ(30, painter.GetPositionX());
}


前回と同様,コードとテストの詳細は前述のGoogle Mock ドキュメント日本語訳の超入門編をみてもらうとして,ポイントだけ説明しておく.
まず,今回,テスト用にPainterクラスにGetPositionX()とMove()を追加してる.まぁこの動作にはあまり意味は無くて,単にテスト例を示すためだけの処理になってる.

で,テストの内容だが,CanDrawSomethingは前回示したので置いておいて,それ以外について説明しておく.

まずReturnOfGetX.
これはEXPECT_CALLでGetX()の戻り値を設定してる.Mockに100, 150, 以後200を返すように設定する場合の例だ.WillOnceが1回だけ,WillRepeatedlyは繰り返し,設定した値を返すようになる.

次にCheckForwardArgsとCheckOnlyForwardCall.
テストするときに,引数チェックまでするか,単にメソッド呼び出しだけチェックしたいか,の2つのパターンがあると思う.で,引数チェックするならEXPECT_CALLで引数指定してやればいいし,呼び出しだけでいいなら,::testing::_というのがが使える.これにしておくと,引数は何でもいいから呼び出しだけをチェックできるようになる(引数の一致はチェックされなくなる).

CheckForwardArgsGe.
引数チェックに条件(たとえば「100以上であること」とか)を設定したければ,::testing::_の代わりに::testing::Ge(値)が使える.他にもいろいろ条件は選べる.これらは::testing::_も含め,matcherと呼ばれるようだ.

CardinalityCheck.
EXPECT_CALLには呼び出し回数としてTimes()を書けるけど,書かない場合でもGoogle Mockがそれなりに推測してうまくやってくれる.例えば,WillOnce()が2回書かれてれば,呼び出し回数は2回と推測してくれる.

MultipleExpectation.
複数のEXPECT_CALLを書く場合.
こいつはちょっと分かりにくいかもしれない.
まず複数のEXPECT_CALLが書かれてる場合だと,Google Mockは書かれてる逆の順番で一致を探索する仕様になってる.なので,今回の例だと,引数10が先に探索され,次に引数不問の呼び出しが探索される.逆の順序で探索してEXPECT_CALLと一致する呼び出しが見つかればOKだけど,そうじゃないとテストは失敗する.
例えば,今回の例だと,引数10の呼び出しが先に探索され,一致しなければ引数不問の呼び出しが探索される.で,今回のMove(3)は,引数10の呼び出しとは一致しないが引数不問の呼び出しとは一致するのでOKとなる.
注意が必要なのは,一致探索は逆順ということと,逆順で最初に一致判断できるものが採用されるという点だ.たとえば今回の例で,EXPECT_CALLの記載順序を変えて,引数不問の呼び出しを後に書くと,このテストは失敗するようになる.なぜなら引数不問呼び出しを後に書くとMove(1)が引数不問と一致していると判断されてしまい,次のMove(3)と一致するものがなくなってしまうからだ.たとえEXPECT_CALLに引数10の呼び出し,すなわちMove(1)と完全一致するEXPECT_CALLが書かれていても,その呼び出しとは判断されない.あくまで,逆順で探索し最初に一致判断できる引数不問のEXPECT_CALLが採用されてしまう.
ちょっと分かりにくい気がするが,まぁそういうことだ.
まぁ,この場合は呼び出しの順番は関係なくてとにかく引数10の呼び出しが2回あることと,それ以外が1回だけあることをテストしたい場合,つまり順番は関係ない場合向けということになる.

NoSequenceとSequenceRequired.
テストするときに,呼び出し順序まで指定したい場合もある.で,その場合の例.
::testing::InSequenceを書くと,EXPECT_CALLを書いた順番で呼び出さないとエラーになる.::testing::InSequenceが無ければ,MultipleExpectationのテストのように,逆順から探索される.順番はどうでもよくてメソッドの呼び出しがされているかだけチェックしたければ::testing::InSequenceはいらないけど,呼び出し順序,すなわちシーケンスを守らなければならないのであれば::testing::InSequenceを書いてやる.

SaturationNoSequenceとSaturationInSequence.
これも最初はちょっと分かりにくいかも.
基本は,呼び出しが一致するEXPECT_CALLがあった場合に,消えたりしないということだ.
んー,表現が分かりにくいな.ドキュメントによれば,「呼び出し回数が上限に達してもアクティブであり続ける」とある.
例えば,SaturationNoSequenceの例で言えば,もしRetiresOnSaturation()の記載がなければ,
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(30));
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(20));
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(10));
と書かれているのと等価で,前述の通り,EXPECT_CALLは逆順から探索され,最初に一致したものが有効になる.この場合だと,GetX()呼び出しをすると,最初に
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(10));
が一致するのでこれが有効となる.で,問題は,一回一致した
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(10));
はずっと有効で,消えたりしないということだ.再度GetX()を呼び出すと,やはり
EXPECT_CALL(turtle, GetX()).WillOnce(::testing::Return(10));
が一致することになるが,WiiOnce()といっているので,2回目の呼び出しはテストとしてエラーとなる.
で,これを避けたければ,RetiresOnSaturation()を書いてやればいい.これなら指定した呼び出し回数に一致したら消える(破棄される)ようになる.
で,さらに::testing::InSequenceも書いてやると,EXPECT_CALLの順番通り,指定回数通りの呼び出しチェックができるようになる.
このへんは,テスト内容(何をテストしたいか)によって使い分けるといいだろう.

では,お試しあれ.
タグ:Google Mock
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Google Mockを使ってみる [Google Test]

いままで,Google Testを使ってきたが,さらにGoogle Mockも使ってみる.

Google Mockとは,まぁ日本語ドキュメントがあるので参照してください.って書いてしまうとさすがに寂しいのでもうちょっと書くというか引用すると,Google Mockは「モッククラスを作成して使用するためのライブラリ」ということになる.
で,モックというのは,「Expectation を利用して事前にプログラムされたオブジェクト」だ.呼び出されたら何を返すかとか,何を引数として呼び出されるのかとか,そもそも何回呼び出されるのかとかをあらかじめ指定しておけるオブジェクトだ.なので,テストをするときに「本物」の代わりに使うと何かと便利になる.

ではまず,Google Mockを使えるようにしてみる.Google Testを使えるようにしたときと同様に,まずは適当なディレクトリを作成し,Google Mockをダウンロード,展開してやる.
cd ~/
mkdir googlemock
cd googlemock/
wget http://googlemock.googlecode.com/files/gmock-1.6.0.zip
unzip gmock-1.6.0.zip

そしたらmakeする.
このとき,ubuntuの環境だとエラーが出てmakeできない.なので,1カ所変更する.
変更対象のファイルは,~/googlemock/gmock-1.6.0/make/Makefileだ.このファイルの最後の部分,-lpthreadのところを変更してやる.
変更前
gmock_test : gmock_test.o gmock_main.a
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
変更後
gmock_test : gmock_test.o gmock_main.a
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ -lpthread -o $@

変更したらmakeして,テストする.
cd gmock-1.6.0/make/
make
./gmock_test

テストでエラーが出なければOKだ.

そしたらライブラリはコピーしておく.
cd ~/googlemock/gmock-1.6.0/
mkdir lib
cp make/gmock_main.a lib/libgmock_main.a


あと,Google Mockのパスを設定しておく.~/.bashrcとかに,
export GMOCK_DIR=~/googlemock/gmock-1.6.0

としておけばよい.

これでひとまず準備は完了.
そしたら,Google Mock ドキュメント日本語訳の超入門編に出てくるMock Turtlesの例で試してみる.

いつもと同じように適当なディレクトリにturtles_mock_projディレクトリを作って,ファイル構成は以下のようにしてやる.
turtles_mock_proj/
├── CMakeLists.txt
├── compiler_settings.cmake
├── mock_turtle.h
├── painter.cpp
├── painter.h
├── test
│   └── mock_unittest.cpp
└── turtle.h


それぞれのファイルは以下のようになる.ちょっと長くなるが全部示そう.

CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(mock_test)

include (compiler_settings.cmake)

enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
message(STATUS GTEST_DIR=$ENV{GTEST_DIR})
message(STATUS GMOCK_DIR=$ENV{GTEST_DIR})

include_directories($ENV{GTEST_DIR}/include)
include_directories($ENV{GMOCK_DIR}/include)
link_directories($ENV{GTEST_DIR}/lib)
link_directories($ENV{GMOCK_DIR}/lib)

cxx_gmock_test(mock_test "" painter.cpp  test/mock_unittest.cpp)

compiler_settings.cmake
# compiler settings
find_package(Threads)

set(cxx_base_flags "${cxx_base_flags} -Wall -Wshadow")
set(cxx_base_flags "${cxx_base_flags} -Wextra")
set(cxx_base_flags "${cxx_base_flags} -Werror")

function(cxx_executable_with_flags name cxx_flags libs)
  add_executable(${name} ${ARGN})
  if (cxx_flags)
    set_target_properties(${name}
      PROPERTIES
      COMPILE_FLAGS "${cxx_flags}")
  endif()
  foreach (lib "${libs}")
    target_link_libraries(${name} ${lib})
  endforeach()
endfunction()

function(cxx_executable name libs)
  cxx_executable_with_flags(${name} "${cxx_base_flags}" "${libs}" ${ARGN})
endfunction()

function(cxx_test_with_flags name cxx_flags libs)
  cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
  target_link_libraries(${name} gtest;gtest_main)
  target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
  add_test(${name} ${name})
endfunction()

function(cxx_test name libs)
  cxx_test_with_flags("${name}" "${cxx_base_flags}" "${libs}" ${ARGN})
endfunction()

function(cxx_gmock_test name libs)
  cxx_test_with_flags("${name}" "${cxx_base_flags}" "${libs};gmock_main" ${ARGN})
endfunction()

painter.h
#ifndef PAINTER_H
#define PAINTER_H

#include "turtle.h"

class Painter {
public:
    Painter(Turtle* turtle)
        : turtle_(turtle) {}
    ~Painter() {}

    bool DrawCircle(int x, int y, int r);

private:
    Turtle* turtle_;
};

#endif // PAINTER_H

painter.cpp
#include "painter.h"

bool Painter::DrawCircle(int x, int y, int r)
{
    (void)x;
    (void)y;
    (void)r;

    turtle_->PenDown();
    return true;
}

turtle.h
#ifndef TURTLE_H
#define TURTLE_H

class Turtle {
public:
    Turtle() {}
    virtual ~Turtle() {}
    virtual void PenUp() = 0;
    virtual void PenDown() = 0;
    virtual void Forward(int distance) = 0;
    virtual void Turn(int degrees) = 0;
    virtual void GoTo(int x, int y) = 0;
    virtual int GetX() const = 0;
    virtual int GetY() const = 0;
};

#endif // TURTLE_H

mock_turtle.h
#ifndef MOCK_TURTLE_H
#define MOCK_TURTLE_H

#include "gmock/gmock.h"
#include "turtle.h"

class MockTurtle : public Turtle {
public:
    MOCK_METHOD0(PenUp, void());
    MOCK_METHOD0(PenDown, void());
    MOCK_METHOD1(Forward, void(int distance));
    MOCK_METHOD1(Turn, void(int degrees));
    MOCK_METHOD2(GoTo, void(int x, int y));
    MOCK_CONST_METHOD0(GetX, int());
    MOCK_CONST_METHOD0(GetY, int());
};

#endif // MOCK_TURTLE_H

test/mock_unittest.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "painter.h"
#include "mock_turtle.h"

TEST(PainterTest, CanDrawSomething)
{
    MockTurtle turtle;
    EXPECT_CALL(turtle, PenDown())
        .Times(::testing::AtLeast(1));
  
    Painter painter(&turtle);

    EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}


そしたらいつも通りビルドしてやればテストできる.
コードとテストの詳細は前述のGoogle Mock ドキュメント日本語訳の超入門編をみてもらうとして,ポイントだけ説明しておく.
今回の例だと,CanDrawSomethingのテストで,PenDown()が1回だけ呼ばれることをモックを使ってテストしていて,実際,DrawCircle()でPenDown()を呼び出しているので,テストは成功する.
試しに,DrawCircle()でのPenDown()の呼び出しをコメントアウトしたりするとテストが失敗するようになるはずだ.

今回はとりあえずメソッド呼び出しの回数テストだけしか試してないけど,Google Mockの雰囲気は分かると思う.
Google Mockは他にもいろいろできるんだけど,その辺についてはまた次回.
タグ:Google Mock
nice!(1)  トラックバック(0) 
共通テーマ:日記・雑感

Google Testでテストフィクスチャを使ってみる [Google Test]

Google Testを今回も試してみる.
で,今回は,テストフィクスチャを使ってみる.
テストフィクスチャってのは,Wikipediaを引用すると,「テストを実行、成功させるために必要な状態や前提条件の集合を、フィクスチャと呼ぶ。これらはテストコンテキストとも呼ばれる。開発者はテストの実行前にテストに適した状態を整え、テスト実行後に元の状態を復元することが望ましい。」とある.

要するに,これを使うと,テストの事前準備と事後処理をさせることができるようになる.

で,Google Testでの使い方なんだけど,まずはディレクトリ構成.
fixture_proj/
├── CMakeLists.txt
├── compiler_settings.cmake
└── test
    └── fixture_unittest.cpp


次にファイルについて.
compiler_settings.cmakeは前回と同じ内容で,CMakeLists.txtはプロジェクト名の変更と,テストファイル名がtest/fixture_unittest.cppに変わったことに伴う変更だけ.

で,今回のテストフィクスチャの使い方はfixture_unittest.cppに書かれてるわけだが,最初に言っておくと,今回のテストはあくまでテストフィクスチャの使い方の説明のためだけであって,テストそのものとかは正直あんまり意味は無い.

ではfixture_unittest.cppの中身.
#include <gtest/gtest.h>

class FixtureTest : public ::testing::Test {
protected:
    virtual void SetUp() {
        printf("TestFixture SetUp called\n");
        p = new int[10];
        for (int i = 0; i < 10; i++) {
            p[i] = i;
        }
    }

    virtual void TearDown() {
        printf("TestFixture TearDown called\n");
        delete [] p;
    }

    int *p;
};

TEST_F(FixtureTest, CheckIndex0Value)
{
    EXPECT_EQ(0, p[0]);
}

TEST_F(FixtureTest, UpdateValue)
{
    p[0] = 100;
    p[5] = 500;

    EXPECT_EQ(100, p[0]);
    EXPECT_EQ(500, p[5]);
}

TEST_F(FixtureTest, CheckAllValue)
{
    for (int i = 0; i < 10; i++) {
        EXPECT_EQ(i, p[i]);
    }
}


で,これをビルドしてテストを実行してみると分かるが,1つ1つのテスト(今回の例だとCheckIndex0Value,UpdateValue,CheckAllValue)が実行されるたびに,SetUp()とTearDown()が実行される.
ビルドディレクトリで,
make test ARGS=-V

とすると,SetUp()とTearDown()にあるprintf()がテストごとに実行されているのが分かるはずだ.
また,この例で試してみたところ,CheckAllValueのテストが実行されてからCheckAllValueのテストが実行されるようだが,SetUp()で毎回配列の初期化がされるので,UpdateValueのテストで配列の値の変更をしてもCheckAllValueのテストを実行するときには再度配列の初期化がされているのでテストは成功する.

というわけで,1つ1つのテストに対して前提条件があって,常にその条件を満たすようにしたい場合には,このテストフィクスチャの仕組みがうまく機能するということだ.

今回の例はあんまり参考にならないかもしれないが,実際にいろいろなテストを書いてると,こういった前提条件を整えるというのはどうしても必要になってくる.
これがフレームワークとして用意されてるのはとても助かる.

便利なのでぜひ使ってみてくださいな.
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。