(続) Google Testを導入してみた

以前投稿した[Google Testを導入してみた](https://qiita.com/y-vectorfield/items/6238cfd2d9c34aefe364)という記事をではGoogle Testをローカルにインストールして実行する方法を試みました。今回は[Google Test勉強録 (1) CMakeでのビルド](https://qiita.com/ktrmnm/items/667a7b7c93cd3fb78419#323-cmake%E3%81%AEexternalproject%E3%81%A8%E3%81%97%E3%81%A6%E6%89%B1%E3%81%86)という記事で紹介されている外部プロジェクトとしてダウンロードしてテストを実行する方法を最新版のGoogle Testで試みたいと思います。

# 実施の動機
* 複数人で開発を行う時にGoogle Testのバージョンの違いを考慮しなくても済む様にしたい。
* ローカルに置くスタイルはHomebrewとの相性が良くないため @ macOS

# インストールを試した環境
前回の記事とは検証環境が一部異なります。

* Ubuntu 18.04.2 LTS
* macOS Mojave 10.14.6
* ビルドに使用するCMakeはVer3.10.x以降のバージョンが必要です。

:pensive: Ubuntu 16.04.xはVer3.5までの対応となります。
:sweat_smile: macOSの場合、(ビルド時に)別途追加設定が必要になります。

CMakeのバージョンの確認方法

```bash
$ cmake /V
cmake version 3.15.3
```

現段階で上記どちらのOSでも下記の方法でインストール、サンプルプログラムの実行まで出来ました。因みに前回の記事ではGoogle Test Ver1.8.xが最新版でしたが、現在はGoogle Test Ver1.10.xが最新版です。

# 前準備 : CMakeのインストール(念のため記載)
ソースコードのビルドに必要なCMakeのインストールをまず行います。

## Ubuntuの場合

```bash
sudo apt-get install cmake
```

## macOSの場合([Homebrew](https://brew.sh/index_ja)を利用)

```bash
brew install cmake
```

# 前準備2 : gcc, g++最新版のインストール

## Ubuntuの場合

```bash
sudo apt-get update && sudo apt-get install build-essential
```

## macOSの場合([Homebrew](https://brew.sh/index_ja)を利用)
Google TestはC++11をベースに実装されている様です。故に、macOSデフォルトのgccやg++(試した環境ではVer4.2)ではビルドエラーが発生し、ビルドすることが出来ません。(∵ C++11で導入された新しい構文等に対応していないため)

```bash
brew install gcc
```

```bash
ln -s /usr/local/bin/gcc-9 /usr/local/bin/gcc
```

```bash
ln -s /usr/local/bin/g++-9 /usr/local/bin/g++
```

~/.bash_profileに以下を記載し、有効化します。

```bash
export PATH=$PATH:/usr/local/bin
```

```bash
. ~/.bash_profile
```

これで、gcc, g++双方とも最新版が利用出来ます。(試したmacOSの環境ではVer9.x)

# テストプロジェクトの作成

今回は以下の様なテストプロジェクトを用意しました。

```bash

test_project/ 
├── cmake
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   ├── sample1.cpp
│   └── sample1.h
└── test
    ├── CMakeLists.txt
    └── test_sample1.cpp
```

## 各ディレクトリの作成

```bash
mkdir test_project
```

```bash
cd test_googletest_project
```

```bash
mkdir src test cmake
```

## テストソースの作成
以下の様なサンプルソースを用意しました。[Google Testの公式チュートリアル](https://github.com/google/googletest/tree/master/googletest/samples)から作成したサンプルソースです。(一部のソースは前回の記事でも利用したものです)

```cmake:CMakelists.txt
cmake_minimum_required(VERSION 3.10)
project(Sample1)
enable_testing()
add_subdirectory(src)
add_subdirectory(test)
```

```cmake:src/CMakelists.txt
cmake_minimum_required(VERSION 3.10)
add_library(Sample1 STATIC sample1.cpp)
```
```cpp:src/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;
}
```

```cpp:src/sample1.h
#ifndef GTEST_SAMPLES_SAMPLE1_H_
#define GTEST_SAMPLES_SAMPLE1_H_

int Factorial(int n);

bool IsPrime(int n);

#endif
```

```cmake:test/CMakelists.txt
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 さんから御教授頂いた点を追加しました。御教授ありがとう御座いました。

```cpp:test/test_sample1.cpp
#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でビルドを行うための準備をします。プロジェクトのルートディレクトリ下にビルド用ディレクトリを作成します。

```bash
mkdir build
```

以下の様なディレクトリ構造となりました。

```
test_project/
├── CMakelists.txt
├── build
├── cmake
├── src
│   ├── CMakelists.txt
│   ├── sample1.cpp
│   └── sample1.h
└── test
    ├── CMakelists.txt
    └── test_sample1.cpp
```

## [DownloadProject](https://github.com/Crascit/DownloadProject)をGitHubからクローン
GitHubからDownloadProjectをクローンします。これはCMake実行時に指定した外部プロジェクト(ここではライブラリ群など)をGitHubからクローンしてくれるツールです。

```bash
git clone https://github.com/Crascit/DownloadProject.git cmake/DownloadProject
```

`DownloadProject/DownloadProject.cmake`と`DownloadProject/DownloadProject.CMakeLists.cmake.in`のみビルドに必要なので、この２種類のファイルのみを取り出してプロジェクトに格納するという方法でも良いと思います。test/CMakelists.txtは以下を参考に修正して下さい。

<details><summary>修正版 : test/CMakelists.txt</summary><div>

```cmake: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)
```

</div></details>

## ソースコードのビルド及びGoogle Testによるテストの実行
以下のコマンドを実行し、Google Testによるテストを実行します。

```bash
cd build
```

Ubuntuの場合は以下のコマンドを実行します。

```bash
cmake ..
```

macOSはgcc, g++のパス情報をオプションで渡す必要が有ります。オプションを渡さずに実行するとデフォルトのgcc, g++(試した環境ではVer4.2)がコンパイラとして認識されてしまいます。

```bash
cmake -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++ ..
```

オプション付きのcmakeのコマンドを.bashrcにaliasとして登録しておけば、つらつらとオプションを入力しなくても済みます。

```bash
alias cmake='cmake -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++'
```

以下の二種類の結果を比較するとCのコンパイラやC++のコンパイラのPathが異なっていることが分かります。

<details><summary>オプションを渡さず実行した場合 @ macOS</summary><div>

```
$ 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
```

</div></summary>

<details><summary>オプションを渡して実行した場合 @ macOS</summary><div>

```
$ 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
```

</div></details>

```
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
```

<details><summary>cmakeでオプションを渡さずに実行した場合 @ macOS</summary><div>

以下の様なエラー終了になります。

```
$ 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
```
</div></details>


```
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