2013年7月31日水曜日

Arduino はじめました

ちょっと個人的にやりたいことがあったので、Arduino をはじめました。
最終的な目標は LED マトリックス使って文字や絵を表示することです。
電子工作自体はじめてですが、頑張って納期までに完成させたいと思います。

まずは購入
というわけで、まずは Arduino を購入しました。
今回購入したのはこちら。


とりあえず、初心者なので入門キットを買いました。
LED マトリックスは目星をつけているのがあるのですが、Arduino に慣れてから購入するつもり。

早速 LED チカチカ
早速、届いたので開発環境をインストールして、サンプルを実行しました。

開発環境のインストールはこちらからしました。
http://arduino.cc/en/Guide/windows
サンプルの実行まで、手順が書いてあるのでそのとおりにやるだけでーす。


できた~(^O^)
delay の数値を弄って点滅スピードが変わるのも確認。

非常に簡単ですね!

次へ
今回購入した Arduinoをはじめようキット はこちらの本の内容を実践するためキットなのですが、

この本は買ってないのです。(2,100円だし買ってもいいのだけど…)

というわけで、早速次に何するか迷子になっているわけですが…
タクトスイッチと LED があるので、スイッチに反応して LED チカチカでも、やりましょうかね。

…えーと、ではまた次回!

2013年7月30日火曜日

[gtest] テストの書き方 TIPS

Google Test でテストを書くときのちょっとしたテクニックを紹介します。
(※ こちらの記事では gtest 1.6.0 を使用しています。)

テストフィクスチャの再利用1
テストフィクスチャは typedef で別名を定義することで、再利用できます。
class TestHoge : public ::testing::Test {};

TEST_F(TestHoge, Test)
{
}

typedef TestHoge TestFoo;

TEST_F(TestFoo, Test)
{
}
複数のテストケースで同じセットアップ処理を実行する場合に便利です。

テストフィクスチャの再利用2
値のパラメータ化テスト用に定義したテストフィクスチャは、通常のテストのテストフィクスチャとして利用できます。
class TestHoge : public ::testing::TestWithParam {};

TEST_P(TestHoge, A)
{
}

TEST_F(TestHoge, B)
{
}

INSTANTIATE_TEST_CASE_P(A, TestHoge, ::testing::Range(0, 2));
TEST_P 用の TestWithParam を継承したテストフィクスチャは、TEST_F でも使用できます。
一部分だけパラメータテストをしたい場合に便利です。

複雑なパラメータの作成
パラメータに乱数を使用したり、複雑な計算結果を利用する場合、
パラメータを格納した ::std::vector を返す関数を作成することで、楽に書けます。
::std::vector MakeParam()
{
  ::std::vector v(10);
  ::std::mt19937 engine;
  ::std::uniform_int_distribution dist(0, 1000);
  ::std::generate(v.begin(), v.end(), [&](){ return dist(engine); });
  return v;
}

INSTANTIATE_TEST_CASE_P(A, TestHoge, ::testing::ValuesIn( MakeParam() ) );

まとめ
以上で、TIPS 終わりです…
書き出してから気づきましたが、
なんか、あんま大したこと書いてないですね…

2013年7月23日火曜日

cygwin の clang パッケージで標準ライブラリが見つからないエラー

新しいノートパソコンが来たので、cygwin をインストールしなおしたら、
gcc が 4.7 系になっていたので喜んでいたら、 clang でビルドできなくなって大変困ってしまった。

エラーの内容としては、
../include/iutest_config.hpp:106:14: fatal error: 'set' file not found
#    include 
             ^
と、いう感じで標準ライブラリが見つからないようです。


皆目検討がつかず、WEB を彷徨っていたらそれっぽい情報を見つけました。
clang++ broken by recent GCC update

i386 がサポートされなくなったのか?
ん~困った。

2013/12/16 追記
-v オプションつけて詳細表示させたところ、4.5.3 を参照していることがわかりました。
clang -cc1 version 3.1 based upon LLVM 3.1 default target i386-pc-cygwin
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/i6
86-pc-cygwin"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ba
ckward"
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include"

古い cygwin 環境から 4.5.3 一式をコピペしたところ無事ビルドできました。
(これでいいのかはわかりません。)

2013年7月18日木曜日

[iutest] TDDBC の課題の経過報告(その1)

ちょこちょこ進めていた TDDBC の課題ですが、ステップ0が終わったので経過報告でもしておこうと思います。

環境のおさらい
家の PC が壊れたこともあって、タブレットのみで作業できるように環境を整えました。
ビルドとテストは CloudBees の Jenkins を使用しました。
コミットは Red/Green/Refactoring ごとになるべく細かく行うようにしました。

解説
svn のリビジョンにそって解説していきます。
r1~r4
このへんは最初の環境セットアップをしています。
Makefile の準備やファイルの追加を行なっています。

