CでgoogletestとCMakeを使ってみた

C向けにgoogletestを使ってcmakeでビルドするサンプルです。

例は書籍 [Test-Driven Development for Embedded C](https://pragprog.com/book/jgade/test-driven-development-for-embedded-c)／[テスト駆動開発による組み込みプログラミング](https://www.oreilly.co.jp/books/9784873116143/) のLED Driverのスケルトンとして使えます。

ディレクトリ構成

```
build/            ビルド用のディレクトリ
ext/
ext/googletest    googletestのソースコード
inc/              ヘッダ
src/　　　　　　　 ソース
test/　　　　　　　テスト
```

#googletestの準備
gitの初期化とgoogletestの取得を行う。

```
% git init
% mkdir build ext inc src test
% cd ext
% git submodule add https://github.com/google/googletest.git
```

googletest用のcmakeファイルを作成する。

```CMakeLists.txt
project("led driver")
cmake_minimum_required(VERSION 2.8)
add_subdirectory(ext)
```
```ext/CMakeLists.txt
option(BUILD_GTEST "Builds the googletest subproject" ON)
option(BUILD_GMOCK "Builds the googlemock subproject" OFF)
add_subdirectory(googletest)
```

buildディレクトリでビルドする。

```
$ cd build
$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Found PythonInterp: /usr/bin/python (found version "2.7.6")
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sekine/github/leddriver2/build
$ make
Scanning dependencies of target gtest
[ 50%] Building CXX object ext/googletest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
Linking CXX static library libgtest.a
[ 50%] Built target gtest
Scanning dependencies of target gtest_main
[100%] Building CXX object ext/googletest/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
Linking CXX static library libgtest_main.a
[100%] Built target gtest_main
```

ここまでの作業をgit commit しときます。

```
$ cd ..
$ git add CMakeLists.txt ext/CMakeLists.txt
$ git commit -m "setup googletest."
```

#ソースコードの例

```inc/LedDriver.h
#ifndef LEDDRIVER_H
#define LEDDRIVER_H

void LedDriver_Create(uint16_t *address);
void LedDriver_Destroy(void);
void LedDriver_TurnOn(int ledNumber);
void LedDriver_TurnOff(int ledNumber);

#endif
```

```src/LedDriver.c
#include <stdint.h>
#include "LedDriver.h"

static uint16_t *ledsAddress;

void LedDriver_Create(uint16_t *address)
{
    ledsAddress = address;
    *ledsAddress = 0;
}

void LedDriver_Destroy(void)
{
}

void LedDriver_TurnOn(int ledNumber)
{
    *ledsAddress = 1;
}

void LedDriver_TurnOff(int ledNumber)
{
    *ledsAddress = 0;
}
```

```test/LedDriverTest.cpp
#include <stdint.h>
#include "gtest/gtest.h"
extern "C"
{
#include "LedDriver.h"
}

uint16_t virtualleds;

class LedDriver : public ::testing::Test
{
protected:
    virtual void SetUp()
    {
        LedDriver_Create(&virtualleds);
    }
    virtual void TearDown()
    {
        LedDriver_Destroy();
    }
};

TEST_F(LedDriver, LedsOffAfterCreate)
{
    uint16_t virtualleds1 = 0xffff;
    LedDriver_Create(&virtualleds1);
    ASSERT_EQ(0, virtualleds1);
}

TEST_F(LedDriver, TurnOnLedOne)
{
    LedDriver_TurnOn(1);
    ASSERT_EQ(1, virtualleds);
}

TEST_F(LedDriver, TurnOffLedOne)
{
    LedDriver_TurnOn(1);
    LedDriver_TurnOff(1);
    ASSERT_EQ(0, virtualleds);
}
```

CMakeの設定です。

```CMakeLists.txt
project("led driver")

cmake_minimum_required(VERSION 2.8)

add_subdirectory(ext)
add_subdirectory(src)
add_subdirectory(test)

ADD_CUSTOM_TARGET(check test/led_driver_test_app)
```

```src/CMakeLists.txt
add_library(LedDriver LedDriver.c)
target_include_directories(LedDriver PUBLIC ../inc)
```

```test/CMakeLists.txt
add_executable(led_driver_test_app
    LedDriverTest.cpp)
target_link_libraries(led_driver_test_app
    gtest
    gtest_main
    LedDriver)
```

実行例です。

```
$ cd build
$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sekine/github/leddriver2/build
$ make
[ 25%] Built target gtest
[ 50%] Built target gtest_main
[ 75%] Built target LedDriver
[100%] Built target led_driver_test_app
$ make check
Running main() from gtest_main.cc
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from LedDriver
[ RUN      ] LedDriver.LedsOffAfterCreate
[       OK ] LedDriver.LedsOffAfterCreate (0 ms)
[ RUN      ] LedDriver.TurnOnLedOne
[       OK ] LedDriver.TurnOnLedOne (0 ms)
[ RUN      ] LedDriver.TurnOffLedOne
[       OK ] LedDriver.TurnOffLedOne (0 ms)
[----------] 3 tests from LedDriver (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (1 ms total)
[  PASSED  ] 3 tests.
Built target check
$
```