はじめに
みなさん、こんにちは。今回は CTest について書いていきます。
CTest とは何か?
CTest とは、テストの実行を支援する、いわゆるテストランナーのことで、テストを実行するコマンドを複数登録し、それぞれ実行するというシンプルなものです。しかし、実行するテストの選択・グループ化・並列実行など便利な機能を備えており、テスティングフレームワークや言語を問いません。このため、すべてのテストをCTestを通して行うようにすれば、テストの実行方法が共通化され、テストランナー機能が貧弱なテスティングフレームでのテストも便利に実行することが可能になります。
CTest でのテストは、CMakeLists.txt
にテストの設定を記述し、ビルドディレクトリにてctest
コマンドを実行することにより行います。そのほかにも、CDash といわれるテストサーバーとの連携や、スクリプトファイルによる高度なテスト実行が可能になっているようです。
CTest の有効化
さて、CTest を有効にするためには、enable_testing()
コマンドを利用する必要があります。このコマンドは、プロジェクトルートで実行する必要があり、実行すると各ビルドディレクトリにCTestTestfile.cmake
というテスト用のファイルができます。
cmake_minimum_required(VERSION 3.0.2)
enable_testing()
$ cmake .
project-root/
├── CTestTestfile.cmake
├── ...
└── subdir/
├── CTestTestfile.cmake
└── ...
テストの追加
次に、テストを登録していきます。テストの登録にはadd_test()
コマンドを使用します。定義は下記のとおりになります。
add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] [WORKING_DIRECTORY <dir>])
各引数の意味はそれぞれ下記のようになっています。
NAME
<name>
にはテスト名を指定します。これはテストの識別子となりますので重複することはできません。
COMMAND
<command>
・<arg>...
にはテストを実行するコマンドとその引数を指定します。これらには generator expressions を使用することができます。
CONFIGURATIONS
テスト実行時のテスト構成(Release など)が<config> ...
で指定した値のいずれかの時のみ実行します。
WORKING_DIRECTORY
<dir>
にはテストコマンドの実行ディレクトリを指定します。初期値は、ビルドディレクトリのルートです。また、generator expressions が使用できます。
例えば、以下のように使用します。
add_executable(unit_test hoge_test.cpp foo_test.cpp) # テストを行う実行ファイル unit_test
add_test(
NAME hoge_test # テスト名は hoge_test
COMMAND $<TARGET_FILE:unit_test> --only=mylib::hoge # mylib::hoge というテストのみ実行する
CONFIGURATIONS Release # テスト構成がReleaseのときのみ実行
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/tmp # 実行ディレクトリは、ビルドディレクトリ直下のtmpディレクトリ
)
テストの設定
add_test()
コマンドで追加したテストは、それぞれプロパティを持っており、テストプロパティと呼ばれています。このテストプロパティは、テストの実行に関する設定を行うことができ、これによって柔軟な実行が可能となります。set_property(TEST)
およびset_tests_properties()
コマンドで設定できます。
set_property(TEST hoge_test PROPERTY LABELS hoge)
set_tests_properties(hoge_test PROPERTIES LABELS hoge)
このテストプロパティには様々ありますが、以下のプロパティをよく使うでしょう。
LABELS
プロパティにラベルとよばれるタグを付与することができます。これは、テストしたい実行ファイルを選択するときに使用することができます。また、テスト後にラベルごとに実行時間が集計されます。ラベルは複数持つことができ、リストとして格納されます。
DEPENDS
通常テストの実行順序は、add_test()
コマンドで追加した順となりますが、このプロパティを設定すると、指定されたテストの実行が終了してからテストを実行するようになります。これには複数指定可能で、リストとして格納されます。
ENVIRONMENT
このテストでだけ有効な環境変数を設定します。これは<var-name>=<value>
といった形で指定します。これには複数指定可能で、リストとして格納できます。
RUN_SERIAL
CTest は登録されたテストを並列に実行できますが、このプロパティを設定すると、指定したテストとは並列に実行しなくなります。これには複数指定可能で、リストとして格納されます。
FAIL_REGULAR_EXPRESSION
このプロパティを設定すると、テストの出力が指定した正規表現にマッチする場合は失敗、そうでない場合は成功と判断されます。
PASS_REGULAR_EXPRESSION
このプロパティは、FAIL_REGULAR_EXPRESSION
プロパティと似ていますが、指定した正規表現にマッチする場合は成功、そうでない場合は失敗となります。
SKIP_RETURN_CODE
このプロパティを設定すると、テスト実行コマンドの戻り値が指定した値だった場合は、テストをスキップしたと判断されます。
TIMEOUT
このプロパティを設定すると、テストの実行時間が指定した値を超えると強制的に終了するようになります。値の単位は秒です。
これ以外のプロパティに関しては、cmake-properties(7) — Properties on Tests を参照してください。
テストの実行
テストの実行は、ビルドシステムを生成した後に、ビルドディレクトリでctest
コマンドを使用することで行います。
$ cmake .
$ ctest
また、ビルドシステムが Unix Makefile の場合は、Makefile
にtest
というターゲットが追加されており、これを用いても実行することもできます。この時、ARGS
変数に値を設定するとctest
にオプションを渡せます。
$ cmake .
$ make test ARGS='-V'
ctest
コマンドには様々なオプションがあり、実行するテストの選択や並列実行などを行うことができます。以下は、よく使うと思われるオプションです。
-V, --verbose
詳細実行モードです。このモードでは、テストの出力がすべて表示されます。
--output-on-failure
テストが失敗したときのみ、テストの出力を表示します。
-j <jobs>, --parallel <jobs>
テストを並列に実行します。<jobs>
には、最大の並列数を指定します。CPU のコア数を指定するとよいでしょう。
-R <regex>, --tests-regex <regex>
テスト名が指定した正規表現<regex>
にマッチするテストのみを実行します。
-E <regex>, --exclude-regex <regex>
テスト名が指定した正規表現<regex>
にマッチするテスト以外を実行します。
-L <regex>, --label-regex <regex>
指定した正規表現<regex>
にマッチするラベルに紐づくテストのみを実行します。
-LE <regex>, --label-exclude <regex>
指定した正規表現<regex>
にマッチするラベルに紐づくテスト以外を実行します。
--no-label-summary
ラベルごとの実行時間の集計部分を表示しなくなります。
--timeout <seconds>
テストの実行時間が指定した値<seconds>
を超えると強制的に終了するようになります。値の単位は秒です。このオプションとTIMEOUT
プロパティが同時に指定されると、プロパティの方が優先されます。
これ以外のオプションに関しては、ctest(1) — Options を参照してください。
また注意点として、値をとるオプションを使う場合、短いオプションのオプション名と値の間にはスペースが必要となり、長いオプションのオプション名と値の間は=
ではなくスペースとなります。
$ ctest -j4 # NG
$ ctest -j 4 # OK
$ ctest --parallel=4 # NG
$ ctest --parallel 4 # OK
CTest の使用例
以下は、テスティングフレームワークに Boost.Test を使用しているプロジェクトでの CTest の使用例です。
#ifndef MYLIB_HOGE_HPP
#define MYLIB_HOGE_HPP
namespace mylib {
inline int hoge() {
return 1;
}
}
#endif
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include "hoge.hpp"
BOOST_AUTO_TEST_SUITE(mylib)
BOOST_AUTO_TEST_CASE(mylib) {
BOOST_CHECK_EQUAL(hoge(), 1);
}
BOOST_AUTO_TEST_SUITE_END()
#ifndef MYLIB_FOO_HPP
#define MYLIB_FOO_HPP
namespace mylib {
inline int foo() {
return 2;
}
}
#endif
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include "foo.hpp"
BOOST_AUTO_TEST_SUITE(mylib)
BOOST_AUTO_TEST_CASE(mylib) {
BOOST_CHECK_EQUAL(foo(), 2);
}
BOOST_AUTO_TEST_SUITE_END()
cmake_minimum_required(VERSION 3.0.2)
find_package(Boost REQUIRED)
add_compile_options(-std=gnu++1y)
include_directories(${Boost_INCLUDE_DIRS})
enable_testing()
add_executable(hoge_test hoge_test.cpp)
add_test(NAME hoge COMMAND $<TARGET_FILE:hoge_test>)
set_property(TEST hoge APPEND PROPERTY LABELS ${PROJECT_SOURCE_DIR}/hoge.hpp)
set_property(TEST hoge APPEND PROPERTY LABELS ${PROJECT_SOURCE_DIR}/hoge_test.cpp)
add_executable(foo_test foo_test.cpp)
add_test(NAME foo COMMAND $<TARGET_FILE:foo_test>)
set_property(TEST foo APPEND PROPERTY LABELS ${PROJECT_SOURCE_DIR}/foo.hpp)
set_property(TEST foo APPEND PROPERTY LABELS ${PROJECT_SOURCE_DIR}/foo_test.cpp)
$ cmake .
$ make
$ ctest --no-label-summary
Test project ...
Start 1: foo
1/2 Test #1: foo .............................. Passed 0.01 sec
Start 2: hoge
2/2 Test #2: hoge ............................. Passed 0.01 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 0.02 sec
Boost.Test のテストを CTest で実行することができました。ここで、各テストのLABELS
プロパティに、関連するファイルのパスを設定しているのはテストを素早く行うためです。例えば、Vim でソースコードを編集しているときに、:!ctest -L %
とすると関連するテストだけが実行されます(%
は現在編集しているファイルパス)。
:!ctest -L %
Test project ...
Start 1: foo
1/1 Test #1: foo .............................. Passed 0.02 sec
100% tests passed, 0 tests failed out of 1
...
:!ctest -L %
Test project ...
Start 2: hoge
1/1 Test #2: hoge ............................. Passed 0.04 sec
100% tests passed, 0 tests failed out of 1
...
おわりに
以上、CTest でした。CTest を使うと柔軟にテストを実行できるので便利です。さらに、テスティングフレームワークや言語には依存していないのでテストの実行方法が共通化されます。
明日は、mrk_21 さんの『CMake: カスタムターゲットによるグループ化』です。