/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 :
 * =====================================================================
 *  sc_unittest.c
 *  Copyright (c)  2003 - 2011  sys0tem
 *  LICENSE :
 *	LGPL (GNU Lesser General Public License - Version 3,29 June 2007)
 *	http://www.gnu.org/copyleft/lesser.html
 *	or
 *	EPL (Eclipse Public License - v1.0)
 *	http://www.eclipse.org/legal/epl-v10.html
 * =====================================================================
 */
#include <stdio.h>
#include <string.h>

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

#include <sc_unittest.h>


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


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


/* プロトタイプ宣言		*/
static void SC_Unittest_cleanup(void);
static void SC_Unittest_checkMemoryLeak(SC_MMgr* mmgr);
static void SC_Unittest_checkMemoryError(SC_MMgr* mmgr);


/* テスト関数のリスト	*/
static SC_Unittest_FuncEntry SC_Unittest_testFuncs[SC_UNITTEST_FUNC_MAX];
static int SC_Unittest_testRunIndex  = 0;
static int SC_Unittest_testRunSize   = 0;
static int SC_Unittest_otherFuncSize = 0;


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


/**
 * 単体テストを実行します.
 */
void SC_Unittest_run(void)
{
	int i;
	SC_setAssertOutput(SC_UNITTEST_OUTPUT);
	SC_assertAtExit(SC_Unittest_cleanup);

	SC_Unittest_testRunIndex = 0;
	for (i = 0; i < (SC_Unittest_testRunSize + SC_Unittest_otherFuncSize); i++)
	{
		SC_MMgr_setHandler(NULL, NULL, SC_Unittest_checkMemoryError);
		if (strcmp("OTHER", SC_Unittest_testFuncs[i].funcName) != 0)
		{
			fprintf(SC_UNITTEST_OUTPUT, "[No.%04d] %-50s\n",
				(SC_Unittest_testRunIndex + 1), SC_Unittest_testFuncs[i].funcName);
			SC_Unittest_testRunIndex++;
		}
		SC_Unittest_testFuncs[i].testFunc();
	}

	SC_Unittest_cleanup();
}


/**
 * 単体試験終了時に実行します.
 */
static
void SC_Unittest_cleanup(void)
{
	SC_MMgr_entries(SC_Unittest_checkMemoryLeak);
	fprintf(SC_UNITTEST_OUTPUT, "run test case [ %d / %d ]\n",
			SC_Unittest_testRunIndex, SC_Unittest_testRunSize);
}


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


/**
 * メモリエラーが発生している場合に実行されます。
 *
 * @param mmgr メモリエラーが発生したメモリ管理部
 */
static
void SC_Unittest_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";					 break;
	}

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

