#include <stdio.h>
#include <string.h>

#include <sc_assert.h>
#include <sc_mmgr.h>
#include <sc_error.h>

#include <unittest.h>

/* --------------------------------------------------------------------
 *  定数定義
 * --------------------------------------------------------------------
 */
/* assert エラー時に呼び出す最大関数登録数	*/
#define UT_UNITTEST_FUNC_MAX (4096)
#define UT_OUTPUT            stderr


/**
 * テスト用関数リスト.
 */
typedef struct TestFuncEntry_ {
	const char* funcName;
	void (*testFunc)(void);
} TestFuncEntry;


/* プロトタイプ宣言		*/
static void UT_cleanup(void);
static void UT_checkMemoryLeak(SC_MMgr* mmgr);
static void UT_checkMemoryError(SC_MMgr* mmgr);


/* テスト関数のリスト	*/
static TestFuncEntry testFuncs[UT_UNITTEST_FUNC_MAX];
static int testRunIndex  = 0;
static int testRunSize   = 0;
static int otherFuncSize = 0;


extern void UT_run_kscript_main(void);


/**
 * 単体テスト実行。
 *
 * @param argc 引数の数
 * @param argv コマンド引数
 */
int main(int argc, char* argv[])
{
	UT_run_kscript_main();

	UT_run();

	return 0;
}


/**
 * テスト用関数を追加します.
 *
 * @param testfunc テスト用関数
 */
void UT_add(const char* funcName, void (*testFunc)(void))
{
	testFuncs[testRunSize + otherFuncSize].funcName = funcName;
	testFuncs[testRunSize + otherFuncSize].testFunc = testFunc;
	if (strcmp("OTHER", funcName) == 0)
	{
		otherFuncSize++;
	}
	else
	{
		testRunSize++;
	}
}


/**
 * 単体テストを実行します.
 */
void UT_run(void)
{
	int i;
	SC_setAssertOutput(UT_OUTPUT);
	SC_assertAtExit(UT_cleanup);

	testRunIndex = 0;
	for (i = 0; i < (testRunSize + otherFuncSize); i++)
	{
		SC_MMgr_setHandler(NULL, NULL, UT_checkMemoryError);
		if (strcmp("OTHER", testFuncs[i].funcName) != 0)
		{
			fprintf(UT_OUTPUT, "[No.%05d] %-50s\n",
				(testRunIndex + 1), testFuncs[i].funcName);
			testRunIndex++;
		}
		testFuncs[i].testFunc();
	}

	UT_cleanup();
}


/**
 * 単体試験終了時に実行します.
 */
static
void UT_cleanup(void)
{
	SC_MMgr_entries(UT_checkMemoryLeak);
	fprintf(UT_OUTPUT, "run test case [ %d / %d ]\n",
			testRunIndex, testRunSize);
}


/**
 * メモリリークが発生していないかチェックします。
 */
static
void UT_checkMemoryLeak(SC_MMgr* mmgr)
{
	fprintf(UT_OUTPUT, "%s:%08d (size = %08d byte) memory Leak\n",
			mmgr->file,
			mmgr->line,
			mmgr->size);
}

static
void UT_checkMemoryError(SC_MMgr* mmgr)
{
	const char* errorType;
	int         nowError = SC_getError();
	int         ret;
	switch (nowError)
	{
		case SC_EINVAL: errorType = "memory duplicate free"; break;
		case SC_ENOMEM: errorType = "memory allocate error"; break;
		default: errorType = "other error";
	}

	/* メモリ管理部分 (tests/ut_scmmgr.c) のテストでは
	 * エラーの試験しているため、エラー出力対象外とする
	 */
	ret = strcmp("tests/ut_sc_mmgr.c", mmgr->file);
	if (ret != 0)
	{
		fprintf(UT_OUTPUT, "%s:%08d (size = %08d byte) %s\n",
			mmgr->file,
			mmgr->line,
			mmgr->size,
			errorType);
	}
}

