ctestの導入

ctestはcmakeに付随するテスト実行支援ツールである。 Boost TestやCppUnitとは異なり、あくまで実行を支援するツールである。 公式のページを見ると、ダッシュボードを導入しないといけないような印象を受けるが、 cmake管理化のプロジェクトであれば単独で使用できる。

何ができるか?

あなたがcmake管理化のプロジェクトを開発中、 テストを書く必要があるとする。 テストを書く事の重要性は耳にタコができるほど聴いたが、面倒な事は面倒だ。

ともあれ、mylibraryの機能Func1のテストコードtestFunc1.cppを書いたとしよう。 残念ながら、テストコードは自分でかかなければならない。 しかし、テストコードのビルドと実行は手伝う事ができる。 もしあなたが以下に説明する設定を施している場合、 あなたは単にテストコードtestFunc1.cppを含むディレクトリの CMakeLists.txtに以下の一行を追加すればいい。

mytest(Func1 S testFunc1.cpp mylibrary)

よりよいテストのためには次の行さらに前に追加するべきだろう:

set(CMAKE_BUILD_TYPE Debug)

これであなたはそのディレクトリで

make

とすれば、このコードをTEST_Func1_Sコンパイルする事ができ、

make test

で実行する事ができる。 テストした結果は

Test project /path/to/mylibrary/test
      Start  1: TEST_Func1_S
 1/1  Test  #1: TEST_Func1_S .....................   Passed    0.02 sec

100% tests passed, 0 tsets failed out of 1

Total Test time (real) = 0.02 sec

のように表示される。 もちろん実行ファイルをそのままターミナルから実行する事も可能である。

これは現在のディレクトリ以下にあるテストを再帰的に実行するので、 プロジェクトの一番上で実行すれば全てのテストが実行される。 実行ファイルが異常終了した場合にはテストが失敗したと見做され、 まとめて表示される。

これによりコードを変更した際の再テストが

cd $PROJECT_HOME
make test

とするだけでよくなる。

使い方

この記事では読者はcmakeの基本的な使い方(add_executableくらい)を知っているとする。 まず先に私のCMakeLists.txtの抜粋を示す。

enable_testing()
add_custom_target(build_test)
add_custom_target(short_test COMMAND "ctest" "-R" "TEST_.*_S")
add_custom_target(long_test COMMAND "ctest" "-V" "-R" "TEST_.*_L" "2>&1" ">" "long.log" "&")
add_dependencies(short_test build_test)
macro(mytest name type src libs)
    set(test_name TEST_${name}_${type})
    add_executable(${test_name} ${src})
    target_link_libraries(${test_name} ${libs})
    add_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}/${test_name})
    add_dependencies(build_test ${test_name})
    unset(test_name)
endmacro(mytest)

やっている事は以下の4つ:

  • ctestの有効化 enable_testing()
  • targetの追加 build_test, short_test, long_test
  • 依存関係の追加 (short_test depends on build_test)
  • mytestマクロの定義

ctestの有効化

enable_testing()

これを行なってcmakeを実行すると各ディレクトリにTestingディレクトリが作成される。 ctestを使用するには必須である。

targetの追加

cmakeではshellスクリプトを実行するターゲットを作成する事ができる。

add_custom_target(short_test COMMAND "ctest" "-R" "TEST_.*_S")

によって

make short_test

は依存する物(build_test)をmakeした後、

ctest -R TEST_.*_S

を実行する。 -Rは名前が正規表現に一致するテストをすべて実行する。 実行ファイル名にSが付いているのはこのためである。 というのも上述のコードは私の数値計算のコードなので、 一秒以内に終るもの(Sort)もあれば、数十分かかる物(Long)もある。

依存関係の追加

make short_test

とした際にbuild_testコンパイルされるようにしたのが、

add_dependencies(short_test build_test)

この行である。 さらにbuild_testが全てのテストに依存するようにしたのが マクロ中の

    add_dependencies(build_test ${test_name})

この行である。 つまり結局

make short_test

とすれば全てのテストがコンパイルされ、実行されるのである。

マクロの定義

macro(mytest name type src libs)
    set(test_name TEST_${name}_${type})
    add_executable(${test_name} ${src})
    target_link_libraries(${test_name} ${libs})
    add_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}/${test_name})
    add_dependencies(build_test ${test_name})
    unset(test_name)
endmacro(mytest)

さていよいよマクロの定義である。 macroは最初の引数にマクロの名前を、 以降の引数にマクロの引数を取る。 setでテスト名を生成し、 add_executableで実行ファイルを生成する。 肝心なのがadd_testで、これがテストを定義している。 第一引数がテストの名前、第二引数がテストの実行ファイルである。 この際実行ファイルのパスに注意する。また追加で引数も追加できる。

上述の通りこれをbuild_testの依存関係に追加する事で、 make short_testで一気にmakeできる。

何故複雑な依存関係を構築したか?

ここまでで読者は不思議に思ったに違いない: 依存関係を設定しなくても全てのテストがmakeされるのではないかと。 その通りである。 しかし、ある設定をしている場合、そうはならない。

add_subdirectory(test EXCLUDE_FROM_ALL)

がその正体である。 これはtestディレクトリ以下を追加するが、 makeではtest以下のexecutableはmakeしない、という指示である。 これにより不要なビルドを避ける事で、 ビルド時間を削減するという恩恵が得られる。 上記の設定はこの環境下で行なったための回避策である。