diff --git a/src/utest/object_tc.c b/src/utest/object_tc.c index 99af2241de2..2ec54950758 100644 --- a/src/utest/object_tc.c +++ b/src/utest/object_tc.c @@ -7,170 +7,188 @@ * Date Author Notes * 2025-07-18 kurisaW First commit * 2025-11-13 CYFS Add standardized documentation block for object_tc + * 2025-11-19 Rbb666 Refactor tests, add stress and error-path coverage */ /** * Test Case Name: Kernel Object Management Test * - * Test Objectives: - * - Validate RT-Thread object lifecycle, lookup, enumeration, and metadata utilities - * - Verify core APIs: rt_object_init, rt_object_allocate, rt_object_delete, rt_object_detach, - * rt_object_find, rt_thread_find, rt_device_find, rt_object_get_information, - * rt_object_get_length, rt_object_get_pointers, rt_object_get_name, rt_object_get_type, - * rt_object_is_systemobject + * Objectives: + * - Validate RT-Thread object lifecycle, lookup, enumeration, metadata, and error handling + * - Exercise both static and dynamic objects under realistic embedded constraints + * - Apply repeated stress rounds to ensure determinism across multiple executions * - * Test Scenarios: - * - **Scenario 1 (Name Handling / test_object_name_handling):** - * 1. Create static objects with maximum-length names to validate truncation and null-termination rules. - * 2. Allocate dynamic objects with maximum-length names to confirm dynamic object naming and non-system classification. - * 3. Exercise NULL name handling for both static initialization and dynamic allocation paths. - * 4. Verify exact-length names remain intact alongside proper detach/cleanup. - * - **Scenario 2 (Find Operations / test_object_find_operations):** - * 1. Register static thread objects and verify discovery via rt_object_find. - * 2. Create runtime threads and confirm rt_thread_find returns expected handles. - * 3. (Optional with RT_USING_DEVICE) Register temporary devices, test rt_device_find against multiple entries, and validate deregistration paths. - * 4. Test same-prefix device registrations to confirm distinct name resolution. - * 5. Validate negative paths for nonexistent and NULL names across object, thread, and device lookup helpers. - * - **Scenario 3 (Info Enumeration / test_object_info_enumeration):** - * 1. Mix static and dynamic thread objects and query rt_object_get_information for metadata. - * 2. Retrieve counts with rt_object_get_length and enumerate pointers with sufficient buffer space. - * 3. Probe buffer boundary behavior by providing undersized pointer arrays. - * 4. (Optional with RT_USING_SEMAPHORE) Inspect empty semaphore container reporting. - * - **Scenario 4 (Type Utilities / test_object_type_handling):** - * 1. Inspect object type via rt_object_get_type and verify system-object status. - * 2. Retrieve object names using full buffers, truncated buffers, and invalid parameters. - * 3. Confirm error codes for NULL object pointers, NULL buffers, and zero-length requests. + * Test Implementation: * - * Verification Metrics: - * - **Scenario 1:** Name strings must be null-terminated within TEST_RT_NAME_MAX, retain expected contents, and reflect correct system-object classification. - * - **Scenario 2:** Lookup helpers return valid handles for registered objects, correctly distinguish between devices with similar name prefixes, and return NULL for missing or invalid names; device deregistration must succeed. - * - **Scenario 3:** Enumeration APIs report counts ≥ created objects, populate pointer arrays without overflow, and respect small-buffer contracts. - * - **Scenario 4:** Type and name utilities yield correct metadata and error codes across valid/invalid inputs, preserving partial name copies when truncated. + * 1. test_object_name_handling() + * - Tests object name storage, truncation, and null-termination + * - Validates RT_NAME_MAX boundary conditions + * - Verifies static vs dynamic object identification via rt_object_is_systemobject() + * - Tests both NULL and non-NULL name initialization * - * Dependencies: - * - Scheduler availability (`rt_scheduler_is_available`) required before executing the suite. - * - Dynamic memory (rt_malloc/rt_free) needed for pointer buffer allocation. - * - `RT_USING_UTEST` enabled with test entry registered under `core.object`. - * - Optional paths: `RT_USING_DEVICE` for device lookup tests, `RT_USING_SEMAPHORE` for semaphore enumeration checks. + * 2. test_object_find_operations() + * - Tests rt_object_find() with static objects + * - Tests rt_thread_find() with dynamically created threads + * - Tests rt_device_find() with registered devices (when RT_USING_DEVICE enabled) + * - Validates NULL parameter handling and non-existent object queries + * - Ensures proper cleanup and object removal from containers * - * Expected Results: - * - Test runs to completion with all assertions passing in the utest framework. - * - Console shows `[ PASSED ] [ result ] testcase (core.object)` when invoked via `utest_run core.object`. + * 3. test_object_info_enumeration() + * - Tests rt_object_get_information() for object container metadata + * - Tests rt_object_get_length() for accurate object counting + * - Tests rt_object_get_pointers() for batch object retrieval + * - Validates mixed static and dynamic object enumeration + * - Tests boundary conditions with NULL/invalid parameters + * + * 4. test_object_type_handling() + * - Tests rt_object_get_type() for correct type identification + * - Tests rt_object_get_name() with various buffer sizes + * - Validates truncation behavior when buffer is too small + * - Tests error handling for invalid parameters + * + * 5. test_object_error_paths() + * - Tests invalid object class (RT_Object_Class_Null) + * - Tests rt_object_for_each() iteration mechanism + * - Validates early termination and error propagation in iterators + * + * 6. test_custom_object_lifecycle() (when RT_USING_HEAP enabled) + * - Tests rt_custom_object_create() and rt_custom_object_destroy() + * - Validates custom cleanup callback execution + * + * 7. test_object_pressure() + * - Stress tests with OBJECT_STRESS_BATCH (24) objects across OBJECT_STRESS_ROUNDS (3) rounds + * - Validates memory cleanup and object container consistency under load + * - Ensures no resource leaks after repeated create/delete cycles + * + * Helper Functions: + * - generate_unique_name(): Creates collision-free object names using incremental counter + * - name_in_use(): Checks if a name is already registered in the object system + * + * Memory Safety: + * - All dynamic objects are properly deleted with appropriate delays for defunct queue processing + * - Static objects use stack allocation to prevent leaks + * - testcase_cleanup() includes 50ms delay to ensure complete resource cleanup */ #include #include #include -/** - * @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 RT_NAME_MAX +#define OBJECT_STRESS_BATCH 24 +#define OBJECT_STRESS_ROUNDS 3 -/* Define TEST_RT_NAME_MAX for testing purposes */ -#define TEST_RT_NAME_MAX RT_NAME_MAX +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif /* Global counter for unique object names */ -static rt_uint32_t name_counter = 0; +static rt_uint32_t name_counter; -/* 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) +static rt_bool_t name_in_use(const char *name, enum rt_object_class_type type) { + if (!name) + return RT_FALSE; + + if (rt_object_find(name, (rt_uint8_t)type)) + return RT_TRUE; + + if (type == RT_Object_Class_Thread) + { + if (rt_thread_find((char *)name)) + return RT_TRUE; + } +#ifdef RT_USING_DEVICE + if (type == RT_Object_Class_Device) + { + if (rt_device_find(name)) + return RT_TRUE; + } +#endif + + return RT_FALSE; +} + +static rt_err_t generate_unique_name(char *buf, + rt_size_t size, + const char *prefix, + enum rt_object_class_type type) +{ + rt_size_t prefix_len, max_prefix_len; + if (!buf || !prefix || size < TEST_RT_NAME_MAX) return -RT_EINVAL; - for (int i = 0; i < 1000; i++) /* Limit attempts to prevent infinite loop */ + /* Reserve space for up to 5-digit counter (99999) plus '\0' */ + max_prefix_len = TEST_RT_NAME_MAX - 6; + prefix_len = rt_strlen(prefix); + + /* Limit prefix length to fit within name constraints */ + if (prefix_len > max_prefix_len) + prefix_len = max_prefix_len; + + for (int i = 0; i < 2000; i++) { - 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; - } + /* Generate name with truncated prefix if necessary */ + rt_snprintf(buf, size, "%.*s%u", (int)prefix_len, prefix, name_counter++); + buf[TEST_RT_NAME_MAX - 1] = '\0'; - /* 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) - { + if (!name_in_use(buf, type)) return RT_EOK; - } } + return -RT_ENOMEM; } static void test_object_name_handling(void) { - struct rt_object static_obj1, static_obj2, static_obj3; + struct rt_object static_obj; rt_object_t dyn_obj = RT_NULL; char test_name[TEST_RT_NAME_MAX]; - char unique_name[TEST_RT_NAME_MAX]; + char exact_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); + rt_object_init(&static_obj, RT_Object_Class_Thread, test_name); + uassert_true(rt_strlen(static_obj.name) <= TEST_RT_NAME_MAX - 1); + uassert_true(static_obj.name[TEST_RT_NAME_MAX - 1] == '\0'); + uassert_true(rt_strncmp(static_obj.name, test_name, TEST_RT_NAME_MAX - 1) == 0); + uassert_true(rt_object_is_systemobject(&static_obj)); + rt_object_detach(&static_obj); + 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); + rt_object_init(&static_obj, RT_Object_Class_Thread, RT_NULL); + uassert_true(static_obj.name[0] == '\0'); + rt_object_detach(&static_obj); - uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "dynull") == RT_EOK); - dyn_obj = rt_object_allocate(RT_Object_Class_Thread, NULL); + dyn_obj = rt_object_allocate(RT_Object_Class_Thread, RT_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); + rt_object_init(&static_obj, RT_Object_Class_Thread, exact_name); + uassert_str_equal(static_obj.name, exact_name); + rt_object_detach(&static_obj); } static void test_object_find_operations(void) { - rt_object_t found; - rt_thread_t found_thread; - rt_device_t found_device; + struct rt_object static_obj; char name[TEST_RT_NAME_MAX]; - rt_err_t ret; + rt_thread_t thread; + rt_thread_t found_thread; + rt_object_t found; + char missing_name[] = "object.not.exists"; - /* 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); + uassert_true(generate_unique_name(name, sizeof(name), "sobj", RT_Object_Class_Thread) == 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); @@ -178,195 +196,327 @@ static void test_object_find_operations(void) 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_true(generate_unique_name(name, sizeof(name), "thr", RT_Object_Class_Thread) == RT_EOK); + thread = rt_thread_create(name, RT_NULL, RT_NULL, 512, RT_THREAD_PRIORITY_MAX / 2, 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); + rt_thread_mdelay(10); + uassert_null(rt_thread_find(name)); #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); + rt_device_t found_device; + + uassert_true(generate_unique_name(name, sizeof(name), "dev", RT_Object_Class_Device) == RT_EOK); + uassert_int_equal(rt_device_register(&device, name, RT_DEVICE_FLAG_RDONLY), 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); + /* Verify device is properly unregistered */ + uassert_null(rt_device_find(name)); #endif - /* Test 2: Same Prefix Within TEST_RT_NAME_MAX */ + uassert_null(rt_object_find(missing_name, RT_Object_Class_Thread)); + uassert_null(rt_thread_find(missing_name)); + #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); + uassert_null(rt_device_find(missing_name)); #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); - + uassert_null(rt_object_find(RT_NULL, RT_Object_Class_Thread)); + uassert_null(rt_thread_find(RT_NULL)); #ifdef RT_USING_DEVICE - found_device = rt_device_find(NULL); - uassert_null(found_device); + uassert_null(rt_device_find(RT_NULL)); #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]; + enum { STATIC_OBJ_COUNT = 2, DYNAMIC_OBJ_COUNT = 2 }; + struct rt_object static_objs[STATIC_OBJ_COUNT]; + rt_object_t dyn_objs[DYNAMIC_OBJ_COUNT] = {RT_NULL}; + char names[STATIC_OBJ_COUNT + DYNAMIC_OBJ_COUNT][TEST_RT_NAME_MAX]; + int baseline; - /* Generate unique names */ - for (int i = 0; i < 5; i++) + for (int i = 0; i < ARRAY_SIZE(names); i++) { - uassert_true(generate_unique_name(names[i], TEST_RT_NAME_MAX, "enum") == RT_EOK); + uassert_true(generate_unique_name(names[i], sizeof(names[i]), "obj", RT_Object_Class_Thread) == RT_EOK); } - /* Create test objects */ - for (int i = 0; i < 3; i++) + baseline = rt_object_get_length(RT_Object_Class_Thread); + + for (int i = 0; i < STATIC_OBJ_COUNT; i++) { rt_object_init(&static_objs[i], RT_Object_Class_Thread, names[i]); } - for (int i = 0; i < 2; i++) + + for (int i = 0; i < DYNAMIC_OBJ_COUNT; i++) { - dyn_objs[i] = rt_object_allocate(RT_Object_Class_Thread, names[i + 3]); + dyn_objs[i] = rt_object_allocate(RT_Object_Class_Thread, names[i + STATIC_OBJ_COUNT]); 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); + int count_after = rt_object_get_length(RT_Object_Class_Thread); + uassert_true(count_after >= baseline + STATIC_OBJ_COUNT + DYNAMIC_OBJ_COUNT); - /* Test 3: Get object pointers with sufficient buffer */ - rt_object_t *objects = (rt_object_t *)rt_malloc((count + 2) * sizeof(rt_object_t)); + int max_objects = count_after + 2; + rt_object_t *objects = (rt_object_t *)rt_malloc(max_objects * 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++) + int ret = rt_object_get_pointers(RT_Object_Class_Thread, objects, max_objects); + uassert_true(ret <= max_objects); + + for (int i = 0; i < STATIC_OBJ_COUNT; i++) + { + rt_bool_t seen = RT_FALSE; + for (int j = 0; j < ret; j++) + { + if (objects[j] == &static_objs[i]) + { + seen = RT_TRUE; + break; + } + } + uassert_true(seen); + } + + for (int i = 0; i < DYNAMIC_OBJ_COUNT; 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++; + rt_bool_t seen = RT_FALSE; + for (int j = 0; j < ret; j++) + { + if (objects[j] == dyn_objs[i]) + { + seen = RT_TRUE; + break; + } + } + uassert_true(seen); } - 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); + uassert_int_equal(rt_object_get_pointers(RT_Object_Class_Thread, RT_NULL, 0), 0); + uassert_int_equal(rt_object_get_pointers(RT_Object_Class_Thread, RT_NULL, -1), 0); - /* 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); + uassert_true(rt_object_get_length(RT_Object_Class_Semaphore) >= 0); #endif - /* Cleanup */ - for (int i = 0; i < 3; i++) + for (int i = 0; i < STATIC_OBJ_COUNT; i++) + { rt_object_detach(&static_objs[i]); - for (int i = 0; i < 2; i++) - if (dyn_objs[i]) rt_object_delete(dyn_objs[i]); + } + + for (int i = 0; i < DYNAMIC_OBJ_COUNT; 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); + char name_buf[TEST_RT_NAME_MAX]; + char small_buf[4] = {0}; + rt_err_t ret; + + uassert_true(generate_unique_name(name, sizeof(name), "typ", RT_Object_Class_Thread) == 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)); + 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)); + ret = rt_object_get_name(&obj, RT_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); + ret = rt_object_get_name(&obj, name_buf, 1); + uassert_int_equal(ret, RT_EOK); + + rt_object_detach(&obj); +} + +struct for_each_ctx +{ + const char *target; + rt_bool_t matched; + rt_int8_t mode; /* 0: stop on match, 1: error on match */ +}; + +static rt_err_t for_each_iter(struct rt_object *obj, void *data) +{ + struct for_each_ctx *ctx = (struct for_each_ctx *)data; + + if (!ctx || !ctx->target) + return -RT_EINVAL; + + if (rt_strcmp(obj->name, ctx->target) == 0) + { + ctx->matched = RT_TRUE; + if (ctx->mode == 0) + return 1; /* early break */ + else + return -RT_ERROR; + } + + return RT_EOK; +} + +static void test_object_error_paths(void) +{ + rt_object_t list_sample[2] = {RT_NULL}; + struct rt_object obj; + char name[TEST_RT_NAME_MAX]; + struct for_each_ctx ctx; + + uassert_null(rt_object_get_information(RT_Object_Class_Null)); + uassert_int_equal(rt_object_get_length(RT_Object_Class_Null), 0); + uassert_int_equal(rt_object_get_pointers(RT_Object_Class_Null, list_sample, ARRAY_SIZE(list_sample)), 0); + rt_memset(&ctx, 0, sizeof(ctx)); + uassert_int_equal(rt_object_for_each(RT_Object_Class_Null, for_each_iter, &ctx), -RT_EINVAL); + + uassert_true(generate_unique_name(name, sizeof(name), "err", RT_Object_Class_Thread) == RT_EOK); + rt_object_init(&obj, RT_Object_Class_Thread, name); + + ctx.target = name; + ctx.mode = 0; + ctx.matched = RT_FALSE; + uassert_int_equal(rt_object_for_each(RT_Object_Class_Thread, for_each_iter, &ctx), RT_EOK); + uassert_true(ctx.matched); + + ctx.mode = 1; + ctx.matched = RT_FALSE; + uassert_int_equal(rt_object_for_each(RT_Object_Class_Thread, for_each_iter, &ctx), -RT_ERROR); + uassert_true(ctx.matched); rt_object_detach(&obj); } +#ifdef RT_USING_HEAP +static rt_err_t custom_destroy_cb(void *data) +{ + rt_uint32_t *counter = (rt_uint32_t *)data; + if (counter) + { + (*counter)++; + return RT_EOK; + } + return -RT_ERROR; +} + +static void test_custom_object_lifecycle(void) +{ + char name[TEST_RT_NAME_MAX]; + rt_uint32_t destroy_counter = 0; + rt_object_t obj; + + uassert_true(generate_unique_name(name, sizeof(name), "cust", RT_Object_Class_Custom) == RT_EOK); + obj = rt_custom_object_create(name, &destroy_counter, custom_destroy_cb); + uassert_not_null(obj); + uassert_false(rt_object_is_systemobject(obj)); + uassert_int_equal(rt_custom_object_destroy(obj), RT_EOK); + uassert_int_equal(destroy_counter, 1); +} +#endif /* RT_USING_HEAP */ + +static void test_object_pressure(void) +{ + rt_object_t objects[OBJECT_STRESS_BATCH] = {RT_NULL}; + char names[OBJECT_STRESS_BATCH][TEST_RT_NAME_MAX]; + int baseline = rt_object_get_length(RT_Object_Class_Thread); + + for (int round = 0; round < OBJECT_STRESS_ROUNDS; round++) + { + for (int i = 0; i < OBJECT_STRESS_BATCH; i++) + { + uassert_true(generate_unique_name(names[i], sizeof(names[i]), "stress", RT_Object_Class_Thread) == RT_EOK); + objects[i] = rt_object_allocate(RT_Object_Class_Thread, names[i]); + uassert_not_null(objects[i]); + } + + uassert_true(rt_object_get_length(RT_Object_Class_Thread) >= baseline + OBJECT_STRESS_BATCH); + + for (int i = 0; i < OBJECT_STRESS_BATCH; i += 5) + { + uassert_not_null(rt_object_find(names[i], RT_Object_Class_Thread)); + } + + for (int i = 0; i < OBJECT_STRESS_BATCH; i++) + { + if (objects[i]) + { + rt_object_delete(objects[i]); + objects[i] = RT_NULL; + } + } + + /* Allow time for memory cleanup */ + rt_thread_mdelay(5); + + for (int i = 0; i < OBJECT_STRESS_BATCH; i += 5) + { + uassert_null(rt_object_find(names[i], RT_Object_Class_Thread)); + } + } + + uassert_true(rt_object_get_length(RT_Object_Class_Thread) >= baseline); +} + static rt_err_t testcase_init(void) { if (!rt_scheduler_is_available()) { return -RT_ERROR; } + /* Reset counter to ensure consistent naming across multiple test runs */ name_counter = 0; return RT_EOK; } static rt_err_t testcase_cleanup(void) { + /* Force garbage collection delay to ensure all deferred cleanup completes */ + rt_thread_mdelay(50); 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"); +#if RT_NAME_MAX < 8 + rt_kprintf("Error: RT_NAME_MAX=%d is too small, please increase to at least 8.\n", RT_NAME_MAX); 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_UNIT_RUN(test_object_error_paths); +#ifdef RT_USING_HEAP + UTEST_UNIT_RUN(test_custom_object_lifecycle); +#endif + UTEST_UNIT_RUN(test_object_pressure); } -UTEST_TC_EXPORT(test_object_suite, "core.object", testcase_init, testcase_cleanup, 20); \ No newline at end of file +UTEST_TC_EXPORT(test_object_suite, "core.object", testcase_init, testcase_cleanup, 20);