diff options
| author | Chen Wang <[email protected]> | 2025-09-22 14:45:35 +0800 |
|---|---|---|
| committer | R b b666 <[email protected]> | 2025-09-26 09:59:05 +0800 |
| commit | 6e51a8405ff220424fe7d435958a787dba621a35 (patch) | |
| tree | 89a535a3fe613dae617372e089516bad35443228 /src | |
| parent | 495c8cfc25e0d03eb6ba9914df17487f839c557d (diff) | |
utest: move core tesetcases under to directiry "src"
Signed-off-by: Chen Wang <[email protected]>
Diffstat (limited to 'src')
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 *)¶ms[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 *)¶ms[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 *)¶ms[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 *)¶ms[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, ¶ms[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 *)¶ms[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 ****************************/ |
