CMake基礎 #3 - テスト

Last Edited: 1/28/2025

このブログ記事では、CMakeでテストをセットアップする方法ついて紹介します。

DevOps

信頼性の高いプロジェクトを構築するためには、さまざまな入力に対して機能が正しく動作するかを確認するテストを設定する必要があります。 CMakeにはテスト用のいくつかのツールが用意されており、本記事ではそれらを紹介します。

セットアップ

通常、testsディレクトリを設定し、テストを行いたい各機能のテスト用サブディレクトリを格納します。 各テストサブディレクトリには、通常testerと名付けられるテスト用のソースコードと、 テスト用の実行ファイルをビルドするためのCMakeLists.txtファイルを含めます。 以下は、カスタムGraphライブラリを使用した例のファイル構造です。 このライブラリは隣接リストとグラフ探索アルゴリズム(BFSとDFS)を実装しています。 本記事の目的は、BFSとDFSが正しく動作するかをテストすることです。

CMakeLists.txt
main.cc
Graph
tests/
├── CMakeLists.txt
├── bfs/
│   ├── CMakeLists.txt
│   └── tester.cc
├── dfs/
│   ├── CMakeLists.txt
│   └── tester.cc

以下のトップレベルのCMakeLists.txtでは、BFSおよび/またはDFSをテストするオプションを定義し、testsサブディレクトリを含めるか、 または実行ファイルmain.ccのみをビルドするかを選択できるようにしています。

CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
 
project(Project VERSION 1.0)
 
option(TEST_BFS "Testing BFS" ON)
option(TEST_DFS "Testing DFS" ON)
 
add_subdirectory(Graph)
 
if(TEST_BFS OR TEST_DFS)
    add_subdirectory(tests)
endif()
 
if(NOT (TEST_BFS OR TEST_DFS))
    add_executable(${PROJECT_NAME} main.cc)
 
    target_link_libraries(${PROJECT_NAME} Graph)
 
    target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/Graph/include)
endif()

Graphライブラリはテストとメインソースコードの両方で使用することを想定しているため、オプションを確認する前にGraphサブディレクトリを含めます。 このセットアップにより、テストに関連するロジックを他のディレクトリから分離することができます。

CTest

CMakeにはテストプログラムのCTestが含まれており、これを使って簡単にテストを設定・実行できます。使用するには、 testsディレクトリ内のCMakeLists.txtenable_testing()を追加してテストを有効化し、 add_testを使用してテストケースを設定します。以下の例をご覧ください。

tests/CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
enable_testing()
 
if(TEST_BFS)
    add_subdirectory(bfs)
    add_test(NAME "BFS_TEST1" COMMAND bfs_test 2 6 0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
    add_test(NAME "BFS_TEST2" COMMAND bfs_test 0 4 1 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()
 
if(TEST_DFS)
    add_subdirectory(dfs)
    add_test(NAME "DFS_TEST1" COMMAND dfs_test 2 6 0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
    add_test(NAME "DFS_TEST2" COMMAND dfs_test 0 4 1 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()

ここで、bfs_testdfs_testは実行ファイルの名前であり、開始ノード、ターゲットノード、および開始ノードからターゲットノードへのパスが存在するかどうかを受け取ります。 BFSとDFSは、ターゲットノードが開始ノードから到達可能かどうかを正しく判断する必要があります。テストが0を返す場合、CTestは成功として分類し、 それ以外の場合は失敗と見なします。以下にbfs_testにコンパイルされるBFS用のtester.ccの例を示します。

bfs/tester.cc
#include <AdjacencyList.h>
#include <GraphSearch.h>
#include <stdlib.h>
 
using namespace GraphSearch;
 
int main(int argc, char* argv[]) {
 
    AdjacencyList g1(5);
    g1.addEdge(0, 1);
    g1.addEdge(0, 2);
    g1.addEdge(1, 3);
    g1.addEdge(2, 4);
 
    int node1 = atoi(argv[1]);
    int node2 = atoi(argv[2]);
    int expectedResult = atoi(argv[3]);
 
    return (expectedResult == BFS(g1, node1, node2));
}

ここでは、文字列を整数に変換する<stdlib.h>atoiを使用しています。上記のtester.ccは、 必要なGraphライブラリをリンクし、インクルードディレクトリの場所を指定する通常のCMakeLists.txtでビルドできます。 プロジェクトはcmake -S . -B build/およびビルドディレクトリ内でのmakeを使用してビルドされ、 ビルドディレクトリ内にtestsディレクトリが作成されます。

build/testsディレクトリに移動してctest -Nを実行すると、設定したすべてのテストの一覧が表示され、 それをctest -VVで実行できます。正しい実装がGraphに提供されていれば、上記のテストは成功するはずです。 テストは、cmake呼び出し時に-DTEST_BFS=OFFおよび-DTEST_DFS=OFFを含めることで簡単に無効化できます。 その後、ビルドディレクトリ内で./Projectを実行できます。

CDash

CTestを使用して、機能が開発環境で正しく動作することを確認できますが、他の環境でテストを実行する他のユーザーの結果を追跡し、 機能が正しくセットアップされていることを確認したい場合があります。CDashは、これを実現するためのダッシュボードを提供するオープンソースプロジェクトです。 CDashを使用するには独自のCDashサーバーをセットアップするか、CDashの公式ウェブサイトを利用できます。

CDashのダッシュボードでアカウントを登録した後、プロジェクト名、説明、リポジトリの場所など必要な情報を入力して新しいプロジェクトを作成できます。 プロジェクト作成後、プロジェクトセクションのその他のセクションに移動し、CTestConfig.cmakeを確認します。これをコピーして、 プロジェクトディレクトリ内にCTestConfig.cmakeを作成します。

リモートリポジトリに変更をプッシュした後、CMakeLists.txttestsサブディレクトリを追加した後にinclude(CTest)を含め、 プロジェクトをビルドします。その後、build/tests内でctest -D Experimentalを実行すると、ダッシュボードでテスト結果が更新されているのを確認できます。 興味があれば、記事の末尾に引用されているCDashの動画を視聴し、公式ドキュメントを確認することをお勧めします。

結論

この記事では、CTestを使用したテストのセットアップと実行方法を説明し、CDashを簡単に紹介しました。CDashにはリモートリポジトリが必要ですが、 これは別の記事で取り上げます。CMakeに関する一連の記事が、プロジェクト構築の基本を理解する助けになったことを願っています。

リソース