Newer
Older
libkc / modules / test / src / test_list_linked.c
#include <errno.h>

#include <kc.h>
#include <kc_ut.h>
#include <kc_assert.h>
#include <kc_list.h>
#include <kc_memory.h>

#include "ut.h"

// プロトタイプ宣言
static void test_list_linked_new(void);
static void test_list_linked_add(void);
static void test_list_linked_remove(void);
static void test_list_linked_set(void);
static void test_list_linked_search(void);
static void test_list_linked_sort(void);
static void test_list_linked_malloc_error(void);

/**
 * LinkedList 単体テストスイート
 */
void suite_list_linked(void)
{
    KcUt *ut = KcUt_get_instance();
    ut->add(ut, UT_TESTCASE, "list_linked new LinkedList", test_list_linked_new);
    ut->add(ut, UT_TESTCASE, "list_linked add/get", test_list_linked_add);
    ut->add(ut, UT_TESTCASE, "list_linked remove", test_list_linked_remove);
    ut->add(ut, UT_TESTCASE, "list_linked set", test_list_linked_set);
    ut->add(ut, UT_TESTCASE, "list_linked search", test_list_linked_search);
    ut->add(ut, UT_TESTCASE, "list_linked sort", test_list_linked_sort);
    ut->add(ut, UT_TESTCASE, "list_linked malloc error", test_list_linked_malloc_error);
}

/**
 * LinkedList 生成/破棄。
 *
 * @process KcList_new_LinkedList を実行する。。
 * @result LinkedList が生成されること。
 *
 * @process KcList_delete にて LinkedList を破棄する。
 * @result LinkedList が破棄されること。
 */
static void test_list_linked_new(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);
    KcList_delete(list);
}

/**
 * LinkedList データ追加/取得。
 *
 * @process 初回追加 (index = 0)
 * @process 2つめ追加 (index = 1)
 * @process 先頭に追加 (index = 0)
 * @process 末尾に追加 (index = 負値(-1))
 * @process 追加(index 不正)
 * @process 値取得(サイズ取得あり)
 * @process 値取得(サイズ取得なし)
 * @process 値取得 (index 不正[負の値を指定])
 * @process 値取得 (index 不正[サイズ以上を指定])
 */
static void test_list_linked_add(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);

    // 空
    bool is_empty = list->is_empty(list);
    assert_true(is_empty);

    // 1つめ追加
    int val = 10;
    bool res = list->add(list, 0, &val, sizeof(int));
    assert_true(res);
    is_empty = list->is_empty(list);
    assert_false(is_empty);

    // 2つめ追加
    val = 20;
    res = list->add(list, 1, &val, sizeof(int));
    assert_true(res);

    // 先頭に追加
    val = 30;
    res = list->add(list, 0, &val, sizeof(int));
    assert_true(res);

    // 末尾に追加
    val = 40;
    res = list->add(list, -1, &val, sizeof(int));
    assert_true(res);

    // 追加不可位置への追加
    val = 50;
    res = list->add(list, 10, &val, sizeof(int));
    assert_false(res);

    // 1つめ取得
    size_t size;
    int *res_val = list->get(list, 0, &size);
    assert_equals((int)sizeof(int), (int)size);
    assert_equals(30, *res_val);

    // 2つめ取得
    res_val = list->get(list, 1, &size);
    assert_equals((int)sizeof(int), (int)size);
    assert_equals(10, *res_val);

    // 3つめ取得(サイズ取得なし)
    res_val = list->get(list, 2, NULL);
    assert_equals(20, *res_val);

    // 4つめ取得(サイズ取得なし)
    res_val = list->get(list, 3, NULL);
    assert_equals(40, *res_val);

    // 値取得 (index 不正[負の値を指定])
    res_val = list->get(list, -1, NULL);
    assert_null(res_val);

    // 値取得 (index 不正[サイズを指定])
    res_val = list->get(list, 4, NULL);
    assert_null(res_val);

    // 値取得 (index 不正[サイズ以上を指定])
    res_val = list->get(list, 5, NULL);
    assert_null(res_val);

    KcList_delete(list);
}

