以前投稿したGoogle Testを導入してみたという記事をではGoogle Testをローカルにインストールして実行する方法を試みました。今回はGoogle Test勉強録 (1) CMakeでのビルドという記事で紹介されている外部プロジェクトとしてダウンロードしてテストを実行する方法を最新版のGoogle Testで試みたいと思います。
実施の動機
- 複数人で開発を行う時にGoogle Testのバージョンの違いを考慮しなくても済む様にしたい。
- ローカルに置くスタイルはHomebrewとの相性が良くないため @ macOS
インストールを試した環境
前回の記事とは検証環境が一部異なります。
- Ubuntu 18.04.2 LTS
- macOS Mojave 10.14.6
- ビルドに使用するCMakeはVer3.10.x以降のバージョンが必要です。
Ubuntu 16.04.xはVer3.5までの対応となります。
macOSの場合、(ビルド時に)別途追加設定が必要になります。
CMakeのバージョンの確認方法
$ cmake /V
cmake version 3.15.3
現段階で上記どちらのOSでも下記の方法でインストール、サンプルプログラムの実行まで出来ました。因みに前回の記事ではGoogle Test Ver1.8.xが最新版でしたが、現在はGoogle Test Ver1.10.xが最新版です。
前準備 : CMakeのインストール(念のため記載)
ソースコードのビルドに必要なCMakeのインストールをまず行います。
Ubuntuの場合
sudo apt-get install cmake
macOSの場合(Homebrewを利用)
brew install cmake
前準備2 : gcc, g++最新版のインストール
Ubuntuの場合
sudo apt-get update && sudo apt-get install build-essential
macOSの場合(Homebrewを利用)
Google TestはC++11をベースに実装されている様です。故に、macOSデフォルトのgccやg++(試した環境ではVer4.2)ではビルドエラーが発生し、ビルドすることが出来ません。(∵ C++11で導入された新しい構文等に対応していないため)
brew install gcc
ln -s /usr/local/bin/gcc-9 /usr/local/bin/gcc
ln -s /usr/local/bin/g++-9 /usr/local/bin/g++
~/.bash_profileに以下を記載し、有効化します。
export PATH=$PATH:/usr/local/bin
. ~/.bash_profile
これで、gcc, g++双方とも最新版が利用出来ます。(試したmacOSの環境ではVer9.x)
テストプロジェクトの作成
今回は以下の様なテストプロジェクトを用意しました。
test_project/
├── cmake
├── CMakeLists.txt
├── src
│ ├── CMakeLists.txt
│ ├── sample1.cpp
│ └── sample1.h
└── test
├── CMakeLists.txt
└── test_sample1.cpp
各ディレクトリの作成
mkdir test_project
cd test_googletest_project
mkdir src test cmake
テストソースの作成
以下の様なサンプルソースを用意しました。Google Testの公式チュートリアルから作成したサンプルソースです。(一部のソースは前回の記事でも利用したものです)
cmake_minimum_required(VERSION 3.10)
project(Sample1)
enable_testing()
add_subdirectory(src)
add_subdirectory(test)
cmake_minimum_required(VERSION 3.10)
add_library(Sample1 STATIC sample1.cpp)
#include "sample1.h"
int Factorial(int n){
int result = 1;
for(int i = 1; i <= n; i++){
result *= i;
}
return result;
}
bool IsPrime(int n){
if(n <= 1) return false;
if(n % 2 == 0) return n == 2;
for(int i = 3; ; i += 2){
if(i > n/i) break;
if(n % i == 0) return false;
}
return true;
}
#ifndef GTEST_SAMPLES_SAMPLE1_H_
#define GTEST_SAMPLES_SAMPLE1_H_
int Factorial(int n);
bool IsPrime(int n);
#endif
cmake_minimum_required(VERSION 3.10)
include(${PROJECT_SOURCE_DIR}/cmake/DownloadProject/DownloadProject.cmake)
download_project(PROJ googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
UPDATE_DISCONNECTED 1
)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
add_executable(TestSample1 test_sample1.cpp)
target_link_libraries(TestSample1 Sample1 gtest_main)
include_directories(${PROJECT_SOURCE_DIR}/src)
# C++11をオプションに加える
target_compile_features(TestSample1 PUBLIC cxx_std_11)
include(GoogleTest)
gtest_add_tests(TARGET TestSample1)
- 2019/10/11訂正: @shohirose さんから御教授頂いた点を追加しました。御教授ありがとう御座いました。
#include <limits.h>
#include <gtest/gtest.h>
#include "sample1.h"
namespace{
TEST(FactorialTest, Negative){
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
}
TEST(FactorialTest, Zero){
EXPECT_EQ(1, Factorial(0));
}
TEST(FactorialTest, Positive){
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
TEST(IsPrimeTest, Negative){
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
}
TEST(IsPrimeTest, Trivial){
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
}
TEST(IsPrimeTest, Positive){
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
}
ビルドの準備
cmake, makeでビルドを行うための準備をします。プロジェクトのルートディレクトリ下にビルド用ディレクトリを作成します。
mkdir build
以下の様なディレクトリ構造となりました。
test_project/
├── CMakelists.txt
├── build
├── cmake
├── src
│ ├── CMakelists.txt
│ ├── sample1.cpp
│ └── sample1.h
└── test
├── CMakelists.txt
└── test_sample1.cpp
DownloadProjectをGitHubからクローン
GitHubからDownloadProjectをクローンします。これはCMake実行時に指定した外部プロジェクト(ここではライブラリ群など)をGitHubからクローンしてくれるツールです。
git clone https://github.com/Crascit/DownloadProject.git cmake/DownloadProject
DownloadProject/DownloadProject.cmake
とDownloadProject/DownloadProject.CMakeLists.cmake.in
のみビルドに必要なので、この2種類のファイルのみを取り出してプロジェクトに格納するという方法でも良いと思います。test/CMakelists.txtは以下を参考に修正して下さい。
修正版 : test/CMakelists.txt
cmake_minimum_required(VERSION 3.10)
include(${PROJECT_SOURCE_DIR}/cmake/DownloadProject.cmake)
download_project(PROJ googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
UPDATE_DISCONNECTED 1
)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
add_executable(TestSample1 test_sample1.cpp)
target_link_libraries(TestSample1 Sample1 gtest_main)
include_directories(${PROJECT_SOURCE_DIR}/src)
include(GoogleTest)
gtest_add_tests(TARGET TestSample1)
ソースコードのビルド及びGoogle Testによるテストの実行
以下のコマンドを実行し、Google Testによるテストを実行します。
cd build
Ubuntuの場合は以下のコマンドを実行します。
cmake ..
macOSはgcc, g++のパス情報をオプションで渡す必要が有ります。オプションを渡さずに実行するとデフォルトのgcc, g++(試した環境ではVer4.2)がコンパイラとして認識されてしまいます。
cmake -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++ ..
オプション付きのcmakeのコマンドを.bashrcにaliasとして登録しておけば、つらつらとオプションを入力しなくても済みます。
alias cmake='cmake -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++'
以下の二種類の結果を比較するとCのコンパイラやC++のコンパイラのPathが異なっていることが分かります。
オプションを渡さず実行した場合 @ macOS
$ cmake ..
-- The C compiler identification is AppleClang 11.0.0.11000020
-- The CXX compiler identification is AppleClang 11.0.0.11000020
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Downloading/updating googletest
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/User/google-test-download-test/test_project/build/googletest-download
Scanning dependencies of target googletest-download
[ 11%] Creating directories for 'googletest-download'
[ 22%] Performing download step (git clone) for 'googletest-download'
Cloning into 'googletest-src'...
Already on 'master'
Your branch is up to date with 'origin/master'.
[ 33%] No patch step for 'googletest-download'
[ 44%] Skipping update step for 'googletest-download'
[ 55%] No configure step for 'googletest-download'
[ 66%] No build step for 'googletest-download'
[ 77%] No install step for 'googletest-download'
[ 88%] No test step for 'googletest-download'
[100%] Completed 'googletest-download'
[100%] Built target googletest-download
-- Found PythonInterp: /Users/User/anaconda3/bin/python (found version "3.7.3")
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/User/google-test-download-test/test_project/build
オプションを渡して実行した場合 @ macOS
$ cmake -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++ ..
-- The C compiler identification is GNU 9.2.0
-- The CXX compiler identification is GNU 9.2.0
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
-- Check for working C compiler: /usr/local/bin/gcc
-- Check for working C compiler: /usr/local/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Checking whether CXX compiler has -isysroot
-- Checking whether CXX compiler has -isysroot - yes
-- Checking whether CXX compiler supports OSX deployment target flag
-- Checking whether CXX compiler supports OSX deployment target flag - yes
-- Check for working CXX compiler: /usr/local/bin/g++
-- Check for working CXX compiler: /usr/local/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Downloading/updating googletest
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/yuhkiyano/developing/google-test-download-test/test_project/build/googletest-download
Scanning dependencies of target googletest-download
[ 11%] Creating directories for 'googletest-download'
[ 22%] Performing download step (git clone) for 'googletest-download'
Cloning into 'googletest-src'...
Already on 'master'
Your branch is up to date with 'origin/master'.
[ 33%] No patch step for 'googletest-download'
[ 44%] Skipping update step for 'googletest-download'
[ 55%] No configure step for 'googletest-download'
[ 66%] No build step for 'googletest-download'
[ 77%] No install step for 'googletest-download'
[ 88%] No test step for 'googletest-download'
[100%] Completed 'googletest-download'
[100%] Built target googletest-download
-- Found PythonInterp: /Users/yuhkiyano/anaconda3/bin/python (found version "3.7.3")
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/yuhkiyano/developing/google-test-download-test/test_project/build
make
正常にビルドが完了した場合、以下の様に表示されます。
$ make
Scanning dependencies of target Sample1
[ 8%] Building CXX object src/CMakeFiles/Sample1.dir/sample1.cpp.o
[ 16%] Linking CXX static library libSample1.a
[ 16%] Built target Sample1
Scanning dependencies of target gtest
[ 25%] Building CXX object googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 33%] Linking CXX static library ../../lib/libgtest.a
[ 33%] Built target gtest
Scanning dependencies of target gtest_main
[ 41%] Building CXX object googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[ 50%] Linking CXX static library ../../lib/libgtest_main.a
[ 50%] Built target gtest_main
Scanning dependencies of target TestSample1
[ 58%] Building CXX object test/CMakeFiles/TestSample1.dir/test_sample1.cpp.o
[ 66%] Linking CXX executable TestSample1
[ 66%] Built target TestSample1
Scanning dependencies of target gmock
[ 75%] Building CXX object googletest-build/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o
[ 83%] Linking CXX static library ../../lib/libgmock.a
[ 83%] Built target gmock
Scanning dependencies of target gmock_main
[ 91%] Building CXX object googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o
[100%] Linking CXX static library ../../lib/libgmock_main.a
[100%] Built target gmock_main
cmakeでオプションを渡さずに実行した場合 @ macOS
以下の様なエラー終了になります。
$ make
...
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
make[2]: *** [test/CMakeFiles/TestSample1.dir/test_sample1.cpp.o] Error 1
make[1]: *** [test/CMakeFiles/TestSample1.dir/all] Error 2
make: *** [all] Error 2
make test
無事テストが通ると以下の様に表示されます。
$ make test
Running tests...
Test project /Users/yuhkiyano/developing/google-test-download-test/test_project/build
Start 1: FactorialTest.Negative
1/6 Test #1: FactorialTest.Negative ........... Passed 0.03 sec
Start 2: FactorialTest.Zero
2/6 Test #2: FactorialTest.Zero ............... Passed 0.01 sec
Start 3: FactorialTest.Positive
3/6 Test #3: FactorialTest.Positive ........... Passed 0.01 sec
Start 4: IsPrimeTest.Negative
4/6 Test #4: IsPrimeTest.Negative ............. Passed 0.01 sec
Start 5: IsPrimeTest.Trivial
5/6 Test #5: IsPrimeTest.Trivial .............. Passed 0.01 sec
Start 6: IsPrimeTest.Positive
6/6 Test #6: IsPrimeTest.Positive ............. Passed 0.01 sec
100% tests passed, 0 tests failed out of 6
Total Test time (real) = 0.08 sec
まとめ
本記事ではGoogle Testを外部プロジェクトとして取り込む方法に挑戦しました。今後はMakefileによる自動化にも挑戦したいと思います。OSSの常とはいえ、前回の記事で試したUbuntu16.04.xでエラーが続出して苦戦したり、macOSのCMakeのオプション渡しによるエラー回避等トラブル解決に苦戦させられた挑戦となりました。
Reference
https://qiita.com/ktrmnm/items/667a7b7c93cd3fb78419
https://github.com/Crascit/DownloadProject
https://qiita.com/wawawa/items/50c2c612b0937f28d92b
https://qiita.com/tamorieeeen/items/94c9eac62093f039dab6
https://qiita.com/yoyomion/items/7974bb093038c21db26d
https://stackoverflow.com/questions/24380456/how-can-i-make-cmake-use-gcc-instead-of-clang-on-mac-os-x