Newer
Older
libkc / modules / test / src / test_memory.c
#include <stdio.h>
#include <string.h>

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

#include "ut.h"

// プロトタイプ宣言
static void test_memory_start(void);
static void test_memory_dump(void);
static void test_memory_set_listener(void);
static void test_memory_entries(void);
static void test_memory_freeif(void);
static void test_memory_aligned(void);
static void test_memory_calloc(void);
static void test_memory_realloc(void);
static void test_memory_raw_xxxxx(void);

/**
 * memory_mark 単体テストスイート
 */
void suite_memory(void)
{
	KcUt *ut = KcUt_get_instance();
	ut->add(ut, UT_TESTCASE, "memory start", test_memory_start);
	ut->add(ut, UT_TESTCASE, "memory dump", test_memory_dump);
	ut->add(ut, UT_TESTCASE, "memory set_listener", test_memory_set_listener);
	ut->add(ut, UT_TESTCASE, "memory entries", test_memory_entries);
	ut->add(ut, UT_TESTCASE, "memory freeif", test_memory_freeif);
	ut->add(ut, UT_TESTCASE, "memory aligned", test_memory_aligned);
	ut->add(ut, UT_TESTCASE, "memory calloc", test_memory_calloc);
	ut->add(ut, UT_TESTCASE, "memory realloc", test_memory_realloc);
	ut->add(ut, UT_TESTCASE, "memory raw_xxxxx", test_memory_raw_xxxxx);
}

/**
 * メモリ管理開始。
 *
 * @process メモリ管理開始(情報詳細出力あり)
 * @process メモリ管理開始(情報詳細出力無し)
 */
static void test_memory_start(void)
{
	// NOP
	// ut.c にて、下記実施
	// KcMemory_start(false);
	// KcMemory_start(true);
}

extern void KcMemory_dump_leak(void);
static bool test_memory_dump_handler(const char *data)
{
	printf("%s", data);
	return true;
}

/**
 * 確保中のメモリ情報出力。
 *
 * @process dump を実行する。
 * @result 確保中のメモリ情報が出力されること。
 *
 * @param dump_leak を実行する。
 * @result メモリリーク情報が出力されること。
 *
 * @process 確保しているメモリがない状態で dump を実行する。
 * @result メモリ情報が出力されないこと。
 *
 * @process 出力用ハンドラを指定してメモリをダンプする。
 * @result ハンドラに対してメモリ情報が渡されること。
 */
static void test_memory_dump(void)
{
	int *val = (int *)malloc(sizeof(int));
	*val = 10;

	printf("---DUMP---\n");
	KcMemory_dump();

	printf("---DUMP LEAK---\n");
	KcMemory_dump_leak();

	free(val);
	printf("---DUMP---\n");
	KcMemory_dump();

	val = (int *)malloc(sizeof(int));
	*val = 20;
	kc_memory_manager->dump(
		test_memory_dump_handler, 16, true, true, 8192);
	free(val);
}

/**
 * リスナ設定(NULL指定)。
 *
 * @process リスナを登録する。(NULL指定)
 * @result リスナが設定されないこと。
 */
static void test_memory_set_listener(void)
{
	KcMemoryListener listener;
	listener.allocate = NULL;
	listener.free = NULL;
	listener.error = NULL;
	kc_memory_manager->set_listener(&listener);
}

// ハンドラ実行用
static int test_memory_entries_handler_counter = 0;
static int test_memory_entries_handler_values[10];
static int test_memory_entries_handler_can_loop = 0;
static bool test_memory_entries_handler(const KcMemoryEntry *entry, void *info)
{
	assert_equals(
		test_memory_entries_handler_values[test_memory_entries_handler_counter],
		*((int *)entry->data));
	test_memory_entries_handler_counter++;
	test_memory_entries_handler_can_loop--;
	assert_equals("X", (const char *)info);
	if (test_memory_entries_handler_can_loop <= 0)
	{
		return false;
	}
	return true;
}
/**
 * メモリエントリハンドラ実行
 */
static void test_memory_entries(void)
{
	int *val1 = (int *)malloc(sizeof(int));
	int *val2 = (int *)malloc(sizeof(int));
	int *val3 = (int *)malloc(sizeof(int));
	*val1 = 123;
	*val2 = 456;
	*val3 = 789;
	test_memory_entries_handler_values[0] = 123;
	test_memory_entries_handler_values[1] = 456;
	test_memory_entries_handler_values[2] = 789;

	// 途中中断パターン
	test_memory_entries_handler_counter = 0;
	test_memory_entries_handler_can_loop = 2;
	kc_memory_manager->entries(test_memory_entries_handler, "X");
	assert_equals(2, test_memory_entries_handler_counter);

	// 全てダンプパターン
	test_memory_entries_handler_counter = 0;
	test_memory_entries_handler_can_loop = 999;
	kc_memory_manager->entries(test_memory_entries_handler, "X");
	assert_equals(3, test_memory_entries_handler_counter);

	free(val1);
	free(val2);
	free(val3);
}