/**
 * LinkedList データ削除。
 *
 * @process 末尾削除
 * @process 中間削除
 * @process 先頭削除
 * @process 削除(index 不正[負の値を指定])
 * @process 削除(index 不正[サイズ以上を指定])
 * @process 残っているデータを確認
 * @process 容量調整(初期容量→1/4以下)
 */
static void test_list_linked_remove(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);

    // 値設定
    int vals[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160};
    int default_size = 16;
    for (int i = 0; i < (int)(sizeof(vals) / sizeof(int)); i++)
    {
        int val = vals[i];
        bool res = list->add(list, -1, &val, sizeof(int));
        assert_true(res);
    }

    // 末尾削除 (値、サイズ取得あり)
    int rval;
    size_t rsize = sizeof(int);
    bool ret = list->remove(list, (default_size - 1), &rval, &rsize);
    assert_true(ret);
    assert_equals(vals[(default_size - 1)], rval);
    assert_equals((int)sizeof(int), (int)rsize);
    int now_size = list->size(list);
    assert_equals((default_size - 1), now_size);

    // 値を追加して戻しておく
    list->add(list, (default_size - 1), &vals[(default_size - 1)], sizeof(int));
    now_size = list->size(list);
    assert_equals(default_size, now_size);

    // 中間削除 (値取得設定あり、サイズ指定なし [=値取得できない])
    // 10, 20, <30>, 40, 50, ...
    rval = 9999;
    ret = list->remove(list, 2, &rval, NULL);
    assert_true(ret);
    assert_equals(9999, rval); // size 指定なしのため、値は取得できない。
    now_size = list->size(list);
    assert_equals((default_size - 1), now_size);

    // 先頭削除 (値取得なし)
    // <10>, 20, 40, 50, ...
    ret = list->remove(list, 0, NULL, NULL);
    assert_true(ret);
    now_size = list->size(list);
    assert_equals((default_size - 2), now_size);

    // 削除(index 不正[負の値指定])
    ret = list->remove(list, -1, NULL, NULL);
    assert_false(ret);

    // 削除(index 不正[サイズ以上を指定])
    ret = list->remove(list, default_size, NULL, NULL);
    assert_false(ret);

    // 残り3つになるまで削除 (3つめを削除)
    int rest_size = list->size(list);
    while (rest_size > 3)
    {
        ret = list->remove(list, 3, NULL, NULL);
        assert_true(ret);
        rest_size = list->size(list);
    }

    // 残っているデータの確認
    // 20, 40, 50
    int *res_val_1 = list->get(list, 0, NULL);
    int *res_val_2 = list->get(list, 1, NULL);
    int *res_val_3 = list->get(list, 2, NULL);
    assert_equals(20, *res_val_1);
    assert_equals(40, *res_val_2);
    assert_equals(50, *res_val_3);

    bool is_empty = list->is_empty(list);
    assert_false(is_empty);

    // クリア
    list->clear(list);
    is_empty = list->is_empty(list);
    assert_true(is_empty);

    KcList_delete(list);
}

/**
 * LinkedList データ設定。
 *
 * @process 値設定(複数設定)
 * @process 値設定(元の値、サイズ取得)
 * @process 値設定(元の値取得)
 * @process 値設定(不正index[負の値指定])
 * @process 値設定(不正index[サイズ以上指定])
 */