r5
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 4)
+++ test/VendingMachineTest.cpp (revision 5)
@@ -1,3 +1,13 @@
 #include "gtest/iutest_switch.hpp"
+#include "VendingMachine.h"

+class VMTest : public ::iutest::Test
+{
+public:
+    VendingMachine vm;
+};

+IUTEST_F(VMTest, totalAmount)
+{
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
+}

まずは、投入金額の取得関数のテストを書きました。
この時点ではビルドは通りません。
テストフィクスチャのことは知っているので、初めからフィクスチャを使っています。
テストフィクスチャを使うことで VendingMachine の構築などテストで共通の処理をまとめて書くことができます。

r6
Makefile を修正しました。
r7
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 6)
+++ src/VendingMachine.cpp      (revision 7)
@@ -1,2 +1,6 @@
 #include "VendingMachine.h"

+int VendingMachine::GetTotalAmount(void)
+{
+    return -1;
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 6)
+++ src/VendingMachine.h        (revision 7)
@@ -1,4 +1,10 @@
 #ifndef INCG_VendingMachine_H_
 #define INCG_VendingMachine_H_

+class VendingMachine
+{
+public:
+    int GetTotalAmount(void);
+};
+
 #endif
GetTotalAmount 関数を実装しました。
ここではテストが失敗するように実装をします。
テストが正しく失敗することを確認することで、テスト自体が正しく書けているかがわかります。
ここで仮にテストに成功した場合、テストの方が間違っているはずです。

こちらが Jenkins の結果。


r8
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 7)
+++ src/VendingMachine.cpp      (revision 8)
@@ -2,5 +2,5 @@

 int VendingMachine::GetTotalAmount(void)
 {
-    return -1;
+    return 0;
 }

テストが通るように実装しました。

ここで重要なのが、テストが通る最小の実装に留めることです。
分かっていてもついつい実装が進み過ぎてしまうことがあります。
今回もやってしまったので、これについては後ほど解説します。


r9
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 8)
+++ test/VendingMachineTest.cpp (revision 9)
@@ -11,3 +11,9 @@
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }
+
+IUTEST_F(VMTest, insert)
+{
+    vm.Insert(100);
+    IUTEST_ASSERT_EQ(100, vm.GetTotalAmount());
+}
特にリファクタリングすることもないので、次のテストを書きました。
次はお金の投入のテストを書きました。

r10
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 9)
+++ src/VendingMachine.cpp      (revision 10)
@@ -4,3 +4,7 @@
 {
     return 0;
 }
+
+void VendingMachine::Insert(int money)
+{
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 9)
+++ src/VendingMachine.h        (revision 10)
@@ -5,6 +5,7 @@
 {
 public:
     int GetTotalAmount(void);
+    void Insert(int money);
 };

 #endif
テストが失敗する最小の Insert 関数を実装しました。

Jekins で失敗を確認。

r11~r13
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 10)
+++ src/VendingMachine.cpp      (revision 11)
@@ -2,9 +2,10 @@

 int VendingMachine::GetTotalAmount(void)
 {
-    return 0;
+    return m_TotalAmount;
 }

 void VendingMachine::Insert(int money)
 {
+    m_TotalAmount += money;
 }
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 10)
+++ src/VendingMachine.h        (revision 11)
@@ -6,6 +6,8 @@
 public:
     int GetTotalAmount(void);
     void Insert(int money);
+private:
+    int m_TotalAmount;
 };

 #endif

テストが通るように実装しました。
が、実はここでミスをしました。
これではテストが通るための最小の実装でないからです。

r12 で意気揚々とリファクタリングしてますが、次のテストを考えた時に間違いに気づきました。
次のテストとして、100円以外のお金が投入された場合のテストを書こうと思ったのですが、
r11 の実装では、テストを失敗させることができないのです。

テストを失敗させることができないとテストの正当性が確認できなくなってしまい、
たとえそのテストが成功したとしても、誤ったテストで誤った結果を確認しているだけになってしまう恐れがあります。

というわけで、r13 で最小の実装に退行させています。
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 12)
+++ src/VendingMachine.cpp      (revision 13)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount += money;
+    m_TotalAmount = 100;
 }

Jenkins の結果も確認しておきます。

r14~r17
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 13)
+++ test/VendingMachineTest.cpp (revision 14)
@@ -7,13 +7,20 @@
     VendingMachine vm;
 };

+class VMParamTest : public VMTest, public ::iutest::TestWithParam<int>
+{};
+
 IUTEST_F(VMTest, totalAmount)
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_F(VMTest, insert)
+IUTEST_P(VMParamTest, insert)
 {
-    vm.Insert(100);
-    IUTEST_ASSERT_EQ(100, vm.GetTotalAmount());
+    int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }
+
+IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMParamTest
+    , ::iutest::Values(10, 50, 100, 500));
100円以外のお金の投入テストに対応するため、値のパラメータ化テストを使用しました。(1000円抜けてますが…)

