Cpputestでモックなしではテストできないようなシステム関数を置き換えてみました。

ずっとgoogle mockしか知らなかったのですが、最近C言語（≠C++言語）周りを復習して、今更ですが[Cpputest](https://cpputest.github.io/mocking_manual.html)があることを知りました。google mockは随分更新されていないようだし、これからはCpputestなのかな？と入門してみました。

「Linuxのシステム関数（例：statvfs）をモックにしたい」が実現できました。

## セットアップ

[cppUTest のインストール](https://qiita.com/Paragraph_14/items/c83362266003ad5a5079)を拝見してインストールしました。

私は、/usr/localにはインストールせず、$HOME/.localにインストールしています（好みです）。

```bash:インストール
$ cd ~/.local/src
$ git clone git://github.com/cpputest/cpputest.git 
$ cd cpputest
$ cd cpputest_build
$ autoreconf .. -i
$ ../configure --prefix=$HOME/.local
$ make -j4
$ make install
```

## モックを使う目的（今回）

sys/statvfs.h内のstatvfs()関数をモックに置き換えて、コンピュータのディスク容量を入力とする関数を色々テストしたい、というケースを試してみました。

このように、自分（プログラマ）ではどうすることもできない情報を入力とする関数のテストは、モックを使わないとテストができません。

### 例（テスト対象）

```c:テスト対象コード
#include "target.h"
#include <sys/statvfs.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>

/*
    ディスクの残量を取得する
 */
ulong get_disk_space(const char * const disk)
{
    ulong ds = -1;
    struct statvfs stat;

    // 思ったような試験をするためには、制御可能なstatvfsを使う必要がある
    if (statvfs(disk, &stat) == -1) {
        perror(disk); // 例えば本物のstatvfsならこのパスはまず通らない
    } else {
        ds = stat.f_bavail * stat.f_blocks;

    }

    return ds;
}
```

## やってみた

```cpp:テストコード（メイン）
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTestExt/MockSupport.h"
#include <sys/statvfs.h>
#include "target.h"

// テストグループを定義
TEST_GROUP(TestFuncGroup) {
    // 各テストケースの実行直前に呼ばれる仮想メソッド
    TEST_SETUP() {

    }

    // 各テストケースの実行直後に呼ばれる仮想メソッド
    TEST_TEARDOWN() {
        mock().checkExpectations();
        mock().removeAllComparatorsAndCopiers();
        mock().clear(); // Mock資源をクリア
    }
};

class StatvfsCopier : public MockNamedValueCopier
{
public:
    virtual void copy(void* out, const void* in)
    {
        struct statvfs *outp = (struct statvfs*)out;
        const struct statvfs *inp = (const struct statvfs*)in;
        outp->f_blocks = inp->f_blocks;
        outp->f_bavail = inp->f_bavail;
    }
};

// テストを実行するメソッド
TEST(TestFuncGroup, Test1)
{
    #define EXPECT_BLOCKS   12345
    #define EXPECT_BAVAIL   67890

    /* setup */
    StatvfsCopier copier;
    mock().installCopier("StatvfsBuf", copier);
    struct statvfs out;
    out.f_blocks = EXPECT_BLOCKS;
    out.f_bavail = EXPECT_BAVAIL;
    mock().expectOneCall("statvfs")
            .withOutputParameterOfTypeReturning("StatvfsBuf", "buf", &out)
            .andReturnValue(0);

    /* test */
    ulong ds = get_disk_space(".");
    CHECK_EQUAL(EXPECT_BLOCKS*EXPECT_BAVAIL, ds);
}

int main(int argc, char **argv) {
    // テストランナー
    return RUN_ALL_TESTS(argc, argv);
}

```

```c:statvfsを置き換えるモック
#include <sys/statvfs.h>
#include "target.h"

#include "CppUTestExt/MockSupport_c.h"

int statvfs(const char *path, struct statvfs *buf) {
    mock_c()->actualCall("statvfs")
            ->withOutputParameterOfType("StatvfsBuf", "buf", buf);

    return mock_c()->intReturnValue();
}
```

メモリリークは、new/deleteを使用する（C++）ケースだけのよう。malloc/freeは検出してくれず。それでも、statvfsのようなシステム関数をモック化できるのはありがたい。

## 参考

  * [cppUTest のインストール](https://qiita.com/Paragraph_14/items/c83362266003ad5a5079)
  * [./ Cpputest](https://cpputest.github.io/mocking_manual.html)
  * [davisford/bcm2835-mock.c](https://gist.github.com/davisford/3894730)
  * [Gcc 5.3.0 warns about sized operator delete #991](https://github.com/cpputest/cpputest/issues/991)
  * [今回のソース](https://github.com/tacchang001/example_c/tree/master/hello_CppUMock02)