summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2018-12-12 12:55:36 +0800
committerGitHub <[email protected]>2018-12-12 12:55:36 +0800
commitdcf7bce2cc5d04bcb006b84a27a5498f42268184 (patch)
tree142d000d78e3b874b7bdd2313388b00a3ddf1000 /components
parenta5b22a5b060f48798865d88c230349717338da02 (diff)
parentbce7f85907a3ef233111ca6799228b1598017bdc (diff)
Merge pull request #2041 from zhaojuntao/utest-dev
[components][utilities] 增加 utest(单元测试框架)
Diffstat (limited to 'components')
-rw-r--r--components/utilities/Kconfig4
-rw-r--r--components/utilities/utest/SConscript8
-rw-r--r--components/utilities/utest/utest.c238
-rw-r--r--components/utilities/utest/utest.h159
-rw-r--r--components/utilities/utest/utest_assert.h58
-rw-r--r--components/utilities/utest/utest_log.h31
6 files changed, 498 insertions, 0 deletions
diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig
index e830d94578..8e43cfa0a0 100644
--- a/components/utilities/Kconfig
+++ b/components/utilities/Kconfig
@@ -230,4 +230,8 @@ config RT_USING_ULOG
sfotware module version number
endif
+config RT_USING_UTEST
+ bool "Enable utest (RT-Thread test framework)"
+ default n
+
endmenu
diff --git a/components/utilities/utest/SConscript b/components/utilities/utest/SConscript
new file mode 100644
index 0000000000..dc77bc2591
--- /dev/null
+++ b/components/utilities/utest/SConscript
@@ -0,0 +1,8 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+group = DefineGroup('utest', src, depend = ['RT_USING_UTEST'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/utilities/utest/utest.c b/components/utilities/utest/utest.c
new file mode 100644
index 0000000000..0f5a206d3c
--- /dev/null
+++ b/components/utilities/utest/utest.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-19 MurphyZhao the first version
+ */
+
+#include "utest.h"
+#include <rtthread.h>
+#include <finsh.h>
+
+#undef DBG_SECTION_NAME
+#undef DBG_LEVEL
+#undef DBG_COLOR
+#undef DBG_ENABLE
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME "utest"
+#ifdef UTEST_DEBUG
+#define DBG_LEVEL DBG_LOG
+#else
+#define DBG_LEVEL DBG_INFO
+#endif
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#if RT_CONSOLEBUF_SIZE < 256
+#error "RT_CONSOLEBUF_SIZE is less than 256!"
+#endif
+
+static utest_tc_export_t tc_table = RT_NULL;
+static rt_size_t tc_num;
+static struct utest local_utest = {UTEST_PASSED, 0, 0};
+
+#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
+#pragma section="UtestTcTab"
+#endif
+
+int utest_init(void)
+{
+ /* initialize the utest commands table.*/
+#if defined(__CC_ARM) /* ARM C Compiler */
+ extern const int UtestTcTab$$Base;
+ extern const int UtestTcTab$$Limit;
+ tc_table = (utest_tc_export_t)&UtestTcTab$$Base;
+ tc_num = (utest_tc_export_t)&UtestTcTab$$Limit - tc_table;
+#elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
+ tc_table = (utest_tc_export_t)__section_begin("UtestTcTab");
+ tc_num = (utest_tc_export_t)__section_end("UtestTcTab") - tc_table;
+#elif defined (__GNUC__) /* for GCC Compiler */
+ extern const int __rt_utest_tc_tab_start;
+ extern const int __rt_utest_tc_tab_end;
+ tc_table = (utest_tc_export_t)&__rt_utest_tc_tab_start;
+ tc_num = (utest_tc_export_t) &__rt_utest_tc_tab_end - tc_table;
+#endif /* defined(__CC_ARM) */
+
+ LOG_I("utest is initialize success.");
+ LOG_I("total utest testcase num: (%d)", tc_num);
+ return tc_num;
+}
+INIT_COMPONENT_EXPORT(utest_init);
+
+static void utest_tc_list(void)
+{
+ rt_size_t i = 0;
+
+ LOG_I("Commands list : ");
+
+ for (i = 0; i < tc_num; i++)
+ {
+ LOG_I("[testcase name]:%s; [run timeout]:%d", tc_table[i].name, tc_table[i].run_timeout);
+ }
+}
+MSH_CMD_EXPORT_ALIAS(utest_tc_list, utest_list, output all utest testcase);
+
+static const char *file_basename(const char *file)
+{
+ char *end_ptr = RT_NULL;
+ char *rst = RT_NULL;
+
+ if (!((end_ptr = strrchr(file, '\\')) != RT_NULL || \
+ (end_ptr = strrchr(file, '/')) != RT_NULL) || \
+ (rt_strlen(file) < 2))
+ {
+ rst = (char *)file;
+ }
+ else
+ {
+ rst = (char *)(end_ptr + 1);
+ }
+ return (const char *)rst;
+}
+
+static void utest_run(const char *utest_name)
+{
+ rt_size_t i = 0;
+
+ LOG_I("[==========] [ utest ] started");
+ while(i < tc_num)
+ {
+ if (utest_name && rt_strcmp(utest_name, tc_table[i].name))
+ {
+ i++;
+ continue;
+ }
+
+ LOG_I("[----------] [ testcase ] (%s) started", tc_table[i].name);
+ if (tc_table[i].init != RT_NULL)
+ {
+ if (tc_table[i].init() != RT_EOK)
+ {
+ LOG_I("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
+ goto __tc_continue;
+ }
+ }
+
+ if (tc_table[i].tc != RT_NULL)
+ {
+ tc_table[i].tc();
+ if (local_utest.failed_num == 0)
+ {
+ LOG_I("[ PASSED ] [ result ] testcase (%s)", tc_table[i].name);
+ }
+ else
+ {
+ LOG_I("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
+ }
+ }
+ else
+ {
+ LOG_I("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
+ }
+
+ if (tc_table[i].cleanup != RT_NULL)
+ {
+ if (tc_table[i].cleanup() != RT_EOK)
+ {
+ LOG_I("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
+ goto __tc_continue;
+ }
+ }
+
+__tc_continue:
+ LOG_I("[----------] [ testcase ] (%s) finished", tc_table[i].name);
+
+ i++;
+ }
+ LOG_I("[==========] [ utest ] finished");
+}
+
+static void utest_testcase_run(int argc, char** argv)
+{
+ char utest_name[UTEST_NAME_MAX_LEN];
+
+ if (argc == 1)
+ {
+ utest_run(RT_NULL);
+ }
+ else if (argc == 2)
+ {
+ rt_memset(utest_name, 0x0, sizeof(utest_name));
+ rt_strncpy(utest_name, argv[1], sizeof(utest_name) -1);
+ utest_run(utest_name);
+ }
+ else
+ {
+ LOG_E("[ error ] at (%s:%d), in param error.", __func__, __LINE__);
+ }
+}
+MSH_CMD_EXPORT_ALIAS(utest_testcase_run, utest_run, utest_run [testcase name]);
+
+utest_t utest_handle_get(void)
+{
+ return (utest_t)&local_utest;
+}
+
+void utest_unit_run(test_unit_func func, const char *unit_func_name)
+{
+ // LOG_I("[==========] utest unit name: (%s)", unit_func_name);
+ local_utest.error = UTEST_PASSED;
+ local_utest.passed_num = 0;
+ local_utest.failed_num = 0;
+
+ if (func != RT_NULL)
+ {
+ func();
+ }
+}
+
+void utest_assert(int value, const char *file, int line, const char *func, const char *msg)
+{
+ if (!(value))
+ {
+ local_utest.error = UTEST_FAILED;
+ local_utest.failed_num ++;
+ LOG_E("[ ASSERT ] [ unit ] at (%s); func: (%s:%d); msg: (%s)", file_basename(file), func, line, msg);
+ }
+ else
+ {
+ LOG_D("[ OK ] [ unit ] (%s:%d) is passed", func, line);
+ local_utest.error = UTEST_PASSED;
+ local_utest.passed_num ++;
+ }
+}
+
+void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg)
+{
+ if (a == RT_NULL || b == RT_NULL)
+ {
+ utest_assert(0, file, line, func, msg);
+ }
+
+ if (equal)
+ {
+ if (rt_strcmp(a, b) == 0)
+ {
+ utest_assert(1, file, line, func, msg);
+ }
+ else
+ {
+ utest_assert(0, file, line, func, msg);
+ }
+ }
+ else
+ {
+ if (rt_strcmp(a, b) == 0)
+ {
+ utest_assert(0, file, line, func, msg);
+ }
+ else
+ {
+ utest_assert(1, file, line, func, msg);
+ }
+ }
+}
diff --git a/components/utilities/utest/utest.h b/components/utilities/utest/utest.h
new file mode 100644
index 0000000000..5a7e8af721
--- /dev/null
+++ b/components/utilities/utest/utest.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-19 MurphyZhao the first version
+ */
+
+#ifndef __UTEST_H__
+#define __UTEST_H__
+
+#include <rtthread.h>
+#include "utest_log.h"
+
+/**
+ * utest_error
+ *
+ * @brief Test result.
+ *
+ * @member UTEST_PASSED Test success.
+ * @member UTEST_FAILED Test failed.
+ * @member UTEST_PASSED Test skipped.
+ *
+*/
+enum utest_error
+{
+ UTEST_PASSED = 0,
+ UTEST_FAILED = 1,
+ UTEST_SKIPPED = 2
+};
+typedef enum utest_error utest_err_e;
+
+/**
+ * utest
+ *
+ * @brief utest data structure.
+ *
+ * @member error Error number from enum `utest_error`.
+ * @member passed_num Total number of tests passed.
+ * @member failed_num Total number of tests failed.
+ *
+*/
+struct utest
+{
+ utest_err_e error;
+ uint32_t passed_num;
+ uint32_t failed_num;
+};
+typedef struct utest *utest_t;
+
+/**
+ * utest_tc_export
+ *
+ * @brief utest testcase data structure.
+ * Will export the data to `UtestTcTab` section in flash.
+ *
+ * @member name Testcase name.
+ * @member run_timeout Testcase maximum test time.
+ * @member init Necessary initialization before executing the test case function.
+ * @member tc Total number of tests failed.
+ * @member cleanup Total number of tests failed.
+ *
+*/
+struct utest_tc_export {
+ const char *name;
+ uint32_t run_timeout;
+ rt_err_t (*init)(void);
+ void (*tc)(void);
+ rt_err_t (*cleanup)(void);
+};
+typedef struct utest_tc_export *utest_tc_export_t;
+
+/**
+ * test_unit_func
+ *
+ * @brief Unit test handler function pointer.
+ *
+*/
+typedef void (*test_unit_func)(void);
+
+/**
+ * utest_unit_run
+ *
+ * @brief Unit test function executor.
+ * No need for the user to call this function directly
+ *
+ * @param func Unit test function.
+ * @param unit_func_name Unit test function name.
+ *
+ * @return void
+ *
+*/
+void utest_unit_run(test_unit_func func, const char *unit_func_name);
+
+/**
+ * utest_handle_get
+ *
+ * @brief Get the utest data structure handle.
+ * No need for the user to call this function directly
+ *
+ * @param void
+ *
+ * @return utest_t type. (struct utest *)
+ *
+*/
+utest_t utest_handle_get(void);
+
+/**
+ * UTEST_NAME_MAX_LEN
+ *
+ * @brief Testcase name maximum length.
+ *
+*/
+#define UTEST_NAME_MAX_LEN (128u)
+
+/**
+ * UTEST_TC_EXPORT
+ *
+ * @brief Export testcase function to `UtestTcTab` section in flash.
+ * Used in application layer.
+ *
+ * @param testcase The testcase function.
+ * @param name The testcase name.
+ * @param init The initialization function of the test case.
+ * @param cleanup The cleanup function of the test case.
+ * @param timeout Testcase maximum test time.
+ *
+ * @return None
+ *
+*/
+#define UTEST_TC_EXPORT(testcase, name, init, cleanup, timeout) \
+ RT_USED static const struct utest_tc_export _utest_testcase \
+ SECTION("UtestTcTab") = \
+ { \
+ name, \
+ timeout, \
+ init, \
+ testcase, \
+ cleanup \
+ }
+
+/**
+ * UTEST_UNIT_RUN
+ *
+ * @brief Unit test function executor.
+ * Used in `testcase` function in application.
+ *
+ * @param test_unit_func Unit test function
+ *
+ * @return None
+ *
+*/
+#define UTEST_UNIT_RUN(test_unit_func) \
+ utest_unit_run(test_unit_func, #test_unit_func); \
+ if(utest_handle_get()->failed_num != 0) return;
+
+#endif /* __UTEST_H__ */
diff --git a/components/utilities/utest/utest_assert.h b/components/utilities/utest/utest_assert.h
new file mode 100644
index 0000000000..997e8ae7f3
--- /dev/null
+++ b/components/utilities/utest/utest_assert.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-19 MurphyZhao the first version
+ */
+
+#ifndef __UTEST_ASSERT_H__
+#define __UTEST_ASSERT_H__
+
+#include "utest.h"
+#include <rtthread.h>
+
+/* No need for the user to use this function directly */
+void utest_assert(int value, const char *file, int line, const char *func, const char *msg);
+
+/* No need for the user to use this function directly */
+void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg);
+
+/* No need for the user to use this macro directly */
+#define __utest_assert(value, msg) utest_assert(value, __FILE__, __LINE__, __func__, msg)
+
+/**
+ * uassert_x macros
+ *
+ * @brief Get the utest data structure handle.
+ * No need for the user to call this function directly.
+ *
+ * @macro uassert_true if @value is true, not assert, means passing.
+ * @macro uassert_false if @value is false, not assert, means passing.
+ * @macro uassert_null if @value is null, not assert, means passing.
+ * @macro uassert_not_null if @value is not null, not assert, means passing.
+ * @macro uassert_int_equal if @a equal to @b, not assert, means passing. Integer type test.
+ * @macro uassert_int_not_equal if @a not equal to @b, not assert, means passing. Integer type test.
+ * @macro uassert_str_equal if @a equal to @b, not assert, means passing. String type test.
+ * @macro uassert_str_not_equal if @a not equal to @b, not assert, means passing. String type test.
+ * @macro uassert_in_range if @value is in range of min and max, not assert, means passing.
+ * @macro uassert_not_in_range if @value is not in range of min and max, not assert, means passing.
+ *
+*/
+#define uassert_true(value) __utest_assert(value, "(" #value ") is false")
+#define uassert_false(value) __utest_assert(!(value), "(" #value ") is true")
+#define uassert_null(value) __utest_assert((const char *)(value) == NULL, "(" #value ") is not null")
+#define uassert_not_null(value) __utest_assert((const char *)(value) != NULL, "(" #value ") is null")
+
+#define uassert_int_equal(a, b) __utest_assert((a) == (b), "(" #a ") not equal to (" #b ")")
+#define uassert_int_not_equal(a, b) __utest_assert((a) != (b), "(" #a ") equal to (" #b ")")
+
+#define uassert_str_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_TRUE, __FILE__, __LINE__, __func__, "string not equal")
+#define uassert_str_not_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_FALSE, __FILE__, __LINE__, __func__, "string equal")
+
+#define uassert_in_range(value, min, max) __utest_assert(((value >= min) && (value <= max)), "(" #value ") not in range("#min","#max")")
+#define uassert_not_in_range(value, min, max) __utest_assert(!((value >= min) && (value <= max)), "(" #value ") in range("#min","#max")")
+
+#endif /* __UTEST_ASSERT_H__ */
diff --git a/components/utilities/utest/utest_log.h b/components/utilities/utest/utest_log.h
new file mode 100644
index 0000000000..c954046b76
--- /dev/null
+++ b/components/utilities/utest/utest_log.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-19 MurphyZhao the first version
+ */
+
+#ifndef __UTEST_LOG_H__
+#define __UTEST_LOG_H__
+
+#define UTEST_DEBUG
+
+#undef DBG_SECTION_NAME
+#undef DBG_LEVEL
+#undef DBG_COLOR
+#undef DBG_ENABLE
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME "testcase"
+#ifdef UTEST_DEBUG
+#define DBG_LEVEL DBG_LOG
+#else
+#define DBG_LEVEL DBG_INFO
+#endif
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#endif /* __UTEST_LOG_H__ */