opamp_sando's blog

クソザコが割りと適当なことを書くためにある備忘録です。あとたまに普通の日記も書きます

I try using Boost Test Library with CMake.

英語でタイトル書いてみたけど記事は日本語。英語あってるかな...まあいいや。

とりあえず今日はBoost Test LibraryをCMakeと一緒に使ってみる。
もしCMakeしらない人が読むとちょっとわからないかもしれないけど今日の記事....

Boost -> 1.48.0 (boostのビルドはboost 1.48.0 がリリースされてたのでビルドしてみる - opamp_sandoの日記 ...でいいよね)
CMake -> 2.8.7

でやってみる。あとOSは MacOSX
多分UNIX系全般でここに書いてることは同様にできると思うけどWindowsでは環境によってはboostのリンクするライブラリ名が違うかも。

まず今日やること。 Boost Test Libraryを使って簡単なUnit testを書いてみる。で、それをCMakeを使ってビルドする。
で、"make test"とかで走らせる.

※ 最初に書いておくけどboostのUnitTestは"libboost_unit_test_framework"っていうライブラリとリンクする必要があるんだけど、これどうやら共有リンクすると失敗するようだ。(静的リンクのlibboost_unit_test_frameworkにしかmainが含まれていないため)
なのでlibboost_unit_test_frameworkとは静的リンクするようにする。



ということでやってみた。
解説は主に貼っつけるソースコードにコメントで書いていくのでそれを読めばいいかも。

※ ディレクトリ階層なんかがぱっとしない場合に備えてgithubにリポジトリを作ってみた-> https://github.com/opamp/testproject2

今回はhogeとpiyoという2つのクラスを(適当に)書いて、それぞれのテストをboostで書く感じ.
ということでまずはhogeクラスが定義してあるHogeClass.hppとHogeClass.cppをそれぞれ順番に貼る。

/*HogeClass.hpp*/
#ifndef HOGE_CLASS_HPP //インクルードガード
#define HOGE_CLASS_HPP
#include<iostream>
#include<string>
#include<exception>

using namespace std;

namespace hogeclass{//hogeクラスはhogeclassというnamespace内部に定義
class opamp_exception : public exception{}; //ちょっとした例外クラス

class hoge{ //hogeクラス
/*
簡単なhogeクラスの働きとしてはコンストラクタで整数型(int)と文字列string型を受け取る。(privateなメンバー変数に保持)
getWordクラス関数を使うとコンストラクタで渡したstring型の値を得ることができる。

で、helloクラス関数を実行するとコンストラクタで渡した文字列をintで渡した回数分だけ表示する。

※コンストラクタに渡すint型の値が小さいと上に定義してるopamp_exceptionが飛ぶ。
*/
public:
    hoge(const int,const string&) throw();
    int hello();
    string getWord(){return word;};

private:
    string word;
    int n;
};
}
#endif
/*HogeClass.cpp*/
#include"HogeClass.hpp"


namespace hogeclass{
hoge::hoge(const int n,const string& str) throw()
{
    if(n < 0){ // 0未満だとoapmp_exceptionが飛ぶ。
        throw opamp_exception();
    }else{
        this->n = n;
        this->word = str;
    }
};

int hoge::hello(){
    for(int i = 0;i < n;i++)
    cout<<this->word<<endl;

    return n;
};

}//end

で続いてpiyoクラスの方... それぞれpiyoClass.hppとpiyoClass.cppを貼る

/*piyoClass.hpp*/
#ifndef PIYO_CLASS_HPP //インクルードガード
#define PIYO_CLASS_HPP
#include"HogeClass.hpp" //HogeClassをなにげに使うのでHogeClassをインクルードしておく

namespace piyoclass{ //piyoクラスはpiyoclass namesapceに定義される。
class piyo{ //piyoクラス
/*
piyoクラスは2つのhogeclass::hoge*を受け取ってそれぞれメンバー変数に保持しておく。
helloメンバ関数を呼ぶとその2つのhogeクラスからgetWord()して得られたstring型をつなげた文字列を引数int n回表示する。
正直実験用とはいえなんでこんなクラスにしたのか意味不明だけど。
*/
public:
    piyo(hogeclass::hoge*,hogeclass::hoge*); //2つのhogeclass:hoge*を受け取る
    int hello(int n = 10); 

private:
    hogeclass::hoge *obja,*objb;
};
}// end of namespace
#endif
/*piyoClass.cpp*/
#include"piyoClass.hpp"

namespace piyoclass{

piyo::piyo(hogeclass::hoge* o,hogeclass::hoge* p){
    obja = o;
    objb = p;
};

int piyo::hello(int n){//helloクラス関数は内部的にはhogeを使って表示を行う。
    hogeclass::hoge a(n, obja->getWord() + objb->getWord());
    a.hello();
    return n;
};


}//end of namesapce

という感じ。これらを${PROJECT_SOURCE_DIR}/srcを作ってそこに保存しておく。


