cutterを使ってみよう

# 動機
別の記事を書くのに必要だったので。
まずはとりあえず、記事を書くためにC用のユニットテストフレームワークを探していたら国産で見つけたのでトライしてみんとするなり。
作りたいものはCSVのパーサーです。

# Step.1 インストール
[公式ページ](http://cutter.sourceforge.net/reference/ja/install.html)  に書いてありますから・・・なお、私は
```brew install cutter```
で済ませました。

# Step.2 テスト実行
今回のテスト対象は1行読み込み、「,」を\0に置き換え、それぞれ、先頭のポインタを保持する配列を作る関数を作ることです。

イメージ的にはこんな感じ

```
入力: "A,B,C"

作成するデータ:
データ: ['A', '\0', 'B', '\0', 'C', '\0']
ポインタ配列: array = [0, 2, 4]
```
こうすることでarray[2]とすることで"C"が取れるようになるわけです。

## Step 2.0 構成
その前に構成を次のようにします。

```
-+-src
 +-test
 +-obj
```

# Step.2.1 テストを書く

テスト対象はread_recordという関数です。
引数は順に

1. 入力文字
2. 配列
3. 配列が収容できる最大のサイズ
4. データ格納配列
5. データ格納配列が収容できる最大のサイズ

です。

```C:test/t_record_read.c
#include <cutter.h>
#include <string.h>
#include "record_read.h"

#define BUF_LEN 4096
void test_record_read(void) {
    char *test = "A,B,C";
    char *result[BUF_LEN];
    char result_store[BUF_LEN];

    csv_read_result ret = record_read(test, result, BUF_LEN, result_store, BUF_LEN);

    cut_assert_not_null(result);
    cut_assert_equal_int(ret, 0);
    cut_assert(strcmp(result[0], "A"));
    cut_assert(strcmp(result[1], "B"));
    cut_assert(strcmp(result[2], "C"));
}
```

さて、ここで私は詰まりました。チュートリアルにあるAutotoolの使い方を知らないからです。
勉強しなければならないのですが、とりあえず、コンパイルして・・・と力技で行くことにしました(最悪)。

## Step 2.1 テスト対象関数の作成

とりあえず、ヘッダを作成します。

```C:csv_read.h
#ifndef __CSV_READER_H__
#define __CSV_READER_H__
typedef enum csv_read_result {
    csv_read_normal = 0,
    csv_read_destination_is_not_enough = -1,
    csv_read_destination_is_null = -2,
    csv_read_array_is_null = -3,
    csv_read_array_is_not_enough = -4
} csv_read_result;

/**
 * @brief create string array from src by csv
 *
 * @param[in]   src input string
 * @param[out]  array output array of string
 * @param[in]   array_len length of array
 * @param[out]  dst store output array
 * @param[in]   len length of dst
 * @retval 0 normal end
 * @retval -1 not enough allocated to input to dst
 * @retval -2 dst is NULL
 */
csv_read_result record_read(char* src, char** array, int array_len, char* dst, int len);
#endif
```

えーっと、csv_read_resultはintと読み替えてください。なんか、書き方が変なんです。僕。
テスト対象コードも一緒に作っちゃいます。当然失敗する内容です。

```C:record_read.c
#include <stdio.h>
#include <stdlib.h>
#include "record_read.h"

csv_read_result record_read(char* src, char** array, int array_len, char* dst, int len) {
    return csv_read_normal;
}
```

で、コンパイルします。

```shell-session
#compile objects
cc -c src/record_read.c -o obj/record_read.o

cc -shared -o librecord_read.so obj/record_read.o

#compile test
cc -shared -I /usr/local/include/cutter -lcutter -I src -L . -lrecord_read -o test/libt_record_read.so test/t_record_read.c
```

## 2.2 実行

で、実行。

```
 cutter test/

!
===============================================================================
Crash: test_record_read
===============================================================================


Finished in 0.533029 seconds (total: 0.532630 seconds)

1 test(s), 2 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
0% passed
```

は・・・はやい・・・
2個目のassertionで失敗しているので、いい感じかもしれません。ただ、クラッシュさせているのはよくないですよね・・・

## 2.3 修正

で、通るように修正です。

```C:csv_reader.c
#include <stdio.h>
#include <stdlib.h>
#include "record_read.h"

csv_read_result record_read(char* src, char** array, int array_len, char* dst, int len) {
    int index = 0; //array index
    int i;
    if ( dst == NULL )  return csv_read_destination_is_null;
    if ( array == NULL ) return csv_read_array_is_null;

    array[0] = dst;
    index++;
    for( i = 0; i < len; i++ ) {
        if (src[i] == '\0') break;
        if (src[i] == ',') {
            dst[i] = '\0';
            // check array length is nough
            if ( index >= array_len ) {
                return csv_read_array_is_not_enough;
            }
            array[index] = dst + i;
            index++;
        }
        dst[i] = src[i];
    }
    if ( i == len ) {
        dst[i] = '\0';
        return csv_read_destination_is_not_enough;
    }
    return csv_read_normal;
}
```

再度コンパイルして実行。

```shell-session
$ sh first_compie_and_test.sh
.

Finished in 0.000625 seconds (total: 0.000122 seconds)

1 test(s), 5 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed
```

まずはOKですかね。