bool test_memory_freeif_handler(const KcMemoryEntry *entry, void *info)
{
	assert_equals("FREEIF", (const char *)info);
	if (strcmp("test_memory_freeif", entry->func) == 0)
	{
		int *data = (int *)entry->data;
		if (*data == 456)
		{
			return true;
		}
	}
	return false;
}

/**
 * 強制メモリ破棄
 */
static void test_memory_freeif(void)
{
	int *val1 = (int *)malloc(sizeof(int));
	int *val2 = (int *)malloc(sizeof(int));
	int *val3 = (int *)malloc(sizeof(int));
	*val1 = 123;
	*val2 = 456;
	*val3 = 789;
	test_memory_entries_handler_values[0] = 123;
	test_memory_entries_handler_values[1] = 456;
	test_memory_entries_handler_values[2] = 789;

	kc_memory_manager->freeif(test_memory_freeif_handler, "FREEIF");
	free(val1);
	// free(val2);
	free(val3);
}

// メモリ確保失敗動作確認用
static int UT_Memory_can_alloc_counter = 0;
static bool UT_Memory_can_alloc(
	KcMemoryEntry *entry, size_t alignment, size_t size,
	KcMemoryMark mark, const char *file, const char *func, int line)
{
	UNUSED_VARIABLE(entry);
	UNUSED_VARIABLE(alignment);
	UNUSED_VARIABLE(size);
	UNUSED_VARIABLE(mark);
	UNUSED_VARIABLE(file);
	UNUSED_VARIABLE(func);
	UNUSED_VARIABLE(line);

	UT_Memory_can_alloc_counter--;
	if (UT_Memory_can_alloc_counter < 0)
	{
		return false;
	}
	return true;
}
/**
 * アライメント指定メモリ確保
 *
 * @process aligned_alloc によりメモリを確保する。
 * @result メモリが確保されること。
 *
 * @process メモリ確保が失敗する状態で、aligned_alloc を実行する。
 * @result メモリ確保に失敗し、NULL が返されること。
 */
static void test_memory_aligned(void)
{
	int *val = (int *)aligned_alloc(sizeof(long long), sizeof(int));
	assert_not_null(val);
	*val = 123;
	free(val);

	UT_Memory_can_alloc_counter = 0;
	_UT_KcMemory_can_alloc = UT_Memory_can_alloc;
	val = (int *)aligned_alloc(sizeof(long long), sizeof(int));
	assert_null(val);
	_UT_KcMemory_can_alloc = NULL;
}

/**
 * calloc によるメモリ確保
 *
 * @process calloc によりメモリを確保する。
 * @result メモリが確保されること。内容がすべて 0 のこと。
 *
 * @process メモリ確保が失敗する状態で、calloc を実行する。
 * @result メモリ確保に失敗し、NULL が返されること。
 */
static void test_memory_calloc(void)
{
	int *val = (int *)calloc(10, sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		assert_equals(0, val[i]);
	}
	free(val);

	UT_Memory_can_alloc_counter = 0;
	_UT_KcMemory_can_alloc = UT_Memory_can_alloc;
	val = (int *)calloc(10, sizeof(int));
	assert_null(val);
	_UT_KcMemory_can_alloc = NULL;
}

/**
 * realloc によるメモリ確保
 *
 * @process NULL を指定して、realloc によりメモリを確保する。
 * @result メモリが確保されること。
 *
 * @process メモリ確保が失敗する状態で、calloc を実行する。
 * @result メモリ確保に失敗し、NULL が返されること。
 */
static void test_memory_realloc(void)
{
	// NULL 指定
	int *val = realloc(NULL, sizeof(int));
	assert_not_null(val);
	*val = 123;
	free(val);

	// 管理外メモリによる realloc
	int *unmng_ptr = raw_malloc(sizeof(int) * 2);
	unmng_ptr[0] = 123;
	unmng_ptr[1] = 456;
	assert_equals(123, unmng_ptr[0]);
	assert_equals(456, unmng_ptr[1]);

	int *mng_ptr = (int *)realloc(unmng_ptr, sizeof(int) * 3);
	assert_null(mng_ptr);
	free(unmng_ptr);
}

/**
 * raw_xxxxx 関数動作確認。
 */
static void test_memory_raw_xxxxx(void)
{
	int *ptr = (int *)raw_malloc(sizeof(int));
	assert_not_null(ptr);
	raw_free(ptr);

	ptr = raw_aligned_alloc(sizeof(int), sizeof(int));
	assert_not_null(ptr);
#if (KC_IS_WINDOWS)
	_aligned_free(ptr);
#else
	raw_free(ptr);
#endif

	ptr = raw_calloc(10, sizeof(int));
	assert_not_null(ptr);

	ptr = raw_realloc(ptr, 20);
	assert_not_null(ptr);
	raw_free(ptr);
}