- /**
- * @file kc_ut.c
- * @brief 単体テストモジュール
- * @copyright 2003 - 2024 Nomura Kei
- */
- #include <stdio.h>
- #include <kc_ut.h>
- #include <kc_term.h>
- #include <kc_assert.h>
-
- // 最大登録関数
- #define KC_UT_ENTRY_MAX (16384)
-
- /**
- * テスト用関数リスト。
- */
- typedef struct KcUtEntry_
- {
- UtType type;
- const char *title;
- void (*func)(void);
- } KcUtEntry;
-
- /**
- * テスト管理情報
- */
- typedef struct KcUtInfo_
- {
- int test_counter; //!< 実施テスト数
- int ok_counter; //!< OK 件数
- int ng_counter; //!< NG 件数
- int func_counter; //!< 登録関数の数
- int test_case; //!< テストケース数
- int run_index; //!< 実行関数インデックス
- bool is_ng; //!< NG が発生したか否かの判定用
- KcUtEntry entry[KC_UT_ENTRY_MAX];
- } KcUtInfo;
-
- // プロトタイプ宣言
- static void KcUt_assert_handler(const char *msg);
- static void KcUt_add(KcUt *ut, UtType type, const char *title, void (*func)(void));
- static void KcUt_run(KcUt *ut);
-
- // 公開関数
- FILE *kc_ut_output = NULL;
-
- /**
- * 単体テスト用のインスタンスを取得します。
- *
- * @return 単体テスト用の唯一のインスタンス
- */
- KcUt *
- KcUt_get_instance(void)
- {
- static KcUt instance;
- static KcUtInfo info = {
- .test_counter = 0,
- .ok_counter = 0,
- .ng_counter = 0,
- .func_counter = 0,
- .run_index = 0,
- .test_case = 0,
- .is_ng = false,
- };
- if (instance._info != &info)
- { // 初回のみ初期化する。
- instance.add = KcUt_add;
- instance.run = KcUt_run;
- instance._info = &info;
- kc_ut_output = stderr;
- }
- return &instance;
- }
-
- // 内部関数
-
- /**
- * 単体テスト用 assert ハンドラ。
- *
- * @param msg メッセージ
- */
- static void KcUt_assert_handler(const char *msg)
- {
- KcUt *ut = KcUt_get_instance();
- ((KcUtInfo *)ut->_info)->is_ng = true;
- fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg);
- fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
- fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
- }
-
- /**
- * テストケースまたは、事前処理/事後処理の関数を追加します。
- *
- * @param ut KcUT インスタンス
- * @param type タイプ(UT_TESTCASE/UT_SETUP/UT_TEARDOWN/UT_OTHER)
- * @param title タイトル
- * @param func 追加する関数
- */
- static void KcUt_add(KcUt *ut, UtType type, const char *title, void (*func)(void))
- {
- KcUtInfo *info = (KcUtInfo *)ut->_info;
- info->entry[info->func_counter].type = type;
- info->entry[info->func_counter].title = title;
- info->entry[info->func_counter].func = func;
- info->func_counter++;
- if (type == UT_TESTCASE)
- {
- info->test_case++;
- }
- }
-
- /**
- * テストケースを実行します。
- *
- * @param ut KcUT インスタンス
- */
- static void KcUt_run(KcUt *ut)
- { // assert_handler を単体試験用ハンドラに切り替えておく。
- assert_handler = KcUt_assert_handler;
-
- const char *result;
- KcUtInfo *info = (KcUtInfo *)ut->_info;
- // KcMemory_start(true);
- for (info->run_index = 0; info->run_index < info->func_counter; info->run_index++)
- {
- info->is_ng = false;
- info->entry[info->run_index].func();
- if (info->entry[info->run_index].type == UT_TESTCASE)
- { // テストケースの場合のみエラー判定&結果表示する。
- info->test_counter++;
- if (!info->is_ng)
- { // テスト成功
- info->ok_counter++;
- result = KC_TERM_GRN "OK" KC_TERM_DEF;
- }
- else
- { // テスト失敗
- info->ng_counter++;
- result = KC_TERM_RED "NG" KC_TERM_DEF;
- }
- // 実行結果表示
- fprintf(kc_ut_output,
- KC_TERM_BLD KC_TERM_CYN
- "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n",
- info->test_counter,
- info->entry[info->run_index].title,
- result);
- fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
- fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
- }
- }
- fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n");
- fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter);
- fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter);
- fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter);
- fprintf(kc_ut_output, "\n");
- fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
- fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
- }