2011年6月21日火曜日

macro trick in googletest

googletestでは、テストケースはこう書くわけ。
#include <gtest/gtest.h>

TEST(SampleTestCase, TestFunc) {
    EXPECT_TRUE(true);
}
C++な割にはスマート。で、なんで、こんなことができるのか。その仕組みを追ってみた。まずは、TEST()マクロ。

include/gtest/gtest.h:
#define TEST(test_case_name, test_name) \
  GTEST_TEST_(test_case_name, test_name, \
              ::testing::Test,\
              ::testing::internal::GetTestTypeId())
ほむ。次はGTEST_TEST_()マクロ。

include/gtest/internal/gtest-internal.h(抜粋):
#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
 public:\
  GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
 private:\
  virtual void TestBody();\
};\
\
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
ほむほむ。GTEST_TEST_CLASS_NAME_()マクロは、test_case_nameとtest_nameを繋げて重複しないクラス名を生成する。上の例だと SampleTestCase_TestFunc_Test となる。最後の行は TestBody() のシグニチャだけ置いて、マクロに続くブロックが TestBody() の実装となるようになっているわけだ。

ちなみに、親クラスとなる ::testing::Test は include/gtest/gtest.h で定義されている。
include/gtest/gtest.h(抜粋):
class Test {
 protected:
  virtual void SetUp();
  virtual void TearDown();
 private:
  virtual void TestBody() = 0;
};
ほむほむほむ。予想どおり。

まとめ

最後に始めに出した例と、展開されたあとのイメージを載せておく。

展開前:
TEST(SampleTestCase, TestFunc) {
    EXPECT_TRUE(true);
}

展開後:
class SampleTestCase_TestFunc_Test : public ::testing::Test {
 public:
  SampleTestCase_TestFunc_Test() {}
 private:
  virtual void TestBody();
};

void SampleTestCase_TestFunc_Test::TestBody() {
    EXPECT_TRUE(true);
}
なるほどねぇ。

2011年6月20日月曜日

Rubyのoptparseでgccみたいなオプションを取る

やりたいことは、これ。
./hoge source -o output
sourceにオプションスイッチが無いのがミソ。

options = {
  :input => $stdin,
  :output => $stdout
}

optp = OptionParser.new
optp.on("-o outputfile", String) {|out_filename|
  options[:output] = File.open(out_filename, 'w')
}

remain_opts = optp.permute(ARGV)
if (remain_opts.size > 1)
  raise "Invalid input count."
elsif (remain_opts.size == 1)
  options[:input] = File.open(remain_opts[0], 'r')
end                                                   
OptionParser#parseでなくて、OptionParser#permuteを使う。

googlemockで引数アサーション

googlemockでクラスや構造体のメンバが正しい値でモック関数が呼び出されているかを確認したい。

struct Param {
    bool flag;
    int value;
};

class Interface {
public:
    virtual ~Interface() { }
    virtual void f(const Param& p) = 0;
};

class Actor {
public:
    Actor(Interface& d) : derived(d) { }
    void DoIt(const Param& p) {
        Param tmp;
        tmp.flag = !(p.flag);
        tmp.value = -1 * p.value;
        derived.f(tmp);
    }
private:
    Interface& derived;
};

確認したいこと

Actor::f()がParam::flagを反転、Param::valueの正負を反転させてからInterface::f()を呼び出していることを確認したい。

Param::operator==()を定義しちゃえば良いじゃんというツッコミはナシ。

順当にgooglemockのマッチャーを使う

TEST(GMockSample, Use_GMock_MemberMathcer) {
    using ::testing::AllOf;
    using ::testing::Eq;
    using ::testing::Field;
    using ::testing::Truly;

    Mock mock;
    Param p = {false, 345};

    Actor actor(mock);

    EXPECT_CALL(mock, f(
        AllOf(
            Field(&Param::flag, Eq(true)),
            Field(&Param::value, Eq(-345))
        )
    ));

    // Act!!
    actor.DoIt(p);
}
フィールドの数が増えてきたら、どうするんだろうね。

boost::bindを使う

TEST(GMockSample, Use_Boost_Bind) {
    using ::testing::Truly;
    using ::boost::bind;

    Mock mock;
    Param p = {false, 345};
    Param expect = {true, -345};

    Actor actor(mock);

    EXPECT_CALL(mock, f(
        Truly(
            bind(EqualParameter(), expect, _1)
        )
    ));

    // Act!!
    actor.DoIt(p);
}
期待値をローカル変数に用意しているのがなぁ。

boost::lambdaを使う

TEST(GMockSample, Use_Boost_Lambda) {
    using ::boost::bind;
    using ::boost::lambda::_1;
    using ::testing::Truly;

    Mock mock;
    Param p = {false, 345};

    Actor actor(mock);

    EXPECT_CALL(mock, f(
        Truly(
            bind(&Param::flag, _1) == true
                && bind(&Param::value, _1) == -345
        )
    ));

    // Act!!
    actor.DoIt(p);
}
なんだか、もう一息な気がする。