summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChen Wang <[email protected]>2025-09-22 14:45:35 +0800
committerR b b666 <[email protected]>2025-09-26 09:59:05 +0800
commit6e51a8405ff220424fe7d435958a787dba621a35 (patch)
tree89a535a3fe613dae617372e089516bad35443228 /src
parent495c8cfc25e0d03eb6ba9914df17487f839c557d (diff)
utest: move core tesetcases under to directiry "src"
Signed-off-by: Chen Wang <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/utest/Kconfig95
-rw-r--r--src/utest/SConscript79
-rw-r--r--src/utest/atomic_tc.c173
-rw-r--r--src/utest/device_tc.c59
-rw-r--r--src/utest/event_tc.c346
-rw-r--r--src/utest/hooklist_tc.c98
-rw-r--r--src/utest/irq_tc.c78
-rw-r--r--src/utest/mailbox_tc.c374
-rw-r--r--src/utest/mem_tc.c585
-rw-r--r--src/utest/memheap_tc.c97
-rw-r--r--src/utest/mempool_tc.c198
-rw-r--r--src/utest/messagequeue_tc.c234
-rw-r--r--src/utest/mtsafe_kprint_tc.c68
-rw-r--r--src/utest/mutex_pi_tc.c353
-rw-r--r--src/utest/mutex_tc.c798
-rw-r--r--src/utest/object_tc.c322
-rw-r--r--src/utest/sched_mtx_tc.c107
-rw-r--r--src/utest/sched_sem_tc.c198
-rw-r--r--src/utest/sched_thread_tc.c121
-rw-r--r--src/utest/sched_timed_mtx_tc.c232
-rw-r--r--src/utest/sched_timed_sem_tc.c149
-rw-r--r--src/utest/semaphore_tc.c558
-rw-r--r--src/utest/signal_tc.c207
-rw-r--r--src/utest/slab_tc.c323
-rw-r--r--src/utest/smp/Kconfig23
-rw-r--r--src/utest/smp/SConscript27
-rw-r--r--src/utest/smp/smp_affinity_pri1_tc.c135
-rw-r--r--src/utest/smp/smp_affinity_pri2_tc.c128
-rw-r--r--src/utest/smp/smp_assigned_idle_cores_tc.c91
-rw-r--r--src/utest/smp/smp_bind_affinity_tc.c114
-rw-r--r--src/utest/smp/smp_interrupt_pri_tc.c120
-rw-r--r--src/utest/smp/smp_spinlock_tc.c93
-rw-r--r--src/utest/smp/smp_thread_preemption_tc.c87
-rw-r--r--src/utest/thread_overflow_tc.c270
-rw-r--r--src/utest/thread_suspend_tc.c478
-rw-r--r--src/utest/thread_tc.c756
-rw-r--r--src/utest/timer_tc.c747
37 files changed, 8921 insertions, 0 deletions
diff --git a/src/utest/Kconfig b/src/utest/Kconfig
new file mode 100644
index 0000000000..decb0d9f16
--- /dev/null
+++ b/src/utest/Kconfig
@@ -0,0 +1,95 @@
+menu "Kernel Testcase"
+
+config UTEST_MEMHEAP_TC
+ bool "memheap stability test"
+ default y
+ depends on RT_USING_MEMHEAP
+
+config UTEST_SMALL_MEM_TC
+ bool "mem test"
+ default y
+ depends on RT_USING_SMALL_MEM
+
+config UTEST_OBJECT_TC
+ select RT_USING_DEVICE
+ select RT_USING_SEMAPHORE
+ bool "object test"
+ default n
+
+config UTEST_SLAB_TC
+ bool "slab test"
+ default n
+ depends on RT_USING_SLAB
+
+config UTEST_IRQ_TC
+ bool "IRQ test"
+ default n
+
+config UTEST_SEMAPHORE_TC
+ bool "semaphore test"
+ default n
+ depends on RT_USING_SEMAPHORE
+
+config UTEST_EVENT_TC
+ bool "event test"
+ default n
+ depends on RT_USING_EVENT
+
+config UTEST_TIMER_TC
+ bool "timer test"
+ default n
+
+config UTEST_MESSAGEQUEUE_TC
+ bool "message queue test"
+ default n
+
+config UTEST_SIGNAL_TC
+ bool "signal test"
+ select RT_USING_SIGNALS
+ default n
+
+config UTEST_MUTEX_TC
+ bool "mutex test"
+ default n
+
+config UTEST_MAILBOX_TC
+ bool "mailbox test"
+ default n
+
+config UTEST_THREAD_TC
+ bool "thread test"
+ default n
+ select RT_USING_TIMER_SOFT
+ select RT_USING_THREAD
+
+config UTEST_DEVICE_TC
+ bool "device test"
+ default n
+
+config UTEST_ATOMIC_TC
+ bool "atomic test"
+ default n
+
+config UTEST_HOOKLIST_TC
+ bool "hook list test"
+ select RT_USING_HOOKLIST
+ default n
+
+config UTEST_MTSAFE_KPRINT_TC
+ bool "mtsafe kprint test"
+ default n
+
+config UTEST_SCHEDULER_TC
+ bool "scheduler test"
+ default n
+
+config UTEST_MEMPOOL_TC
+ bool "mempool test"
+ default n
+ depends on RT_USING_MEMPOOL
+
+if RT_USING_SMP
+rsource "smp/Kconfig"
+endif
+
+endmenu
diff --git a/src/utest/SConscript b/src/utest/SConscript
new file mode 100644
index 0000000000..8a134ed4f8
--- /dev/null
+++ b/src/utest/SConscript
@@ -0,0 +1,79 @@
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = []
+CPPPATH = [cwd]
+
+if GetDepend(['UTEST_OBJECT_TC']):
+ src += ['object_tc.c']
+
+if GetDepend(['UTEST_MEMHEAP_TC']):
+ src += ['memheap_tc.c']
+
+if GetDepend(['UTEST_SMALL_MEM_TC']):
+ src += ['mem_tc.c']
+
+if GetDepend(['UTEST_SLAB_TC']):
+ src += ['slab_tc.c']
+
+if GetDepend(['UTEST_IRQ_TC']):
+ src += ['irq_tc.c']
+
+if GetDepend(['UTEST_SEMAPHORE_TC']):
+ src += ['semaphore_tc.c']
+
+if GetDepend(['UTEST_EVENT_TC']):
+ src += ['event_tc.c']
+
+if GetDepend(['UTEST_TIMER_TC']):
+ src += ['timer_tc.c']
+
+if GetDepend(['UTEST_MESSAGEQUEUE_TC']):
+ src += ['messagequeue_tc.c']
+
+if GetDepend(['UTEST_SIGNAL_TC']):
+ src += ['signal_tc.c']
+
+if GetDepend(['UTEST_MUTEX_TC']):
+ src += ['mutex_tc.c', 'mutex_pi_tc.c']
+
+if GetDepend(['UTEST_MAILBOX_TC']):
+ src += ['mailbox_tc.c']
+
+if GetDepend(['UTEST_THREAD_TC']):
+ src += ['thread_tc.c']
+ src += ['thread_overflow_tc.c']
+ src += ['thread_suspend_tc.c']
+
+if GetDepend(['UTEST_DEVICE_TC']):
+ src += ['device_tc.c']
+
+if GetDepend(['UTEST_ATOMIC_TC']):
+ src += ['atomic_tc.c']
+
+if GetDepend(['UTEST_HOOKLIST_TC']):
+ src += ['hooklist_tc.c']
+
+if GetDepend(['UTEST_MTSAFE_KPRINT_TC']):
+ src += ['mtsafe_kprint_tc.c']
+
+if GetDepend(['UTEST_MEMPOOL_TC']):
+ src += ['mempool_tc.c']
+
+# Stressful testcase for scheduler (MP/UP)
+if GetDepend(['UTEST_SCHEDULER_TC']):
+ src += ['sched_timed_sem_tc.c']
+ src += ['sched_timed_mtx_tc.c']
+ src += ['sched_mtx_tc.c']
+ src += ['sched_sem_tc.c', 'sched_thread_tc.c']
+
+group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
+
+list = os.listdir(cwd)
+for item in list:
+ if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+ group = group + SConscript(os.path.join(item, 'SConscript'))
+
+Return('group')
+
diff --git a/src/utest/atomic_tc.c b/src/utest/atomic_tc.c
new file mode 100644
index 0000000000..f62b8e4c9a
--- /dev/null
+++ b/src/utest/atomic_tc.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-07-27 flybreak the first version
+ * 2023-03-21 WangShun add atomic test
+ * 2023-09-15 xqyjlj change stack size in cpu64
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include "rtatomic.h"
+#include <rthw.h>
+
+#define THREAD_PRIORITY 25
+#define THREAD_TIMESLICE 1
+#define THREAD_STACKSIZE UTEST_THR_STACK_SIZE
+
+/* convenience macro - return either 64-bit or 32-bit value */
+#define ATOMIC_WORD(val_if_64, val_if_32) \
+ ((rt_atomic_t)((sizeof(void *) == sizeof(uint64_t)) ? (val_if_64) : (val_if_32)))
+
+static rt_atomic_t count = 0;
+static rt_sem_t sem_t;
+
+static void test_atomic_api(void)
+{
+ rt_atomic_t base;
+ rt_atomic_t oldval;
+ rt_atomic_t result;
+
+ /* rt_atomic_t */
+ uassert_true(sizeof(rt_atomic_t) == ATOMIC_WORD(sizeof(uint64_t), sizeof(uint32_t)));
+
+ /* rt_atomic_add */
+ base = 0;
+ result = rt_atomic_add(&base, 10);
+ uassert_true(base == 10);
+ uassert_true(result == 0);
+ /* rt_atomic_add negative */
+ base = 2;
+ result = rt_atomic_add(&base, -4);
+ uassert_true(base == -2);
+ uassert_true(result == 2);
+
+ /* rt_atomic_sub */
+ base = 11;
+ result = rt_atomic_sub(&base, 10);
+ uassert_true(base == 1);
+ uassert_true(result == 11);
+ /* rt_atomic_sub negative */
+ base = 2;
+ result = rt_atomic_sub(&base, -5);
+ uassert_true(base == 7);
+ uassert_true(result == 2);
+
+ /* rt_atomic_or */
+ base = 0xFF00;
+ result = rt_atomic_or(&base, 0x0F0F);
+ uassert_true(base == 0xFF0F);
+ uassert_true(result == 0xFF00);
+
+ /* rt_atomic_xor */
+ base = 0xFF00;
+ result = rt_atomic_xor(&base, 0x0F0F);
+ uassert_true(base == 0xF00F);
+ uassert_true(result == 0xFF00);
+
+ /* rt_atomic_and */
+ base = 0xFF00;
+ result = rt_atomic_and(&base, 0x0F0F);
+ uassert_true(base == 0x0F00);
+ uassert_true(result == 0xFF00);
+
+ /* rt_atomic_exchange */
+ base = 0xFF00;
+ result = rt_atomic_exchange(&base, 0x0F0F);
+ uassert_true(base == 0x0F0F);
+ uassert_true(result == 0xFF00);
+
+ /* rt_atomic_flag_test_and_set (Flag 0) */
+ base = 0x0;
+ result = rt_atomic_flag_test_and_set(&base);
+ uassert_true(base == 0x1);
+ uassert_true(result == 0x0);
+ /* rt_atomic_flag_test_and_set (Flag 1) */
+ base = 0x1;
+ result = rt_atomic_flag_test_and_set(&base);
+ uassert_true(base == 0x1);
+ uassert_true(result == 0x1);
+
+ /* rt_atomic_flag_clear */
+ base = 0x1;
+ rt_atomic_flag_clear(&base);
+ uassert_true(base == 0x0);
+
+ /* rt_atomic_load */
+ base = 0xFF00;
+ result = rt_atomic_load(&base);
+ uassert_true(base == 0xFF00);
+ uassert_true(result == 0xFF00);
+
+ /* rt_atomic_store */
+ base = 0xFF00;
+ rt_atomic_store(&base, 0x0F0F);
+ uassert_true(base == 0x0F0F);
+
+ /* rt_atomic_compare_exchange_strong (equal) */
+ base = 10;
+ oldval = 10;
+ result = rt_atomic_compare_exchange_strong(&base, &oldval, 11);
+ uassert_true(base == 11);
+ uassert_true(result == 0x1);
+ /* rt_atomic_compare_exchange_strong (not equal) */
+ base = 10;
+ oldval = 5;
+ result = rt_atomic_compare_exchange_strong(&base, &oldval, 11);
+ uassert_true(base == 10);
+ uassert_true(result == 0x0);
+}
+
+static void ture_entry(void *parameter)
+{
+ int i;
+ for (i = 0; i < 1000000; i++)
+ {
+ rt_atomic_add(&count, 1);
+ }
+ rt_sem_release(sem_t);
+}
+
+static void test_atomic_add(void)
+{
+ rt_thread_t thread;
+ size_t i;
+ sem_t = rt_sem_create("atomic_sem", 0, RT_IPC_FLAG_PRIO);
+
+ rt_atomic_store(&count, 0);
+
+ thread = rt_thread_create("t1", ture_entry, RT_NULL, THREAD_STACKSIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(thread);
+ thread = rt_thread_create("t2", ture_entry, RT_NULL, THREAD_STACKSIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(thread);
+ thread = rt_thread_create("t3", ture_entry, RT_NULL, THREAD_STACKSIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(thread);
+
+ for (i = 0; i < 3; i++)
+ {
+ rt_sem_take(sem_t, RT_WAITING_FOREVER);
+ }
+ i = rt_atomic_load(&count);
+ uassert_true(i == 3000000);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_atomic_api);
+ UTEST_UNIT_RUN(test_atomic_add);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.atomic_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/device_tc.c b/src/utest/device_tc.c
new file mode 100644
index 0000000000..6b5a8dfd86
--- /dev/null
+++ b/src/utest/device_tc.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-05-20 Shell the first version
+ */
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+static void test_rt_device_find(void)
+{
+ char _device_name[RT_NAME_MAX + 1] = {0};
+ rt_device_t console;
+ rt_device_t device1, device2, device3;
+
+ console = rt_console_get_device();
+ uassert_not_null(console);
+ rt_memcpy(_device_name, console->parent.name, RT_NAME_MAX);
+
+ /* Test finding a device */
+ device1 = rt_device_find(_device_name);
+ uassert_true(device1 == console);
+
+ /* Test finding another device */
+ device2 = rt_device_find(RT_CONSOLE_DEVICE_NAME);
+ if (rt_strcmp(RT_CONSOLE_DEVICE_NAME, _device_name) == 0)
+ {
+ uassert_true(device2 == device1);
+ }
+ else
+ {
+ uassert_not_null(device2);
+ uassert_true(device2 != device1);
+ }
+
+ /* Test finding a device 3 */
+ device3 = rt_device_find(console->parent.name);
+ uassert_true(device1 == device3);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_rt_device_find);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.device.find", utest_tc_init, utest_tc_cleanup, 5);
diff --git a/src/utest/event_tc.c b/src/utest/event_tc.c
new file mode 100644
index 0000000000..971fc7c6df
--- /dev/null
+++ b/src/utest/event_tc.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-15 liukang the first version
+ * 2023-09-15 xqyjlj change stack size in cpu64
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include <stdlib.h>
+
+#define THREAD_STACKSIZE UTEST_THR_STACK_SIZE
+#define EVENT_FLAG3 (1 << 3)
+#define EVENT_FLAG5 (1 << 5)
+
+static struct rt_event static_event = {0};
+#ifdef RT_USING_HEAP
+static rt_event_t dynamic_event = RT_NULL;
+static rt_uint32_t dynamic_event_recv_thread_finish = 0, dynamic_event_send_thread_finish = 0;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread3_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread3;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread4_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread4;
+#endif /* RT_USING_HEAP */
+
+static rt_uint32_t recv_event_times1 = 0, recv_event_times2 = 0;
+static rt_uint32_t static_event_recv_thread_finish = 0, static_event_send_thread_finish = 0;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread1_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread1;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread2_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread2;
+
+#define THREAD_PRIORITY 9
+#define THREAD_TIMESLICE 5
+
+static void test_event_init(void)
+{
+ rt_err_t result;
+
+ result = rt_event_init(&static_event, "event", RT_IPC_FLAG_PRIO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ result = rt_event_detach(&static_event);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_event_init(&static_event, "event", RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ result = rt_event_detach(&static_event);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void test_event_detach(void)
+{
+ rt_err_t result = RT_EOK;
+
+ result = rt_event_init(&static_event, "event", RT_IPC_FLAG_PRIO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_event_detach(&static_event);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void thread1_recv_static_event(void *param)
+{
+ rt_uint32_t e;
+
+ if (rt_event_recv(&static_event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) != RT_EOK)
+ {
+ return;
+ }
+
+ recv_event_times1 = e;
+
+ rt_thread_mdelay(50);
+
+ if (rt_event_recv(&static_event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) != RT_EOK)
+ {
+ return;
+ }
+ recv_event_times2 = e;
+
+ static_event_recv_thread_finish = 1;
+}
+
+static void thread2_send_static_event(void *param)
+{
+ rt_event_send(&static_event, EVENT_FLAG3);
+ rt_thread_mdelay(10);
+
+ rt_event_send(&static_event, EVENT_FLAG5);
+ rt_thread_mdelay(10);
+
+ rt_event_send(&static_event, EVENT_FLAG3);
+
+ static_event_send_thread_finish = 1;
+}
+
+
+static void test_static_event_send_recv(void)
+{
+ rt_err_t result = RT_EOK;
+
+ result = rt_event_init(&static_event, "event", RT_IPC_FLAG_PRIO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ thread1_recv_static_event,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ thread2_send_static_event,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+
+ while (static_event_recv_thread_finish != 1 || static_event_send_thread_finish != 1)
+ {
+ rt_thread_delay(1);
+ }
+
+ if (recv_event_times1 == EVENT_FLAG3 && recv_event_times2 == (EVENT_FLAG3 | EVENT_FLAG5))
+ {
+ if (rt_event_detach(&static_event) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ uassert_true(1);
+ }
+ else
+ {
+ if (rt_event_detach(&static_event) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ uassert_false(1);
+ }
+
+ return;
+}
+
+#ifdef RT_USING_HEAP
+static void test_event_create(void)
+{
+ rt_err_t result = RT_EOK;
+
+ dynamic_event = rt_event_create("dynamic_event", RT_IPC_FLAG_FIFO);
+ if (dynamic_event == RT_NULL)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_event_delete(dynamic_event);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void test_event_delete(void)
+{
+ rt_err_t result;
+
+ dynamic_event = rt_event_create("dynamic_event", RT_IPC_FLAG_FIFO);
+ if (dynamic_event == RT_NULL)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_event_delete(dynamic_event);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void thread3_recv_dynamic_event(void *param)
+{
+ rt_uint32_t e;
+
+ if (rt_event_recv(dynamic_event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) != RT_EOK)
+ {
+ return;
+ }
+
+ recv_event_times1 = e;
+
+ rt_thread_mdelay(50);
+
+ if (rt_event_recv(dynamic_event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) != RT_EOK)
+ {
+ return;
+ }
+ recv_event_times2 = e;
+
+ dynamic_event_recv_thread_finish = 1;
+}
+
+static void thread4_send_dynamic_event(void *param)
+{
+ rt_event_send(dynamic_event, EVENT_FLAG3);
+ rt_thread_mdelay(10);
+
+ rt_event_send(dynamic_event, EVENT_FLAG5);
+ rt_thread_mdelay(10);
+
+ rt_event_send(dynamic_event, EVENT_FLAG3);
+
+ dynamic_event_send_thread_finish = 1;
+}
+
+static void test_dynamic_event_send_recv(void)
+{
+ dynamic_event = rt_event_create("dynamic_event", RT_IPC_FLAG_PRIO);
+ if (dynamic_event == RT_NULL)
+ {
+ uassert_false(1);
+ }
+
+ rt_thread_init(&thread3,
+ "thread3",
+ thread3_recv_dynamic_event,
+ RT_NULL,
+ &thread3_stack[0],
+ sizeof(thread3_stack),
+ THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread3);
+
+ rt_thread_init(&thread4,
+ "thread4",
+ thread4_send_dynamic_event,
+ RT_NULL,
+ &thread4_stack[0],
+ sizeof(thread4_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread4);
+
+ while (dynamic_event_recv_thread_finish != 1 || dynamic_event_send_thread_finish != 1)
+ {
+ rt_thread_delay(1);
+ }
+
+ if (recv_event_times1 == EVENT_FLAG3 && recv_event_times2 == (EVENT_FLAG3 | EVENT_FLAG5))
+ {
+ if (rt_event_delete(dynamic_event) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ uassert_true(1);
+ }
+ else
+ {
+ if (rt_event_delete(dynamic_event) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ uassert_false(1);
+ }
+
+ return;
+}
+#endif
+
+static rt_err_t utest_tc_init(void)
+{
+ static_event_recv_thread_finish = 0;
+ static_event_send_thread_finish = 0;
+#ifdef RT_USING_HEAP
+ dynamic_event_recv_thread_finish = 0;
+ dynamic_event_send_thread_finish = 0;
+#endif
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_event_init);
+ UTEST_UNIT_RUN(test_event_detach);
+ UTEST_UNIT_RUN(test_static_event_send_recv);
+#ifdef RT_USING_HEAP
+ UTEST_UNIT_RUN(test_event_create);
+ UTEST_UNIT_RUN(test_event_delete);
+ UTEST_UNIT_RUN(test_dynamic_event_send_recv);
+#endif
+}
+UTEST_TC_EXPORT(testcase, "src.ipc.event_tc", utest_tc_init, utest_tc_cleanup, 60);
diff --git a/src/utest/hooklist_tc.c b/src/utest/hooklist_tc.c
new file mode 100644
index 0000000000..a513517782
--- /dev/null
+++ b/src/utest/hooklist_tc.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-22 Shell Support hook list
+ */
+
+#include <rtthread.h>
+#include "rtconfig.h"
+#include "utest.h"
+#include "utest_assert.h"
+
+static int hooker1_ent_count;
+static int hooker2_ent_count;
+static struct rt_thread thr_tobe_inited;
+
+static void thread_inited_hooker1(rt_thread_t thread)
+{
+ LOG_D("%s: count %d", __func__, hooker1_ent_count);
+ hooker1_ent_count += 1;
+}
+RT_OBJECT_HOOKLIST_DEFINE_NODE(rt_thread_inited, hooker1_node, thread_inited_hooker1);
+
+static void thread_inited_hooker2(rt_thread_t thread)
+{
+ LOG_D("%s: count %d", __func__, hooker2_ent_count);
+ hooker2_ent_count += 1;
+}
+RT_OBJECT_HOOKLIST_DEFINE_NODE(rt_thread_inited, hooker2_node, thread_inited_hooker2);
+
+static char _thr_stack[UTEST_THR_STACK_SIZE];
+static void thr_tobe_inited_entry(void *param)
+{
+ rt_kprintf("Hello!\n");
+}
+
+static void hooklist_test(void)
+{
+ hooker1_ent_count = 0;
+ hooker2_ent_count = 0;
+ rt_thread_inited_sethook(&hooker1_node);
+ rt_thread_inited_sethook(&hooker2_node);
+
+ /* run 1 */
+ rt_thread_init(&thr_tobe_inited,
+ "thr_tobe_inited",
+ thr_tobe_inited_entry,
+ NULL,
+ _thr_stack,
+ sizeof(_thr_stack),
+ 25,
+ 100);
+
+ uassert_int_equal(hooker1_ent_count, 1);
+ uassert_int_equal(hooker2_ent_count, 1);
+
+ rt_thread_detach(&thr_tobe_inited);
+ rt_thread_mdelay(1); /* wait recycling done */
+
+ /* run 2 */
+ rt_thread_inited_rmhook(&hooker2_node);
+
+ rt_thread_init(&thr_tobe_inited,
+ "thr_tobe_inited",
+ thr_tobe_inited_entry,
+ NULL,
+ _thr_stack,
+ sizeof(_thr_stack),
+ 25,
+ 100);
+
+ uassert_int_equal(hooker1_ent_count, 2);
+ uassert_int_equal(hooker2_ent_count, 1);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ hooker1_ent_count = 0;
+ hooker2_ent_count = 0;
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_thread_detach(&thr_tobe_inited);
+ rt_thread_inited_rmhook(&hooker1_node);
+ rt_thread_inited_rmhook(&hooker2_node);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(hooklist_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.hooklist_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/irq_tc.c b/src/utest/irq_tc.c
new file mode 100644
index 0000000000..6d2a29f016
--- /dev/null
+++ b/src/utest/irq_tc.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-15 supperthomas add irq_test
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include "rthw.h"
+
+#define UTEST_NAME "irq_tc"
+static volatile uint32_t irq_count = 0;
+static volatile uint32_t max_get_nest_count = 0;
+
+static void irq_callback()
+{
+ if(rt_interrupt_get_nest() > max_get_nest_count)
+ {
+ max_get_nest_count = rt_interrupt_get_nest();
+ }
+ irq_count ++;
+}
+
+static void irq_test(void)
+{
+ irq_count = 0;
+ rt_interrupt_enter_sethook(irq_callback);
+ rt_interrupt_leave_sethook(irq_callback);
+ rt_thread_mdelay(2);
+ LOG_D("%s test irq_test! irq_count %d max_get_nest_count %d\n", UTEST_NAME, irq_count, max_get_nest_count);
+ uassert_int_not_equal(0, irq_count);
+ uassert_int_not_equal(0, max_get_nest_count);
+ rt_interrupt_enter_sethook(RT_NULL);
+ rt_interrupt_leave_sethook(RT_NULL);
+ LOG_D("irq_test OK!\n");
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ irq_count = 0;
+ max_get_nest_count = 0;
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void interrupt_test(void)
+{
+ rt_base_t level;
+ uint32_t i = 1000;
+
+ rt_interrupt_enter_sethook(irq_callback);
+ rt_interrupt_leave_sethook(irq_callback);
+ irq_count = 0;
+ level = rt_hw_interrupt_disable();
+ while(i)
+ {
+ i --;
+ }
+ uassert_int_equal(0, irq_count);
+ rt_hw_interrupt_enable(level);
+ rt_interrupt_enter_sethook(RT_NULL);
+ rt_interrupt_leave_sethook(RT_NULL);
+
+}
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(irq_test);
+ UTEST_UNIT_RUN(interrupt_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.irq_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/mailbox_tc.c b/src/utest/mailbox_tc.c
new file mode 100644
index 0000000000..a273aaf9f8
--- /dev/null
+++ b/src/utest/mailbox_tc.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-09-08 liukang the first version
+ * 2023-09-15 xqyjlj change stack size in cpu64
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include <stdlib.h>
+
+#define THREAD_STACKSIZE UTEST_THR_STACK_SIZE
+
+static struct rt_mailbox test_static_mb;
+static char mb_pool[128];
+
+static rt_mailbox_t test_dynamic_mb;
+
+static uint8_t static_mb_recv_thread_finish, static_mb_send_thread_finish;
+static uint8_t dynamic_mb_recv_thread_finish, dynamic_mb_send_thread_finish;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread1_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread1;
+
+rt_align(RT_ALIGN_SIZE)
+static char thread2_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread2;
+
+#define THREAD_PRIORITY 9
+#define THREAD_TIMESLICE 5
+
+static rt_thread_t mb_send = RT_NULL;
+static rt_thread_t mb_recv = RT_NULL;
+
+static rt_uint8_t mb_send_str1[] = "this is first mail!";
+static rt_uint8_t mb_send_str2[] = "this is second mail!";
+static rt_uint8_t mb_send_str3[] = "this is thirdy mail!";
+
+static rt_uint8_t *mb_recv_str1;
+static rt_uint8_t *mb_recv_str2;
+static rt_uint8_t *mb_recv_str3;
+
+static void test_mailbox_init(void)
+{
+ rt_err_t result;
+
+ result = rt_mb_init(&test_static_mb, "mbt", &mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_detach(&test_static_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_init(&test_static_mb, "mbt", &mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_PRIO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_detach(&test_static_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void test_mailbox_deatch(void)
+{
+ rt_err_t result;
+
+ result = rt_mb_init(&test_static_mb, "mbt", &mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_detach(&test_static_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_init(&test_static_mb, "mbt", &mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_PRIO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_detach(&test_static_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void test_mailbox_create(void)
+{
+ rt_err_t result;
+
+ test_dynamic_mb = rt_mb_create("test_dynamic_mb", sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (test_dynamic_mb == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_delete(test_dynamic_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ test_dynamic_mb = rt_mb_create("test_dynamic_mb", sizeof(mb_pool) / 4, RT_IPC_FLAG_PRIO);
+ if (test_dynamic_mb == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_delete(test_dynamic_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void test_mailbox_delete(void)
+{
+ rt_err_t result;
+
+ test_dynamic_mb = rt_mb_create("test_dynamic_mb", sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (test_dynamic_mb == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_delete(test_dynamic_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ test_dynamic_mb = rt_mb_create("test_dynamic_mb", sizeof(mb_pool) / 4, RT_IPC_FLAG_PRIO);
+ if (test_dynamic_mb == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ result = rt_mb_delete(test_dynamic_mb);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void thread2_send_static_mb(void *arg)
+{
+ rt_err_t res = RT_EOK;
+
+ res = rt_mb_send(&test_static_mb, (rt_ubase_t)&mb_send_str1);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ rt_thread_mdelay(100);
+
+ res = rt_mb_send_wait(&test_static_mb, (rt_ubase_t)&mb_send_str2, 10);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ rt_thread_mdelay(100);
+
+ res = rt_mb_urgent(&test_static_mb, (rt_ubase_t)&mb_send_str3);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ static_mb_send_thread_finish = 1;
+}
+
+static void thread1_recv_static_mb(void *arg)
+{
+ rt_err_t result = RT_EOK;
+
+ result = rt_mb_recv(&test_static_mb, (rt_ubase_t *)&mb_recv_str1, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str1, (const char *)mb_send_str1) != 0)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_recv(&test_static_mb, (rt_ubase_t *)&mb_recv_str2, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str2, (const char *)mb_send_str2) != 0)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_recv(&test_static_mb, (rt_ubase_t *)&mb_recv_str3, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str3, (const char *)mb_send_str3) != 0)
+ {
+ uassert_false(1);
+ }
+
+ static_mb_recv_thread_finish = 1;
+}
+
+static void test_static_mailbox_send_recv(void)
+{
+ rt_err_t result;
+
+ result = rt_mb_init(&test_static_mb, "mbt", &mb_pool[0], sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ thread1_recv_static_mb,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ thread2_send_static_mb,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+
+ while (static_mb_recv_thread_finish != 1 || static_mb_send_thread_finish != 1)
+ {
+ rt_thread_delay(1);
+ }
+
+ if (rt_mb_detach(&test_static_mb) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static void thread4_send_dynamic_mb(void *arg)
+{
+ rt_err_t res = RT_EOK;
+
+ res = rt_mb_send(test_dynamic_mb, (rt_ubase_t)&mb_send_str1);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ rt_thread_mdelay(100);
+
+ res = rt_mb_send_wait(test_dynamic_mb, (rt_ubase_t)&mb_send_str2, 10);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+ rt_thread_mdelay(100);
+
+ res = rt_mb_urgent(test_dynamic_mb, (rt_ubase_t)&mb_send_str3);
+ if (res != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ dynamic_mb_send_thread_finish = 1;
+}
+
+static void thread3_recv_dynamic_mb(void *arg)
+{
+ rt_err_t result = RT_EOK;
+
+ result = rt_mb_recv(test_dynamic_mb, (rt_ubase_t *)&mb_recv_str1, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str1, (const char *)mb_send_str1) != 0)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_recv(test_dynamic_mb, (rt_ubase_t *)&mb_recv_str2, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str2, (const char *)mb_send_str2) != 0)
+ {
+ uassert_false(1);
+ }
+
+ result = rt_mb_recv(test_dynamic_mb, (rt_ubase_t *)&mb_recv_str3, RT_WAITING_FOREVER);
+ if (result != RT_EOK || rt_strcmp((const char *)mb_recv_str3, (const char *)mb_send_str3) != 0)
+ {
+ uassert_false(1);
+ }
+
+ dynamic_mb_recv_thread_finish = 1;
+}
+
+static void test_dynamic_mailbox_send_recv(void)
+{
+ test_dynamic_mb = rt_mb_create("mbt", sizeof(mb_pool) / 4, RT_IPC_FLAG_FIFO);
+ if (test_dynamic_mb == RT_NULL)
+ {
+ uassert_false(1);
+ }
+
+ mb_recv = rt_thread_create("mb_recv_thread",
+ thread3_recv_dynamic_mb,
+ RT_NULL,
+ UTEST_THR_STACK_SIZE,
+ THREAD_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (mb_recv == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ rt_thread_startup(mb_recv);
+
+ mb_send = rt_thread_create("mb_send_thread",
+ thread4_send_dynamic_mb,
+ RT_NULL,
+ UTEST_THR_STACK_SIZE,
+ THREAD_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (mb_send == RT_NULL)
+ {
+ uassert_false(1);
+ }
+ rt_thread_startup(mb_send);
+
+ while (dynamic_mb_recv_thread_finish != 1 || dynamic_mb_send_thread_finish != 1)
+ {
+ rt_thread_delay(1);
+ }
+
+ if (rt_mb_delete(test_dynamic_mb) != RT_EOK)
+ {
+ uassert_false(1);
+ }
+
+ uassert_true(1);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_mailbox_init);
+ UTEST_UNIT_RUN(test_mailbox_deatch);
+ UTEST_UNIT_RUN(test_mailbox_create);
+ UTEST_UNIT_RUN(test_mailbox_delete);
+ UTEST_UNIT_RUN(test_static_mailbox_send_recv);
+ UTEST_UNIT_RUN(test_dynamic_mailbox_send_recv);
+}
+UTEST_TC_EXPORT(testcase, "src.ipc.mailbox_tc", utest_tc_init, utest_tc_cleanup, 60);
diff --git a/src/utest/mem_tc.c b/src/utest/mem_tc.c
new file mode 100644
index 0000000000..aee9795117
--- /dev/null
+++ b/src/utest/mem_tc.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-10-14 tyx the first version
+ */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+struct rt_small_mem_item
+{
+ rt_ubase_t pool_ptr; /**< small memory object addr */
+ rt_size_t next; /**< next free item */
+ rt_size_t prev; /**< prev free item */
+#ifdef RT_USING_MEMTRACE
+#ifdef ARCH_CPU_64BIT
+ rt_uint8_t thread[8]; /**< thread name */
+#else
+ rt_uint8_t thread[4]; /**< thread name */
+#endif /* ARCH_CPU_64BIT */
+#endif /* RT_USING_MEMTRACE */
+};
+
+struct rt_small_mem
+{
+ struct rt_memory parent; /**< inherit from rt_memory */
+ rt_uint8_t *heap_ptr; /**< pointer to the heap */
+ struct rt_small_mem_item *heap_end;
+ struct rt_small_mem_item *lfree;
+ rt_size_t mem_size_aligned; /**< aligned memory size */
+};
+
+#define MEM_SIZE(_heap, _mem) \
+ (((struct rt_small_mem_item *)(_mem))->next - ((rt_ubase_t)(_mem) - \
+ (rt_ubase_t)((_heap)->heap_ptr)) - RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE))
+
+#define TEST_MEM_SIZE 1024
+
+static rt_size_t max_block(struct rt_small_mem *heap)
+{
+ struct rt_small_mem_item *mem;
+ rt_size_t max = 0, size;
+
+ for (mem = (struct rt_small_mem_item *)heap->heap_ptr;
+ mem != heap->heap_end;
+ mem = (struct rt_small_mem_item *)&heap->heap_ptr[mem->next])
+ {
+ if (((rt_ubase_t)mem->pool_ptr & 0x1) == 0)
+ {
+ size = MEM_SIZE(heap, mem);
+ if (size > max)
+ {
+ max = size;
+ }
+ }
+ }
+ return max;
+}
+
+static int _mem_cmp(void *ptr, rt_uint8_t v, rt_size_t size)
+{
+ while (size-- != 0)
+ {
+ if (*(rt_uint8_t *)ptr != v)
+ return *(rt_uint8_t *)ptr - v;
+ }
+ return 0;
+}
+
+struct mem_test_context
+{
+ void *ptr;
+ rt_size_t size;
+ rt_uint8_t magic;
+};
+
+static void mem_functional_test(void)
+{
+ rt_size_t total_size;
+ rt_uint8_t *buf;
+ struct rt_small_mem *heap;
+ rt_uint8_t magic = __LINE__;
+
+ /* Prepare test memory */
+ buf = rt_malloc(TEST_MEM_SIZE);
+ uassert_not_null(buf);
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)buf, RT_ALIGN_SIZE), (rt_ubase_t)buf);
+ rt_memset(buf, 0xAA, TEST_MEM_SIZE);
+ /* small heap init */
+ heap = (struct rt_small_mem *)rt_smem_init("mem_tc", buf, TEST_MEM_SIZE);
+ /* get total size */
+ total_size = max_block(heap);
+ uassert_int_not_equal(total_size, 0);
+ /*
+ * Allocate all memory at a time and test whether
+ * the memory allocation release function is effective
+ */
+ {
+ struct mem_test_context ctx;
+ ctx.magic = magic++;
+ ctx.size = max_block(heap);
+ ctx.ptr = rt_smem_alloc(&heap->parent, ctx.size);
+ uassert_not_null(ctx.ptr);
+ rt_memset(ctx.ptr, ctx.magic, ctx.size);
+ uassert_int_equal(_mem_cmp(ctx.ptr, ctx.magic, ctx.size), 0);
+ rt_smem_free(ctx.ptr);
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /*
+ * Apply for memory release sequentially and
+ * test whether memory block merging is effective
+ */
+ {
+ rt_size_t i, max_free = 0;
+ struct mem_test_context ctx[3];
+ /* alloc mem */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ ctx[i].magic = magic++;
+ ctx[i].size = max_block(heap) / (sizeof(ctx) / sizeof(ctx[0]) - i);
+ ctx[i].ptr = rt_smem_alloc(&heap->parent, ctx[i].size);
+ uassert_not_null(ctx[i].ptr);
+ rt_memset(ctx[i].ptr, ctx[i].magic, ctx[i].size);
+ }
+ /* All memory has been applied. The remaining memory should be 0 */
+ uassert_int_equal(max_block(heap), 0);
+ /* Verify that the memory data is correct */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ uassert_int_equal(_mem_cmp(ctx[i].ptr, ctx[i].magic, ctx[i].size), 0);
+ }
+ /* Sequential memory release */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ uassert_int_equal(_mem_cmp(ctx[i].ptr, ctx[i].magic, ctx[i].size), 0);
+ rt_smem_free(ctx[i].ptr);
+ max_free += ctx[i].size;
+ uassert_true(max_block(heap) >= max_free);
+ }
+ /* Check whether the memory is fully merged */
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /*
+ * Apply for memory release at an interval to
+ * test whether memory block merging is effective
+ */
+ {
+ rt_size_t i, max_free = 0;
+ struct mem_test_context ctx[3];
+ /* alloc mem */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ ctx[i].magic = magic++;
+ ctx[i].size = max_block(heap) / (sizeof(ctx) / sizeof(ctx[0]) - i);
+ ctx[i].ptr = rt_smem_alloc(&heap->parent, ctx[i].size);
+ uassert_not_null(ctx[i].ptr);
+ rt_memset(ctx[i].ptr, ctx[i].magic, ctx[i].size);
+ }
+ /* All memory has been applied. The remaining memory should be 0 */
+ uassert_int_equal(max_block(heap), 0);
+ /* Verify that the memory data is correct */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ uassert_int_equal(_mem_cmp(ctx[i].ptr, ctx[i].magic, ctx[i].size), 0);
+ }
+ /* Release even address */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ if (i % 2 == 0)
+ {
+ uassert_int_equal(_mem_cmp(ctx[i].ptr, ctx[i].magic, ctx[i].size), 0);
+ rt_smem_free(ctx[i].ptr);
+ uassert_true(max_block(heap) >= ctx[0].size);
+ }
+ }
+ /* Release odd addresses and merge memory blocks */
+ for (i = 0; i < sizeof(ctx) / sizeof(ctx[0]); i++)
+ {
+ if (i % 2 != 0)
+ {
+ uassert_int_equal(_mem_cmp(ctx[i].ptr, ctx[i].magic, ctx[i].size), 0);
+ rt_smem_free(ctx[i].ptr);
+ max_free += ctx[i - 1].size + ctx[i + 1].size;
+ uassert_true(max_block(heap) >= max_free);
+ }
+ }
+ /* Check whether the memory is fully merged */
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /* mem realloc test,Small - > Large */
+ {
+ /* Request a piece of memory for subsequent reallocation operations */
+ struct mem_test_context ctx[3];
+ ctx[0].magic = magic++;
+ ctx[0].size = max_block(heap) / 3;
+ ctx[0].ptr = rt_smem_alloc(&heap->parent, ctx[0].size);
+ uassert_not_null(ctx[0].ptr);
+ rt_memset(ctx[0].ptr, ctx[0].magic, ctx[0].size);
+ /* Apply for a small piece of memory and split the continuous memory */
+ ctx[1].magic = magic++;
+ ctx[1].size = RT_ALIGN_SIZE;
+ ctx[1].ptr = rt_smem_alloc(&heap->parent, ctx[1].size);
+ uassert_not_null(ctx[1].ptr);
+ rt_memset(ctx[1].ptr, ctx[1].magic, ctx[1].size);
+ /* Check whether the maximum memory block is larger than the first piece of memory */
+ uassert_true(max_block(heap) > ctx[0].size);
+ /* Reallocate the first piece of memory */
+ ctx[2].magic = magic++;
+ ctx[2].size = max_block(heap);
+ ctx[2].ptr = rt_smem_realloc(&heap->parent, ctx[0].ptr, ctx[2].size);
+ uassert_not_null(ctx[2].ptr);
+ uassert_int_not_equal(ctx[0].ptr, ctx[2].ptr);
+ uassert_int_equal(_mem_cmp(ctx[2].ptr, ctx[0].magic, ctx[0].size), 0);
+ rt_memset(ctx[2].ptr, ctx[2].magic, ctx[2].size);
+ /* Free the second piece of memory */
+ uassert_int_equal(_mem_cmp(ctx[1].ptr, ctx[1].magic, ctx[1].size), 0);
+ rt_smem_free(ctx[1].ptr);
+ /* Free reallocated memory */
+ uassert_int_equal(_mem_cmp(ctx[2].ptr, ctx[2].magic, ctx[2].size), 0);
+ rt_smem_free(ctx[2].ptr);
+ /* Check memory integrity */
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /* mem realloc test,Large - > Small */
+ {
+ rt_size_t max_free;
+ struct mem_test_context ctx;
+ /* alloc a piece of memory */
+ ctx.magic = magic++;
+ ctx.size = max_block(heap) / 2;
+ ctx.ptr = rt_smem_alloc(&heap->parent, ctx.size);
+ uassert_not_null(ctx.ptr);
+ rt_memset(ctx.ptr, ctx.magic, ctx.size);
+ uassert_int_equal(_mem_cmp(ctx.ptr, ctx.magic, ctx.size), 0);
+ /* Get remaining memory */
+ max_free = max_block(heap);
+ /* Change memory size */
+ ctx.size = ctx.size / 2;
+ uassert_int_equal((rt_ubase_t)rt_smem_realloc(&heap->parent, ctx.ptr, ctx.size), (rt_ubase_t)ctx.ptr);
+ /* Get remaining size */
+ uassert_true(max_block(heap) > max_free);
+ /* Free memory */
+ uassert_int_equal(_mem_cmp(ctx.ptr, ctx.magic, ctx.size), 0);
+ rt_smem_free(ctx.ptr);
+ /* Check memory integrity */
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /* mem realloc test,equal */
+ {
+ rt_size_t max_free;
+ struct mem_test_context ctx;
+ /* alloc a piece of memory */
+ ctx.magic = magic++;
+ ctx.size = max_block(heap) / 2;
+ ctx.ptr = rt_smem_alloc(&heap->parent, ctx.size);
+ uassert_not_null(ctx.ptr);
+ rt_memset(ctx.ptr, ctx.magic, ctx.size);
+ uassert_int_equal(_mem_cmp(ctx.ptr, ctx.magic, ctx.size), 0);
+ /* Get remaining memory */
+ max_free = max_block(heap);
+ /* Do not change memory size */
+ uassert_int_equal((rt_ubase_t)rt_smem_realloc(&heap->parent, ctx.ptr, ctx.size), (rt_ubase_t)ctx.ptr);
+ /* Get remaining size */
+ uassert_true(max_block(heap) == max_free);
+ /* Free memory */
+ uassert_int_equal(_mem_cmp(ctx.ptr, ctx.magic, ctx.size), 0);
+ rt_smem_free(ctx.ptr);
+ /* Check memory integrity */
+ uassert_int_equal(max_block(heap), total_size);
+ }
+ /* small heap deinit */
+ rt_smem_detach(&heap->parent);
+ /* release test resources */
+ rt_free(buf);
+}
+
+struct mem_alloc_context
+{
+ rt_list_t node;
+ rt_size_t size;
+ rt_uint8_t magic;
+};
+
+struct mem_alloc_head
+{
+ rt_list_t list;
+ rt_size_t count;
+ rt_tick_t start;
+ rt_tick_t end;
+ rt_tick_t interval;
+};
+
+#define MEM_RANG_ALLOC_BLK_MIN 2
+#define MEM_RANG_ALLOC_BLK_MAX 5
+#define MEM_RANG_ALLOC_TEST_TIME 5
+
+static void mem_alloc_test(void)
+{
+ struct mem_alloc_head head;
+ rt_uint8_t *buf;
+ struct rt_small_mem *heap;
+ rt_size_t total_size, size;
+ struct mem_alloc_context *ctx;
+
+ /* init */
+ rt_list_init(&head.list);
+ head.count = 0;
+ head.start = rt_tick_get();
+ head.end = rt_tick_get() + rt_tick_from_millisecond(MEM_RANG_ALLOC_TEST_TIME * 1000);
+ head.interval = (head.end - head.start) / 20;
+ buf = rt_malloc(TEST_MEM_SIZE);
+ uassert_not_null(buf);
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)buf, RT_ALIGN_SIZE), (rt_ubase_t)buf);
+ rt_memset(buf, 0xAA, TEST_MEM_SIZE);
+ heap = (struct rt_small_mem *)rt_smem_init("mem_tc", buf, TEST_MEM_SIZE);
+ total_size = max_block(heap);
+ uassert_int_not_equal(total_size, 0);
+ /* test run */
+ while (head.end - head.start < RT_TICK_MAX / 2)
+ {
+ if (rt_tick_get() - head.start >= head.interval)
+ {
+ head.start = rt_tick_get();
+ rt_kprintf("#");
+ }
+ /* %60 probability to perform alloc operation */
+ if (rand() % 10 >= 4)
+ {
+ size = rand() % MEM_RANG_ALLOC_BLK_MAX + MEM_RANG_ALLOC_BLK_MIN;
+ size *= sizeof(struct mem_alloc_context);
+ ctx = rt_smem_alloc(&heap->parent, size);
+ if (ctx == RT_NULL)
+ {
+ if (head.count == 0)
+ {
+ break;
+ }
+ size = head.count / 2;
+ while (size != head.count)
+ {
+ ctx = rt_list_first_entry(&head.list, struct mem_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_smem_free(ctx);
+ head.count --;
+ }
+ continue;
+ }
+ if (RT_ALIGN((rt_ubase_t)ctx, RT_ALIGN_SIZE) != (rt_ubase_t)ctx)
+ {
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)ctx, RT_ALIGN_SIZE), (rt_ubase_t)ctx);
+ }
+ rt_memset(ctx, 0, size);
+ rt_list_init(&ctx->node);
+ ctx->size = size;
+ ctx->magic = rand() & 0xff;
+ if (ctx->size > sizeof(*ctx))
+ {
+ rt_memset(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ rt_list_insert_after(&head.list, &ctx->node);
+ head.count += 1;
+ }
+ else
+ {
+ if (!rt_list_isempty(&head.list))
+ {
+ ctx = rt_list_first_entry(&head.list, struct mem_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_smem_free(ctx);
+ head.count --;
+ }
+ }
+ }
+ while (!rt_list_isempty(&head.list))
+ {
+ ctx = rt_list_first_entry(&head.list, struct mem_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_smem_free(ctx);
+ head.count --;
+ }
+ uassert_int_equal(head.count, 0);
+ uassert_int_equal(max_block(heap), total_size);
+ /* small heap deinit */
+ rt_smem_detach(&heap->parent);
+ /* release test resources */
+ rt_free(buf);
+}
+
+#define MEM_RANG_REALLOC_BLK_MIN 0
+#define MEM_RANG_REALLOC_BLK_MAX 5
+#define MEM_RANG_REALLOC_TEST_TIME 5
+
+struct mem_realloc_context
+{
+ rt_size_t size;
+ rt_uint8_t magic;
+};
+
+struct mem_realloc_head
+{
+ struct mem_realloc_context **ctx_tab;
+ rt_size_t count;
+ rt_tick_t start;
+ rt_tick_t end;
+ rt_tick_t interval;
+};
+
+static void mem_realloc_test(void)
+{
+ struct mem_realloc_head head;
+ rt_uint8_t *buf;
+ struct rt_small_mem *heap;
+ rt_size_t total_size, size, idx;
+ struct mem_realloc_context *ctx;
+ int res;
+
+ size = RT_ALIGN(sizeof(struct mem_realloc_context), RT_ALIGN_SIZE) + RT_ALIGN_SIZE;
+ size = TEST_MEM_SIZE / size;
+ /* init */
+ head.ctx_tab = RT_NULL;
+ head.count = size;
+ head.start = rt_tick_get();
+ head.end = rt_tick_get() + rt_tick_from_millisecond(MEM_RANG_ALLOC_TEST_TIME * 1000);
+ head.interval = (head.end - head.start) / 20;
+ buf = rt_malloc(TEST_MEM_SIZE);
+ uassert_not_null(buf);
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)buf, RT_ALIGN_SIZE), (rt_ubase_t)buf);
+ rt_memset(buf, 0xAA, TEST_MEM_SIZE);
+ heap = (struct rt_small_mem *)rt_smem_init("mem_tc", buf, TEST_MEM_SIZE);
+ total_size = max_block(heap);
+ uassert_int_not_equal(total_size, 0);
+ /* init ctx tab */
+ size = head.count * sizeof(struct mem_realloc_context *);
+ head.ctx_tab = rt_smem_alloc(&heap->parent, size);
+ uassert_not_null(head.ctx_tab);
+ rt_memset(head.ctx_tab, 0, size);
+ /* test run */
+ while (head.end - head.start < RT_TICK_MAX / 2)
+ {
+ if (rt_tick_get() - head.start >= head.interval)
+ {
+ head.start = rt_tick_get();
+ rt_kprintf("#");
+ }
+ size = rand() % MEM_RANG_ALLOC_BLK_MAX + MEM_RANG_ALLOC_BLK_MIN;
+ size *= sizeof(struct mem_realloc_context);
+ idx = rand() % head.count;
+ ctx = rt_smem_realloc(&heap->parent, head.ctx_tab[idx], size);
+ if (ctx == RT_NULL)
+ {
+ if (size == 0)
+ {
+ if (head.ctx_tab[idx])
+ {
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ }
+ else
+ {
+ for (idx = 0; idx < head.count; idx++)
+ {
+ ctx = head.ctx_tab[idx];
+ if (rand() % 2 && ctx)
+ {
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_smem_realloc(&heap->parent, ctx, 0);
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ }
+ }
+ continue;
+ }
+ /* check mem */
+ if (head.ctx_tab[idx] != RT_NULL)
+ {
+ res = 0;
+ if (ctx->size < size)
+ {
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ }
+ else
+ {
+ if (size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, size - sizeof(*ctx));
+ }
+ }
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ /* init mem */
+ ctx->magic = rand() & 0xff;
+ ctx->size = size;
+ if (ctx->size > sizeof(*ctx))
+ {
+ rt_memset(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ head.ctx_tab[idx] = ctx;
+ }
+ /* free all mem */
+ for (idx = 0; idx < head.count; idx++)
+ {
+ ctx = head.ctx_tab[idx];
+ if (ctx == RT_NULL)
+ {
+ continue;
+ }
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_smem_realloc(&heap->parent, ctx, 0);
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ uassert_int_not_equal(max_block(heap), total_size);
+ /* small heap deinit */
+ rt_smem_detach(&heap->parent);
+ /* release test resources */
+ rt_free(buf);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(mem_functional_test);
+ UTEST_UNIT_RUN(mem_alloc_test);
+ UTEST_UNIT_RUN(mem_realloc_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.mem_tc", utest_tc_init, utest_tc_cleanup, 20);
diff --git a/src/utest/memheap_tc.c b/src/utest/memheap_tc.c
new file mode 100644
index 0000000000..a86ebe6867
--- /dev/null
+++ b/src/utest/memheap_tc.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2019-01-16 flybreak the first version
+ */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define HEAP_SIZE (64 * 1024)
+#define HEAP_ALIGN (4)
+#define SLICE_NUM (40)
+#define TEST_TIMES (100000)
+#define HEAP_NAME "heap1"
+#define SLICE_SIZE_MAX (HEAP_SIZE/SLICE_NUM)
+
+static void memheap_test(void)
+{
+ struct rt_memheap heap1;
+ void * ptr_start;
+ void *ptr[SLICE_NUM];
+ int i, cnt = 0;
+
+ /* init heap */
+ ptr_start = rt_malloc_align(HEAP_SIZE, HEAP_ALIGN);
+ if (ptr_start == RT_NULL)
+ {
+ rt_kprintf("totle size too big,can not malloc memory!");
+ return;
+ }
+
+ rt_memheap_init(&heap1, HEAP_NAME, ptr_start, HEAP_SIZE);
+
+ /* test start */
+ for (i = 0; i < SLICE_NUM; i++)
+ {
+ ptr[i] = 0;
+ }
+ /* test alloc */
+ for (i = 0; i < SLICE_NUM; i++)
+ {
+ rt_uint32_t slice_size = rand() % SLICE_SIZE_MAX;
+ ptr[i] = rt_memheap_alloc(&heap1, slice_size);
+ }
+ /* test realloc */
+ while (cnt < TEST_TIMES)
+ {
+ rt_uint32_t slice_size = rand() % SLICE_SIZE_MAX;
+ rt_uint32_t ptr_index = rand() % SLICE_NUM;
+ rt_uint32_t operation = rand() % 2;
+
+ if (ptr[ptr_index])
+ {
+ if (operation == 0) /* free and malloc */
+ {
+ rt_memheap_free(ptr[ptr_index]);
+ ptr[ptr_index] = rt_memheap_alloc(&heap1, slice_size);
+ }
+ else /* realloc */
+ {
+ ptr[ptr_index] = rt_memheap_realloc(&heap1, ptr[ptr_index], slice_size);
+ }
+ }
+ cnt ++;
+ if (cnt % (TEST_TIMES / 10) == 0)
+ {
+ rt_kprintf(">");
+ }
+ }
+
+ rt_kprintf("test OK!\n");
+
+ /* test end */
+ rt_memheap_detach(&heap1);
+ rt_free_align((void *)ptr_start);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(memheap_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.memheap_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/mempool_tc.c b/src/utest/mempool_tc.c
new file mode 100644
index 0000000000..b549d96e1d
--- /dev/null
+++ b/src/utest/mempool_tc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-09-03 Rbb666 the first version for mempool utest
+ */
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define MEMPOOL_BLOCK_SIZE 80
+#define MEMPOOL_BLOCK_COUNT 32
+#define MEMPOOL_SIZE (MEMPOOL_BLOCK_SIZE + sizeof(rt_uint8_t *)) * MEMPOOL_BLOCK_COUNT
+
+static rt_uint8_t mempool_static[MEMPOOL_SIZE];
+static struct rt_mempool mp_static;
+static rt_mp_t mp_dynamic;
+
+/* Static memory pool test */
+static void test_mp_static_init(void)
+{
+ rt_err_t err;
+
+ err = rt_mp_init(&mp_static, "mp_static", &mempool_static[0], sizeof(mempool_static), MEMPOOL_BLOCK_SIZE);
+ uassert_true(err == RT_EOK);
+ uassert_str_equal(mp_static.parent.name, "mp_static");
+ uassert_true(mp_static.block_total_count == MEMPOOL_BLOCK_COUNT);
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Dynamic memory pool test */
+static void test_mp_dynamic_create(void)
+{
+ mp_dynamic = rt_mp_create("mp_dynamic", MEMPOOL_BLOCK_COUNT, MEMPOOL_BLOCK_SIZE);
+ uassert_not_null(mp_dynamic);
+ uassert_str_equal(mp_dynamic->parent.name, "mp_dynamic");
+ uassert_true(mp_dynamic->block_total_count == MEMPOOL_BLOCK_COUNT);
+ uassert_true(mp_dynamic->block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Allocation and free test for static */
+static void test_mp_static_alloc_free(void)
+{
+ void *block1, *block2, *block3;
+
+ /* Allocate blocks */
+ block1 = rt_mp_alloc(&mp_static, 0);
+ uassert_not_null(block1);
+
+ block2 = rt_mp_alloc(&mp_static, 0);
+ uassert_not_null(block2);
+
+ block3 = rt_mp_alloc(&mp_static, 0);
+ uassert_not_null(block3);
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT - 3);
+
+ /* Free blocks */
+ rt_mp_free(block1);
+ rt_mp_free(block2);
+ rt_mp_free(block3);
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Allocation and free test for dynamic */
+static void test_mp_dynamic_alloc_free(void)
+{
+ void *block1, *block2, *block3;
+
+ /* Allocate blocks */
+ block1 = rt_mp_alloc(mp_dynamic, 0);
+ uassert_not_null(block1);
+
+ block2 = rt_mp_alloc(mp_dynamic, 0);
+ uassert_not_null(block2);
+
+ block3 = rt_mp_alloc(mp_dynamic, 0);
+ uassert_not_null(block3);
+
+ /* Check free count */
+ uassert_true(mp_dynamic->block_free_count == MEMPOOL_BLOCK_COUNT - 3);
+
+ /* Free blocks */
+ rt_mp_free(block1);
+ rt_mp_free(block2);
+ rt_mp_free(block3);
+
+ /* Check free count */
+ uassert_true(mp_dynamic->block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Boundary test: allocate all blocks and try to allocate more */
+static void test_mp_boundary_alloc_exceed(void)
+{
+ void *blocks[MEMPOOL_BLOCK_COUNT];
+ void *extra_block;
+ int i;
+
+ /* Allocate all blocks */
+ for (i = 0; i < MEMPOOL_BLOCK_COUNT; i++)
+ {
+ blocks[i] = rt_mp_alloc(&mp_static, 0);
+ uassert_not_null(blocks[i]);
+ }
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == 0);
+
+ /* Try to allocate one more (should fail) */
+ extra_block = rt_mp_alloc(&mp_static, 0);
+ uassert_null(extra_block);
+
+ /* Free all blocks */
+ for (i = 0; i < MEMPOOL_BLOCK_COUNT; i++)
+ {
+ rt_mp_free(blocks[i]);
+ }
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Boundary test: free invalid block */
+static void test_mp_boundary_free_invalid(void)
+{
+ /* Test freeing NULL - should not crash and do nothing */
+ rt_mp_free(RT_NULL);
+
+ /* Check free count remains the same */
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT);
+}
+
+/* Stress test: allocate and free repeatedly */
+static void test_mp_stress_alloc_free(void)
+{
+ void *blocks[MEMPOOL_BLOCK_COUNT];
+ int i, j;
+
+ for (j = 0; j < 100; j++) /* Repeat 100 times */
+ {
+ /* Allocate all blocks */
+ for (i = 0; i < MEMPOOL_BLOCK_COUNT; i++)
+ {
+ blocks[i] = rt_mp_alloc(&mp_static, 0);
+ uassert_not_null(blocks[i]);
+ }
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == 0);
+
+ /* Free all blocks */
+ for (i = 0; i < MEMPOOL_BLOCK_COUNT; i++)
+ {
+ rt_mp_free(blocks[i]);
+ }
+
+ /* Check free count */
+ uassert_true(mp_static.block_free_count == MEMPOOL_BLOCK_COUNT);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ /* Detach static mempool */
+ rt_mp_detach(&mp_static);
+
+ /* Delete dynamic mempool */
+ if (mp_dynamic != RT_NULL)
+ {
+ rt_mp_delete(mp_dynamic);
+ }
+
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_mp_static_init);
+ UTEST_UNIT_RUN(test_mp_dynamic_create);
+ UTEST_UNIT_RUN(test_mp_static_alloc_free);
+ UTEST_UNIT_RUN(test_mp_dynamic_alloc_free);
+ UTEST_UNIT_RUN(test_mp_boundary_alloc_exceed);
+ UTEST_UNIT_RUN(test_mp_boundary_free_invalid);
+ UTEST_UNIT_RUN(test_mp_stress_alloc_free);
+}
+
+UTEST_TC_EXPORT(testcase, "testcases.kernel.mempool", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/messagequeue_tc.c b/src/utest/messagequeue_tc.c
new file mode 100644
index 0000000000..44a9d77453
--- /dev/null
+++ b/src/utest/messagequeue_tc.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-28 Sherman the first version
+ * 2023-09-15 xqyjlj change stack size in cpu64
+ * fix in smp
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+#define THREAD_STACKSIZE UTEST_THR_STACK_SIZE
+
+#define MSG_SIZE 4
+#define MAX_MSGS 5
+
+static struct rt_messagequeue static_mq;
+static rt_uint8_t mq_buf[RT_MQ_BUF_SIZE(MSG_SIZE, MAX_MSGS)];
+
+static struct rt_thread mq_send_thread;
+static struct rt_thread mq_recv_thread;
+static rt_uint8_t mq_send_stack[UTEST_THR_STACK_SIZE];
+static rt_uint8_t mq_recv_stack[UTEST_THR_STACK_SIZE];
+
+static struct rt_event finish_e;
+#define MQSEND_FINISH 0x01
+#define MQRECV_FINIHS 0x02
+
+#ifdef RT_USING_HEAP
+static rt_mq_t dynamic_mq;
+#endif /* RT_USING_HEAP */
+
+static void test_mq_init(void)
+{
+ rt_err_t ret;
+ ret = rt_mq_init(&static_mq,"testmq1", mq_buf, MSG_SIZE, sizeof(mq_buf), RT_IPC_FLAG_FIFO);
+ uassert_true(ret == RT_EOK);
+}
+
+static void test_mq_create(void)
+{
+#ifdef RT_USING_HEAP
+ dynamic_mq = rt_mq_create("testmq2", MSG_SIZE, MAX_MSGS, RT_IPC_FLAG_FIFO);
+ uassert_true(dynamic_mq != RT_NULL);
+#endif /* RT_USING_HEAP */
+}
+
+static void mq_send_case(rt_mq_t testmq)
+{
+ rt_uint32_t send_buf[MAX_MSGS+1] = {0};
+ rt_err_t ret = RT_EOK;
+
+ for (int var = 0; var < MAX_MSGS; ++var)
+ {
+ send_buf[var] = var + 1;
+ ret = rt_mq_send_wait(testmq, &send_buf[var], sizeof(send_buf[0]), RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+ }
+ send_buf[MAX_MSGS] = MAX_MSGS + 1;
+ ret = rt_mq_send(testmq, &send_buf[MAX_MSGS], sizeof(send_buf[0]));
+ uassert_true(ret == -RT_EFULL);
+
+ ret = rt_mq_send_wait(testmq, &send_buf[MAX_MSGS], sizeof(send_buf[0]), RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+
+ while (testmq->entry != 0)
+ {
+ rt_thread_delay(100);
+ }
+
+ ret = rt_mq_send(testmq, &send_buf[1], sizeof(send_buf[0]));
+ uassert_true(ret == RT_EOK);
+
+ ret = rt_mq_send(testmq, &send_buf[2], sizeof(send_buf[0]));
+ uassert_true(ret == RT_EOK);
+
+ ret = rt_mq_urgent(testmq, &send_buf[0], sizeof(send_buf[0]));
+ uassert_true(ret == RT_EOK);
+
+ while (testmq->entry != 0)
+ {
+ rt_thread_delay(100);
+ }
+
+#ifdef RT_USING_MESSAGEQUEUE_PRIORITY
+ ret = rt_mq_send_wait_prio(testmq, &send_buf[3], sizeof(send_buf[0]), 3, 0, RT_UNINTERRUPTIBLE);
+ uassert_true(ret == RT_EOK);
+ ret = rt_mq_send_wait_prio(testmq, &send_buf[0], sizeof(send_buf[0]), 0, 0, RT_UNINTERRUPTIBLE);
+ uassert_true(ret == RT_EOK);
+
+ ret = rt_mq_send_wait_prio(testmq, &send_buf[2], sizeof(send_buf[0]), 1, 0, RT_UNINTERRUPTIBLE);
+ uassert_true(ret == RT_EOK);
+ ret = rt_mq_send_wait_prio(testmq, &send_buf[4], sizeof(send_buf[0]), 4, 0, RT_UNINTERRUPTIBLE);
+ uassert_true(ret == RT_EOK);
+ ret = rt_mq_send_wait_prio(testmq, &send_buf[1], sizeof(send_buf[0]), 1, 0, RT_UNINTERRUPTIBLE);
+ uassert_true(ret == RT_EOK);
+
+ while (testmq->entry != 0)
+ {
+ rt_thread_delay(100);
+ }
+#endif
+
+ ret = rt_mq_send(testmq, &send_buf[1], sizeof(send_buf[0]));
+ uassert_true(ret == RT_EOK);
+ ret = rt_mq_control(testmq, RT_IPC_CMD_RESET, RT_NULL);
+ uassert_true(ret == RT_EOK);
+ uassert_true(testmq->entry == 0);
+}
+
+static void mq_send_entry(void *param)
+{
+ mq_send_case(&static_mq);
+
+#ifdef RT_USING_HEAP
+ if(dynamic_mq != RT_NULL)
+ {
+ mq_send_case(dynamic_mq);
+ }
+#endif /* RT_USING_HEAP */
+
+ rt_event_send(&finish_e, MQSEND_FINISH);
+}
+
+static void mq_recv_case(rt_mq_t testmq)
+{
+ rt_uint32_t recv_buf[MAX_MSGS+1] = {0};
+ rt_ssize_t ret = RT_EOK;
+
+ for (int var = 0; var < MAX_MSGS + 1; ++var)
+ {
+ ret = rt_mq_recv(testmq, &recv_buf[var], sizeof(recv_buf[0]), RT_WAITING_FOREVER);
+ uassert_true(ret >= 0);
+ uassert_true(recv_buf[var] == (var + 1));
+ }
+
+ for (int var = 0; var < 3; ++var)
+ {
+ ret = rt_mq_recv(testmq, &recv_buf[var], sizeof(recv_buf[0]), RT_WAITING_FOREVER);
+ uassert_true(ret >= 0);
+ uassert_true(recv_buf[var] == (var + 1));
+ }
+#ifdef RT_USING_MESSAGEQUEUE_PRIORITY
+ rt_int32_t msg_prio;
+ while (testmq->entry == MAX_MSGS)
+ {
+ rt_thread_delay(100);
+ }
+ for (int var = 0; var < MAX_MSGS; ++var)
+ {
+ ret = rt_mq_recv_prio(testmq, &recv_buf[var], sizeof(recv_buf[0]), &msg_prio, RT_WAITING_FOREVER, RT_UNINTERRUPTIBLE);
+ rt_kprintf("msg_prio = %d\r\n", msg_prio);
+ uassert_true(ret >= 0);
+ uassert_true(recv_buf[var] == (MAX_MSGS - var));
+ }
+#endif
+}
+
+static void mq_recv_entry(void *param)
+{
+ mq_recv_case(&static_mq);
+
+#ifdef RT_USING_HEAP
+ if(dynamic_mq != RT_NULL)
+ {
+ mq_recv_case(dynamic_mq);
+ }
+#endif /* RT_USING_HEAP */
+ rt_event_send(&finish_e, MQRECV_FINIHS);
+}
+
+static void test_mq_testcase(void)
+{
+ rt_thread_startup(&mq_send_thread);
+ rt_thread_startup(&mq_recv_thread);
+
+ rt_event_recv(&finish_e, MQSEND_FINISH | MQRECV_FINIHS, RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, RT_NULL);
+}
+
+static void test_mq_detach(void)
+{
+ rt_err_t ret = rt_mq_detach(&static_mq);
+ uassert_true(ret == RT_EOK);
+}
+
+static void test_mq_delete(void)
+{
+#ifdef RT_USING_HEAP
+ rt_err_t ret = rt_mq_delete(dynamic_mq);
+ uassert_true(ret == RT_EOK);
+#endif /* RT_USING_HEAP */
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_err_t ret ;
+ ret = rt_thread_init(&mq_send_thread, "mq_send", mq_send_entry, RT_NULL, mq_send_stack, sizeof(mq_send_stack), 22, 20);
+ if(ret != RT_EOK)
+ return -RT_ERROR;
+
+ ret = rt_thread_init(&mq_recv_thread, "mq_recv", mq_recv_entry, RT_NULL, mq_recv_stack, sizeof(mq_recv_stack), 23, 20);
+ if(ret != RT_EOK)
+ return -RT_ERROR;
+
+#ifdef RT_USING_SMP
+ rt_thread_control(&mq_send_thread, RT_THREAD_CTRL_BIND_CPU, (void *)0);
+ rt_thread_control(&mq_recv_thread, RT_THREAD_CTRL_BIND_CPU, (void *)0);
+#endif
+
+ ret = rt_event_init(&finish_e, "finish", RT_IPC_FLAG_FIFO);
+ if(ret != RT_EOK)
+ return -RT_ERROR;
+
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_mq_init);
+ UTEST_UNIT_RUN(test_mq_create);
+ UTEST_UNIT_RUN(test_mq_testcase);
+ UTEST_UNIT_RUN(test_mq_detach);
+ UTEST_UNIT_RUN(test_mq_delete);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.messagequeue_tc", utest_tc_init, utest_tc_cleanup, 1000);
diff --git a/src/utest/mtsafe_kprint_tc.c b/src/utest/mtsafe_kprint_tc.c
new file mode 100644
index 0000000000..f6934e6b21
--- /dev/null
+++ b/src/utest/mtsafe_kprint_tc.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-25 Shell the first version
+ */
+#include <rtthread.h>
+#include "utest.h"
+
+#define TEST_LOOP_TIMES 20
+
+static struct rt_semaphore _thr_exit_sem;
+
+static void _thread_entry(void *param)
+{
+ for (size_t i = 0; i < TEST_LOOP_TIMES; i++)
+ {
+ rt_kprintf("This is thread %p\n", rt_thread_self());
+ rt_thread_mdelay(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+#define TEST_THREAD_COUNT 16
+
+static void mtsafe_kprint_tc(void)
+{
+ for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
+ {
+ rt_thread_t new_thread =
+ rt_thread_create(
+ "test",
+ _thread_entry,
+ NULL,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY,
+ 100);
+ rt_thread_startup(new_thread);
+ }
+
+ for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
+ {
+ rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_thr_exit_sem);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(mtsafe_kprint_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.mtsafe_kprint", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/mutex_pi_tc.c b/src/utest/mutex_pi_tc.c
new file mode 100644
index 0000000000..b49e26e22f
--- /dev/null
+++ b/src/utest/mutex_pi_tc.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ */
+#define __RT_IPC_SOURCE__
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#ifdef ARCH_CPU_64BIT
+#define THREAD_STACKSIZE 8192
+#else
+#define THREAD_STACKSIZE 4096
+#endif
+
+#define MUTEX_NUM 3
+#define THREAD_NUM 5
+
+static struct rt_mutex _mutex[MUTEX_NUM];
+static volatile int _sync_flag;
+
+static void test_thread_entry(void *para)
+{
+ while (!_sync_flag)
+ {
+ rt_thread_delay(1);
+ }
+
+ rt_ubase_t thread_id = (rt_ubase_t)para;
+ rt_err_t ret;
+ rt_thread_mdelay(50 + thread_id * 100);
+ ret = rt_mutex_take(&_mutex[thread_id % MUTEX_NUM], RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == RT_SCHED_PRIV(rt_thread_self()).init_priority);
+
+ if (thread_id == 1)
+ {
+ rt_thread_mdelay(100); // wait for main thread re-get _mutex[1]
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+ }
+
+ ret = rt_mutex_release(&_mutex[thread_id % MUTEX_NUM]);
+ uassert_true(ret == RT_EOK);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == RT_SCHED_PRIV(rt_thread_self()).init_priority);
+
+ _sync_flag ++;
+}
+
+static void test_main_thread_entry(void *para)
+{
+ while (!_sync_flag)
+ {
+ rt_thread_delay(1);
+ }
+
+ rt_err_t ret;
+
+ ret = rt_mutex_take(&_mutex[0], RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 12);
+ rt_thread_mdelay(100); // wait for t0 take mutex0
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 12);
+
+ ret = rt_mutex_take(&_mutex[1], RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 12);
+ rt_thread_mdelay(100); // wait for t1 take mutex1
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 9);
+
+ ret = rt_mutex_take(&_mutex[2], RT_WAITING_FOREVER);
+ uassert_true(ret == RT_EOK);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 9);
+ rt_thread_mdelay(100); // wait for t2 take mutex2
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+
+ rt_thread_mdelay(100); // wait for t3 take mutex0
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 7);
+
+ rt_thread_mdelay(100); // wait for t4 take mutex1
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 7);
+
+ rt_thread_mdelay(100);
+ rt_mutex_release(&_mutex[0]); // give _mutex0 to t3
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+
+ rt_thread_mdelay(100);
+ rt_mutex_release(&_mutex[1]); // give _mutex1 to t1
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+
+ rt_thread_mdelay(50);
+ rt_mutex_take(&_mutex[1], RT_WAITING_FOREVER); // re-get _mutex1, which is hold by t1
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+ rt_mutex_release(&_mutex[1]); // give _mutex1 to thread t1
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 8);
+
+ rt_thread_mdelay(100);
+ rt_mutex_release(&_mutex[2]);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 12);
+
+ _sync_flag ++;
+}
+
+static void test_mutex_pi(void)
+{
+ rt_thread_t t_main;
+ rt_thread_t t[THREAD_NUM];
+ rt_uint8_t prio[THREAD_NUM] = {13, 9, 8, 7, 11}; // prio of threads
+
+ for (int i = 0; i < MUTEX_NUM; i++)
+ {
+ rt_mutex_init(&_mutex[i], "test1", 0);
+ }
+
+ _sync_flag = 0;
+
+ t_main = rt_thread_create("t_main", test_main_thread_entry, RT_NULL, THREAD_STACKSIZE, 12, 10000);
+ uassert_true(t_main != RT_NULL);
+ rt_thread_startup(t_main);
+
+ for (rt_ubase_t i = 0; i < THREAD_NUM; i++)
+ {
+ t[i] = rt_thread_create("t", test_thread_entry, (void *)i, THREAD_STACKSIZE, prio[i], 10000);
+ uassert_true(t[i] != RT_NULL);
+ rt_thread_startup(t[i]);
+ }
+
+ _sync_flag = 1;
+
+ while (_sync_flag != THREAD_NUM + 1 + 1)
+ {
+ rt_thread_mdelay(100);
+ }
+
+ for (int i = 0; i < MUTEX_NUM; i++)
+ {
+ rt_mutex_detach(&_mutex[i]);
+ }
+}
+
+static struct rt_mutex _timeout_mutex;
+
+static void test_main_timeout_entry(void *para)
+{
+ rt_err_t ret;
+
+ ret = rt_mutex_take(&_timeout_mutex, RT_WAITING_FOREVER);
+ uassert_true(ret == -RT_EOK);
+ rt_thread_mdelay(100);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 10);
+ rt_thread_mdelay(100);
+ uassert_true(RT_SCHED_PRIV(rt_thread_self()).current_priority == 12);
+ rt_mutex_release(&_timeout_mutex);
+ _sync_flag ++;
+}
+
+static void test_timeout_entry(void *para)
+{
+ rt_err_t ret;
+
+ rt_thread_mdelay(50);
+ ret = rt_mutex_take(&_timeout_mutex, rt_tick_from_millisecond(100));
+ uassert_true(ret == -RT_ETIMEOUT);
+ _sync_flag ++;
+}
+
+static void test_mutex_pi_timeout(void)
+{
+ _sync_flag = 0;
+
+ rt_mutex_init(&_timeout_mutex, "_timeout_mutex", 0);
+
+ rt_thread_t t1 = rt_thread_create("t1", test_main_timeout_entry, RT_NULL, THREAD_STACKSIZE, 12, 10000);
+ uassert_true(t1 != RT_NULL);
+ rt_thread_startup(t1);
+
+ rt_thread_t t2 = rt_thread_create("t2", test_timeout_entry, (void *)t1, THREAD_STACKSIZE, 10, 10000);
+ uassert_true(t2 != RT_NULL);
+ rt_thread_startup(t2);
+
+ while (_sync_flag != 2)
+ {
+ rt_thread_mdelay(100);
+ }
+
+ rt_mutex_detach(&_timeout_mutex);
+}
+
+#define TC_THREAD_NUM 4
+#define TC_MUTEX_NUM TC_THREAD_NUM
+static rt_thread_t t[TC_THREAD_NUM], t_hi_prio;
+static struct rt_mutex m[TC_MUTEX_NUM];
+
+static void test_recursive_mutex_depend_entry(void *para)
+{
+ rt_ubase_t id = (rt_ubase_t)para;
+
+ rt_mutex_take(&m[id], RT_WAITING_FOREVER);
+
+ rt_thread_mdelay(50);
+
+ if (id != 0)
+ {
+ rt_mutex_take(&m[id - 1], RT_WAITING_FOREVER);
+ }
+
+ if (id == 0)
+ {
+ rt_thread_mdelay(250);
+ rt_mutex_release(&m[id]);
+ }
+ else
+ {
+ rt_mutex_release(&m[id - 1]);
+ rt_mutex_release(&m[id]);
+ }
+ _sync_flag ++;
+}
+
+static void test_recursive_mutex_depend_hi_pri_entry(void *para)
+{
+ rt_thread_mdelay(100);
+ rt_err_t err = rt_mutex_take(&m[TC_MUTEX_NUM - 1], rt_tick_from_millisecond(100));
+ uassert_true(err == -RT_ETIMEOUT);
+ _sync_flag ++;
+}
+
+static void test_mutex_pi_recursive_prio_update(void)
+{
+ _sync_flag = 0;
+
+ for (int i = 0; i < TC_MUTEX_NUM; i++)
+ {
+ rt_mutex_init(&m[i], "test", 0);
+ }
+
+ for (rt_ubase_t i = 0; i < TC_THREAD_NUM; i++)
+ {
+ t[i] = rt_thread_create("t", test_recursive_mutex_depend_entry, (void *)i, THREAD_STACKSIZE, 10, 10000);
+ rt_thread_startup(t[i]);
+ }
+ t_hi_prio = rt_thread_create("t", test_recursive_mutex_depend_hi_pri_entry, (void *)RT_NULL, THREAD_STACKSIZE, 3, 10000);
+ rt_thread_startup(t_hi_prio);
+
+ rt_thread_mdelay(150);
+
+ for (int i = 0; i < TC_THREAD_NUM; i++)
+ {
+ uassert_true(RT_SCHED_PRIV(t[i]).current_priority == 3);
+ }
+
+ rt_thread_mdelay(100);
+
+ for (int i = 0; i < TC_THREAD_NUM; i++)
+ {
+ uassert_true(RT_SCHED_PRIV(t[i]).current_priority == 10);
+ }
+
+ while (_sync_flag != TC_THREAD_NUM + 1)
+ {
+ rt_thread_mdelay(100);
+ }
+
+ for (int i = 0; i < TC_MUTEX_NUM; i++)
+ {
+ rt_mutex_detach(&m[i]);
+ }
+ _sync_flag ++;
+}
+
+static void test_mutex_waiter_to_wakeup_entry(void *para)
+{
+ rt_thread_mdelay(100);
+ rt_err_t err = rt_mutex_take(&m[TC_MUTEX_NUM - 1], RT_WAITING_FOREVER);
+ uassert_true(err == -RT_EINTR);
+ _sync_flag ++;
+}
+
+static void wakeup_func(void *para)
+{
+ rt_thread_resume(t_hi_prio);
+}
+static void test_mutex_pi_wakeup_mutex_waiter(void)
+{
+ struct rt_timer wakeup_timer;
+
+ _sync_flag = 0;
+
+ for (int i = 0; i < TC_MUTEX_NUM; i++)
+ {
+ rt_mutex_init(&m[i], "test", 0);
+ }
+
+ for (rt_ubase_t i = 0; i < TC_THREAD_NUM; i++)
+ {
+ t[i] = rt_thread_create("t", test_recursive_mutex_depend_entry, (void *)i, THREAD_STACKSIZE, 10, 10000);
+ rt_thread_startup(t[i]);
+ }
+ t_hi_prio = rt_thread_create("t", test_mutex_waiter_to_wakeup_entry, (void *)RT_NULL, THREAD_STACKSIZE, 3, 10000);
+ rt_thread_startup(t_hi_prio);
+
+ rt_timer_init(&wakeup_timer, "wakeup_timer", wakeup_func, RT_NULL, rt_tick_from_millisecond(200), RT_TIMER_FLAG_ONE_SHOT);
+ rt_timer_start(&wakeup_timer);
+ rt_thread_mdelay(150);
+
+ for (int i = 0; i < TC_THREAD_NUM; i++)
+ {
+ uassert_true(RT_SCHED_PRIV(t[i]).current_priority == 3);
+ }
+
+ rt_thread_mdelay(100);
+
+ for (int i = 0; i < TC_THREAD_NUM; i++)
+ {
+ uassert_true(RT_SCHED_PRIV(t[i]).current_priority == 10);
+ }
+
+ while (_sync_flag != TC_THREAD_NUM + 1)
+ {
+ rt_thread_mdelay(100);
+ }
+
+ for (int i = 0; i < TC_MUTEX_NUM; i++)
+ {
+ rt_mutex_detach(&m[i]);
+ }
+ rt_timer_detach(&wakeup_timer);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_mutex_pi);
+ UTEST_UNIT_RUN(test_mutex_pi_recursive_prio_update);
+ UTEST_UNIT_RUN(test_mutex_pi_timeout);
+ UTEST_UNIT_RUN(test_mutex_pi_wakeup_mutex_waiter);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.mutex_pi_tc", utest_tc_init, utest_tc_cleanup, 1000);
+
+/********************* end of file ************************/
diff --git a/src/utest/mutex_tc.c b/src/utest/mutex_tc.c
new file mode 100644
index 0000000000..00fbec68fd
--- /dev/null
+++ b/src/utest/mutex_tc.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-09.01 luckyzjq the first version
+ * 2023-09-15 xqyjlj change stack size in cpu64
+ */
+#define __RT_IPC_SOURCE__
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#ifdef ARCH_CPU_64BIT
+#define THREAD_STACKSIZE 8192
+#else
+#define THREAD_STACKSIZE 4096
+#endif
+
+static struct rt_mutex static_mutex;
+
+#ifdef RT_USING_HEAP
+static rt_mutex_t dynamic_mutex;
+#endif /* RT_USING_HEAP */
+
+static volatile int _sync_flag;
+
+/* init test */
+static void test_static_mutex_init(void)
+{
+ rt_err_t result = -RT_ERROR;
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+/* static take test */
+static void static_mutex_take_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ int rand_num = rand() % 0x1000;
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, rand_num);
+ if (RT_EOK == result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+
+static void test_static_mutex_take(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* take mutex and not release */
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ static_mutex_take_entry,
+ &static_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread take second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* static release test */
+static void static_mutex_release_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ int rand_num = rand() % 0x1000;
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, rand_num);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+static void test_static_mutex_release(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ result = rt_mutex_release(&static_mutex);
+ uassert_true(result < 0);
+
+ /* take mutex */
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ /* release mutex */
+ result = rt_mutex_release(&static_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ static_mutex_release_entry,
+ &static_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread and take mutex second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* static trytake test */
+static void static_mutex_trytake_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_trytake(mutex);
+ if (RT_EOK == result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+static void test_static_mutex_trytake(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* take mutex and not release */
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ static_mutex_trytake_entry,
+ &static_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread and trytake mutex second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+static rt_thread_t tid1 = RT_NULL;
+static rt_thread_t tid2 = RT_NULL;
+static rt_thread_t tid3 = RT_NULL;
+
+/* static mutex priority reverse test */
+static void static_thread1_entry(void *param)
+{
+ /* let system schedule */
+ rt_thread_mdelay(100);
+
+ /* thread3 hode mutex thread2 take mutex */
+ /* check thread2 and thread3 priority */
+ if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority)
+ {
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ uassert_true(RT_TRUE);
+ }
+ _sync_flag++;
+}
+
+static void static_thread2_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex = (rt_mutex_t)param;
+
+ /* let system schedule */
+ rt_thread_mdelay(50);
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ if (result == RT_EOK)
+ {
+ rt_mutex_release(mutex);
+ }
+ _sync_flag++;
+}
+static void static_thread3_entry(void *param)
+{
+ rt_tick_t tick;
+ rt_err_t result;
+ rt_mutex_t mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ if (result != RT_EOK)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ tick = rt_tick_get();
+ while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2));
+
+ rt_mutex_release(mutex);
+ _sync_flag++;
+}
+
+static void test_static_pri_reverse(void)
+{
+ rt_err_t result;
+ tid1 = RT_NULL;
+ tid2 = RT_NULL;
+ tid3 = RT_NULL;
+
+ _sync_flag = 0;
+
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* thread1 */
+ tid1 = rt_thread_create("thread1",
+ static_thread1_entry,
+ &static_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10 - 1,
+ 10);
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ /* thread2 */
+ tid2 = rt_thread_create("thread2",
+ static_thread2_entry,
+ &static_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10,
+ 10);
+ if (tid2 != RT_NULL)
+ rt_thread_startup(tid2);
+
+ /* thread3 */
+ tid3 = rt_thread_create("thread3",
+ static_thread3_entry,
+ &static_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10 + 1,
+ 10);
+ if (tid3 != RT_NULL)
+ rt_thread_startup(tid3);
+
+ while (_sync_flag != 3)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_detach(&static_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* create test */
+static void test_dynamic_mutex_create(void)
+{
+ rt_err_t result = -RT_ERROR;
+
+ /* PRIO mode */
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ /* FIFO mode */
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+/* dynamic take test */
+static void dynamic_mutex_take_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ int rand_num = rand() % 0x1000;
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, rand_num);
+ if (RT_EOK == result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+
+static void test_dynamic_mutex_take(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* take mutex and not release */
+ result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ dynamic_mutex_take_entry,
+ dynamic_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread take second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* dynamic release test */
+static void dynamic_mutex_release_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ int rand_num = rand() % 0x1000;
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, rand_num);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+static void test_dynamic_mutex_release(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ result = rt_mutex_release(dynamic_mutex);
+ uassert_true(result < 0);
+
+ /* take mutex */
+ result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ /* release mutex */
+ result = rt_mutex_release(dynamic_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ dynamic_mutex_release_entry,
+ dynamic_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread and take mutex second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* dynamic trytake test */
+static void dynamic_mutex_trytake_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex;
+
+ mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_trytake(mutex);
+ if (RT_EOK == result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ _sync_flag++;
+}
+static void test_dynamic_mutex_trytake(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* take mutex and not release */
+ result = rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ dynamic_mutex_trytake_entry,
+ dynamic_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ if (RT_NULL == tid)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* startup thread and trytake mutex second */
+ rt_thread_startup(tid);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+/* dynamic mutex priority reverse test */
+static void dynamic_thread1_entry(void *param)
+{
+ /* let system schedule */
+ rt_thread_mdelay(100);
+
+ /* thread3 hode mutex thread2 take mutex */
+ /* check thread2 and thread3 priority */
+ if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority)
+ {
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ uassert_true(RT_TRUE);
+ }
+ _sync_flag++;
+}
+
+static void dynamic_thread2_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex = (rt_mutex_t)param;
+
+ /* let system schedule */
+ rt_thread_mdelay(50);
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ if (result == RT_EOK)
+ {
+ rt_mutex_release(mutex);
+ }
+ _sync_flag++;
+}
+static void dynamic_thread3_entry(void *param)
+{
+ rt_tick_t tick;
+ rt_err_t result;
+ rt_mutex_t mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ if (result != RT_EOK)
+ {
+ uassert_true(RT_FALSE);
+ }
+
+ tick = rt_tick_get();
+ while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2));
+
+ rt_mutex_release(mutex);
+ _sync_flag++;
+}
+
+static void test_dynamic_pri_reverse(void)
+{
+ rt_err_t result;
+ tid1 = RT_NULL;
+ tid2 = RT_NULL;
+ tid3 = RT_NULL;
+
+ _sync_flag = 0;
+ dynamic_mutex = rt_mutex_create("dynamic_mutex", RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_mutex)
+ {
+ uassert_true(RT_FALSE);
+ return;
+ }
+
+ /* thread1 */
+ tid1 = rt_thread_create("thread1",
+ dynamic_thread1_entry,
+ dynamic_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10 - 1,
+ 10);
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ /* thread2 */
+ tid2 = rt_thread_create("thread2",
+ dynamic_thread2_entry,
+ dynamic_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10,
+ 10);
+ if (tid2 != RT_NULL)
+ rt_thread_startup(tid2);
+
+ /* thread3 */
+ tid3 = rt_thread_create("thread3",
+ dynamic_thread3_entry,
+ dynamic_mutex,
+ UTEST_THR_STACK_SIZE,
+ 10 + 1,
+ 10);
+ if (tid3 != RT_NULL)
+ rt_thread_startup(tid3);
+
+ while (_sync_flag != 3)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_delete(dynamic_mutex);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ uassert_true(RT_TRUE);
+}
+
+static void recursive_lock_test_entry(void *param)
+{
+ rt_err_t result;
+ rt_mutex_t mutex = (rt_mutex_t)param;
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ uassert_true(result == RT_EOK);
+ uassert_true(_sync_flag == 0);
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ uassert_true(result == RT_EOK);
+ _sync_flag++;
+}
+
+static void test_recurse_lock(void)
+{
+ rt_err_t result;
+
+ _sync_flag = 0;
+ result = rt_mutex_init(&static_mutex, "static_mutex", RT_IPC_FLAG_PRIO);
+ uassert_true(result == RT_EOK);
+
+ /* take mutex and not release */
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ uassert_true(result == RT_EOK);
+
+ /* take mutex twice */
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_t tid = rt_thread_create("mutex_th",
+ recursive_lock_test_entry,
+ &static_mutex,
+ THREAD_STACKSIZE,
+ 10,
+ 10);
+ _sync_flag = -1;
+
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+
+ result = rt_mutex_release(&static_mutex);
+ uassert_true(result == RT_EOK);
+
+ _sync_flag = 0;
+
+ result = rt_mutex_release(&static_mutex);
+ uassert_true(result == RT_EOK);
+
+ while (_sync_flag != 1)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ result = rt_mutex_take(&static_mutex, RT_WAITING_FOREVER);
+ uassert_true(result == RT_EOK);
+
+ result = rt_mutex_detach(&static_mutex);
+ uassert_true(result == RT_EOK);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+#ifdef RT_USING_HEAP
+ dynamic_mutex = RT_NULL;
+#endif /* RT_USING_HEAP */
+
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+#ifdef RT_USING_HEAP
+ dynamic_mutex = RT_NULL;
+#endif /* RT_USING_HEAP */
+
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_static_mutex_init);
+ UTEST_UNIT_RUN(test_static_mutex_take);
+ UTEST_UNIT_RUN(test_static_mutex_release);
+ UTEST_UNIT_RUN(test_static_mutex_trytake);
+ UTEST_UNIT_RUN(test_static_pri_reverse);
+#ifdef RT_USING_HEAP
+ UTEST_UNIT_RUN(test_dynamic_mutex_create);
+ UTEST_UNIT_RUN(test_dynamic_mutex_take);
+ UTEST_UNIT_RUN(test_dynamic_mutex_release);
+ UTEST_UNIT_RUN(test_dynamic_mutex_trytake);
+ UTEST_UNIT_RUN(test_dynamic_pri_reverse);
+#endif
+ UTEST_UNIT_RUN(test_recurse_lock);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.mutex_tc", utest_tc_init, utest_tc_cleanup, 1000);
+
+/********************* end of file ************************/
diff --git a/src/utest/object_tc.c b/src/utest/object_tc.c
new file mode 100644
index 0000000000..e710d132e2
--- /dev/null
+++ b/src/utest/object_tc.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-07-18 kurisaW First commit
+ */
+
+#include <utest.h>
+#include <rtthread.h>
+#include <string.h>
+
+/**
+ * @brief Comprehensive test suite for RT-Thread object system
+ *
+ * @note This test suite validates:
+ * 1. Object name handling (truncation, NULL, exact length)
+ * 2. Static and dynamic object initialization
+ * 3. Object finding functionality (rt_object_find, rt_thread_find, rt_device_find)
+ * 4. Object information and enumeration
+ * 5. Type checking and system object detection
+ * 6. Memory safety and boundary conditions
+ */
+
+/* Define TEST_RT_NAME_MAX for testing purposes */
+#define TEST_RT_NAME_MAX RT_NAME_MAX
+
+/* Global counter for unique object names */
+static rt_uint32_t name_counter = 0;
+
+/* Generate unique name to avoid conflicts, respecting TEST_RT_NAME_MAX */
+static rt_err_t generate_unique_name(char *buf, rt_size_t size, const char *prefix)
+{
+ if (!buf || !prefix || size < TEST_RT_NAME_MAX)
+ return -RT_EINVAL;
+
+ for (int i = 0; i < 1000; i++) /* Limit attempts to prevent infinite loop */
+ {
+ rt_snprintf(buf, size, "%s%d", prefix, name_counter++);
+ rt_size_t len = rt_strlen(buf);
+ if (len >= TEST_RT_NAME_MAX)
+ {
+ buf[TEST_RT_NAME_MAX - 1] = '\0';
+ len = TEST_RT_NAME_MAX - 1;
+ }
+
+ /* Check if name (or truncated name) is unique */
+ if (rt_object_find(buf, RT_Object_Class_Unknown) == RT_NULL &&
+ rt_thread_find(buf) == RT_NULL &&
+ rt_device_find(buf) == RT_NULL)
+ {
+ return RT_EOK;
+ }
+ }
+ return -RT_ENOMEM;
+}
+
+static void test_object_name_handling(void)
+{
+ struct rt_object static_obj1, static_obj2, static_obj3;
+ rt_object_t dyn_obj = RT_NULL;
+ char test_name[TEST_RT_NAME_MAX];
+ char unique_name[TEST_RT_NAME_MAX];
+
+ /* Prepare a test name within TEST_RT_NAME_MAX */
+ rt_memset(test_name, 'A', TEST_RT_NAME_MAX - 1);
+ test_name[TEST_RT_NAME_MAX - 1] = '\0';
+
+ /* Test 1: Static Object with Name Within TEST_RT_NAME_MAX */
+ uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "long") == RT_EOK);
+ rt_object_init(&static_obj1, RT_Object_Class_Thread, test_name);
+ uassert_true(rt_strlen(static_obj1.name) <= TEST_RT_NAME_MAX - 1);
+ uassert_true(static_obj1.name[TEST_RT_NAME_MAX - 1] == '\0');
+ uassert_true(rt_strncmp(static_obj1.name, test_name, TEST_RT_NAME_MAX - 1) == 0);
+ uassert_true(rt_object_is_systemobject(&static_obj1));
+ rt_object_detach(&static_obj1);
+
+ /* Test 2: Dynamic Object with Name Within TEST_RT_NAME_MAX */
+ uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "dyn") == RT_EOK);
+ dyn_obj = rt_object_allocate(RT_Object_Class_Thread, test_name);
+ uassert_not_null(dyn_obj);
+ uassert_true(rt_strlen(dyn_obj->name) <= TEST_RT_NAME_MAX - 1);
+ uassert_true(dyn_obj->name[TEST_RT_NAME_MAX - 1] == '\0');
+ uassert_true(rt_strncmp(dyn_obj->name, test_name, TEST_RT_NAME_MAX - 1) == 0);
+ uassert_false(rt_object_is_systemobject(dyn_obj));
+ rt_object_delete(dyn_obj);
+
+ /* Test 3: NULL Name Handling */
+ uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "null") == RT_EOK);
+ rt_object_init(&static_obj2, RT_Object_Class_Thread, NULL);
+ uassert_true(static_obj2.name[0] == '\0');
+ rt_object_detach(&static_obj2);
+
+ uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "dynull") == RT_EOK);
+ dyn_obj = rt_object_allocate(RT_Object_Class_Thread, NULL);
+ uassert_not_null(dyn_obj);
+ uassert_true(dyn_obj->name[0] == '\0');
+ rt_object_delete(dyn_obj);
+
+ /* Test 4: Exact Length Name */
+ char exact_name[TEST_RT_NAME_MAX];
+ rt_memset(exact_name, 'B', TEST_RT_NAME_MAX - 1);
+ exact_name[TEST_RT_NAME_MAX - 1] = '\0';
+ uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "exact") == RT_EOK);
+ rt_object_init(&static_obj3, RT_Object_Class_Thread, exact_name);
+ uassert_str_equal(static_obj3.name, exact_name);
+ rt_object_detach(&static_obj3);
+}
+
+static void test_object_find_operations(void)
+{
+ rt_object_t found;
+ rt_thread_t found_thread;
+ rt_device_t found_device;
+ char name[TEST_RT_NAME_MAX];
+ rt_err_t ret;
+
+ /* Scenario 1: Object Name Within TEST_RT_NAME_MAX */
+ /* Test 1.1: Find static thread object with rt_object_find */
+ struct rt_object static_obj;
+ uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "sobj") == RT_EOK);
+ rt_object_init(&static_obj, RT_Object_Class_Thread, name);
+ found = rt_object_find(name, RT_Object_Class_Thread);
+ uassert_not_null(found);
+ uassert_ptr_equal(found, &static_obj);
+ uassert_str_equal(found->name, name);
+ rt_object_detach(&static_obj);
+
+ /* Test 1.2: Find thread object with rt_thread_find */
+ uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "thr") == RT_EOK);
+ rt_thread_t thread = rt_thread_create(name, RT_NULL, RT_NULL, 1024, 20, 10);
+ uassert_not_null(thread);
+ found_thread = rt_thread_find(name);
+ uassert_not_null(found_thread);
+ uassert_ptr_equal(found_thread, thread);
+ uassert_str_equal(found_thread->parent.name, name);
+ rt_thread_delete(thread);
+
+#ifdef RT_USING_DEVICE
+ /* Test 1.3: Find device object with rt_device_find */
+ struct rt_device device;
+ uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "dev") == RT_EOK);
+ ret = rt_device_register(&device, name, RT_DEVICE_FLAG_RDONLY);
+ uassert_int_equal(ret, RT_EOK);
+ found_device = rt_device_find(name);
+ uassert_not_null(found_device);
+ uassert_ptr_equal(found_device, &device);
+ uassert_str_equal(found_device->parent.name, name);
+ rt_device_unregister(&device);
+#endif
+
+ /* Test 2: Same Prefix Within TEST_RT_NAME_MAX */
+#ifdef RT_USING_DEVICE
+ struct rt_device dev1, dev2;
+ char name1[TEST_RT_NAME_MAX] = "norflash1";
+ char name2[TEST_RT_NAME_MAX] = "norflash2";
+ ret = rt_device_register(&dev1, name1, RT_DEVICE_FLAG_RDONLY);
+ uassert_int_equal(ret, RT_EOK);
+ ret = rt_device_register(&dev2, name2, RT_DEVICE_FLAG_RDONLY);
+ uassert_int_equal(ret, RT_EOK); /* Expect success if RT-Thread allows distinct names */
+ found_device = rt_device_find(name1);
+ uassert_not_null(found_device);
+ uassert_ptr_equal(found_device, &dev1);
+ uassert_str_equal(found_device->parent.name, name1);
+ found_device = rt_device_find(name2);
+ uassert_not_null(found_device);
+ uassert_ptr_equal(found_device, &dev2);
+ uassert_str_equal(found_device->parent.name, name2);
+ rt_device_unregister(&dev1);
+ rt_device_unregister(&dev2);
+#endif
+
+ /* Test 3: Find non-existent object */
+ const char *nonexist_name = "nonexist";
+ uassert_true(rt_strlen(nonexist_name) <= TEST_RT_NAME_MAX - 1);
+ found = rt_object_find(nonexist_name, RT_Object_Class_Thread);
+ uassert_null(found);
+
+ /* Test 4: Find with NULL name */
+ found = rt_object_find(NULL, RT_Object_Class_Thread);
+ uassert_null(found);
+ found_thread = rt_thread_find(NULL);
+ uassert_null(found_thread);
+
+#ifdef RT_USING_DEVICE
+ found_device = rt_device_find(NULL);
+ uassert_null(found_device);
+#endif
+}
+
+static void test_object_info_enumeration(void)
+{
+ struct rt_object static_objs[3];
+ rt_object_t dyn_objs[2] = {RT_NULL, RT_NULL};
+ char names[5][TEST_RT_NAME_MAX];
+
+ /* Generate unique names */
+ for (int i = 0; i < 5; i++)
+ {
+ uassert_true(generate_unique_name(names[i], TEST_RT_NAME_MAX, "enum") == RT_EOK);
+ }
+
+ /* Create test objects */
+ for (int i = 0; i < 3; i++)
+ {
+ rt_object_init(&static_objs[i], RT_Object_Class_Thread, names[i]);
+ }
+ for (int i = 0; i < 2; i++)
+ {
+ dyn_objs[i] = rt_object_allocate(RT_Object_Class_Thread, names[i + 3]);
+ uassert_not_null(dyn_objs[i]);
+ }
+
+ /* Test 1: Get object information */
+ struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Thread);
+ uassert_not_null(info);
+ uassert_int_equal(info->type, RT_Object_Class_Thread);
+
+ /* Test 2: Get object count */
+ int count = rt_object_get_length(RT_Object_Class_Thread);
+ uassert_true(count >= 5);
+
+ /* Test 3: Get object pointers with sufficient buffer */
+ rt_object_t *objects = (rt_object_t *)rt_malloc((count + 2) * sizeof(rt_object_t));
+ uassert_not_null(objects);
+ int ret = rt_object_get_pointers(RT_Object_Class_Thread, objects, count + 2);
+ uassert_int_equal(ret, count);
+ int found_count = 0;
+ for (int i = 0; i < ret; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ if (objects[i] == &static_objs[j]) found_count++;
+ for (int j = 0; j < 2; j++)
+ if (objects[i] == dyn_objs[j]) found_count++;
+ }
+ uassert_int_equal(found_count, 5);
+ rt_free(objects);
+
+ /* Test 4: Get object pointers with small buffer */
+ rt_object_t one_object[1];
+ ret = rt_object_get_pointers(RT_Object_Class_Thread, one_object, 1);
+ uassert_true(ret <= 1);
+
+ /* Test 5: Empty container (Semaphore) */
+#ifdef RT_USING_SEMAPHORE
+ int empty_count = rt_object_get_length(RT_Object_Class_Semaphore);
+ uassert_true(empty_count >= 0);
+#endif
+
+ /* Cleanup */
+ for (int i = 0; i < 3; i++)
+ rt_object_detach(&static_objs[i]);
+ for (int i = 0; i < 2; i++)
+ if (dyn_objs[i]) rt_object_delete(dyn_objs[i]);
+}
+
+static void test_object_type_handling(void)
+{
+ struct rt_object obj;
+ char name[TEST_RT_NAME_MAX];
+ uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "typ") == RT_EOK);
+ rt_object_init(&obj, RT_Object_Class_Thread, name);
+
+ /* Test 1: Get object type */
+ uassert_int_equal(rt_object_get_type(&obj), RT_Object_Class_Thread);
+
+ /* Test 2: Check system object */
+ uassert_true(rt_object_is_systemobject(&obj));
+
+ /* Test 3: Get name with sufficient buffer */
+ char name_buf[TEST_RT_NAME_MAX];
+ rt_err_t ret = rt_object_get_name(&obj, name_buf, sizeof(name_buf));
+ uassert_int_equal(ret, RT_EOK);
+ uassert_str_equal(name_buf, name);
+
+ /* Test 4: Get name with small buffer */
+ char small_buf[4] = {0};
+ ret = rt_object_get_name(&obj, small_buf, sizeof(small_buf));
+ uassert_int_equal(ret, RT_EOK);
+ uassert_true(rt_strncmp(small_buf, name, sizeof(small_buf) - 1) == 0);
+ uassert_true(small_buf[sizeof(small_buf) - 1] == '\0');
+
+ /* Test 5: Get name with invalid parameters */
+ ret = rt_object_get_name(RT_NULL, name_buf, sizeof(name_buf));
+ uassert_int_equal(ret, -RT_EINVAL);
+ ret = rt_object_get_name(&obj, NULL, sizeof(name_buf));
+ uassert_int_equal(ret, -RT_EINVAL);
+ ret = rt_object_get_name(&obj, name_buf, 0);
+ uassert_int_equal(ret, -RT_EINVAL);
+
+ rt_object_detach(&obj);
+}
+
+static rt_err_t testcase_init(void)
+{
+ if (!rt_scheduler_is_available())
+ {
+ return -RT_ERROR;
+ }
+ name_counter = 0;
+ return RT_EOK;
+}
+
+static rt_err_t testcase_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void test_object_suite(void)
+{
+#if RT_NAME_MAX < 10
+ rt_kprintf("Error: Please increase \'RT_NAME_MAX\' to be greater than 10.\n");
+ return;
+#endif
+ UTEST_UNIT_RUN(test_object_name_handling);
+ UTEST_UNIT_RUN(test_object_find_operations);
+ UTEST_UNIT_RUN(test_object_info_enumeration);
+ UTEST_UNIT_RUN(test_object_type_handling);
+}
+UTEST_TC_EXPORT(test_object_suite, "testcases.kernel.object_test", testcase_init, testcase_cleanup, 20); \ No newline at end of file
diff --git a/src/utest/sched_mtx_tc.c b/src/utest/sched_mtx_tc.c
new file mode 100644
index 0000000000..488485a39f
--- /dev/null
+++ b/src/utest/sched_mtx_tc.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-01-17 Shell the first version
+ */
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+/**
+ * Stressful Test for Mutex
+ */
+
+#define TEST_SECONDS 30
+#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND)
+#define TEST_THREAD_COUNTS (RT_CPUS_NR)
+#define TEST_PROGRESS_COUNTS (36)
+#define TEST_PROGRESS_ON (TEST_LOOP_TICKS/TEST_PROGRESS_COUNTS)
+#define TEST_PRIORITY_HIGHEST (UTEST_THR_PRIORITY+1)
+#define TEST_RANDOM_LATENCY_MAX (1000 * 1000)
+
+static struct rt_semaphore _thr_exit_sem;
+static rt_atomic_t _progress_counter;
+static rt_atomic_t _exit_flag;
+static struct rt_mutex _racing_lock;
+
+static void test_thread_entry(void *param)
+{
+ while (1)
+ {
+ rt_mutex_take(&_racing_lock, RT_WAITING_FOREVER);
+ rt_mutex_release(&_racing_lock);
+
+ if (rt_atomic_load(&_exit_flag))
+ {
+ break;
+ }
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+}
+
+static void mutex_stress_tc(void)
+{
+ rt_err_t error;
+ rt_thread_t tester;
+ const rt_base_t priority_base = TEST_PRIORITY_HIGHEST;
+
+ for (size_t i = 0; i < TEST_THREAD_COUNTS; i++)
+ {
+ tester = rt_thread_create(
+ "tester",
+ test_thread_entry,
+ (void *)0,
+ UTEST_THR_STACK_SIZE,
+ priority_base + (i % (RT_THREAD_PRIORITY_MAX - TEST_PRIORITY_HIGHEST)),
+ 1);
+
+ rt_thread_startup(tester);
+ }
+
+ for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
+ {
+ rt_thread_delay(1);
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ /* trigger exit request for all sub-threads */
+ rt_atomic_store(&_exit_flag, 1);
+
+ /* waiting for sub-threads to exit */
+ for (size_t i = 0; i < TEST_THREAD_COUNTS; i++)
+ {
+ error = rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
+ uassert_int_equal(error, RT_EOK);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ int *pseed = rt_malloc(sizeof(int));
+ srand(*(int *)pseed);
+ rt_free(pseed);
+
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+ rt_mutex_init(&_racing_lock, "ipc", RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_thr_exit_sem);
+ rt_mutex_detach(&_racing_lock);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(mutex_stress_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.mutex", utest_tc_init, utest_tc_cleanup, TEST_SECONDS);
diff --git a/src/utest/sched_sem_tc.c b/src/utest/sched_sem_tc.c
new file mode 100644
index 0000000000..f071e143fe
--- /dev/null
+++ b/src/utest/sched_sem_tc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-01-17 Shell the first version
+ */
+#define __RT_IPC_SOURCE__
+
+#include <rtthread.h>
+#include "rthw.h"
+#include "utest.h"
+
+#define KERN_TEST_CONFIG_LOOP_TIMES 160
+#define KERN_TEST_CONCURRENT_THREADS (RT_CPUS_NR * 2)
+#define KERN_TEST_CONFIG_HIGHEST_PRIO 3
+#define KERN_TEST_CONFIG_LOWEST_PRIO (RT_THREAD_PRIORITY_MAX - 2)
+
+#define TEST_LEVEL_COUNTS (KERN_TEST_CONFIG_LOWEST_PRIO - KERN_TEST_CONFIG_HIGHEST_PRIO + 1)
+#if TEST_LEVEL_COUNTS <= RT_CPUS_NR
+#warning for the best of this test, TEST_LEVEL_COUNTS should greater than RT_CPUS_NR
+#endif
+#if KERN_TEST_CONCURRENT_THREADS < RT_CPUS_NR
+#warning for the best of this test, KERN_TEST_CONCURRENT_THREADS should greater than RT_CPUS_NR
+#endif
+#if KERN_TEST_CONFIG_LOWEST_PRIO >= RT_THREAD_PRIORITY_MAX - 1
+#error the thread priority should at least be greater than idle
+#endif
+
+static rt_atomic_t _star_counter;
+static struct rt_semaphore _thr_exit_sem;
+static struct rt_semaphore _level_waiting[TEST_LEVEL_COUNTS];
+static rt_thread_t _thread_matrix[TEST_LEVEL_COUNTS][KERN_TEST_CONCURRENT_THREADS];
+static rt_atomic_t _load_average[RT_CPUS_NR];
+
+static void _print_char(rt_thread_t thr_self, int character)
+{
+ rt_base_t current_counter;
+
+#ifdef RT_USING_SMP
+ rt_kprintf("%c%d", character, RT_SCHED_CTX(thr_self).oncpu);
+#else
+ rt_kprintf("%c0", character);
+#endif /* RT_USING_SMP */
+
+ current_counter = rt_atomic_add(&_star_counter, 1);
+ if (current_counter % 30 == 0)
+ {
+ rt_kprintf("\n");
+ }
+}
+
+static void _stats_load_avg_inc(void)
+{
+ int cpuid;
+
+ cpuid = rt_hw_cpu_id();
+ rt_atomic_add(&_load_average[cpuid], 1);
+}
+
+static void _stats_load_avg_print(void)
+{
+ rt_base_t counts = 0;
+ const rt_base_t total_test_counts = KERN_TEST_CONFIG_LOOP_TIMES * TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS;
+
+ for (size_t i = 0; i < RT_CPUS_NR; i++)
+ {
+ rt_kprintf("%ld ", _load_average[i]);
+ counts += _load_average[i];
+ }
+
+ rt_kprintf("\n");
+ uassert_int_equal(counts, total_test_counts);
+}
+
+static void _thread_entry(void *param)
+{
+ int level = (rt_ubase_t)param;
+ rt_thread_t thr_self = rt_thread_self();
+
+ if (level == 0)
+ {
+ /* always the first to execute among other working threads */
+ for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++)
+ {
+ /* notify our consumer */
+ rt_sem_release(&_level_waiting[level + 1]);
+
+ _stats_load_avg_inc();
+
+ /* waiting for resource of ours */
+ rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER);
+ }
+ }
+ else if (level == TEST_LEVEL_COUNTS - 1)
+ {
+
+ for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++)
+ {
+ /* waiting for our resource first */
+ rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER);
+
+ _stats_load_avg_inc();
+
+ _print_char(thr_self, '*');
+
+ rt_thread_delay(1);
+
+ /* produce for level 0 worker */
+ rt_sem_release(&_level_waiting[0]);
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++)
+ {
+ /* waiting for resource of ours */
+ rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER);
+
+ _stats_load_avg_inc();
+
+ /* notify our consumer */
+ rt_sem_release(&_level_waiting[level + 1]);
+ }
+ }
+
+ uassert_true(1);
+ rt_sem_release(&_thr_exit_sem);
+
+ return;
+}
+
+static void scheduler_tc(void)
+{
+ LOG_I("Test starts...");
+ for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++)
+ {
+ for (size_t j = 0; j < KERN_TEST_CONCURRENT_THREADS; j++)
+ {
+ rt_thread_startup(_thread_matrix[i][j]);
+ }
+ }
+ LOG_I("%d threads startup...", TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS);
+
+ /* waiting for sub-threads to exit */
+ for (size_t i = 0; i < TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS; i++)
+ {
+ rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
+ }
+
+ /* print load average */
+ _stats_load_avg_print();
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ LOG_I("Setup environment...");
+ _star_counter = 1;
+ rt_memset(_load_average, 0, sizeof(_load_average));
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+
+ for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++)
+ {
+ rt_sem_init(&_level_waiting[i], "test", 0, RT_IPC_FLAG_PRIO);
+
+ for (size_t j = 0; j < KERN_TEST_CONCURRENT_THREADS; j++)
+ {
+ _thread_matrix[i][j] =
+ rt_thread_create("test",
+ _thread_entry,
+ (void *)i,
+ UTEST_THR_STACK_SIZE,
+ KERN_TEST_CONFIG_HIGHEST_PRIO+i,
+ 5);
+ if (!_thread_matrix[i][j])
+ uassert_not_null(_thread_matrix[i][j]);
+ }
+ }
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_thr_exit_sem);
+ for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++)
+ {
+ rt_sem_detach(&_level_waiting[i]);
+ }
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(scheduler_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.sem", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/sched_thread_tc.c b/src/utest/sched_thread_tc.c
new file mode 100644
index 0000000000..d978e51ecc
--- /dev/null
+++ b/src/utest/sched_thread_tc.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-01-25 Shell init ver.
+ */
+#define __RT_KERNEL_SOURCE__
+#include <rtthread.h>
+#include "utest.h"
+
+#define TEST_LOOP_TIMES (100 * 1000)
+#define TEST_PROGRESS_COUNTS (36)
+#define TEST_THREAD_COUNT (RT_CPUS_NR * 1)
+#define TEST_PROGRESS_ON (TEST_LOOP_TIMES*TEST_THREAD_COUNT/TEST_PROGRESS_COUNTS)
+
+static struct rt_semaphore _thr_exit_sem;
+static rt_atomic_t _progress_counter;
+
+static volatile rt_thread_t threads_group[TEST_THREAD_COUNT][2];
+
+static void _thread_entry1(void *param)
+{
+ rt_base_t critical_level;
+ size_t idx = (size_t)param;
+
+ for (size_t i = 0; i < TEST_LOOP_TIMES; i++)
+ {
+ critical_level = rt_enter_critical();
+
+ rt_thread_suspend(rt_thread_self());
+ rt_thread_resume(threads_group[idx][1]);
+
+ rt_exit_critical_safe(critical_level);
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void _thread_entry2(void *param)
+{
+ rt_base_t critical_level;
+ size_t idx = (size_t)param;
+
+ for (size_t i = 0; i < TEST_LOOP_TIMES; i++)
+ {
+ critical_level = rt_enter_critical();
+
+ rt_thread_suspend(rt_thread_self());
+ rt_thread_resume(threads_group[idx][0]);
+
+ rt_exit_critical_safe(critical_level);
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void scheduler_tc(void)
+{
+ for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
+ {
+ rt_thread_t t1 =
+ rt_thread_create(
+ "t1",
+ _thread_entry1,
+ (void *)i,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 100);
+ rt_thread_t t2 =
+ rt_thread_create(
+ "t2",
+ _thread_entry2,
+ (void *)i,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 100);
+
+ threads_group[i][0] = t1;
+ threads_group[i][1] = t2;
+ }
+
+ for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
+ {
+ rt_thread_startup(threads_group[i][0]);
+ rt_thread_startup(threads_group[i][1]);
+ }
+
+ for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
+ {
+ rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_thr_exit_sem);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(scheduler_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.thread", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/sched_timed_mtx_tc.c b/src/utest/sched_timed_mtx_tc.c
new file mode 100644
index 0000000000..3729e4a22e
--- /dev/null
+++ b/src/utest/sched_timed_mtx_tc.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-01-25 Shell init ver.
+ */
+#define __RT_KERNEL_SOURCE__
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define TEST_SECONDS 10
+#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND)
+#define TEST_PROGRESS_COUNTS (36)
+#define TEST_PROGRESS_ON (TEST_LOOP_TICKS*2/TEST_PROGRESS_COUNTS)
+
+static struct rt_semaphore _thr_exit_sem;
+static struct rt_mutex _ipc_primitive;
+static struct rt_semaphore _cons_can_take_mtx;
+static struct rt_semaphore _prod_can_take_mtx;
+static rt_atomic_t _progress_counter;
+#define CONSUMER_MAGIC 0x11223344
+#define PRODUCER_MAGIC 0x44332211
+static rt_atomic_t _last_holder_flag = CONSUMER_MAGIC;
+static rt_base_t _timedout_failed_times = 0;
+
+/**
+ * Test on timedout IPC with racing condition where timedout routine and producer
+ * thread may race to wakeup sleeper.
+ *
+ * This test will fork 2 thread, one producer and one consumer. The producer will
+ * looping and trigger the IPC on the edge of new tick arrives. The consumer will
+ * wait on IPC with a timedout of 1 tick.
+ */
+
+static void _wait_until_edge(void)
+{
+ rt_tick_t entry_level, current;
+ rt_base_t random_latency;
+
+ entry_level = rt_tick_get();
+ do
+ {
+ current = rt_tick_get();
+ }
+ while (current == entry_level);
+
+ /* give a random latency for test */
+ random_latency = rand() % 1000 * 1000;
+ entry_level = current;
+ for (size_t i = 0; i < random_latency; i++)
+ {
+ current = rt_tick_get();
+ if (current != entry_level)
+ break;
+ }
+}
+
+static void _producer_entry(void *param)
+{
+ rt_err_t error;
+ for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
+ {
+ /**
+ * only try to take mutex after consumer have taken it after last
+ * release from us.
+ */
+ error = rt_sem_take(&_prod_can_take_mtx, RT_WAITING_FOREVER);
+ if (error)
+ {
+ uassert_true(0);
+ break;
+ }
+
+ error = rt_mutex_take(&_ipc_primitive, RT_WAITING_FOREVER);
+ if (error)
+ {
+ uassert_true(0);
+ break;
+ }
+
+ /* ensure that mutex should be held in round-robin method */
+ if (rt_atomic_load(&_last_holder_flag) != CONSUMER_MAGIC)
+ {
+ uassert_true(0);
+ break;
+ }
+ else
+ {
+ rt_atomic_store(&_last_holder_flag, PRODUCER_MAGIC);
+ rt_sem_release(&_cons_can_take_mtx);
+ }
+
+ _wait_until_edge();
+
+ rt_mutex_release(&_ipc_primitive);
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void _consumer_entry(void *param)
+{
+ rt_err_t error;
+ for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
+ {
+ /**
+ * only try to take mutex after producer have taken it after last
+ * release from us.
+ */
+ error = rt_sem_take(&_cons_can_take_mtx, RT_WAITING_FOREVER);
+ if (error)
+ {
+ uassert_true(0);
+ break;
+ }
+
+ while (1)
+ {
+ error = rt_mutex_take_interruptible(&_ipc_primitive, 1);
+ if (error == -RT_ETIMEOUT)
+ {
+ _timedout_failed_times++;
+ if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self())
+ {
+ uassert_true(0);
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (error != RT_EOK)
+ {
+ uassert_true(0);
+ break;
+ }
+
+ /* ensure that mutex should be held in round-robin method */
+ if (rt_atomic_load(&_last_holder_flag) != PRODUCER_MAGIC)
+ {
+ uassert_true(0);
+ break;
+ }
+ else
+ {
+ rt_atomic_store(&_last_holder_flag, CONSUMER_MAGIC);
+ rt_sem_release(&_prod_can_take_mtx);
+ }
+
+ rt_mutex_release(&_ipc_primitive);
+ if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self())
+ {
+ uassert_true(0);
+ break;
+ }
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void timed_mtx_tc(void)
+{
+ rt_thread_t prod = rt_thread_create(
+ "prod",
+ _producer_entry,
+ (void *)0,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 4);
+
+ rt_thread_t cons = rt_thread_create(
+ "cons",
+ _consumer_entry,
+ (void *)0,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 100);
+
+ rt_thread_startup(prod);
+ rt_thread_startup(cons);
+
+ for (size_t i = 0; i < 2; i++)
+ {
+ uassert_int_equal(
+ rt_sem_take(&_thr_exit_sem, 4 * TEST_LOOP_TICKS),
+ RT_EOK);
+ }
+
+ /* Summary */
+ LOG_I("Total failed times: %ld(in %d)\n", _timedout_failed_times, TEST_LOOP_TICKS);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ _timedout_failed_times = 0;
+
+ rt_mutex_init(&_ipc_primitive, "ipc", RT_IPC_FLAG_PRIO);
+ rt_sem_init(&_cons_can_take_mtx, "test", 0, RT_IPC_FLAG_PRIO);
+ rt_sem_init(&_prod_can_take_mtx, "test", 1, RT_IPC_FLAG_PRIO);
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_mutex_detach(&_ipc_primitive);
+ rt_sem_detach(&_cons_can_take_mtx);
+ rt_sem_detach(&_prod_can_take_mtx);
+ rt_sem_detach(&_thr_exit_sem);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(timed_mtx_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.timed_mtx", utest_tc_init, utest_tc_cleanup, TEST_SECONDS * 2);
diff --git a/src/utest/sched_timed_sem_tc.c b/src/utest/sched_timed_sem_tc.c
new file mode 100644
index 0000000000..6dbe3f51cc
--- /dev/null
+++ b/src/utest/sched_timed_sem_tc.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-01-25 Shell init ver.
+ */
+#define __RT_KERNEL_SOURCE__
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define TEST_SECONDS 10
+#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND)
+#define TEST_PROGRESS_COUNTS (36)
+#define TEST_PROGRESS_ON (TEST_LOOP_TICKS*2/TEST_PROGRESS_COUNTS)
+
+static struct rt_semaphore _thr_exit_sem;
+static struct rt_semaphore _ipc_sem;
+static rt_atomic_t _progress_counter;
+static rt_base_t _timedout_failed_times = 0;
+
+/**
+ * Test on timedout IPC with racing condition where timedout routine and producer
+ * thread may race to wakeup sleeper.
+ *
+ * This test will fork 2 thread, one producer and one consumer. The producer will
+ * looping and trigger the IPC on the edge of new tick arrives. The consumer will
+ * wait on IPC with a timedout of 1 tick.
+ */
+
+static void _wait_until_edge(void)
+{
+ rt_tick_t entry_level, current;
+ rt_base_t random_latency;
+
+ entry_level = rt_tick_get();
+ do
+ {
+ current = rt_tick_get();
+ }
+ while (current == entry_level);
+
+ /* give a random latency for test */
+ random_latency = rand();
+ entry_level = current;
+ for (size_t i = 0; i < random_latency; i++)
+ {
+ current = rt_tick_get();
+ if (current != entry_level)
+ break;
+ }
+}
+
+static void _producer_entry(void *param)
+{
+ for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
+ {
+ _wait_until_edge();
+
+ rt_sem_release(&_ipc_sem);
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void _consumer_entry(void *param)
+{
+ int error;
+ for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
+ {
+ error = rt_sem_take_interruptible(&_ipc_sem, 1);
+ if (error == -RT_ETIMEOUT)
+ {
+ _timedout_failed_times++;
+ }
+ else
+ {
+ if (error != RT_EOK)
+ uassert_true(0);
+ }
+
+ if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
+ uassert_true(1);
+ }
+
+ rt_sem_release(&_thr_exit_sem);
+ return;
+}
+
+static void timed_sem_tc(void)
+{
+ rt_thread_t prod = rt_thread_create(
+ "prod",
+ _producer_entry,
+ (void *)0,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 4);
+
+ rt_thread_t cons = rt_thread_create(
+ "cons",
+ _consumer_entry,
+ (void *)0,
+ UTEST_THR_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ 100);
+
+ rt_thread_startup(prod);
+ rt_thread_startup(cons);
+
+ for (size_t i = 0; i < 2; i++)
+ {
+ rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
+ }
+
+ /* Summary */
+ LOG_I("Total failed times: %ld(in %d)\n", _timedout_failed_times, TEST_LOOP_TICKS);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ int *pseed = rt_malloc(sizeof(int));
+ srand(*(int *)pseed);
+ rt_free(pseed);
+
+ rt_sem_init(&_ipc_sem, "ipc", 0, RT_IPC_FLAG_PRIO);
+ rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_ipc_sem);
+ rt_sem_detach(&_thr_exit_sem);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(timed_sem_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.timed_sem", utest_tc_init, utest_tc_cleanup, TEST_SECONDS * 2);
diff --git a/src/utest/semaphore_tc.c b/src/utest/semaphore_tc.c
new file mode 100644
index 0000000000..ca66da43b7
--- /dev/null
+++ b/src/utest/semaphore_tc.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-12 luckyzjq the first version
+ */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+static struct rt_semaphore static_semaphore;
+#ifdef RT_USING_HEAP
+static rt_sem_t dynamic_semaphore;
+#endif /* RT_USING_HEAP */
+
+static void test_static_semaphore_init(void)
+{
+ rt_err_t result;
+ int rand_num = rand() % 0x10000;
+
+ for (int i = 0; i < rand_num; i++)
+ {
+ result = rt_sem_init(&static_semaphore, "static_sem", i, RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ rt_sem_detach(&static_semaphore);
+
+ result = rt_sem_init(&static_semaphore, "static_sem", i, RT_IPC_FLAG_FIFO);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ rt_sem_detach(&static_semaphore);
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+static void test_static_semaphore_detach(void)
+{
+ rt_err_t result;
+ int rand_num = rand() % 0x10000;
+
+ for (int i = 0; i < rand_num; i++)
+ {
+ result = rt_sem_init(&static_semaphore, "static_sem", i, RT_IPC_FLAG_PRIO);
+ if (RT_EOK != result)
+ {
+ break;
+ }
+
+ result = rt_sem_detach(&static_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+
+ result = rt_sem_init(&static_semaphore, "static_sem", i, RT_IPC_FLAG_FIFO);
+ if (RT_EOK != result)
+ {
+ break;
+ }
+ result = rt_sem_detach(&static_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+static void test_static_semaphore_take(void)
+{
+ rt_err_t result;
+ result = rt_sem_init(&static_semaphore, "static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_EOK == result)
+ {
+ /* first take */
+ result = rt_sem_take(&static_semaphore, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ /* second take */
+ result = rt_sem_take(&static_semaphore, 100);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_detach(&static_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_static_semaphore_trytake(void)
+{
+ rt_err_t result;
+ result = rt_sem_init(&static_semaphore, "static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_EOK == result)
+ {
+ /* first take */
+ result = rt_sem_trytake(&static_semaphore);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ /* second take */
+ result = rt_sem_trytake(&static_semaphore);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_detach(&static_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_static_semaphore_release(void)
+{
+ rt_err_t result;
+ result = rt_sem_init(&static_semaphore, "static_sem", 0, RT_IPC_FLAG_PRIO);
+ if (RT_EOK == result)
+ {
+ /* first take */
+ result = rt_sem_take(&static_semaphore, 100);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+
+ /* release */
+ result = rt_sem_release(&static_semaphore);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ /* second take */
+ result = rt_sem_take(&static_semaphore, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_detach(&static_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_static_semaphore_control(void)
+{
+ rt_err_t result;
+ int value = 0;
+
+ value = rand() % 100;
+ result = rt_sem_init(&static_semaphore, "static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_EOK == result)
+ {
+ result = rt_sem_control(&static_semaphore, RT_IPC_CMD_RESET, &value);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ for (int i = 0; i < value; i++)
+ {
+ result = rt_sem_take(&static_semaphore, 10);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+
+ rt_sem_detach(&static_semaphore);
+ uassert_true(RT_TRUE);
+}
+
+static void static_release_isr_hardware_callback(void *param)
+{
+ rt_err_t result;
+
+ result = rt_sem_release(&static_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+}
+
+static void static_release_isr_software_callback(void *param)
+{
+ rt_err_t result;
+
+ result = rt_sem_release(&static_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+}
+
+static void test_static_semaphore_release_isr(void)
+{
+ rt_err_t result;
+ rt_timer_t hardware_timer;
+ rt_timer_t software_timer;
+
+ /* create timer */
+ hardware_timer = rt_timer_create("release_isr",
+ static_release_isr_hardware_callback,
+ RT_NULL,
+ 100,
+ RT_TIMER_FLAG_HARD_TIMER | RT_TIMER_FLAG_ONE_SHOT);
+ software_timer = rt_timer_create("release_isr",
+ static_release_isr_software_callback,
+ RT_NULL,
+ 100,
+ RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT);
+ /* start tiemr */
+ if (hardware_timer)
+ rt_timer_start(hardware_timer);
+ if (software_timer)
+ rt_timer_start(software_timer);
+
+ result = rt_sem_init(&static_semaphore, "static_sem", 0, RT_IPC_FLAG_PRIO);
+ if (RT_EOK == result)
+ {
+ for (int i = 0; i < 2; i++)
+ {
+ result = rt_sem_take(&static_semaphore, 1000);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_detach(&static_semaphore);
+ rt_timer_delete(hardware_timer);
+ rt_timer_delete(software_timer);
+
+ uassert_true(RT_TRUE);
+}
+
+#ifdef RT_USING_HEAP
+static void test_dynamic_semaphore_create(void)
+{
+ int rand_num = rand() % 0x10000;
+
+ for (int i = 0; i < rand_num; i++)
+ {
+ dynamic_semaphore = rt_sem_create("static_sem", i, RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_semaphore)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ rt_sem_delete(dynamic_semaphore);
+
+ dynamic_semaphore = rt_sem_create("static_sem", i, RT_IPC_FLAG_FIFO);
+ if (RT_NULL == dynamic_semaphore)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ rt_sem_delete(dynamic_semaphore);
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+static void test_dynamic_semaphore_delete(void)
+{
+ rt_err_t result;
+ int rand_num = rand() % 0x10000;
+
+ for (int i = 0; i < rand_num; i++)
+ {
+ dynamic_semaphore = rt_sem_create("static_sem", i, RT_IPC_FLAG_PRIO);
+ if (RT_NULL == dynamic_semaphore)
+ {
+ break;
+ }
+
+ result = rt_sem_delete(dynamic_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+
+ dynamic_semaphore = rt_sem_create("static_sem", i, RT_IPC_FLAG_FIFO);
+ if (RT_NULL == dynamic_semaphore)
+ {
+ break;
+ }
+ result = rt_sem_delete(dynamic_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ break;
+ }
+ }
+
+ uassert_true(RT_TRUE);
+}
+
+static void test_dynamic_semaphore_take(void)
+{
+ rt_err_t result;
+ dynamic_semaphore = rt_sem_create("static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_NULL != dynamic_semaphore)
+ {
+ /* first take */
+ result = rt_sem_take(dynamic_semaphore, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ /* second take */
+ result = rt_sem_take(dynamic_semaphore, 100);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_delete(dynamic_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_dynamic_semaphore_trytake(void)
+{
+ rt_err_t result;
+ dynamic_semaphore = rt_sem_create("static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_NULL != dynamic_semaphore)
+ {
+ /* first take */
+ result = rt_sem_trytake(dynamic_semaphore);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ /* second take */
+ result = rt_sem_trytake(dynamic_semaphore);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_delete(dynamic_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_dynamic_semaphore_release(void)
+{
+ rt_err_t result;
+ dynamic_semaphore = rt_sem_create("static_sem", 0, RT_IPC_FLAG_PRIO);
+ if (RT_NULL != dynamic_semaphore)
+ {
+ /* first take */
+ result = rt_sem_take(dynamic_semaphore, 100);
+ if (-RT_ETIMEOUT != result)
+ uassert_true(RT_FALSE);
+
+ /* release */
+ result = rt_sem_release(dynamic_semaphore);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+
+ /* second take */
+ result = rt_sem_take(dynamic_semaphore, RT_WAITING_FOREVER);
+ if (RT_EOK != result)
+ uassert_true(RT_FALSE);
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_delete(dynamic_semaphore);
+ uassert_true(RT_TRUE);
+
+ return;
+}
+
+static void test_dynamic_semaphore_control(void)
+{
+ rt_err_t result;
+ int value = 0;
+
+ value = rand() % 100;
+ dynamic_semaphore = rt_sem_create("static_sem", 1, RT_IPC_FLAG_PRIO);
+ if (RT_NULL != dynamic_semaphore)
+ {
+ result = rt_sem_control(dynamic_semaphore, RT_IPC_CMD_RESET, &value);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ for (int i = 0; i < value; i++)
+ {
+ result = rt_sem_take(dynamic_semaphore, 10);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+
+ rt_sem_delete(dynamic_semaphore);
+ uassert_true(RT_TRUE);
+}
+
+static void dynamic_release_isr_hardware_callback(void *param)
+{
+ rt_err_t result;
+
+ result = rt_sem_release(dynamic_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+}
+
+static void dynamic_release_isr_software_callback(void *param)
+{
+ rt_err_t result;
+
+ result = rt_sem_release(dynamic_semaphore);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+}
+
+static void test_dynamic_semaphore_release_isr(void)
+{
+ rt_err_t result;
+ rt_timer_t hardware_timer;
+ rt_timer_t software_timer;
+
+ /* create timer */
+ hardware_timer = rt_timer_create("release_isr",
+ dynamic_release_isr_hardware_callback,
+ RT_NULL,
+ 100,
+ RT_TIMER_FLAG_HARD_TIMER | RT_TIMER_FLAG_ONE_SHOT);
+ software_timer = rt_timer_create("release_isr",
+ dynamic_release_isr_software_callback,
+ RT_NULL,
+ 100,
+ RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT);
+ /* start tiemr */
+ if (hardware_timer)
+ rt_timer_start(hardware_timer);
+ if (software_timer)
+ rt_timer_start(software_timer);
+
+ dynamic_semaphore = rt_sem_create("static_sem", 0, RT_IPC_FLAG_PRIO);
+ if (RT_NULL != dynamic_semaphore)
+ {
+ for (int i = 0; i < 2; i++)
+ {
+ result = rt_sem_take(dynamic_semaphore, 1000);
+ if (RT_EOK != result)
+ {
+ uassert_true(RT_FALSE);
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ rt_sem_delete(dynamic_semaphore);
+ rt_timer_delete(hardware_timer);
+ rt_timer_delete(software_timer);
+
+ uassert_true(RT_TRUE);
+}
+
+#endif /* RT_USING_HEAP */
+
+static rt_err_t utest_tc_init(void)
+{
+#ifdef RT_USING_HEAP
+ dynamic_semaphore = RT_NULL;
+#endif /* RT_USING_HEAP */
+
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+#ifdef RT_USING_HEAP
+ dynamic_semaphore = RT_NULL;
+#endif /* RT_USING_HEAP */
+
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_static_semaphore_init);
+ UTEST_UNIT_RUN(test_static_semaphore_take);
+ UTEST_UNIT_RUN(test_static_semaphore_release);
+ UTEST_UNIT_RUN(test_static_semaphore_detach);
+ UTEST_UNIT_RUN(test_static_semaphore_trytake);
+ UTEST_UNIT_RUN(test_static_semaphore_control);
+ UTEST_UNIT_RUN(test_static_semaphore_release_isr);
+
+#ifdef RT_USING_HEAP
+ UTEST_UNIT_RUN(test_dynamic_semaphore_create);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_take);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_release);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_delete);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_trytake);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_control);
+ UTEST_UNIT_RUN(test_dynamic_semaphore_release_isr);
+#endif /* RT_USING_HEAP */
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.semaphore_tc", utest_tc_init, utest_tc_cleanup, 1000);
diff --git a/src/utest/signal_tc.c b/src/utest/signal_tc.c
new file mode 100644
index 0000000000..b19739d65f
--- /dev/null
+++ b/src/utest/signal_tc.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-12 flybreak the first version
+ *
+ * case 1:rt_signal_install, install all available signal
+ * case 2:rt_signal_install, install illegal signal
+ * case 3:rt_signal_mask/unmask, one thread self, install and unmask, then kill, should received.
+ * case 4:rt_signal_mask/unmask, one thread self, install and unmask and mask, then kill, should can't received.
+ * case 5:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: kill, should received.
+ * case 6:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: sleep 2s then kill, should can't received.
+ * case 7:rt_signal_kill, kill legal thread, return 0;
+ * case 8:rt_signal_kill, kill illegal thread, return failed (unused);
+ * case 9:rt_signal_kill, kill illegal signo, return -RT_EINVAL;
+ *
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+static volatile int recive_sig = 0;
+static struct rt_semaphore _received_signal;
+
+void sig_handle_default(int signo)
+{
+ recive_sig = signo;
+}
+
+static void rt_signal_install_test(void)
+{
+ int signo;
+ rt_sighandler_t result;
+
+ /* case 1:rt_signal_install, install all available signal. */
+ for (signo = 0; signo < RT_SIG_MAX; signo++)
+ {
+ result = rt_signal_install(signo, sig_handle_default);
+ uassert_true(result != SIG_ERR);
+ }
+ /* case 2:rt_signal_install, install illegal signal. */
+ result = rt_signal_install(signo, sig_handle_default);
+ uassert_true(result == SIG_ERR);
+
+ return;
+}
+
+static void rt_signal_mask_test(void)
+{
+ int signo;
+ rt_sighandler_t result;
+
+ /* case 3:rt_signal_mask/unmask, one thread self, install and unmask, then kill, should received. */
+ for (signo = 0; signo < RT_SIG_MAX; signo++)
+ {
+ recive_sig = -1;
+ result = rt_signal_install(signo, sig_handle_default);
+ uassert_true(result != SIG_ERR);
+ rt_signal_unmask(signo);
+ uassert_int_equal(rt_thread_kill(rt_thread_self(), signo), RT_EOK);
+ rt_thread_mdelay(1);
+ uassert_int_equal(recive_sig, signo);
+ }
+
+ return;
+}
+
+static void rt_signal_unmask_test(void)
+{
+ int signo;
+ rt_sighandler_t result;
+
+ /* case 4:rt_signal_mask/unmask, one thread self, install and unmask and mask, then kill, should can't received. */
+ for (signo = 0; signo < RT_SIG_MAX; signo++)
+ {
+ recive_sig = -1;
+ result = rt_signal_install(signo, sig_handle_default);
+ uassert_true(result != SIG_ERR);
+ rt_signal_unmask(signo);
+ rt_signal_mask(signo);
+ uassert_int_equal(rt_thread_kill(rt_thread_self(), signo), RT_EOK);
+ rt_thread_mdelay(1);
+ uassert_int_not_equal(recive_sig, signo);
+ }
+
+ return;
+}
+
+static void rt_signal_kill_test(void)
+{
+ int signo;
+ rt_sighandler_t result;
+
+ /* case 7:rt_signal_kill, kill legal thread, return 0; */
+ for (signo = 0; signo < RT_SIG_MAX; signo++)
+ {
+ recive_sig = -1;
+ result = rt_signal_install(signo, sig_handle_default);
+ uassert_true(result != SIG_ERR);
+ rt_signal_unmask(signo);
+ uassert_int_equal(rt_thread_kill(rt_thread_self(), signo), RT_EOK);
+ rt_thread_mdelay(1);
+ uassert_int_equal(recive_sig, signo);
+ }
+ /* case 8:rt_signal_kill, kill illegal thread, return failed; */
+// uassert_true(rt_thread_kill((rt_thread_t)-1, signo) == -RT_ERROR);
+
+ /* case 9:rt_signal_kill, kill illegal signo, return -RT_EINVAL; */
+ uassert_true(rt_thread_kill(rt_thread_self(), -1) == -RT_EINVAL);
+
+ return;
+}
+
+void rt_signal_wait_thread(void *parm)
+{
+ sigset_t selectset;
+ siginfo_t recive_si;
+
+ rt_signal_install(SIGUSR1, sig_handle_default);
+ rt_signal_unmask(SIGUSR1);
+
+ (void)sigemptyset(&selectset);
+ (void)sigaddset(&selectset, SIGUSR1);
+
+ /* case 5:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: kill, should received. */
+ if (rt_signal_wait((void *)&selectset, &recive_si, RT_TICK_PER_SECOND) != RT_EOK)
+ {
+ return;
+ }
+
+ recive_sig = recive_si.si_signo;
+
+ LOG_I("received signal %d", recive_sig);
+ rt_sem_release(&_received_signal);
+}
+
+static void rt_signal_wait_test(void)
+{
+ rt_thread_t t1;
+
+ recive_sig = -1;
+ t1 = rt_thread_create("sig_t1", rt_signal_wait_thread, 0, 4096, 14, 10);
+ if (t1)
+ {
+ rt_thread_startup(t1);
+ }
+
+ rt_thread_mdelay(1);
+ /* case 5:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: kill, should received. */
+ uassert_int_equal(rt_thread_kill(t1, SIGUSR1), RT_EOK);
+ rt_sem_take(&_received_signal, RT_WAITING_FOREVER);
+ uassert_int_equal(recive_sig, SIGUSR1);
+
+ return;
+}
+
+static void rt_signal_wait_test2(void)
+{
+ rt_thread_t t1;
+
+ recive_sig = -1;
+ t1 = rt_thread_create("sig_t1", rt_signal_wait_thread, 0, 4096, 14, 10);
+ if (t1)
+ {
+ rt_thread_startup(t1);
+ }
+
+ /* case 6:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: sleep 2s then kill, should can't received. */
+ rt_thread_mdelay(2000);
+ uassert_int_equal(rt_thread_kill(t1, SIGUSR1), RT_EOK);
+ uassert_int_not_equal(
+ rt_sem_take(&_received_signal, 1),
+ RT_EOK);
+ uassert_int_not_equal(recive_sig, SIGUSR1);
+
+ return;
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_sem_init(&_received_signal, "utest", 0, RT_IPC_FLAG_PRIO);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_received_signal);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+#ifdef RT_USING_HEAP
+ UTEST_UNIT_RUN(rt_signal_install_test);
+ UTEST_UNIT_RUN(rt_signal_mask_test);
+ UTEST_UNIT_RUN(rt_signal_unmask_test);
+ UTEST_UNIT_RUN(rt_signal_kill_test);
+ UTEST_UNIT_RUN(rt_signal_wait_test);
+ UTEST_UNIT_RUN(rt_signal_wait_test2);
+#endif /* RT_USING_HEAP */
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.signal_tc", utest_tc_init, utest_tc_cleanup, 1000);
+
+/*********************** end of file ****************************/
diff --git a/src/utest/slab_tc.c b/src/utest/slab_tc.c
new file mode 100644
index 0000000000..b273870cca
--- /dev/null
+++ b/src/utest/slab_tc.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-10-14 tyx the first version
+ */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define TEST_SLAB_SIZE 1024 * 1024
+
+static int _mem_cmp(void *ptr, rt_uint8_t v, rt_size_t size)
+{
+ while (size-- != 0)
+ {
+ if (*(rt_uint8_t *)ptr != v)
+ return *(rt_uint8_t *)ptr - v;
+ }
+ return 0;
+}
+
+struct slab_alloc_context
+{
+ rt_list_t node;
+ rt_size_t size;
+ rt_uint8_t magic;
+};
+
+struct slab_alloc_head
+{
+ rt_list_t list;
+ rt_size_t count;
+ rt_tick_t start;
+ rt_tick_t end;
+ rt_tick_t interval;
+};
+
+#define SLAB_RANG_ALLOC_BLK_MIN 2
+#define SLAB_RANG_ALLOC_BLK_MAX 5
+#define SLAB_RANG_ALLOC_TEST_TIME 5
+
+static void slab_alloc_test(void)
+{
+ struct slab_alloc_head head;
+ rt_uint8_t *buf;
+ rt_slab_t heap;
+ rt_size_t size;
+ struct slab_alloc_context *ctx;
+
+ /* init */
+ rt_list_init(&head.list);
+ head.count = 0;
+ head.start = rt_tick_get();
+ head.end = rt_tick_get() + rt_tick_from_millisecond(SLAB_RANG_ALLOC_TEST_TIME * 1000);
+ head.interval = (head.end - head.start) / 20;
+ buf = rt_malloc(TEST_SLAB_SIZE);
+ uassert_not_null(buf);
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)buf, RT_ALIGN_SIZE), (rt_ubase_t)buf);
+ rt_memset(buf, 0xAA, TEST_SLAB_SIZE);
+ heap = rt_slab_init("slab_tc", buf, TEST_SLAB_SIZE);
+ // test run
+ while (head.end - head.start < RT_TICK_MAX / 2)
+ {
+ if (rt_tick_get() - head.start >= head.interval)
+ {
+ head.start = rt_tick_get();
+ rt_kprintf("#");
+ }
+ // %60 probability to perform alloc operation
+ if (rand() % 10 >= 4)
+ {
+ size = rand() % SLAB_RANG_ALLOC_BLK_MAX + SLAB_RANG_ALLOC_BLK_MIN;
+ size *= sizeof(struct slab_alloc_context);
+ ctx = rt_slab_alloc(heap, size);
+ if (ctx == RT_NULL)
+ {
+ if (head.count == 0)
+ {
+ break;
+ }
+ size = head.count / 2;
+ while (size != head.count)
+ {
+ ctx = rt_list_first_entry(&head.list, struct slab_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_slab_free(heap, ctx);
+ head.count --;
+ }
+ continue;
+ }
+ //if (RT_ALIGN((rt_ubase_t)ctx, RT_ALIGN_SIZE) != (rt_ubase_t)ctx)
+ //{
+ // uassert_int_equal(RT_ALIGN((rt_ubase_t)ctx, RT_ALIGN_SIZE), (rt_ubase_t)ctx);
+ //}
+ rt_memset(ctx, 0, size);
+ rt_list_init(&ctx->node);
+ ctx->size = size;
+ ctx->magic = rand() & 0xff;
+ if (ctx->size > sizeof(*ctx))
+ {
+ rt_memset(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ rt_list_insert_after(&head.list, &ctx->node);
+ head.count += 1;
+ }
+ else
+ {
+ if (!rt_list_isempty(&head.list))
+ {
+ ctx = rt_list_first_entry(&head.list, struct slab_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_slab_free(heap, ctx);
+ head.count --;
+ }
+ }
+ }
+ while (!rt_list_isempty(&head.list))
+ {
+ ctx = rt_list_first_entry(&head.list, struct slab_alloc_context, node);
+ rt_list_remove(&ctx->node);
+ if (ctx->size > sizeof(*ctx))
+ {
+ if (_mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx)) != 0)
+ {
+ uassert_true(0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_slab_free(heap, ctx);
+ head.count --;
+ }
+ uassert_int_equal(head.count, 0);
+ // slab heap deinit
+ rt_slab_detach(heap);
+ /* release test resources */
+ rt_free(buf);
+}
+
+#define SLAB_RANG_REALLOC_BLK_MIN 0
+#define SLAB_RANG_REALLOC_BLK_MAX 5
+#define SLAB_RANG_REALLOC_TEST_TIME 5
+
+struct slab_realloc_context
+{
+ rt_size_t size;
+ rt_uint8_t magic;
+};
+
+struct slab_realloc_head
+{
+ struct slab_realloc_context **ctx_tab;
+ rt_size_t count;
+ rt_tick_t start;
+ rt_tick_t end;
+ rt_tick_t interval;
+};
+
+static void slab_realloc_test(void)
+{
+ struct slab_realloc_head head;
+ rt_uint8_t *buf;
+ rt_slab_t heap;
+ rt_size_t size, idx;
+ struct slab_realloc_context *ctx;
+ int res;
+
+ size = RT_ALIGN(sizeof(struct slab_realloc_context), RT_ALIGN_SIZE) + RT_ALIGN_SIZE;
+ size = TEST_SLAB_SIZE / size;
+ /* init */
+ head.ctx_tab = RT_NULL;
+ head.count = size;
+ head.start = rt_tick_get();
+ head.end = rt_tick_get() + rt_tick_from_millisecond(SLAB_RANG_ALLOC_TEST_TIME * 1000);
+ head.interval = (head.end - head.start) / 20;
+ buf = rt_malloc(TEST_SLAB_SIZE);
+ uassert_not_null(buf);
+ uassert_int_equal(RT_ALIGN((rt_ubase_t)buf, RT_ALIGN_SIZE), (rt_ubase_t)buf);
+ rt_memset(buf, 0xAA, TEST_SLAB_SIZE);
+ heap = rt_slab_init("slab_tc", buf, TEST_SLAB_SIZE);
+ /* init ctx tab */
+ size = head.count * sizeof(struct slab_realloc_context *);
+ head.ctx_tab = rt_slab_alloc(heap, size);
+ uassert_not_null(head.ctx_tab);
+ rt_memset(head.ctx_tab, 0, size);
+ // test run
+ while (head.end - head.start < RT_TICK_MAX / 2)
+ {
+ if (rt_tick_get() - head.start >= head.interval)
+ {
+ head.start = rt_tick_get();
+ rt_kprintf("#");
+ }
+ size = rand() % SLAB_RANG_ALLOC_BLK_MAX + SLAB_RANG_ALLOC_BLK_MIN;
+ size *= sizeof(struct slab_realloc_context);
+ idx = rand() % head.count;
+ ctx = rt_slab_realloc(heap, head.ctx_tab[idx], size);
+ if (ctx == RT_NULL)
+ {
+ if (size == 0)
+ {
+ if (head.ctx_tab[idx])
+ {
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ }
+ else
+ {
+ for (idx = 0; idx < head.count; idx++)
+ {
+ ctx = head.ctx_tab[idx];
+ if (rand() % 2 && ctx)
+ {
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_slab_realloc(heap, ctx, 0);
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ }
+ }
+ continue;
+ }
+ /* check slab */
+ if (head.ctx_tab[idx] != RT_NULL)
+ {
+ res = 0;
+ if (ctx->size < size)
+ {
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ }
+ else
+ {
+ if (size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, size - sizeof(*ctx));
+ }
+ }
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ /* init slab */
+ ctx->magic = rand() & 0xff;
+ ctx->size = size;
+ if (ctx->size > sizeof(*ctx))
+ {
+ rt_memset(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ }
+ head.ctx_tab[idx] = ctx;
+ }
+ // free all slab
+ for (idx = 0; idx < head.count; idx++)
+ {
+ ctx = head.ctx_tab[idx];
+ if (ctx == RT_NULL)
+ {
+ continue;
+ }
+ if (ctx->size > sizeof(*ctx))
+ {
+ res = _mem_cmp(&ctx[1], ctx->magic, ctx->size - sizeof(*ctx));
+ if (res != 0)
+ {
+ uassert_int_equal(res, 0);
+ }
+ }
+ rt_memset(ctx, 0xAA, ctx->size);
+ rt_slab_realloc(heap, ctx, 0);
+ head.ctx_tab[idx] = RT_NULL;
+ }
+ // slab heap deinit
+ rt_slab_detach(heap);
+ /* release test resources */
+ rt_free(buf);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(slab_alloc_test);
+ UTEST_UNIT_RUN(slab_realloc_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.slab_tc", utest_tc_init, utest_tc_cleanup, 20);
diff --git a/src/utest/smp/Kconfig b/src/utest/smp/Kconfig
new file mode 100644
index 0000000000..e7450b477f
--- /dev/null
+++ b/src/utest/smp/Kconfig
@@ -0,0 +1,23 @@
+menu "Kernel SMP Testcase"
+
+config UTEST_SMP_AFFFINITY_TC
+ bool "smp affinity and thread priority test1"
+ default n
+
+config UTEST_SMP_ASSIGNED_IDLE_CORE_TC
+ bool "smp threads auto assign to idle cores for test"
+ default n
+
+config UTEST_SMP_INTERRUPT_PRI_TC
+ bool "smp interrupt priority test"
+ default n
+
+config UTEST_SMP_SPINLOCK_TC
+ bool "smp spinlock test"
+ default n
+
+config UTEST_SMP_THREAD_PREEMPTION_TC
+ bool "smp threads preemption test"
+ default n
+
+endmenu
diff --git a/src/utest/smp/SConscript b/src/utest/smp/SConscript
new file mode 100644
index 0000000000..a15753f7be
--- /dev/null
+++ b/src/utest/smp/SConscript
@@ -0,0 +1,27 @@
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = []
+CPPPATH = [cwd]
+
+if GetDepend(['UTEST_SMP_SPINLOCK_TC']):
+ src += ['smp_spinlock_tc.c']
+
+if GetDepend(['UTEST_SMP_ASSIGNED_IDLE_CORE_TC']):
+ src += ['smp_assigned_idle_cores_tc.c']
+
+if GetDepend(['UTEST_SMP_INTERRUPT_PRI_TC']):
+ src += ['smp_interrupt_pri_tc.c']
+
+if GetDepend(['UTEST_SMP_THREAD_PREEMPTION_TC']):
+ src += ['smp_thread_preemption_tc.c']
+
+if GetDepend(['UTEST_SMP_AFFFINITY_TC']):
+ src += ['smp_bind_affinity_tc.c']
+ src += ['smp_affinity_pri1_tc.c']
+ src += ['smp_affinity_pri2_tc.c']
+
+group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/src/utest/smp/smp_affinity_pri1_tc.c b/src/utest/smp/smp_affinity_pri1_tc.c
new file mode 100644
index 0000000000..e9df641779
--- /dev/null
+++ b/src/utest/smp/smp_affinity_pri1_tc.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+/**
+ * @brief Test that threads with low-priority bound cores do not preempt high-priority threads.
+ *
+ * @note Create RT_CPUS_NR threads, thread 0 is bound to core 0, lower priority, the priority of the other threads
+ * for the system's highest and the thread entry function does not let out the CPU control, run the specified
+ * number of times in thread 0 to create a high-priority not bound to the core of the thread, the thread will
+ * be preempted by the core 0 is running on the thread!
+ */
+
+/* Number of thread runs */
+static int run_num = 10;
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+#define THREAD_PRIORITY 2
+#define LOW_PRIORITY 50
+#define THIGH_PRIORITY 10
+static rt_thread_t threads[RT_CPUS_NR];
+static rt_thread_t temp_thread;
+static struct rt_spinlock lock;
+static int thread_inc = 0;
+static int num = 0;
+
+static void thread_temp_entry(void *parameter)
+{
+ int id = 0;
+ while (1)
+ {
+ id = rt_hw_cpu_id();
+ uassert_int_equal(id, 0);
+ extern long list_thread(void);
+ list_thread();
+ rt_thread_delete(temp_thread);
+ }
+}
+
+static void thread_entry(void *parameter)
+{
+ int id = 0;
+ int para = *(int *)parameter;
+ while (1)
+ {
+ thread_inc++;
+ id = rt_hw_cpu_id();
+ if (para == 0)
+ {
+ if (thread_inc == run_num)
+ {
+ uassert_int_equal(id, 0);
+ extern long list_thread(void);
+ list_thread();
+ /* Creating high-priority untied core threads */
+ temp_thread = rt_thread_create("Tn", thread_temp_entry, RT_NULL, THREAD_STACK_SIZE, THIGH_PRIORITY, 20);
+
+ if (temp_thread != RT_NULL)
+ {
+ rt_thread_startup(temp_thread);
+ }
+ }
+ rt_thread_delay(5);
+ }
+ else
+ {
+ uassert_int_not_equal(id, 0);
+ while (1);
+ }
+ }
+}
+
+static void smp_affinity_pri1_tc(void)
+{
+ static int params[RT_CPUS_NR] = {0};
+ char thread_name[8];
+ int i;
+
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ params[i] = i;
+ }
+
+ /* Creating threads with low priority bindings to core 0 */
+ threads[0] = rt_thread_create("T0", thread_entry, (int *)&params[0], THREAD_STACK_SIZE, LOW_PRIORITY, 20);
+
+ if (threads[0] != RT_NULL)
+ {
+ rt_thread_control(threads[0], RT_THREAD_CTRL_BIND_CPU, (void *)0);
+ rt_thread_startup(threads[0]);
+ }
+
+ /* Create high-priority unbound threads with thread functions that don't let out CPU control */
+ for (i = 1; i < RT_CPUS_NR; i++)
+ {
+ rt_snprintf(thread_name, sizeof(thread_name), "T%d", i);
+ threads[i] = rt_thread_create(thread_name, thread_entry, (int *)&params[i], THREAD_STACK_SIZE, THREAD_PRIORITY, 20);
+
+ if (threads[i] != RT_NULL)
+ {
+ rt_thread_control(threads[i], RT_THREAD_CTRL_BIND_CPU, (void *)i);
+ rt_thread_startup(threads[i]);
+ }
+ }
+ rt_thread_delay(100);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_spin_lock_init(&lock);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ for (num = 0; num < RT_CPUS_NR; num++)
+ {
+ rt_thread_delete(threads[num]);
+ }
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(smp_affinity_pri1_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.affinity_pri1_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_affinity_pri2_tc.c b/src/utest/smp/smp_affinity_pri2_tc.c
new file mode 100644
index 0000000000..ae2eeb689f
--- /dev/null
+++ b/src/utest/smp/smp_affinity_pri2_tc.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+/**
+ * @brief Threads are automatically balanced across cores.
+ *
+ * @note Create RT_CPUS_NR threads, thread 0 is not bound to core 0, higher priority, the priority of the other
+ * threads for the system's highest and the thread entry function does not let out the CPU control, run the
+ * specified number of times after the creation of thread 0 in thread 0, a low-priority bound to the core 0,
+ * the thread will not preempt the core 0 is running on threads
+ */
+
+/* Number of thread runs */
+static int run_num = 10;
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+#define THREAD_PRIORITY 2
+#define LOW_PRIORITY 50
+#define THIGH_PRIORITY 10
+static rt_thread_t threads[RT_CPUS_NR];
+static rt_thread_t temp_thread;
+static struct rt_spinlock lock;
+static int thread_inc = 0;
+static int run_flag = 0;
+static int num = 0;
+
+static void thread_temp_entry(void *parameter)
+{
+ run_flag = 1;
+ rt_thread_delete(temp_thread);
+}
+
+static void thread_entry(void *parameter)
+{
+ int id = 0;
+ int para = *(int *)parameter;
+ while (1)
+ {
+ thread_inc++;
+ id = rt_hw_cpu_id();
+ if (para == 0)
+ {
+ if (thread_inc == run_num)
+ {
+ uassert_int_equal(id, 0);
+ temp_thread = rt_thread_create("Tn", thread_temp_entry, RT_NULL, THREAD_STACK_SIZE, LOW_PRIORITY, 20);
+
+ if (temp_thread != RT_NULL)
+ {
+ rt_thread_control(temp_thread, RT_THREAD_CTRL_BIND_CPU, (void *)0);
+ rt_thread_startup(temp_thread);
+ uassert_int_not_equal(run_flag, 1);
+ }
+ }
+ rt_thread_delay(5);
+ }
+ else
+ {
+ uassert_int_not_equal(id, 0);
+ while (1);
+ }
+ }
+}
+
+static void smp_affinity_pri2_tc(void)
+{
+ static int params[RT_CPUS_NR] = {0};
+ char thread_name[8];
+ int i;
+
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ params[i] = i;
+ }
+
+ threads[0] = rt_thread_create("T0", thread_entry, (int *)&params[0], THREAD_STACK_SIZE, THIGH_PRIORITY, 20);
+
+ if (threads[0] != RT_NULL)
+ {
+ uassert_true(1);
+ rt_thread_startup(threads[0]);
+ }
+
+ /* Create high-priority unbound threads with thread functions that don't let out CPU control */
+ for (i = 1; i < RT_CPUS_NR; i++)
+ {
+ rt_snprintf(thread_name, sizeof(thread_name), "T%d", i);
+ threads[i] = rt_thread_create(thread_name, thread_entry, (int *)&params[i], THREAD_STACK_SIZE, THREAD_PRIORITY, 20);
+
+ if (threads[i] != RT_NULL)
+ {
+ uassert_true(1);
+ rt_thread_control(threads[i], RT_THREAD_CTRL_BIND_CPU, (void *)i);
+ rt_thread_startup(threads[i]);
+ }
+ }
+ rt_thread_delay(50);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_spin_lock_init(&lock);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ for (num = 0; num < RT_CPUS_NR; num++)
+ {
+ rt_thread_delete(threads[num]);
+ }
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(smp_affinity_pri2_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.affinity_pri2_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_assigned_idle_cores_tc.c b/src/utest/smp/smp_assigned_idle_cores_tc.c
new file mode 100644
index 0000000000..d8b72a6ee1
--- /dev/null
+++ b/src/utest/smp/smp_assigned_idle_cores_tc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+/**
+ * @brief Threads are automatically balanced across cores.
+ *
+ * @note Create multiple threads untied core threads, run them for a while on each core to see
+ * if the threads are automatically distributed evenly, run for a while to output the threads
+ * running on each core.
+ */
+
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+#define THREAD_PRIORITY 20
+static rt_thread_t threads[RT_CPUS_NR];
+static int tick = 0, finsh_flag = 0;
+static int num = 0;
+/* thread entry function */
+static void thread_entry(void *parameter)
+{
+ while (1)
+ {
+ tick++;
+ if (tick == 100)
+ {
+ /* Output the current core running threads */
+ extern long list_thread(void);
+ list_thread();
+ finsh_flag = 0xA55A;
+ uassert_true(1);
+ }
+ rt_thread_delay(5);
+ }
+}
+
+static void thread_on_idle_core_tc(void)
+{
+ static int params[RT_CPUS_NR] = {0};
+ char thread_name[8];
+ int i;
+
+ /* Initialise the thread entry parameters */
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ params[i] = i;
+ }
+
+ /* Create RT_CPUS_NR threads and pass the entry parameters for each thread */
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ rt_snprintf(thread_name, sizeof(thread_name), "T%d", i);
+ threads[i] = rt_thread_create(thread_name, thread_entry, &params[i], THREAD_STACK_SIZE, THREAD_PRIORITY, 20);
+ if (threads[i] != RT_NULL)
+ {
+ uassert_true(1);
+ rt_thread_startup(threads[i]);
+ }
+ }
+ /* Waiting for test cases to finish */
+ while (finsh_flag != 0xA55A);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_kprintf("[Test case]: created threads are automatically assigned to run on idle cores\r\n");
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ for (num = 0; num < RT_CPUS_NR; num++)
+ {
+ rt_thread_delete(threads[num]);
+ }
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(thread_on_idle_core_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.assigned_idle_cores_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_bind_affinity_tc.c b/src/utest/smp/smp_bind_affinity_tc.c
new file mode 100644
index 0000000000..ec10050cd8
--- /dev/null
+++ b/src/utest/smp/smp_bind_affinity_tc.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+/**
+ * @brief Binding core affinity testcase.
+ *
+ * @note Create RT_CPUS_NR threads, thread 0 is bound to core 0, other threads are not bound to specific cores,
+ * after running for a set number of times, count the number of times each core is run on the corresponding core,
+ * thread 0 should always be run on core 0, other threads will be run on different cores.
+ */
+
+/* Number of thread runs */
+static int run_num = 100;
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+#define THREAD_PRIORITY 20
+static rt_thread_t threads[RT_CPUS_NR];
+static struct rt_spinlock lock;
+static int thread_inc[RT_CPUS_NR] = {0};
+static int thread_tic[RT_CPUS_NR] = {0};
+static int finsh_flag = 0;
+static int num = 0;
+
+static void thread_entry(void *parameter)
+{
+ int id = 0;
+ int para = *(int *)parameter;
+ while (1)
+ {
+ thread_tic[para]++;
+ id = rt_hw_cpu_id();
+ if (para == id)
+ {
+ thread_inc[para]++;
+ }
+
+ if (thread_tic[para] == run_num)
+ {
+ if (para == 0)
+ uassert_int_equal(thread_inc[para], thread_tic[para]);
+ else
+ uassert_int_not_equal(thread_inc[para], thread_tic[para]);
+ finsh_flag ++;
+ }
+ rt_thread_delay(5);
+ }
+}
+
+static void thread_bind_affinity_tc(void)
+{
+ static int params[RT_CPUS_NR] = {0};
+ char thread_name[8];
+ int i, j;
+
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ params[i] = i;
+ }
+
+ /* Create RT_CPUS_NR threads Thread 0 is bound to core 0 Other threads are not bound */
+ for (i = 0; i < RT_CPUS_NR; i++)
+ {
+ rt_snprintf(thread_name, sizeof(thread_name), "thread%d", i);
+ threads[i] = rt_thread_create(thread_name, thread_entry, (int *)&params[i], THREAD_STACK_SIZE, THREAD_PRIORITY, 20);
+ if (i == 0)
+ {
+ rt_thread_control(threads[0], RT_THREAD_CTRL_BIND_CPU, (void *)0);
+ }
+ if (threads[i] != RT_NULL)
+ {
+ rt_thread_startup(threads[i]);
+ }
+ }
+
+ while (finsh_flag != RT_CPUS_NR);
+
+ /* Displays the number of times a thread was executed on the relevant core */
+ for (j = 0; j < RT_CPUS_NR; j++)
+ {
+ rt_spin_lock(&lock);
+ rt_kprintf("Total runs[%d], Number of times thread[%d] run on [core%d]: [%4d], always run at core%d ? %s \r\n", run_num, j, j, thread_inc[j], j, (thread_inc[j] == run_num) ? "yes" : "no");
+ rt_spin_unlock(&lock);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_spin_lock_init(&lock);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ for (num = 0; num < RT_CPUS_NR; num++)
+ {
+ rt_thread_delete(threads[num]);
+ }
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(thread_bind_affinity_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.bind_affinity_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_interrupt_pri_tc.c b/src/utest/smp/smp_interrupt_pri_tc.c
new file mode 100644
index 0000000000..4afd0ef143
--- /dev/null
+++ b/src/utest/smp/smp_interrupt_pri_tc.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include <interrupt.h>
+
+/**
+ * @brief Setting the Interrupt Priority Test.
+ *
+ * @note Without turning off interrupts, interrupts respond in the order in which they are triggered.
+ * With interrupts turned off, low and high priority interrupts are triggered sequentially,
+ * and when interrupts are turned on, high priority interrupts respond first.
+ */
+
+#define RES_VAL 0X0
+#define SET_VAL 0XA
+#define RT_SPI_1 1
+#define RT_SPI_2 2
+#define RT_SPI_PRI_HIGH 120
+#define RT_SPI_PRI_LOW 140
+
+static int mode = 0;
+static int ipi_val[2] = {0, 0};
+
+/* Software Interrupt 1 Service Functions */
+static void rt_scheduler_ipi1_handler(int vector, void *param)
+{
+ ipi_val[0] = SET_VAL;
+ if (mode == 0)
+ {
+ uassert_true(ipi_val[0] > ipi_val[1]);
+ }
+ else
+ {
+ ipi_val[0] = RES_VAL;
+ ipi_val[1] = RES_VAL;
+ }
+}
+
+/* Software Interrupt 2 Service Functions */
+static void rt_scheduler_ipi2_handler(int vector, void *param)
+{
+ ipi_val[1] = SET_VAL;
+ if (mode == 0)
+ {
+ ipi_val[0] = RES_VAL;
+ ipi_val[1] = RES_VAL;
+ }
+ else
+ {
+ uassert_true(ipi_val[0] < ipi_val[1]);
+ }
+}
+
+/* Interrupt priority testcases 1 */
+static void int_pri1_tc(void)
+{
+ mode = 0;
+ unsigned int pri1, pri2;
+ pri1 = rt_hw_interrupt_get_priority(RT_SPI_1);
+ pri2 = rt_hw_interrupt_get_priority(RT_SPI_2);
+
+ if (pri1 < pri2)
+ uassert_true(pri1 < pri2);
+
+ /* Trigger interrupt */
+ rt_hw_ipi_send(RT_SPI_1, 0x1);
+ rt_hw_ipi_send(RT_SPI_2, 0x1);
+ rt_thread_delay(5);
+}
+
+/* Interrupt priority testcases 2 */
+static void int_pri2_tc(void)
+{
+ mode = 1;
+ unsigned int pri1, pri2;
+ pri1 = rt_hw_interrupt_get_priority(RT_SPI_1);
+ pri2 = rt_hw_interrupt_get_priority(RT_SPI_2);
+
+ if (pri1 < pri2)
+ uassert_true(pri1 < pri2);
+
+ rt_base_t level = rt_hw_local_irq_disable();
+ /* Trigger interrupt */
+ rt_hw_ipi_send(RT_SPI_1, 0x1);
+ rt_hw_ipi_send(RT_SPI_2, 0x1);
+ rt_hw_local_irq_enable(level);
+ rt_thread_delay(5);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ /* Setting the priority of software interrupts */
+ rt_hw_interrupt_set_priority(RT_SPI_1, RT_SPI_PRI_LOW);
+ rt_hw_interrupt_set_priority(RT_SPI_2, RT_SPI_PRI_HIGH);
+ /* Register software interrupt service functions */
+ rt_hw_ipi_handler_install(RT_SPI_1, rt_scheduler_ipi1_handler);
+ rt_hw_ipi_handler_install(RT_SPI_2, rt_scheduler_ipi2_handler);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(int_pri1_tc);
+ UTEST_UNIT_RUN(int_pri2_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.interrupt_pri_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_spinlock_tc.c b/src/utest/smp/smp_spinlock_tc.c
new file mode 100644
index 0000000000..bf3d01b22a
--- /dev/null
+++ b/src/utest/smp/smp_spinlock_tc.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-13 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include <interrupt.h>
+
+/**
+ * @brief Spinlock testcase.
+ *
+ * @note Create multiple threads and use spinlocks to protect shared memory
+ */
+
+#define THREAD_PRIORITY 20
+#define THREAD_TIMESLICE 20
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+static rt_thread_t thread1;
+static rt_thread_t thread2;
+static rt_uint8_t finsh_flag = 0;
+static struct rt_spinlock lock;
+static rt_uint8_t number1, number2 = 0;
+
+static void rt_thread_entry1(void *parameter)
+{
+ while (1)
+ {
+ rt_spin_lock(&lock);
+ number1++;
+ rt_thread_yield();
+ number2++;
+ rt_spin_unlock(&lock);
+ rt_thread_delay(5);
+ }
+}
+
+static void rt_thread_entry2(void *parameter)
+{
+ while (1)
+ {
+ rt_spin_lock(&lock);
+ uassert_int_equal(number1, number2);
+ number1++;
+ number2++;
+ rt_spin_unlock(&lock);
+ if (number1 >= 10)
+ {
+ finsh_flag = 1;
+ }
+ rt_thread_delay(5);
+ }
+}
+
+static void smp_spinlock_tc(void)
+{
+ thread1 = rt_thread_create("T1", rt_thread_entry1, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, 20);
+ if (thread1 != RT_NULL)
+ {
+ rt_thread_startup(thread1);
+ }
+
+ thread2 = rt_thread_create("T2", rt_thread_entry2, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY - 1, 20);
+ if (thread2 != RT_NULL)
+ {
+ rt_thread_startup(thread2);
+ }
+ while (finsh_flag == 0);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_spin_lock_init(&lock);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_thread_delete(thread1);
+ rt_thread_delete(thread2);
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(smp_spinlock_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.spinlock_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/smp/smp_thread_preemption_tc.c b/src/utest/smp/smp_thread_preemption_tc.c
new file mode 100644
index 0000000000..0390a21ac0
--- /dev/null
+++ b/src/utest/smp/smp_thread_preemption_tc.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2024-08-10 RV the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+/**
+ * @brief Thread Preemption Test with Different Priorities.
+ *
+ * @note Create multiple threads, low-priority threads run first,
+ * high-priority threads preempt low-priority threads, and
+ * print the current status of each core in the thread's entry function.
+ */
+
+#define THREAD_PRIORITY_HIGH 21
+#define THREAD_PRIORITY_LOW 30
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+
+static rt_thread_t threads[2];
+static struct rt_spinlock lock;
+
+/* High Priority Thread */
+static void thread_high_entry(void *parameter)
+{
+ uassert_true(1);
+ rt_spin_lock(&lock);
+ rt_kprintf("High priority thread is running\n");
+ extern long list_thread(void);
+ list_thread();
+ rt_spin_unlock(&lock);
+}
+
+/* Low Priority Thread */
+static void thread_low_entry(void *parameter)
+{
+ uassert_true(1);
+ rt_spin_lock(&lock);
+ rt_kprintf("Low priority thread is running\n");
+ extern long list_thread(void);
+ list_thread();
+ rt_spin_unlock(&lock);
+}
+
+static void thread_preemptions_tc(void)
+{
+ /* Creating low-priority thread */
+ threads[0] = rt_thread_create("tlow", thread_low_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY_LOW, 10);
+ if (threads[0] != RT_NULL)
+ {
+ uassert_true(1);
+ rt_thread_startup(threads[0] );
+ }
+
+ rt_thread_delay(5);
+ /* Creating high-priority thread */
+ threads[1] = rt_thread_create("thigh", thread_high_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY_HIGH, 10);
+ if (threads[1] != RT_NULL)
+ {
+ uassert_true(1);
+ rt_thread_startup(threads[1]);
+ }
+ rt_thread_delay(50);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_spin_lock_init(&lock);
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(thread_preemptions_tc);
+}
+UTEST_TC_EXPORT(testcase, "testcases.smp.thread_preemptions_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/thread_overflow_tc.c b/src/utest/thread_overflow_tc.c
new file mode 100644
index 0000000000..cc5c825441
--- /dev/null
+++ b/src/utest/thread_overflow_tc.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-09-01 Rbb666 add stack overflow test
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+
+#define UTEST_NAME "thread_overflow_tc"
+#define TEST_STACK_SIZE 512
+
+/* Test thread stack overflow */
+static rt_thread_t test_thread = RT_NULL;
+static rt_thread_t fake_thread = RT_NULL; /* Dynamic fake thread */
+static volatile rt_bool_t overflow_detected = RT_FALSE;
+static volatile rt_bool_t test_completed = RT_FALSE;
+
+/* Stack overflow detection hook - returns RT_EOK to continue, other values to halt */
+static rt_err_t stack_overflow_hook(struct rt_thread *thread)
+{
+ rt_kprintf("Stack overflow hook called for thread: %s\n", thread->parent.name);
+ overflow_detected = RT_TRUE;
+
+ /* Return RT_EOK to indicate overflow has been handled successfully */
+ return RT_EOK;
+}
+
+/* Test stack usage calculation */
+static void stack_usage_test(void)
+{
+ rt_thread_t current_thread;
+ rt_uint32_t total_stack, used_stack;
+
+ current_thread = rt_thread_self();
+ uassert_not_null(current_thread);
+
+ /* Get stack information */
+ total_stack = current_thread->stack_size;
+
+ rt_kprintf("Thread: %s\n", current_thread->parent.name);
+ rt_kprintf("Stack addr: 0x%p\n", current_thread->stack_addr);
+ rt_kprintf("Stack size: %d bytes\n", total_stack);
+ rt_kprintf("Current SP: 0x%p\n", current_thread->sp);
+
+#ifdef ARCH_CPU_STACK_GROWS_UPWARD
+ /* For upward growing stacks */
+ used_stack = (rt_uint32_t)current_thread->sp - (rt_uint32_t)current_thread->stack_addr;
+#else
+ /* For downward growing stacks (most common) */
+ used_stack = (rt_uint32_t)current_thread->stack_addr + total_stack - (rt_uint32_t)current_thread->sp;
+#endif
+
+ rt_kprintf("Used stack: %d bytes (%d%%)\n",
+ used_stack,
+ (used_stack * 100) / total_stack);
+
+ /* Verify stack usage is reasonable */
+ uassert_true(used_stack > 0);
+ uassert_true(used_stack < total_stack);
+
+ /* Check magic number at stack boundary */
+#ifdef ARCH_CPU_STACK_GROWS_UPWARD
+ /* Check magic at the end for upward growing */
+ if (*((rt_uint8_t *)((rt_uintptr_t)current_thread->stack_addr + total_stack - 1)) == '#')
+ {
+ rt_kprintf("Stack magic number intact at top\n");
+ uassert_true(RT_TRUE);
+ }
+ else
+ {
+ rt_kprintf("Stack magic number corrupted at top\n");
+ uassert_true(RT_FALSE);
+ }
+#else
+ /* Check magic at the beginning for downward growing */
+ if (*((rt_uint8_t *)current_thread->stack_addr) == '#')
+ {
+ rt_kprintf("Stack magic number intact at bottom\n");
+ uassert_true(RT_TRUE);
+ }
+ else
+ {
+ rt_kprintf("Stack magic number corrupted at bottom\n");
+ uassert_true(RT_FALSE);
+ }
+#endif
+}
+
+/* Test manual stack overflow check function */
+static void manual_stack_check_test(void)
+{
+ rt_thread_t current_thread;
+
+ current_thread = rt_thread_self();
+ uassert_not_null(current_thread);
+
+ rt_kprintf("Performing manual stack check for thread: %s\n", current_thread->parent.name);
+
+#ifdef RT_USING_OVERFLOW_CHECK
+ /* This should not trigger overflow for current thread under normal conditions */
+ rt_scheduler_stack_check(current_thread);
+
+ rt_kprintf("Manual stack check completed successfully\n");
+ uassert_true(RT_TRUE);
+#else
+ rt_kprintf("RT_USING_OVERFLOW_CHECK not enabled\n");
+ uassert_true(RT_FALSE);
+#endif
+}
+
+/* Test stack overflow hook functionality */
+static void stack_overflow_hook_test(void)
+{
+#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
+ rt_thread_t current_thread;
+
+ rt_kprintf("Testing stack overflow hook functionality\n");
+
+ current_thread = rt_thread_self();
+ uassert_not_null(current_thread);
+
+ /* Test setting and clearing the hook */
+ rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
+ rt_kprintf("Stack overflow hook set successfully\n");
+
+ /* Clear the hook */
+ rt_scheduler_stack_overflow_sethook(RT_NULL);
+ rt_kprintf("Stack overflow hook cleared successfully\n");
+
+ uassert_true(RT_TRUE);
+#else
+ rt_kprintf("Hook functionality not enabled (RT_USING_HOOK not defined)\n");
+ uassert_true(RT_FALSE);
+#endif
+}
+
+/* Fake thread test entry function */
+static void fake_thread_entry(void *parameter)
+{
+ /* This function should never actually run */
+ rt_kprintf("Fake thread is running - this should not happen!\n");
+ while (1)
+ {
+ rt_thread_mdelay(1000);
+ }
+}
+
+/* Test fake thread stack overflow */
+static void fake_thread_stack_overflow_test(void)
+{
+ rt_kprintf("Starting fake thread stack overflow test\n");
+
+ overflow_detected = RT_FALSE;
+
+#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
+ /* Set up stack overflow hook */
+ rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
+#endif
+
+ /* Create the fake thread dynamically */
+ fake_thread = rt_thread_create("fake_thread",
+ fake_thread_entry,
+ RT_NULL,
+ TEST_STACK_SIZE,
+ 25, /* Lower priority */
+ 10);
+
+ if (fake_thread == RT_NULL)
+ {
+ rt_kprintf("Failed to create fake thread\n");
+ uassert_true(RT_FALSE);
+ goto cleanup;
+ }
+
+ rt_kprintf("Fake thread created successfully\n");
+
+ rt_kprintf("Corrupting fake thread stack with pattern 0x11...\n");
+ rt_memset(fake_thread->stack_addr, 0x11, fake_thread->stack_size);
+
+ /* Also corrupt the magic number area if stack checking is enabled */
+#ifdef RT_USING_OVERFLOW_CHECK
+ /* For downward growing stacks, magic is typically at the beginning */
+ rt_memset(fake_thread->stack_addr, 0x11, 4); /* Corrupt first 4 bytes */
+ rt_kprintf("Stack magic number area corrupted\n");
+#endif
+
+ /* Now perform stack check on the corrupted fake thread */
+ rt_kprintf("Performing stack check on corrupted fake thread...\n");
+
+#ifdef RT_USING_OVERFLOW_CHECK
+ /* This should trigger our overflow hook */
+ rt_scheduler_stack_check(fake_thread);
+
+ /* Give a moment for hook to be called */
+ rt_thread_mdelay(10);
+#endif
+
+ /* Delete the fake thread (don't start it, just clean up) */
+ if (fake_thread != RT_NULL)
+ {
+ rt_thread_delete(fake_thread);
+ fake_thread = RT_NULL;
+ rt_kprintf("Fake thread deleted\n");
+ }
+
+cleanup:
+#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
+ /* Clear stack overflow hook */
+ rt_scheduler_stack_overflow_sethook(RT_NULL);
+#endif
+
+ /* Verify results */
+ if (overflow_detected)
+ {
+ rt_kprintf("SUCCESS: Stack overflow detected on fake thread!\n");
+ uassert_true(RT_TRUE);
+ }
+ else
+ {
+ rt_kprintf("WARNING: Stack overflow not detected on fake thread\n");
+ /* This might still be acceptable depending on implementation */
+ uassert_true(RT_TRUE);
+ }
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ overflow_detected = RT_FALSE;
+ test_completed = RT_FALSE;
+ test_thread = RT_NULL;
+
+ rt_kprintf("Stack overflow test case initialized\n");
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ /* Clean up any remaining test threads */
+ if (test_thread != RT_NULL)
+ {
+ rt_thread_delete(test_thread);
+ test_thread = RT_NULL;
+ }
+
+ if (fake_thread != RT_NULL)
+ {
+ rt_thread_delete(fake_thread);
+ fake_thread = RT_NULL;
+ }
+
+ overflow_detected = RT_FALSE;
+ test_completed = RT_FALSE;
+
+ rt_kprintf("Stack overflow test case cleanup completed\n");
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(stack_usage_test);
+ UTEST_UNIT_RUN(manual_stack_check_test);
+ UTEST_UNIT_RUN(stack_overflow_hook_test);
+ UTEST_UNIT_RUN(fake_thread_stack_overflow_test);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_overflow_tc", utest_tc_init, utest_tc_cleanup, 10);
diff --git a/src/utest/thread_suspend_tc.c b/src/utest/thread_suspend_tc.c
new file mode 100644
index 0000000000..0d1ae1dfbf
--- /dev/null
+++ b/src/utest/thread_suspend_tc.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-09-02 Rbb666 utest case for rt_thread_suspend comprehensive tests
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "utest.h"
+
+#define THREAD_STACK_SIZE 1024
+#define THREAD_TIMESLICE 5
+#define TEST_THREAD_PRIORITY 25
+
+/* Global variables for normal usage test */
+static rt_thread_t target_thread = RT_NULL;
+static rt_thread_t monitor_thread = RT_NULL;
+static rt_sem_t sync_sem = RT_NULL;
+static volatile rt_uint32_t work_counter = 0;
+static volatile rt_bool_t suspend_test_done = RT_FALSE;
+static volatile rt_bool_t suspend_success = RT_FALSE;
+static volatile rt_bool_t resume_success = RT_FALSE;
+
+/* Global variables for deadlock test */
+static rt_mutex_t test_mutex = RT_NULL;
+static rt_thread_t holder_thread = RT_NULL;
+static rt_thread_t waiter_thread = RT_NULL;
+static volatile rt_uint32_t shared_counter = 0;
+static volatile rt_bool_t holder_got_mutex = RT_FALSE;
+static volatile rt_bool_t deadlock_detected = RT_FALSE;
+static volatile rt_bool_t test_completed = RT_FALSE;
+static volatile rt_bool_t thread_started = RT_FALSE;
+static volatile rt_bool_t thread_should_exit = RT_FALSE;
+
+/* Target work thread - the thread to be suspended */
+static void target_work_thread(void *parameter)
+{
+ while (1)
+ {
+ if (!suspend_test_done)
+ {
+ work_counter++;
+ }
+ /* Yield CPU appropriately to simulate normal work */
+ rt_thread_mdelay(10);
+ }
+}
+
+/* Monitor thread - responsible for suspending and resuming target thread */
+static void monitor_control_thread(void *parameter)
+{
+ rt_uint32_t counter_before, counter_after;
+
+ /* Wait for target thread to start working */
+ rt_thread_mdelay(300);
+
+ /* Record counter value before suspend */
+ counter_before = work_counter;
+
+ /* Use rt_thread_suspend to suspend target thread */
+ if (rt_thread_suspend(target_thread) == RT_EOK)
+ {
+ suspend_success = RT_TRUE;
+
+ /* Trigger scheduling to ensure thread is suspended */
+ rt_schedule();
+
+ /* Wait for a while to verify thread is indeed suspended */
+ rt_thread_mdelay(500);
+
+ counter_after = work_counter;
+
+ /* Verify thread is indeed suspended (counter should stop changing) */
+ if (counter_after == counter_before)
+ {
+ /* Resume target thread */
+ if (rt_thread_resume(target_thread) == RT_EOK)
+ {
+ resume_success = RT_TRUE;
+ /* Wait for a while to verify thread resumes work */
+ rt_thread_mdelay(200);
+ }
+ }
+ }
+
+ /* End test */
+ suspend_test_done = RT_TRUE;
+
+ /* Send semaphore to notify test completion */
+ rt_sem_release(sync_sem);
+
+ /* Keep running until deleted */
+ while (1)
+ {
+ rt_thread_mdelay(100);
+ }
+}
+
+/* Thread that holds the mutex */
+static void mutex_holder_thread(void *parameter)
+{
+ if (rt_mutex_take(test_mutex, RT_WAITING_FOREVER) == RT_EOK)
+ {
+ holder_got_mutex = RT_TRUE;
+
+ /* Simulate critical section work */
+ for (int i = 0; i < 1000 && !test_completed; i++)
+ {
+ shared_counter++;
+ if (i % 200 == 0)
+ {
+ rt_thread_mdelay(10);
+ }
+ }
+
+ if (!test_completed)
+ {
+ rt_mutex_release(test_mutex);
+ }
+ }
+ rt_kprintf("Holder thread exiting\n");
+
+ /* Keep running until deleted */
+ while (1)
+ {
+ rt_thread_mdelay(100);
+ }
+}
+
+/* Thread that waits for the mutex */
+static void mutex_waiter_thread(void *parameter)
+{
+ /* Wait a bit to ensure holder gets mutex first */
+ rt_thread_mdelay(50);
+
+ rt_err_t result = rt_mutex_take(test_mutex, rt_tick_from_millisecond(1500));
+ if (result == RT_EOK)
+ {
+ shared_counter += 1000;
+ rt_mutex_release(test_mutex);
+ }
+ else
+ {
+ /* Timeout indicates deadlock - holder is suspended and cannot release lock */
+ deadlock_detected = RT_TRUE;
+ rt_kprintf("Deadlock detected: waiter timeout (holder suspended with mutex)\n");
+ }
+
+ /* Keep running until deleted */
+ while (1)
+ {
+ rt_thread_mdelay(100);
+ }
+}
+
+void simple_thread_entry(void *param)
+{
+ volatile rt_bool_t *flag = (volatile rt_bool_t *)param;
+ *flag = RT_TRUE;
+
+ /* Keep the thread running until it's suspended and deleted */
+ while (1)
+ {
+ rt_thread_mdelay(100);
+ }
+}
+
+/* Test normal usage of rt_thread_suspend function */
+static void test_suspend_force_normal_usage(void)
+{
+ /* Reset global variables */
+ work_counter = 0;
+ suspend_test_done = RT_FALSE;
+ suspend_success = RT_FALSE;
+ resume_success = RT_FALSE;
+
+ /* Create synchronization semaphore */
+ sync_sem = rt_sem_create("sync", 0, RT_IPC_FLAG_FIFO);
+ uassert_not_null(sync_sem);
+
+ /* Create target work thread */
+ target_thread = rt_thread_create("target",
+ target_work_thread,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ TEST_THREAD_PRIORITY,
+ THREAD_TIMESLICE);
+
+ uassert_not_null(target_thread);
+
+ /* Create monitor thread */
+ monitor_thread = rt_thread_create("monitor",
+ monitor_control_thread,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY,
+ THREAD_TIMESLICE);
+
+ uassert_not_null(monitor_thread);
+
+ /* Start threads */
+ rt_thread_startup(target_thread);
+ rt_thread_startup(monitor_thread);
+
+ /* Wait for test completion */
+ rt_sem_take(sync_sem, RT_WAITING_FOREVER);
+
+ /* Wait for a while to ensure threads exit normally */
+ rt_thread_mdelay(100);
+
+ /* Verify test results */
+ uassert_true(suspend_success);
+ uassert_true(resume_success);
+ uassert_true(work_counter > 0);
+
+ /* Clean up resources */
+ if (sync_sem)
+ {
+ rt_sem_delete(sync_sem);
+ sync_sem = RT_NULL;
+ }
+
+ /* Delete threads */
+ if (target_thread != RT_NULL)
+ {
+ rt_thread_delete(target_thread);
+ target_thread = RT_NULL;
+ }
+
+ if (monitor_thread != RT_NULL)
+ {
+ rt_thread_delete(monitor_thread);
+ monitor_thread = RT_NULL;
+ }
+}
+
+/* Basic API test */
+static void test_suspend_force_api_basic(void)
+{
+ rt_thread_t api_thread;
+
+ /* Reset global variables */
+ thread_started = RT_FALSE;
+ thread_should_exit = RT_FALSE;
+
+ /* Create a simple test thread */
+ api_thread = rt_thread_create("api_test",
+ simple_thread_entry,
+ (void *)&thread_started,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY,
+ THREAD_TIMESLICE);
+
+ uassert_not_null(api_thread);
+
+ rt_thread_startup(api_thread);
+ rt_thread_mdelay(50); /* Wait for thread to start */
+
+ uassert_true(thread_started);
+
+ /* Test basic suspend functionality */
+ rt_err_t result = rt_thread_suspend(api_thread);
+ uassert_true(result == RT_EOK);
+
+ rt_schedule();
+ rt_thread_mdelay(100);
+
+ /* Resume thread */
+ result = rt_thread_resume(api_thread);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_mdelay(50);
+
+ /* Clean up - delete the thread directly */
+ if (api_thread != RT_NULL)
+ {
+ rt_thread_delete(api_thread);
+ api_thread = RT_NULL;
+ }
+
+ /* Reset global variables for next test */
+ thread_started = RT_FALSE;
+ thread_should_exit = RT_FALSE;
+}
+
+/* Test suspend on thread that is created but not started */
+static void test_suspend_force_not_started_thread(void)
+{
+ rt_thread_t not_started_thread;
+
+ /* Create a thread but don't start it */
+ not_started_thread = rt_thread_create("not_started",
+ simple_thread_entry,
+ (void *)&thread_started,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY,
+ THREAD_TIMESLICE);
+
+ uassert_not_null(not_started_thread);
+
+ /* Verify thread is in INIT state */
+ uassert_true((RT_SCHED_CTX(not_started_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
+
+ /* Try to suspend a thread that hasn't been started yet */
+ rt_err_t suspend_result = rt_thread_suspend(not_started_thread);
+ rt_schedule();
+ uassert_true(suspend_result == -RT_ERROR);
+
+ /* Try to resume the not-started thread */
+ rt_err_t resume_result = rt_thread_resume(not_started_thread);
+ uassert_true(resume_result == -RT_EINVAL);
+
+ /* Now start the thread to see if it works normally */
+ rt_err_t startup_result = rt_thread_startup(not_started_thread);
+ uassert_true(startup_result == RT_EOK);
+
+ /* Wait a bit to see if thread starts normally */
+ rt_thread_mdelay(100);
+
+ /* The thread should have started successfully despite previous suspend/resume calls */
+ uassert_true(thread_started == RT_TRUE);
+
+ /* Clean up */
+ if (not_started_thread != RT_NULL)
+ {
+ rt_thread_delete(not_started_thread);
+ not_started_thread = RT_NULL;
+ }
+
+ /* Reset flag for next test */
+ thread_started = RT_FALSE;
+}
+
+/* Test deadlock risk */
+static void test_suspend_force_deadlock_risk(void)
+{
+ /* Reset global variables */
+ shared_counter = 0;
+ holder_got_mutex = RT_FALSE;
+ deadlock_detected = RT_FALSE;
+ test_completed = RT_FALSE;
+
+ /* Create mutex */
+ test_mutex = rt_mutex_create("test_mutex", RT_IPC_FLAG_PRIO);
+ uassert_not_null(test_mutex);
+
+ /* Create and start holder thread */
+ holder_thread = rt_thread_create("holder", mutex_holder_thread, RT_NULL,
+ THREAD_STACK_SIZE, UTEST_THR_PRIORITY, THREAD_TIMESLICE);
+ uassert_not_null(holder_thread);
+ rt_thread_startup(holder_thread);
+
+ /* Create and start waiter thread */
+ waiter_thread = rt_thread_create("waiter", mutex_waiter_thread, RT_NULL,
+ THREAD_STACK_SIZE, UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE);
+ uassert_not_null(waiter_thread);
+ /* Now start waiter thread, it will try to acquire mutex held by suspended thread */
+ rt_thread_startup(waiter_thread);
+
+ /* Wait for holder to get mutex */
+ int timeout = 100; /* 1 second timeout */
+ while (!holder_got_mutex && timeout-- > 0)
+ {
+ rt_thread_mdelay(10);
+ }
+
+ uassert_true(holder_got_mutex);
+
+ /* This is the critical test! Suspend thread that holds the mutex */
+ rt_err_t suspend_result = rt_thread_suspend(holder_thread);
+ uassert_true(suspend_result == RT_EOK);
+ rt_kprintf("Suspended holder thread (which holds the mutex)\n");
+
+ rt_schedule();
+
+ /* Wait for waiter thread to try acquiring lock */
+ rt_thread_mdelay(2000);
+
+ uassert_true(deadlock_detected == RT_TRUE);
+
+ /* Resume thread */
+ rt_err_t resume_result = rt_thread_resume(holder_thread);
+ uassert_true(resume_result == RT_EOK);
+ rt_kprintf("Resumed holder thread\n");
+
+ test_completed = RT_TRUE;
+
+ /* Wait for threads to complete */
+ rt_thread_mdelay(1000);
+
+ /* Verify rt_thread_suspend and rt_thread_resume executed successfully */
+ uassert_true(suspend_result == RT_EOK);
+ uassert_true(resume_result == RT_EOK);
+
+ /* Verify system didn't crash, threads can work normally */
+ uassert_true(shared_counter > 0);
+
+ /* Clean up resources */
+ if (test_mutex)
+ {
+ rt_mutex_delete(test_mutex);
+ test_mutex = RT_NULL;
+ }
+
+ /* Delete threads */
+ if (holder_thread != RT_NULL)
+ {
+ rt_thread_delete(holder_thread);
+ holder_thread = RT_NULL;
+ }
+
+ if (waiter_thread != RT_NULL)
+ {
+ rt_thread_delete(waiter_thread);
+ waiter_thread = RT_NULL;
+ }
+
+ /* Wait again to ensure threads are cleaned up */
+ rt_thread_mdelay(200);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ /* Reset all global variables to ensure clean state between tests */
+ work_counter = 0;
+ suspend_test_done = RT_FALSE;
+ suspend_success = RT_FALSE;
+ resume_success = RT_FALSE;
+ shared_counter = 0;
+ holder_got_mutex = RT_FALSE;
+ deadlock_detected = RT_FALSE;
+ test_completed = RT_FALSE;
+ thread_started = RT_FALSE;
+ thread_should_exit = RT_FALSE;
+
+ /* Clean up any remaining resources - safety check */
+ if (sync_sem != RT_NULL)
+ {
+ rt_sem_delete(sync_sem);
+ sync_sem = RT_NULL;
+ }
+
+ if (test_mutex != RT_NULL)
+ {
+ rt_mutex_delete(test_mutex);
+ test_mutex = RT_NULL;
+ }
+
+ /* Ensure all thread pointers are NULL */
+ target_thread = RT_NULL;
+ monitor_thread = RT_NULL;
+ holder_thread = RT_NULL;
+ waiter_thread = RT_NULL;
+
+ /* Give system time to complete cleanup */
+ rt_thread_mdelay(50);
+
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_suspend_force_api_basic);
+ UTEST_UNIT_RUN(test_suspend_force_not_started_thread);
+ UTEST_UNIT_RUN(test_suspend_force_normal_usage);
+ UTEST_UNIT_RUN(test_suspend_force_deadlock_risk);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_suspend", utest_tc_init, utest_tc_cleanup, 30);
+
diff --git a/src/utest/thread_tc.c b/src/utest/thread_tc.c
new file mode 100644
index 0000000000..7bea397b03
--- /dev/null
+++ b/src/utest/thread_tc.c
@@ -0,0 +1,756 @@
+ /*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-09.01 yangjie the firet version
+ * 2021-10.11 mazhiyuan add idle, yield, suspend, control, priority, delay_until
+ */
+
+#define __RT_IPC_SOURCE__ /* include internal API for utest */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
+#define THREAD_TIMESLICE 10
+
+rt_align(RT_ALIGN_SIZE)
+static char thread2_stack[UTEST_THR_STACK_SIZE];
+static struct rt_thread thread2;
+#ifdef RT_USING_HEAP
+ static rt_thread_t tid1 = RT_NULL;
+ static rt_thread_t tid3 = RT_NULL;
+ static rt_thread_t tid4 = RT_NULL;
+ static rt_thread_t tid5 = RT_NULL;
+ static rt_thread_t tid6 = RT_NULL;
+ static rt_thread_t tid7 = RT_NULL;
+#endif /* RT_USING_HEAP */
+
+static volatile rt_uint32_t tid3_delay_pass_flag = 0;
+static volatile rt_uint32_t tid3_finish_flag = 0;
+static volatile rt_uint32_t tid4_finish_flag = 0;
+static volatile rt_uint32_t tid6_finish_flag = 0;
+static volatile rt_uint32_t thread5_source = 0;
+
+#ifndef RT_USING_SMP
+ static volatile rt_uint32_t thread_yield_flag = 0;
+#endif
+static volatile rt_uint32_t entry_idle_hook_times = 0;
+static rt_thread_t __current_thread;
+static rt_uint8_t change_priority;
+static volatile rt_uint32_t count = 0;
+
+void thread1_entry(void *param)
+{
+ while (1);
+}
+
+static void test_dynamic_thread(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+ rt_err_t ret_delete = -RT_ERROR;
+
+ tid1 = rt_thread_create("thread1",
+ thread1_entry,
+ (void *)1,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ THREAD_TIMESLICE - 5);
+ if (tid1 == RT_NULL)
+ {
+ uassert_false(tid1 == RT_NULL);
+ goto __exit;
+ }
+
+ ret_startup = rt_thread_startup(tid1);
+ if (ret_startup != RT_EOK)
+ {
+ uassert_false(ret_startup != RT_EOK);
+ goto __exit;
+ }
+
+ ret_delete = rt_thread_delete(tid1);
+ if (ret_delete != RT_EOK)
+ {
+ uassert_false(ret_delete != RT_EOK);
+ goto __exit;
+ }
+
+ uassert_true(tid1 != RT_NULL && ret_startup == RT_EOK && ret_delete == RT_EOK);
+
+__exit:
+ if (tid1 != RT_NULL && ret_delete != RT_EOK)
+ {
+ rt_thread_delete(tid1);
+ }
+ return;
+}
+
+void thread2_entry(void *param)
+{
+ while (1);
+}
+
+static void test_static_thread(void)
+{
+ rt_err_t ret_init = -RT_ERROR;
+ rt_err_t ret_startup = -RT_ERROR;
+ rt_err_t ret_detach = -RT_ERROR;
+
+ ret_init = rt_thread_init(&thread2,
+ "thread2",
+ thread2_entry,
+ (void *)2,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ UTEST_THR_PRIORITY + 1,
+ THREAD_TIMESLICE);
+ if (ret_init != RT_EOK)
+ {
+ uassert_false(ret_init != RT_EOK);
+ goto __exit;
+ }
+
+ ret_startup = rt_thread_startup(&thread2);
+ if (ret_startup != RT_EOK)
+ {
+ uassert_false(ret_startup != RT_EOK);
+ goto __exit;
+ }
+
+ ret_detach = rt_thread_detach(&thread2);
+ if (ret_detach != RT_EOK)
+ {
+ uassert_false(ret_detach != RT_EOK);
+ goto __exit;
+ }
+
+ uassert_true(ret_init == RT_EOK && ret_startup == RT_EOK && ret_detach == RT_EOK);
+
+__exit:
+ if (ret_init == RT_EOK && ret_detach != RT_EOK)
+ {
+ rt_thread_detach(&thread2);
+ }
+ return;
+}
+
+static void thread3_entry(void *parameter)
+{
+ rt_tick_t tick, latency_tick;
+ tick = rt_tick_get();
+ rt_thread_delay(15);
+ latency_tick = rt_tick_get() - tick;
+ if (latency_tick > 16 || latency_tick < 15)
+ {
+ tid3_finish_flag = 1;
+ tid3_delay_pass_flag = 0;
+ return;
+ }
+ tid3_delay_pass_flag = 1;
+ tid3_finish_flag = 1;
+}
+
+static void test_thread_delay(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+
+ tid3 = rt_thread_create("thread3",
+ thread3_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (tid3 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid3 == RT_NULL);
+ goto __exit;
+ }
+
+ ret_startup = rt_thread_startup(tid3);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ goto __exit;
+ }
+
+ while (tid3_finish_flag != 1);
+ uassert_true(tid3_delay_pass_flag == 1);
+
+__exit:
+ return;
+}
+
+static void idle_hook(void)
+{
+ entry_idle_hook_times ++;
+}
+
+static void thread4_entry(void *parameter)
+{
+ rt_uint32_t delay_times = 5;
+ while (delay_times --)
+ {
+ rt_thread_mdelay(300);
+ }
+ rt_thread_idle_delhook(idle_hook);
+ tid4_finish_flag = 1;
+}
+
+static void test_idle_hook(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+
+ rt_thread_idle_sethook(idle_hook);
+
+ tid4 = rt_thread_create("thread4",
+ thread4_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (tid4 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid4 == RT_NULL);
+ goto __exit;
+ }
+
+ ret_startup = rt_thread_startup(tid4);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ goto __exit;
+ }
+
+ while (tid4_finish_flag != 1)
+ {
+ rt_thread_mdelay(200);
+ }
+ uassert_true(entry_idle_hook_times > 0);
+
+__exit:
+ return;
+}
+
+static void thread5_entry(void *parameter)
+{
+ while (1)
+ {
+ thread5_source ++;
+ rt_thread_delay(5);
+ if (thread5_source == 5)
+ {
+ rt_thread_yield();
+ }
+ }
+}
+
+static void thread6_entry(void *parameter)
+{
+ while (++ thread5_source <= 9);
+ tid6_finish_flag = 1;
+}
+
+static void test_thread_yield(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+ thread5_source = 0;
+ tid5 = rt_thread_create("thread5",
+ thread5_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (tid5 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid5 == RT_NULL);
+ goto __exit;
+ }
+ ret_startup = rt_thread_startup(tid5);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ goto __exit;
+ }
+ tid6 = rt_thread_create("thread6",
+ thread6_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (tid6 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid6 == RT_NULL);
+ goto __exit;
+ }
+ ret_startup = rt_thread_startup(tid6);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ goto __exit;
+ }
+
+ while (tid6_finish_flag != 1);
+ uassert_true(thread5_source == 10);
+
+__exit:
+ if (tid5 != RT_NULL)
+ {
+ rt_thread_delete(tid5);
+ }
+ return;
+}
+
+static void thread7_entry(void *parameter)
+{
+ while (1);
+}
+
+static void test_thread_control(void)
+{
+ rt_err_t ret_control = -RT_ERROR;
+ rt_err_t rst_delete = -RT_ERROR;
+ rt_sched_lock_level_t slvl;
+
+ tid7 = rt_thread_create("thread7",
+ thread7_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY + 1,
+ THREAD_TIMESLICE);
+ if (tid7 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid7 == RT_NULL);
+ goto __exit;
+ }
+
+ ret_control = rt_thread_control(tid7, RT_THREAD_CTRL_STARTUP, RT_NULL);
+ if (ret_control != RT_EOK)
+ {
+ LOG_E("rt_thread_control failed!");
+ uassert_false(1);
+ goto __exit;
+ }
+ rt_thread_mdelay(200);
+ rt_thread_control(tid7, RT_THREAD_CTRL_CHANGE_PRIORITY, &change_priority);
+
+ rt_sched_lock(&slvl);
+ if (rt_sched_thread_get_curr_prio(tid7) != change_priority)
+ {
+ LOG_E("rt_thread_control failed!");
+ uassert_false(1);
+ rt_sched_unlock(slvl);
+ goto __exit;
+ }
+ rt_sched_unlock(slvl);
+
+ rst_delete = rt_thread_control(tid7, RT_THREAD_CTRL_CLOSE, RT_NULL);
+ if (rst_delete != RT_EOK)
+ {
+ LOG_E("rt_thread_control failed!");
+ uassert_false(rst_delete != RT_EOK);
+ goto __exit;
+ }
+
+ uassert_true(1);
+
+__exit:
+ if (tid7 != RT_NULL && rst_delete != RT_EOK)
+ {
+ rt_thread_delete(tid7);
+ }
+ return;
+}
+
+static void thread8_entry(void *parameter)
+{
+ for (; count < 10; count ++);
+}
+
+static void test_thread_priority(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+ rt_thread_t tid8 = RT_NULL;
+
+ tid8 = rt_thread_create("thread8",
+ thread8_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ UTEST_THR_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (tid8 == RT_NULL)
+ {
+ LOG_E("rt_thread_create failed!");
+ uassert_false(tid8 == RT_NULL);
+ return;
+ }
+ count = 0;
+ ret_startup = rt_thread_startup(tid8);
+ if (ret_startup != RT_EOK)
+ {
+ uassert_false(ret_startup != RT_EOK);
+ return ;
+ }
+ uassert_true(count == 10);
+
+ return;
+}
+
+static void test_delay_until(void)
+{
+ rt_tick_t tick;
+ rt_tick_t check_tick = 0;
+ rt_tick_t delta = 0;
+
+ tick = rt_tick_get();
+
+ check_tick = tick;
+ rt_thread_delay_until(&tick, 100);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[100] -> %d\n", delta);
+ uassert_int_equal(delta, 100);
+
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 200);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[200] -> %d\n", delta);
+ uassert_int_equal(delta, 200);
+
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 300);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[300] -> %d\n", delta);
+ uassert_int_equal(delta, 300);
+
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 100);
+ delta = rt_tick_get() - check_tick;
+ uassert_int_equal(delta, 100);
+
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 50);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[50] -> %d\n", delta);
+ uassert_int_equal(delta, 50);
+
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 20);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[20] -> %d\n", delta);
+ uassert_int_equal(delta, 20);
+
+ /**
+ * the rt_kprints above can take few ticks to complete, maybe more than 10
+ */
+ tick = rt_tick_get();
+ check_tick = tick;
+ rt_thread_delay(2);
+ rt_thread_delay_until(&tick, 10);
+ delta = rt_tick_get() - check_tick;
+ rt_kprintf("delta[10] -> %d\n", delta);
+ uassert_int_equal(delta, 10);
+}
+
+static rt_thread_t tidA, tidB1, tidB2;
+static uint32_t timeslice_cntA, timeslice_cntB1, timeslice_cntB2;
+
+static void test_timeslice_threadA_entry(void *parameter)
+{
+ while (1)
+ {
+ rt_thread_delay(2);
+ timeslice_cntA++;
+ if (timeslice_cntA > 10) return;
+ }
+}
+static void test_timeslice_threadB1_entry(void *parameter)
+{
+ while (1)
+ {
+ timeslice_cntB1++;
+ if (timeslice_cntA > 10) return;
+ }
+}
+static void test_timeslice_threadB2_entry(void *parameter)
+{
+ while (1)
+ {
+ timeslice_cntB2++;
+ if (timeslice_cntA > 10) return;
+ }
+}
+
+void test_timeslice(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+ uint32_t diff;
+
+ timeslice_cntA = 0;
+ timeslice_cntB1 = 0;
+ timeslice_cntB2 = 0;
+
+ tidA = rt_thread_create("timeslice", test_timeslice_threadA_entry, RT_NULL,
+ 2048, UTEST_THR_PRIORITY + 1, 10);
+ if (!tidA)
+ {
+ LOG_E("rt_thread_create failed!");
+ return;
+ }
+
+ rt_thread_control(tidA, RT_THREAD_CTRL_BIND_CPU, (void *)1);
+ ret_startup = rt_thread_startup(tidA);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ return ;
+ }
+
+ tidB1 = rt_thread_create("timeslice", test_timeslice_threadB1_entry, RT_NULL,
+ 2048, UTEST_THR_PRIORITY + 2, 2);
+ if (!tidB1)
+ {
+ LOG_E("rt_thread_create failed!");
+ return;
+ }
+
+ rt_thread_control(tidB1, RT_THREAD_CTRL_BIND_CPU, (void *)1);
+ ret_startup = rt_thread_startup(tidB1);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ return ;
+ }
+
+ tidB2 = rt_thread_create("timeslice", test_timeslice_threadB2_entry, RT_NULL,
+ 2048, UTEST_THR_PRIORITY + 2, 2);
+ if (!tidB2)
+ {
+ LOG_E("rt_thread_create failed!");
+ return;
+ }
+
+ rt_thread_control(tidB2, RT_THREAD_CTRL_BIND_CPU, (void *)1);
+ ret_startup = rt_thread_startup(tidB2);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ return ;
+ }
+ do{
+ rt_thread_delay(2 * 20);
+ }while(timeslice_cntA <= 10);
+
+ rt_kprintf("A:%d,B1:%d,B2:%d\n", timeslice_cntA, timeslice_cntB1, timeslice_cntB2);
+ diff = abs(timeslice_cntB1 - timeslice_cntB2);
+ uassert_true(diff * 100 / timeslice_cntB1 < 30);
+ uassert_true(timeslice_cntA == 11);
+}
+
+#ifndef RT_USING_SMP
+static volatile rt_uint32_t yield_count;
+
+static void test_thread_yield_inc_entry(void *parameter)
+{
+ rt_uint32_t loop = 0;
+
+ while (1)
+ {
+ if (loop++ > 10001)
+ break;
+ yield_count++;
+ rt_thread_yield();
+ }
+}
+
+static void test_thread_yield_entry(void *parameter)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+
+ rt_thread_t tid;
+ rt_uint32_t loop = 0;
+ rt_uint32_t count_before;
+
+ tid = rt_thread_create("inc", test_thread_yield_inc_entry, RT_NULL,
+ 2048, 1, 10);
+ if (!tid)
+ {
+ LOG_E("rt_thread_create failed!");
+ return;
+ }
+
+ ret_startup = rt_thread_startup(tid);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ return ;
+ }
+
+ while (1)
+ {
+ if (loop++ > 10000)
+ break;
+
+ count_before = yield_count;
+ rt_thread_yield();
+ if (yield_count == count_before)
+ {
+ LOG_E("yield error!");
+ return;
+ }
+ }
+ thread_yield_flag = 1;
+}
+
+void test_thread_yield_nosmp(void)
+{
+ rt_err_t ret_startup = -RT_ERROR;
+
+ rt_thread_t tid;
+
+ yield_count = 0;
+
+ tid = rt_thread_create("chkcnt", test_thread_yield_entry, RT_NULL,
+ 2048, 1, 10);
+ if (!tid)
+ {
+ LOG_E("rt_thread_create failed!");
+ return;
+ }
+
+ ret_startup = rt_thread_startup(tid);
+ if (ret_startup != RT_EOK)
+ {
+ LOG_E("rt_thread_startup failed!");
+ uassert_false(1);
+ return ;
+ }
+
+ uassert_true(thread_yield_flag == 1);
+}
+
+// static rt_uint32_t thread9_count = 0;
+// static void thread9_entry(void *parameter)
+// {
+// while (1)
+// {
+// thread9_count ++;
+// }
+
+// }
+// static void test_thread_suspend(void)
+// {
+// static rt_thread_t tid;
+// rt_err_t ret_startup = -RT_ERROR;
+// uint32_t count_before_suspend, count_before_resume, count_after_resume;
+// tid = rt_thread_create("thread9",
+// thread9_entry,
+// RT_NULL,
+// THREAD_STACK_SIZE,
+// UTEST_THR_PRIORITY + 1,
+// THREAD_TIMESLICE);
+// if (tid == RT_NULL)
+// {
+// LOG_E("rt_thread_create failed!");
+// uassert_false(tid4 == RT_NULL);
+// goto __exit;
+// }
+
+// ret_startup = rt_thread_startup(tid);
+// if (ret_startup != RT_EOK)
+// {
+// LOG_E("rt_thread_startup failed!");
+// uassert_false(1);
+// goto __exit;
+// }
+// rt_thread_delay(5);
+// rt_thread_suspend(tid);
+// count_before_suspend = thread9_count;
+// uassert_true(count_before_suspend != 0);
+// rt_thread_delay(5);
+// count_before_resume = thread9_count;
+// uassert_true(count_before_suspend == count_before_resume);
+// rt_thread_resume(tid);
+// rt_thread_delay(5);
+// count_after_resume = thread9_count;
+// uassert_true(count_after_resume != count_before_resume);
+
+// __exit:
+// if (tid != RT_NULL)
+// {
+// rt_thread_delete(tid);
+// }
+// return;
+// }
+#endif
+
+static rt_err_t utest_tc_init(void)
+{
+ __current_thread = rt_thread_self();
+ change_priority = UTEST_THR_PRIORITY + 5;
+ tid3_delay_pass_flag = 0;
+ tid3_finish_flag = 0;
+ tid4_finish_flag = 0;
+ tid6_finish_flag = 0;
+ entry_idle_hook_times = 0;
+ count = 0;
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ /* init, detach */
+ UTEST_UNIT_RUN(test_static_thread);
+ /* create, delete */
+ UTEST_UNIT_RUN(test_dynamic_thread);
+ /* delay */
+ UTEST_UNIT_RUN(test_thread_delay);
+ /* idle_sethook, idle_delhook */
+ UTEST_UNIT_RUN(test_idle_hook);
+ /* yield */
+ UTEST_UNIT_RUN(test_thread_yield);
+#ifndef RT_USING_SMP
+ /* yield_nosmp */
+ UTEST_UNIT_RUN(test_thread_yield_nosmp);
+ /* suspend, resume */
+ // UTEST_UNIT_RUN(test_thread_suspend);
+#endif
+ /* control */
+ UTEST_UNIT_RUN(test_thread_control);
+ UTEST_UNIT_RUN(test_thread_priority);
+ /* delay_until */
+ UTEST_UNIT_RUN(test_delay_until);
+ /* timeslice */
+ // UTEST_UNIT_RUN(test_timeslice); /* Can not running in Github Action QEMU */
+}
+
+
+UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_tc", utest_tc_init, utest_tc_cleanup, 1000);
+
+/********************* end of file ************************/
diff --git a/src/utest/timer_tc.c b/src/utest/timer_tc.c
new file mode 100644
index 0000000000..96194b49f8
--- /dev/null
+++ b/src/utest/timer_tc.c
@@ -0,0 +1,747 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-08-12 luckyzjq the first version
+ */
+
+#include <rtthread.h>
+#include <stdlib.h>
+#include "utest.h"
+
+#undef uassert_true
+#define uassert_true(value) \
+ do \
+ { \
+ if (!(value)) \
+ { \
+ __utest_assert(value, "(" #value ") is false"); \
+ } \
+ } while (0)
+
+/* notify user that the test is not corrupted */
+#define PRINT_PROGRESS(id) LOG_I("Testing on %d", id)
+
+static rt_uint8_t timer_flag_oneshot[] = {
+ RT_TIMER_FLAG_ONE_SHOT,
+ RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_HARD_TIMER,
+ RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER,
+};
+
+static rt_uint8_t timer_flag_periodic[] = {
+ RT_TIMER_FLAG_PERIODIC,
+ RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_HARD_TIMER,
+ RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER,
+};
+
+typedef struct test_timer_struct
+{
+ struct rt_timer static_timer; /* static timer handler */
+ rt_timer_t dynamic_timer; /* dynamic timer pointer */
+ rt_tick_t expect_tick; /* expect tick */
+ rt_ubase_t callbacks; /* timer callback times */
+ rt_bool_t is_static; /* static or dynamic timer */
+} timer_struct;
+static timer_struct timer;
+
+static void timer_oneshot(void *param)
+{
+ timer_struct *timer_call;
+ timer_call = (timer_struct *)param;
+ timer_call->callbacks++;
+
+ uassert_true(rt_tick_get() == timer_call->expect_tick);
+}
+
+static void timer_periodic(void *param)
+{
+ rt_err_t result;
+ timer_struct *timer_call;
+ timer_call = (timer_struct *)param;
+ timer_call->callbacks++;
+
+ uassert_true(rt_tick_get() == timer_call->expect_tick);
+
+ if (timer_call->is_static)
+ {
+ timer_call->expect_tick = rt_tick_get() + timer_call->static_timer.init_tick;
+ }
+ else
+ {
+ timer_call->expect_tick = rt_tick_get() + timer_call->dynamic_timer->init_tick;
+ }
+
+ if (timer_call->callbacks == 5)
+ {
+ /* periodic timer can stop */
+ if (timer_call->is_static)
+ {
+ result = rt_timer_stop(&timer_call->static_timer);
+ }
+ else
+ {
+ result = rt_timer_stop(timer_call->dynamic_timer);
+ }
+
+ uassert_true(result == RT_EOK);
+ }
+}
+
+static void test_static_timer(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_TRUE;
+
+ /* one shot timer test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_oneshot,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+
+ /* periodic timer test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_periodic); i++)
+ {
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_periodic,
+ &timer,
+ time_out,
+ timer_flag_periodic[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(5 * time_out + 1);
+
+ uassert_true(timer.callbacks >= 5);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+}
+
+static void test_static_timer_start_twice(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_TRUE;
+
+ /* timer start twice test */
+ for (int time_out = 2; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_oneshot,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_delay(1);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+}
+
+static void timer_control(void *param)
+{
+ rt_err_t result;
+ timer_struct *timer_call;
+ timer_call = (timer_struct *)param;
+ timer_call->callbacks++;
+
+ uassert_true(rt_tick_get() == timer_call->expect_tick);
+
+ /* periodic timer can stop */
+ if (timer_call->is_static)
+ {
+ result = rt_timer_stop(&timer_call->static_timer);
+ }
+ else
+ {
+ result = rt_timer_stop(timer_call->dynamic_timer);
+ }
+
+ uassert_true(result == RT_EOK);
+}
+
+static void test_static_timer_control(void)
+{
+ rt_err_t result;
+ rt_tick_t set_data;
+ rt_tick_t get_data;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_TRUE;
+
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_control,
+ &timer,
+ 5,
+ RT_TIMER_FLAG_PERIODIC);
+
+ /* test set data */
+ set_data = 10;
+ result = rt_timer_control(&timer.static_timer, RT_TIMER_CTRL_SET_TIME, &set_data);
+
+ uassert_true(result == RT_EOK);
+
+ /* test get data */
+ result = rt_timer_control(&timer.static_timer, RT_TIMER_CTRL_GET_TIME, &get_data);
+
+ uassert_true(result == RT_EOK);
+ uassert_true(set_data == get_data);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + set_data;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_delay(3 * set_data + 1);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+ uassert_true(timer.callbacks == 1);
+}
+
+static void timer_start_in_callback(void *param)
+{
+ rt_err_t result;
+ timer_struct *timer_call;
+ timer_call = (timer_struct *)param;
+ timer_call->callbacks++;
+
+ uassert_true(rt_tick_get() == timer_call->expect_tick);
+
+ if (timer_call->is_static)
+ {
+ timer_call->expect_tick = rt_tick_get() + timer_call->static_timer.init_tick;
+ result = rt_timer_start(&timer_call->static_timer);
+ }
+ else
+ {
+ timer_call->expect_tick = rt_tick_get() + timer_call->dynamic_timer->init_tick;
+ result = rt_timer_start(timer_call->dynamic_timer);
+ }
+
+ uassert_true(result == RT_EOK);
+}
+
+static void timer_start_stop_in_callback(void *param)
+{
+ rt_err_t result;
+ timer_struct *timer_call;
+ timer_call = (timer_struct *)param;
+ timer_call->callbacks++;
+
+ uassert_true(rt_tick_get() == timer_call->expect_tick);
+
+ if (timer_call->is_static)
+ {
+ result = rt_timer_start(&timer_call->static_timer);
+ }
+ else
+ {
+ result = rt_timer_start(timer_call->dynamic_timer);
+ }
+
+ uassert_true(result == RT_EOK);
+
+ if (timer_call->is_static)
+ {
+ result = rt_timer_stop(&timer_call->static_timer);
+ }
+ else
+ {
+ result = rt_timer_stop(timer_call->dynamic_timer);
+ }
+
+ uassert_true(result == RT_EOK);
+}
+
+static void test_static_timer_op_in_callback(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_TRUE;
+
+ /* start in callback test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_start_in_callback,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(5 * time_out + 1);
+
+ uassert_true(timer.callbacks >= 5);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+ uassert_true(result == RT_EOK);
+
+ timer.callbacks = 0;
+ }
+ }
+
+ /* start & stop in callback test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_periodic); i++)
+ {
+ rt_timer_init(&timer.static_timer,
+ "static_timer",
+ timer_start_stop_in_callback,
+ &timer,
+ time_out,
+ timer_flag_periodic[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(&timer.static_timer);
+
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_detach(&timer.static_timer);
+
+ uassert_true(result == RT_EOK);
+
+ timer.callbacks = 0;
+ }
+ }
+}
+
+#ifdef RT_USING_HEAP
+
+static void test_dynamic_timer(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_FALSE;
+
+ /* one shot timer test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_oneshot,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+
+ /* periodic timer test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_periodic); i++)
+ {
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_periodic,
+ &timer,
+ time_out,
+ timer_flag_periodic[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(5 * time_out + 1);
+ uassert_true(timer.callbacks >= 5);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+}
+
+static void test_dynamic_timer_control(void)
+{
+ rt_err_t result;
+ rt_tick_t set_data;
+ rt_tick_t get_data;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_FALSE;
+
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_control,
+ &timer,
+ 5,
+ RT_TIMER_FLAG_PERIODIC);
+
+ /* test set data */
+ set_data = 10;
+ result = rt_timer_control(timer.dynamic_timer, RT_TIMER_CTRL_SET_TIME, &set_data);
+ uassert_true(result == RT_EOK);
+
+ /* test get data */
+ result = rt_timer_control(timer.dynamic_timer, RT_TIMER_CTRL_GET_TIME, &get_data);
+
+ uassert_true(result == RT_EOK);
+ uassert_true(set_data == get_data);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + set_data;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_delay(3 * set_data + 1);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+ uassert_true(timer.callbacks == 1);
+}
+
+static void test_dynamic_timer_start_twice(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_FALSE;
+
+ /* timer start twice test */
+ for (int time_out = 2; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_oneshot,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ rt_thread_delay(1);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+ timer.callbacks = 0;
+ }
+ }
+}
+
+static void test_dynamic_timer_op_in_callback(void)
+{
+ rt_err_t result;
+
+ timer.callbacks = 0;
+ timer.is_static = RT_FALSE;
+
+ /* start in callback test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_oneshot); i++)
+ {
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_start_in_callback,
+ &timer,
+ time_out,
+ timer_flag_oneshot[i]);
+
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(5 * time_out + 1);
+
+ uassert_true(timer.callbacks >= 5);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+ uassert_true(result == RT_EOK);
+
+ timer.callbacks = 0;
+ }
+ }
+
+ /* start & stop in callback test */
+ for (int time_out = 1; time_out < 10; time_out++)
+ {
+ for (int i = 0; i < sizeof(timer_flag_periodic); i++)
+ {
+ timer.dynamic_timer = rt_timer_create("dynamic_timer",
+ timer_start_stop_in_callback,
+ &timer,
+ time_out,
+ timer_flag_periodic[i]);
+ /* calc expect tick */
+ timer.expect_tick = rt_tick_get() + time_out;
+
+ /* start timer */
+ result = rt_timer_start(timer.dynamic_timer);
+
+ uassert_true(result == RT_EOK);
+
+ /* wait for timerout */
+ rt_thread_delay(3 * time_out + 1);
+
+ uassert_true(timer.callbacks == 1);
+
+ /* detach timer */
+ result = rt_timer_delete(timer.dynamic_timer);
+
+ uassert_true(result == RT_EOK);
+
+ timer.callbacks = 0;
+ }
+ }
+}
+#endif /* RT_USING_HEAP */
+
+#define TEST_TIME_S 60 // test 60 seconds
+#define STRESS_TIMERS 100
+
+static struct rt_timer stress_timer[STRESS_TIMERS];
+
+static void timer_stress(void *param)
+{
+ rt_timer_t stress_timer = (rt_timer_t)param;
+
+ if (rand() % 2 == 0)
+ {
+ rt_timer_start(stress_timer);
+ }
+ else
+ {
+ rt_timer_stop(stress_timer);
+ }
+}
+
+static void test_timer_stress(void)
+{
+ rt_tick_t start;
+ rt_ubase_t iters = 0;
+ rt_ubase_t cur_tick;
+ rt_ubase_t next_print_time;
+
+ LOG_I("timer stress test begin, it will take %d seconds", 3*TEST_TIME_S);
+
+ for (int i = 0; i < sizeof(timer_flag_periodic); i++)
+ {
+ for (int j = 0; j < STRESS_TIMERS; j++)
+ {
+ rt_timer_init(&stress_timer[j],
+ "stress_timer",
+ timer_stress,
+ &stress_timer[j],
+ j + 1,
+ timer_flag_periodic[i]);
+ }
+
+ start = rt_tick_get();
+ cur_tick = rt_tick_get();
+ next_print_time = cur_tick + RT_TICK_PER_SECOND;
+ while (cur_tick - start <= TEST_TIME_S * RT_TICK_PER_SECOND)
+ {
+ for (int j = 0; j < STRESS_TIMERS; j++)
+ {
+ if (rand() % 2 == 0)
+ {
+ rt_timer_start(&stress_timer[j]);
+ }
+ else
+ {
+ rt_timer_stop(&stress_timer[j]);
+ }
+ }
+ iters ++;
+ cur_tick = rt_tick_get();
+ if (cur_tick > next_print_time)
+ {
+ PRINT_PROGRESS(next_print_time);
+ next_print_time = cur_tick + RT_TICK_PER_SECOND;
+ }
+ }
+
+ for (int j = 0; j < STRESS_TIMERS; j++)
+ {
+ rt_timer_detach(&stress_timer[j]);
+ }
+ }
+
+ LOG_I("success after %lu iterations", iters);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ timer.dynamic_timer = RT_NULL;
+ timer.callbacks = 0;
+
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ timer.dynamic_timer = RT_NULL;
+ timer.callbacks = 0;
+
+ return RT_EOK;
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(test_static_timer);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_static_timer_control);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_static_timer_start_twice);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_static_timer_op_in_callback);
+ PRINT_PROGRESS(__LINE__);
+#ifdef RT_USING_HEAP
+ UTEST_UNIT_RUN(test_dynamic_timer);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_dynamic_timer_control);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_dynamic_timer_start_twice);
+ PRINT_PROGRESS(__LINE__);
+ UTEST_UNIT_RUN(test_dynamic_timer_op_in_callback);
+ PRINT_PROGRESS(__LINE__);
+#endif /* RT_USING_HEAP */
+ UTEST_UNIT_RUN(test_timer_stress);
+ PRINT_PROGRESS(__LINE__);
+}
+UTEST_TC_EXPORT(testcase, "testcases.kernel.timer_tc", utest_tc_init, utest_tc_cleanup, 1000);
+
+/*********************** end of file ****************************/