Newer
Older
libkc / modules / src / kc_ut.c
  1. /**
  2. * @file kc_ut.c
  3. * @brief 単体テストモジュール
  4. * @copyright 2003 - 2024 Nomura Kei
  5. */
  6. #include <stdio.h>
  7. #include <kc_ut.h>
  8. #include <kc_term.h>
  9. #include <kc_assert.h>
  10.  
  11. // 最大登録関数
  12. #define KC_UT_ENTRY_MAX (16384)
  13.  
  14. /**
  15. * テスト用関数リスト。
  16. */
  17. typedef struct KcUtEntry_
  18. {
  19. UtType type;
  20. const char *title;
  21. void (*func)(void);
  22. } KcUtEntry;
  23.  
  24. /**
  25. * テスト管理情報
  26. */
  27. typedef struct KcUtInfo_
  28. {
  29. int test_counter; //!< 実施テスト数
  30. int ok_counter; //!< OK 件数
  31. int ng_counter; //!< NG 件数
  32. int func_counter; //!< 登録関数の数
  33. int test_case; //!< テストケース数
  34. int run_index; //!< 実行関数インデックス
  35. bool is_ng; //!< NG が発生したか否かの判定用
  36. KcUtEntry entry[KC_UT_ENTRY_MAX];
  37. } KcUtInfo;
  38.  
  39. // プロトタイプ宣言
  40. static void KcUt_assert_handler(const char *msg);
  41. static void KcUt_add(KcUt *ut, UtType type, const char *title, void (*func)(void));
  42. static void KcUt_run(KcUt *ut);
  43.  
  44. // 公開関数
  45. FILE *kc_ut_output = NULL;
  46.  
  47. /**
  48. * 単体テスト用のインスタンスを取得します。
  49. *
  50. * @return 単体テスト用の唯一のインスタンス
  51. */
  52. KcUt *
  53. KcUt_get_instance(void)
  54. {
  55. static KcUt instance;
  56. static KcUtInfo info = {
  57. .test_counter = 0,
  58. .ok_counter = 0,
  59. .ng_counter = 0,
  60. .func_counter = 0,
  61. .run_index = 0,
  62. .test_case = 0,
  63. .is_ng = false,
  64. };
  65. if (instance._info != &info)
  66. { // 初回のみ初期化する。
  67. instance.add = KcUt_add;
  68. instance.run = KcUt_run;
  69. instance._info = &info;
  70. kc_ut_output = stderr;
  71. }
  72. return &instance;
  73. }
  74.  
  75. // 内部関数
  76.  
  77. /**
  78. * 単体テスト用 assert ハンドラ。
  79. *
  80. * @param msg メッセージ
  81. */
  82. static void KcUt_assert_handler(const char *msg)
  83. {
  84. KcUt *ut = KcUt_get_instance();
  85. ((KcUtInfo *)ut->_info)->is_ng = true;
  86. fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg);
  87. fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
  88. fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
  89. }
  90.  
  91. /**
  92. * テストケースまたは、事前処理/事後処理の関数を追加します。
  93. *
  94. * @param ut KcUT インスタンス
  95. * @param type タイプ(UT_TESTCASE/UT_SETUP/UT_TEARDOWN/UT_OTHER)
  96. * @param title タイトル
  97. * @param func 追加する関数
  98. */
  99. static void KcUt_add(KcUt *ut, UtType type, const char *title, void (*func)(void))
  100. {
  101. KcUtInfo *info = (KcUtInfo *)ut->_info;
  102. info->entry[info->func_counter].type = type;
  103. info->entry[info->func_counter].title = title;
  104. info->entry[info->func_counter].func = func;
  105. info->func_counter++;
  106. if (type == UT_TESTCASE)
  107. {
  108. info->test_case++;
  109. }
  110. }
  111.  
  112. /**
  113. * テストケースを実行します。
  114. *
  115. * @param ut KcUT インスタンス
  116. */
  117. static void KcUt_run(KcUt *ut)
  118. { // assert_handler を単体試験用ハンドラに切り替えておく。
  119. assert_handler = KcUt_assert_handler;
  120.  
  121. const char *result;
  122. KcUtInfo *info = (KcUtInfo *)ut->_info;
  123. // KcMemory_start(true);
  124. for (info->run_index = 0; info->run_index < info->func_counter; info->run_index++)
  125. {
  126. info->is_ng = false;
  127. info->entry[info->run_index].func();
  128. if (info->entry[info->run_index].type == UT_TESTCASE)
  129. { // テストケースの場合のみエラー判定&結果表示する。
  130. info->test_counter++;
  131. if (!info->is_ng)
  132. { // テスト成功
  133. info->ok_counter++;
  134. result = KC_TERM_GRN "OK" KC_TERM_DEF;
  135. }
  136. else
  137. { // テスト失敗
  138. info->ng_counter++;
  139. result = KC_TERM_RED "NG" KC_TERM_DEF;
  140. }
  141. // 実行結果表示
  142. fprintf(kc_ut_output,
  143. KC_TERM_BLD KC_TERM_CYN
  144. "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n",
  145. info->test_counter,
  146. info->entry[info->run_index].title,
  147. result);
  148. fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
  149. fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
  150. }
  151. }
  152. fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n");
  153. fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter);
  154. fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter);
  155. fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter);
  156. fprintf(kc_ut_output, "\n");
  157. fprintf(stderr, KC_TERM_DEF KC_TERM_CLR);
  158. fprintf(stdout, KC_TERM_DEF KC_TERM_CLR);
  159. }