static void test_list_linked_set(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);

    // 値設定
    int vals[] = {10, 20, 30, 40, 50};
    for (int i = 0; i < (int)(sizeof(vals) / sizeof(int)); i++)
    {
        int val = vals[i];
        bool res = list->add(list, -1, &val, sizeof(int));
        assert_true(res);
    }
    int now_size = list->size(list);
    assert_equals(5, now_size);

    // 値設定 (元の値, サイズ取得)
    int set_value = 99;
    int orig_val = 9999;
    size_t orig_size = sizeof(int);
    bool ret = list->set(list, 1, &set_value, sizeof(int), &orig_val, &orig_size);
    assert_true(ret);
    assert_equals(20, orig_val);
    assert_equals((int)sizeof(int), (int)orig_size);
    int *res_val = list->get(list, 1, NULL);
    assert_equals(99, *res_val);

    // 値設定 (元の値, サイズ取得[設定値の方がサイズが大きい])
    set_value = 101;
    int orig_val_array[2];
    orig_size = sizeof(int) * 2;
    ret = list->set(list, 1, &set_value, sizeof(int), orig_val_array, &orig_size);
    assert_true(ret);
    assert_equals(99, orig_val_array[0]);
    assert_equals((int)sizeof(int), (int)orig_size);
    res_val = list->get(list, 1, NULL);
    assert_equals(101, *res_val);

    // 値設定 (元の値取得, サイズ指定なし[=値取得不可])
    set_value = 98;
    orig_val = 9999;
    ret = list->set(list, 0, &set_value, sizeof(int), &orig_val, NULL);
    assert_true(ret);
    assert_equals(9999, orig_val); // size 指定なしのため、値取得不可
    res_val = list->get(list, 0, NULL);
    assert_equals(98, *res_val);

    // 値設定 (サイズ取得 [=値指定なしのためサイズ取得不可])
    set_value = 97;
    orig_size = 0;
    ret = list->set(list, 0, &set_value, sizeof(int), NULL, &orig_size);
    assert_true(ret);
    assert_equals(0, (int)orig_size); // size 取得不可
    res_val = list->get(list, 0, NULL);
    assert_equals(97, *res_val);

    now_size = list->size(list);
    assert_equals(5, now_size);

    // 値設定(不正index[負の値指定])
    set_value = 96;
    ret = list->set(list, -1, &set_value, sizeof(int), NULL, NULL);
    assert_false(ret);

    // 値設定(不正index[サイズ指定])
    set_value = 95;
    ret = list->set(list, 5, &set_value, sizeof(int), NULL, NULL);
    assert_false(ret);

    // 値設定(不正index[サイズ以上指定])
    set_value = 95;
    ret = list->set(list, 6, &set_value, sizeof(int), NULL, NULL);
    assert_false(ret);

    KcList_delete(list);
}

/**
 * LinkedList 検索。
 *
 * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20 } を構築する。
 * @result  リストが構築されること
 *
 * @process is_contains でリストに含まれる値を確認する。
 * @result true が返されること。
 *
 * @process index_of で各インデックス位置を取得する。
 * @result 先頭からのインデックス位置が正しく取得されること。
 *
 * @process last_index_of で各インデックス位置を取得する。
 * @result 末尾からのインデックス位置が正しく取得されること。
 *
 * @process リストに含まれない値を指定して、is_contains を実行する。
 * @result false が返されること。
 *
 * @process リストに含まれない値を指定して、index_of を実行する。
 * @result -1 が返されること。
 *
 * @process リストに含まれない値を指定して、last_index_of を実行する。
 * @result -1 が返されること。
 *
 * @process 異なるサイズ指定して、is_contains を実行する。
 * @result -1 が返されること。
 *
 */
static void test_list_linked_search(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);

    // 値設定
    int vals[] = {10, 30, 50, 40, 20, 10, 30, 60, 20};
    for (int i = 0; i < (int)(sizeof(vals) / sizeof(int)); i++)
    {
        int val = vals[i];
        bool res = list->add(list, -1, &val, sizeof(int));
        assert_true(res);
    }
    int now_size = list->size(list);
    assert_equals(9, now_size);

    // 値が含まれることの確認
    // index_of, last_index_of の確認
    int c_vals[] = {10, 20, 30, 40, 50, 60};
    int c_vals_index[] = {0, 4, 1, 3, 2, 7};
    int c_vals_lindex[] = {5, 8, 6, 3, 2, 7};
    bool is_contains;
    int res_index;
    int res_lindex;
    for (int i = 0; i < (int)(sizeof(c_vals) / sizeof(int)); i++)
    {
        is_contains = list->contains(list, &c_vals[i], sizeof(int));
        assert_true(is_contains);

        res_index = list->index_of(list, &c_vals[i], sizeof(int));
        assert_equals(c_vals_index[i], res_index);

        res_lindex = list->last_index_of(list, &c_vals[i], sizeof(int));
        assert_equals(c_vals_lindex[i], res_lindex);
    }

    // 値が含まれないことの確認
    int c_val = 99;
    is_contains = list->contains(list, &c_val, sizeof(int));
    assert_false(is_contains);

    res_index = list->index_of(list, &c_val, sizeof(int));
    assert_equals(-1, res_index);

    res_lindex = list->last_index_of(list, &c_val, sizeof(int));
    assert_equals(-1, res_lindex);

    // 値が含まれないことの確認(異なるサイズ指定)
    c_val = 10;
    is_contains = list->contains(list, &c_val, sizeof(int) * 2);
    assert_false(is_contains);

    res_index = list->index_of(list, &c_val, sizeof(int) * 2);
    assert_equals(-1, res_index);

    res_lindex = list->last_index_of(list, &c_val, sizeof(int) * 2);
    assert_equals(-1, res_lindex);

    KcList_delete(list);
}