Insert の実装は既にあるのでビルドは通る状態のはずでしたが、
テストフレームワークの使い方を間違えたため、ビルドエラーとなってしまいました。
r15,r16 とで修正を試みていますが、そもそもテストフレームワーク側がテストフィクスチャのダイヤモンド継承に対応していませんでした。

というわけで、テストフレームワーク側に手を加えて解決しました。
Jenkins でテストが失敗することも確認。

r18
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 17)
+++ src/VendingMachine.cpp      (revision 18)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount = 100;
+    m_TotalAmount = money;
 }
テストが通るように修正しました。
この実装では不十分なことは分かりきっていますが、ここでも最小の実装に留めています。

r19,r20
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 18)
+++ test/VendingMachineTest.cpp (revision 20)
@@ -1,26 +1,23 @@
 #include "gtest/iutest_switch.hpp"
 #include "VendingMachine.h"

-class VMTest : virtual public ::iutest::Test
+class VMTest : public ::iutest::TestWithParam<int>
 {
 public:
     VendingMachine vm;
 };

-class VMParamTest : public VMTest, public ::iutest::TestWithParam<int>
-{};
-
 IUTEST_F(VMTest, totalAmount)
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_P(VMParamTest, insert)
+IUTEST_P(VMTest, insert)
 {
     int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }

-IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMParamTest
+IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
     , ::iutest::Values(10, 50, 100, 500));

テストのリファクタリングをしました。
ダイヤモンド継承の問題をテストフレームワーク側に解決させましたが、
そもそもそんなことをしなくてもテストフィクスチャの再利用は可能でした。
※1 その辺は後日まとめます。
※2 Google Test でもダイヤモンド継承できなかったので、移植性も考慮。

r21
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 20)
+++ test/VendingMachineTest.cpp (revision 21)
@@ -14,10 +14,19 @@

 IUTEST_P(VMTest, insert)
 {
-    int money=GetParam();
+    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }

+IUTEST_P(VMTest, insert2)
+{
+    const int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money*2, vm.GetTotalAmount());
+}
+
 IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
     , ::iutest::Values(10, 50, 100, 500));

続いて、2枚お金を投入したときのテストを作成しました。
(※ 1000円が抜けているのはミスです。)

テストが失敗することを確認。

r22
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 21)
+++ src/VendingMachine.cpp      (revision 22)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount = money;
+    m_TotalAmount += money;
 }

テストが通るように修正します。
ここでようやくそれっぽくなりましたね。

r23
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 22)
+++ test/VendingMachineTest.cpp (revision 23)
@@ -29,4 +29,4 @@
 }

 IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
-    , ::iutest::Values(10, 50, 100, 500));
+    , ::iutest::Values(10, 50, 100, 500, 1000));

1000円のテストが抜けていたので追加しました。

r24
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 23)
+++ test/VendingMachineTest.cpp (revision 24)
@@ -12,6 +12,11 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

+IUTEST_F(VMTest, refund)
+{
+    IUTEST_ASSERT_EQ(0, vm.Refund());
+}
+
 IUTEST_P(VMTest, insert)
 {
     const int money=GetParam();

払い戻しのテストを追加しました。
r25
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 24)
+++ src/VendingMachine.cpp      (revision 25)
@@ -14,3 +14,8 @@
 {
     m_TotalAmount += money;
 }
+
+int VendingMachine::Refund(void)
+{
+    return -1;
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 24)
+++ src/VendingMachine.h        (revision 25)
@@ -9,6 +9,7 @@
 public:
     int GetTotalAmount(void);
     void Insert(int money);
+    int Refund(void);
 private:
     int m_TotalAmount;
 };

払い戻しの実装をしました。

もちろんテストが失敗するように実装しています。

r26
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 25)
+++ src/VendingMachine.cpp      (revision 26)
@@ -17,5 +17,5 @@

 int VendingMachine::Refund(void)
 {
-    return -1;
+    return 0;
 }

テストが通る最小の実装です。

r27
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 26)
+++ test/VendingMachineTest.cpp (revision 27)
@@ -12,9 +12,13 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_F(VMTest, refund)
+IUTEST_P(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
+
+   const int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.Refund());
 }

 IUTEST_P(VMTest, insert)

続いてお金を投入した後の払い戻しテストを追加しました。

実装は既にあるので、テストが失敗することを確認します。

r28
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 27)
+++ src/VendingMachine.cpp      (revision 28)
@@ -17,5 +17,5 @@

 int VendingMachine::Refund(void)
 {
-    return 0;
+    return m_TotalAmount;
 }
テストが通るように実装。