で、CMakeLists.txtを書く。以下${PROJECT_SOURCE_DIR}/CMakeLists.txt

PROJECT(boost_test) # PROJECT NAME
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) #CMAKEの要求バージョン コレ以下だとエラーになる。

SET(Boost_USE_STATIC_LIBS ON) #必要か怪しいけど一応。多分無くてもいい
FIND_PACKAGE(Boost 1.4.8 REQUIRED) # BOOSTを探す。

SET(SRC_DIR ${PROJECT_SOURCE_DIR}/src) # SRC_DIRを定義

INCLUDE(${PROJECT_SOURCE_DIR}/src/CMakeLists.txt) # 上のclassのビルドを記述したCMakeLists.txtを呼び出し

ENABLE_TESTING() #make testするために必要

INCLUDE(${PROJECT_SOURCE_DIR}/test/CMakeLists.txt) # testをビルドするCMakeLists.txtを呼び出し

で${PROJECT_SOURCE_DIR}/src/CMakeLists.txt

IF(Boost_FOUND)
    INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) #多分ここもいらないと思うけど一応
ENDIF()

ADD_LIBRARY(HogeClass STATIC ${SRC_DIR}/HogeClass.cpp) #HogeClass.cppをビルド (STATIC)

ADD_LIBRARY(PiyoClass STATIC ${SRC_DIR}/piyoClass.cpp) #piyoClass.cppをビルド (STATIC)
TARGET_LINK_LIBRARIES(PiyoClass HogeClass) # piyoClassはhogeクラスを使ったのでHogeClassとリンクする。


さて、じゃあBoost Test Libraryを使ったテストコードを貼る。
まずはhogeクラスのテストを記述した${PROJECT_SOURCE_DIR}/test/Test_HogeClass.cpp

/*Test_HogeClass.cpp*/
#define BOOST_TEST_MODULE HogeClassTest //このテストの名前かな? (任意に決めていい)
#include "../src/HogeClass.hpp" //HogeClassを使うのでヘッダーをインクルード
#include <boost/test/unit_test.hpp> //boost の unit testを使うにはこれをインクルードする。
using namespace std;


BOOST_AUTO_TEST_CASE( hogeClassinit ) // hogeClassinitというテスト。初期化のテストをする。括弧内部の名前は当然任意
{
string s = "hello world";
hogeclass::hoge obj(10,s); //hogeクラスのobjectを作る。

BOOST_CHECK_EQUAL(obj.getWord(),s); //obj.getWord()の値はコンストラクタに渡したsが返るはずなのでBOOST_CHECK_EQUALで確認
}

BOOST_AUTO_TEST_CASE( runningHelloTest) // runningHelloTest Hello関数のテスト 
{
int n = 5;
string s = "hello HogeClass";
hogeclass::hoge obj(n,s); //hogeクラスのobject作成

BOOST_CHECK( obj.hello() == n);//BOOST_CHECK_EQUALを使ってもいいがこのようにBOOST_CHECKを使うこともできる。戻り値がnか確認している。
}

/*
上のように合計2つのテストを記述した。main関数は存在しないがこれはlibboost_unit_test_framework.aに含まれている。
最初に書いたがどこかのバージョンからlibboost_unit_test_framework.soとかlibboost_unit_test_framework.dylibのような
共有ライブラリにmain関数が含まれなくなったらしいので共有ライブラリとリンクするとビルドエラーになる。
*/

続いてpiyoクラスのテスト${PROJECT_SOURCE_DIR}/test/Test_PiyoClass.cpp

/*Test_PiyoClass.cpp*/
#define BOOST_TEST_MODULE PiyoClassTest //Testの名前...と思う
#include<boost/test/unit_test.hpp> //必要なヘッダー
#include"../src/piyoClass.hpp" //piyoクラスを使うのでヘッダーをインクルード
#include"../src/HogeClass.hpp" //hogeクラスも使うのでヘッダーをインクルード

BOOST_AUTO_TEST_CASE( piyoClassTest) 
{
int n = 10;int m = 10;
//以下で2つのhogeクラスを宣言する
hogeclass::hoge a(n,"hello"); 
hogeclass::hoge b(m,"world");

piyoclass::piyo obj(&a,&b);//piyoクラスに上で宣言した2つのhogeクラスのポインタを渡す。
BOOST_CHECK_EQUAL(obj.hello(n + m),(n + m)); //helloの戻り値がn と mの和と等しいかを調べる。
}
/*
このテストには合計1つのテストを記述した
*/

で、最後に${PROJECT_SOURCE_DIR}/test/CMakeLists.txtはこんな感じ

ADD_EXECUTABLE(testHogeClass ${PROJECT_SOURCE_DIR}/test/Test_HogeClass.cpp) #hogeクラスのテストをビルド
TARGET_LINK_LIBRARIES(testHogeClass HogeClass boost_unit_test_framework.a) 
#boost_unit_test_framework.aとリンク。静的リンクする方法よくわからなかったのでboost_unit_test_framework.aとしてみたらうまくいった...
#ldのオプションに-staticを渡してもいい気がするが...まあ、今回はこれで