// ソート用コンパレータ
static int test_list_linked_sort_comparator(
    const void *element1, size_t size1,
    const void *element2, size_t size2,
    void *args)
{
    int val1 = *((int *)element1);
    int val2 = *((int *)element2);
    assert_equals((int)sizeof(int), (int)size1);
    assert_equals((int)sizeof(int), (int)size2);
    assert_equals("ABCDEFG", (const char *)args);
    return (val1 - val2);
}

/**
 * LinkedList ソート。
 *
 * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20} を構築する。
 * @result リストが構築されること。
 *
 * @process 昇順用コンパレータを渡し、ソートする。
 * @result リストがソートされること。
 *
 * @process Iterator を取得する。
 * @result Iterator にて順次値が取得できること。
 */
static void test_list_linked_sort(void)
{
    KcList *list = KcList_new_LinkedList();
    assert_not_null(list);

    // 値設定
    int vals[] = {10, 30, 50, 40, 20, 10, 30, 60, 20};
    for (int i = 0; i < (int)(sizeof(vals) / sizeof(int)); i++)
    {
        int val = vals[i];
        bool res = list->add(list, -1, &val, sizeof(int));
        assert_true(res);
    }
    int now_size = list->size(list);
    assert_equals(9, now_size);

    // ソート実施
    list->sort(list, test_list_linked_sort_comparator, "ABCDEFG");
    int sorted_vals[] = {10, 10, 20, 20, 30, 30, 40, 50, 60};

    // Iterator にて値を取得&確認
    KcIterator *ite = list->iterator(list, 0);
    int sorted_index = 0;
    while (ite->hasNext(ite))
    {
        size_t res_size;
        int *res_val = (int *)ite->next(ite, &res_size);
        assert_equals((int)sizeof(int), (int)res_size);
        assert_equals(sorted_vals[sorted_index], *res_val);
        sorted_index++;
    }
    KcIterator_delete(ite);

    // index = 5 より取得
    ite = list->iterator(list, 5);
    sorted_index = 5;
    while (ite->hasNext(ite))
    {
        int *res_val = (int *)ite->next(ite, NULL);
        assert_equals(sorted_vals[sorted_index], *res_val);
        sorted_index++;
    }
    KcIterator_delete(ite);

    KcList_delete(list);
}

/**
 * LinkedList メモリ確保失敗。
 */
static void test_list_linked_malloc_error(void)
{
    // LinkedList のメモリ確保失敗
    ut_alloc_control(0)
    {
        KcList *list = KcList_new_LinkedList();
        assert_null(list);
    }

    // LinkedList の add 時のメモリ確保失敗
    KcList *list = KcList_new_LinkedList();
    ut_alloc_control(0)
    {
        bool ret = list->add(list, 0, "ABC", 4);
        assert_false(ret);
        assert_equals(0, list->size(list));
    }

    // Iterator 生成時のメモリ確保失敗
    ut_alloc_control(0)
    {
        KcIterator *ite = list->iterator(list, 0);
        assert_null(ite);
    }

    // set 時のメモリ確保失敗
    list->add(list, 0, "ABC", 4);
    ut_alloc_control(0)
    {
        list->set(list, 0, "XYZXYZ", 7, NULL, NULL);
        assert_equals("ABC", (const char *)list->get(list, 0, NULL));
    }

    KcList_delete(list);
}