r29
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 28)
+++ test/VendingMachineTest.cpp (revision 29)
@@ -15,10 +15,12 @@
 IUTEST_P(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());

    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.Refund());
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

 IUTEST_P(VMTest, insert)

最後に払い戻しした後の投入金額のテストを追加しました。
払い戻しをしたあとなので、当然金額は 0円になります。

ここでも、実装は既にありますのでテストが失敗することを確認します。


r30
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 29)
+++ src/VendingMachine.cpp      (revision 30)
@@ -17,5 +17,7 @@

 int VendingMachine::Refund(void)
 {
-    return m_TotalAmount;
+    const int refund = m_TotalAmount;
+    m_TotalAmount = 0;
+    return refund;
 }

払い戻し後の投入金額の処理を実装しました。

テストも通りました。

r31
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 30)
+++ test/VendingMachineTest.cpp (revision 31)
@@ -12,12 +12,15 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_P(VMTest, refund)
+IUTEST_F(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
+}

-   const int money=GetParam();
+IUTEST_P(VMTest, insertAndRefund)
+{
+    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.Refund());
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());

最後にリファクタリングをしています。
投入をしていない場合の払い戻しのテストを別のテストに分けました。


次へ
以上で、ステップ0が完了しました。
テストの推移ばこんな感じになりました。

オーバーフローなど考慮しないといけない点がまだありますが、次のステップ1に進みたいと思います。


まとめ
ここまでは TDDBC でやったことのおさらいなのでスムーズにいきました。
大事なことは「テストを書いたら失敗させる」・「テストを成功させるときは最小の実装で」。
TDDBC の時にも言われましたが、慣れたら一気に数回転進めるのもいいらしいです。

私はまだまだ初心者なので1つ1つやっていきたいと思います。

最後に、ここに書いた手順が正解ではありません。
むしろ間違っていたらツッコミいれてください m(__)m



2013年7月11日木曜日

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

Visual Studio 2013 Preview が公開されましたので、 iutest を対応させました。

主に、C++11 関係で使えるようになった機能の判定プリプロセッサディレクティブを修正しています。
あとは、VS2013 でもコンパイルが通るように template コードを修正したことで、
「Visual C++ Compiler November 2012 CTP」でも ::iutest::Values で可変長テンプレートが使えるようになりました。


そろそろ C言語バージョンの更新もしたいところですが、ほとんど手を付けられていないです。。。
(Combine 対応は後回しにして、ほかのとこやろうかなぁ…)

2013年7月4日木曜日

[Jenkins] SourceMonitor Plugin を使ってみた

Jenkins のプラグインに SourceMonitor Plugin が新しくリリースされていたので、使ってみました。
コードメトリクスツールは Jenkins のプラグインがなかったという理由もあって、
SourceMonitor ではなく CCCC を使って来ました。
が、このプラグインが良ければ乗り換えもありだと思います。(CCCC の Jenkins プラグインはイマイチ。

SourceMonitor に xml を吐かせる
まずは、SourceMonitor から結果の xml を取得します。
/C コマンドオプションを指定して、動作を指示した xml ファイルを渡します。
今回使用した xml はこちらです。
<?xml version="1.0" encoding="UTF-8" ?>
<sourcemonitor_commands>
    <command>
        <project_file>iutest.smp</project_file>
        <project_language>C++</project_language>
        <modified_complexity>true</modified_complexity>
        <source_directory>trunk</source_directory>
        <file_extensions>*.h,*.hpp,*.c,*.cpp,*.cxx,*.ipp</file_extensions>
        <include_subdirectories>true</include_subdirectories>
        <export>
            <export_file>smdump.xml</export_file>
            <export_type>1 (project summary as XML)</export_type>
            <export_option>Include method metrics: option 3</export_option>
        </export>
    </command>
</sourcemonitor_commands>
SourceMonitor.exe /C smcmd.xml

Jenkins の設定
Jenkins の設定は簡単です。
まず、対象のプロジェクトの設定ページから、「ビルド後の処理の追加」をクリックし「Publish SourceMonitor result」を選択します。

次に、「SourceMonitor summary metric file path」に SourceMonitor から出力される xml のパスを指定します。

以上で設定は終わりです。

結果を見る
プロジェクトのページに「SourceMonitor result」があるので、クリックします。

すると、このような結果画面が表示されます。

残念ながら、モジュールや関数ごとの詳細な結果は見れないようです…

まとめ
CCCC Plugin と同様 SourceMonitor Plugin もまだまだイマイチな感じでした。
CCCC は junit 形式の xml に変換する c4ju を作って詳細な要素のテストに対応しました。
SourceMonitor も同じようにツールを作ればいいのですが、面倒臭いのでまたにします。


というわけで、しばらくはこれまで通り CCCC を使っていこうと思います。
以上。