ADD_TEST(NAME hogeClassTest #make test時に実行するように設定 test名 hogeClassTest
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} #作業ディレクトリは${CMAKE_CURRENT_BINARY_DIR}でおk。
         COMMAND testHogeClass) #上のADD_EXECUTABLEでビルドした奴を実行すればいいのでtestHogeClassを指定。





ADD_EXECUTABLE(testPiyoClass ${PROJECT_SOURCE_DIR}/test/Test_PiyoClass.cpp)#piyoクラスのテストをビルド
TARGET_LINK_LIBRARIES(testPiyoClass HogeClass PiyoClass boost_unit_test_framework.a)#hogeと同様...

ADD_TEST(NAME piyoClassTest # make test時に実行するように設定
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} #${CMAKE_CURRENT_BINARY_DIR}でおk
COMMAND testPiyoClass) #piyoクラスのADD_EXECUTABLEで作ったtestPiyoClassを指定

#下の2行は気にしない...でね。
#ADD_TEST(NAME RunAllTest WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}}
# COMMAND ${PROJECT_SOURCE_DIR}/test/runner.sh ${CMAKE_CURRENT_BINARY_DIR})

という感じ。

あとはこれでcmakeを走らせてmakeしてみる

$ cd project_dir
$ mkdir build
$ cd build
$ cmake ../
.... (略)
$ make
Scanning dependencies of target HogeClass
[ 25%] Building CXX object CMakeFiles/HogeClass.dir/src/HogeClass.cpp.o
Linking CXX static library libHogeClass.a
[ 25%] Built target HogeClass
Scanning dependencies of target PiyoClass
[ 50%] Building CXX object CMakeFiles/PiyoClass.dir/src/piyoClass.cpp.o
Linking CXX static library libPiyoClass.a
[ 50%] Built target PiyoClass
Scanning dependencies of target testHogeClass
[ 75%] Building CXX object CMakeFiles/testHogeClass.dir/test/Test_HogeClass.cpp.o
Linking CXX executable testHogeClass
[ 75%] Built target testHogeClass
Scanning dependencies of target testPiyoClass
[100%] Building CXX object CMakeFiles/testPiyoClass.dir/test/Test_PiyoClass.cpp.o
Linking CXX executable testPiyoClass
[100%] Built target testPiyoClass

と、makeできたらばtestしてみる

$ make test
Running tests...
Test project /Users/opamp/myprefix/mysrc/forTest/boost_test/build
    Start 1: hogeClassTest
1/2 Test #1: hogeClassTest ....................   Passed    0.01 sec
    Start 2: piyoClassTest
2/2 Test #2: piyoClassTest ....................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 2

Total Test time (real) =   0.02 sec

とテストする。 成功すれば上のようになる。(Passedって書いてるっしょ)

んで、もし失敗するとこうなる

$ make test
Running tests...
Test project /Users/opamp/myprefix/mysrc/forTest/boost_test/build
    Start 1: hogeClassTest
1/2 Test #1: hogeClassTest ....................***Failed    0.01 sec
    Start 2: piyoClassTest
2/2 Test #2: piyoClassTest ....................   Passed    0.01 sec

50% tests passed, 1 tests failed out of 2

Total Test time (real) =   0.12 sec

The following tests FAILED:
          1 - hogeClassTest (Failed)
Errors while running CTest
make: *** [test] Error 8

hogeClassTestで Failed になっている。hogeClassTestで失敗しているのがわかる。


今日の成果はこんな感じ。CMakeのTARGET_LINK_LIBRARIESの時スタティックリンクを指定するにはどうすればいいか迷ったけど2つしかアイディアが浮かばなかった。
1つは今回やったように ○○○.a と拡張子aを指定する。

もうひとつはLD_FLAG的なものに一時的に-staticを指定する。(これはできるかわかんない試してないし)

で、最終手段としてはboost_unit_test_frameworkのsharedライブラリをrmで削除するw
最後のは正直ほんと最終手段で絶対におすすめしないけど・・・まあ、最初からsharedなしでbuildするのは手かもしれないが。

※もし誰かcmakeでstaticリンクとか指定するときどうすればいいか知ってる人は教えてくれるとありがたいです...

ということでおしまい。


最後に今日やったsample projectをgithubに上げたので上の書き方がみにくいとかうまくいかないって人は見てみるといいかも...
https://github.com/opamp/testproject2

ただしgithubのversionには解説コメントないので...


もっと詳しいboost test libraryについては
http://www.boost.org/doc/libs/1_48_0/libs/test/doc/html/utf.html

cmakeのdocumentsは
http://www.cmake.org/cmake/help/documentation.html


まあ、boost test libraryについてはまた今後スキルアップすればメモ記事かきますよ。

Firefox ブラウザ無料ダウンロード