1 /*
2  * Copyright (c) 2006-2025, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2025-07-18     kurisaW      First commit
9  */
10 
11 #include <utest.h>
12 #include <rtthread.h>
13 #include <string.h>
14 
15 /**
16  * @brief   Comprehensive test suite for RT-Thread object system
17  *
18  * @note    This test suite validates:
19  *          1. Object name handling (truncation, NULL, exact length)
20  *          2. Static and dynamic object initialization
21  *          3. Object finding functionality (rt_object_find, rt_thread_find, rt_device_find)
22  *          4. Object information and enumeration
23  *          5. Type checking and system object detection
24  *          6. Memory safety and boundary conditions
25  */
26 
27 /* Define TEST_RT_NAME_MAX for testing purposes */
28 #define TEST_RT_NAME_MAX RT_NAME_MAX
29 
30 /* Global counter for unique object names */
31 static rt_uint32_t name_counter = 0;
32 
33 /* Generate unique name to avoid conflicts, respecting TEST_RT_NAME_MAX */
generate_unique_name(char * buf,rt_size_t size,const char * prefix)34 static rt_err_t generate_unique_name(char *buf, rt_size_t size, const char *prefix)
35 {
36     if (!buf || !prefix || size < TEST_RT_NAME_MAX)
37         return -RT_EINVAL;
38 
39     for (int i = 0; i < 1000; i++) /* Limit attempts to prevent infinite loop */
40     {
41         rt_snprintf(buf, size, "%s%d", prefix, name_counter++);
42         rt_size_t len = rt_strlen(buf);
43         if (len >= TEST_RT_NAME_MAX)
44         {
45             buf[TEST_RT_NAME_MAX - 1] = '\0';
46             len = TEST_RT_NAME_MAX - 1;
47         }
48 
49         /* Check if name (or truncated name) is unique */
50         if (rt_object_find(buf, RT_Object_Class_Unknown) == RT_NULL &&
51             rt_thread_find(buf) == RT_NULL &&
52             rt_device_find(buf) == RT_NULL)
53         {
54             return RT_EOK;
55         }
56     }
57     return -RT_ENOMEM;
58 }
59 
test_object_name_handling(void)60 static void test_object_name_handling(void)
61 {
62     struct rt_object static_obj1, static_obj2, static_obj3;
63     rt_object_t dyn_obj = RT_NULL;
64     char test_name[TEST_RT_NAME_MAX];
65     char unique_name[TEST_RT_NAME_MAX];
66 
67     /* Prepare a test name within TEST_RT_NAME_MAX */
68     rt_memset(test_name, 'A', TEST_RT_NAME_MAX - 1);
69     test_name[TEST_RT_NAME_MAX - 1] = '\0';
70 
71     /* Test 1: Static Object with Name Within TEST_RT_NAME_MAX */
72     uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "long") == RT_EOK);
73     rt_object_init(&static_obj1, RT_Object_Class_Thread, test_name);
74     uassert_true(rt_strlen(static_obj1.name) <= TEST_RT_NAME_MAX - 1);
75     uassert_true(static_obj1.name[TEST_RT_NAME_MAX - 1] == '\0');
76     uassert_true(rt_strncmp(static_obj1.name, test_name, TEST_RT_NAME_MAX - 1) == 0);
77     uassert_true(rt_object_is_systemobject(&static_obj1));
78     rt_object_detach(&static_obj1);
79 
80     /* Test 2: Dynamic Object with Name Within TEST_RT_NAME_MAX */
81     uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "dyn") == RT_EOK);
82     dyn_obj = rt_object_allocate(RT_Object_Class_Thread, test_name);
83     uassert_not_null(dyn_obj);
84     uassert_true(rt_strlen(dyn_obj->name) <= TEST_RT_NAME_MAX - 1);
85     uassert_true(dyn_obj->name[TEST_RT_NAME_MAX - 1] == '\0');
86     uassert_true(rt_strncmp(dyn_obj->name, test_name, TEST_RT_NAME_MAX - 1) == 0);
87     uassert_false(rt_object_is_systemobject(dyn_obj));
88     rt_object_delete(dyn_obj);
89 
90     /* Test 3: NULL Name Handling */
91     uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "null") == RT_EOK);
92     rt_object_init(&static_obj2, RT_Object_Class_Thread, NULL);
93     uassert_true(static_obj2.name[0] == '\0');
94     rt_object_detach(&static_obj2);
95 
96     uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "dynull") == RT_EOK);
97     dyn_obj = rt_object_allocate(RT_Object_Class_Thread, NULL);
98     uassert_not_null(dyn_obj);
99     uassert_true(dyn_obj->name[0] == '\0');
100     rt_object_delete(dyn_obj);
101 
102     /* Test 4: Exact Length Name */
103     char exact_name[TEST_RT_NAME_MAX];
104     rt_memset(exact_name, 'B', TEST_RT_NAME_MAX - 1);
105     exact_name[TEST_RT_NAME_MAX - 1] = '\0';
106     uassert_true(generate_unique_name(unique_name, TEST_RT_NAME_MAX, "exact") == RT_EOK);
107     rt_object_init(&static_obj3, RT_Object_Class_Thread, exact_name);
108     uassert_str_equal(static_obj3.name, exact_name);
109     rt_object_detach(&static_obj3);
110 }
111 
test_object_find_operations(void)112 static void test_object_find_operations(void)
113 {
114     rt_object_t found;
115     rt_thread_t found_thread;
116     rt_device_t found_device;
117     char name[TEST_RT_NAME_MAX];
118     rt_err_t ret;
119 
120     /* Scenario 1: Object Name Within TEST_RT_NAME_MAX */
121     /* Test 1.1: Find static thread object with rt_object_find */
122     struct rt_object static_obj;
123     uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "sobj") == RT_EOK);
124     rt_object_init(&static_obj, RT_Object_Class_Thread, name);
125     found = rt_object_find(name, RT_Object_Class_Thread);
126     uassert_not_null(found);
127     uassert_ptr_equal(found, &static_obj);
128     uassert_str_equal(found->name, name);
129     rt_object_detach(&static_obj);
130 
131     /* Test 1.2: Find thread object with rt_thread_find */
132     uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "thr") == RT_EOK);
133     rt_thread_t thread = rt_thread_create(name, RT_NULL, RT_NULL, 1024, 20, 10);
134     uassert_not_null(thread);
135     found_thread = rt_thread_find(name);
136     uassert_not_null(found_thread);
137     uassert_ptr_equal(found_thread, thread);
138     uassert_str_equal(found_thread->parent.name, name);
139     rt_thread_delete(thread);
140 
141 #ifdef RT_USING_DEVICE
142     /* Test 1.3: Find device object with rt_device_find */
143     struct rt_device device;
144     uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "dev") == RT_EOK);
145     ret = rt_device_register(&device, name, RT_DEVICE_FLAG_RDONLY);
146     uassert_int_equal(ret, RT_EOK);
147     found_device = rt_device_find(name);
148     uassert_not_null(found_device);
149     uassert_ptr_equal(found_device, &device);
150     uassert_str_equal(found_device->parent.name, name);
151     rt_device_unregister(&device);
152 #endif
153 
154     /* Test 2: Same Prefix Within TEST_RT_NAME_MAX */
155 #ifdef RT_USING_DEVICE
156     struct rt_device dev1, dev2;
157     char name1[TEST_RT_NAME_MAX] = "norflash1";
158     char name2[TEST_RT_NAME_MAX] = "norflash2";
159     ret = rt_device_register(&dev1, name1, RT_DEVICE_FLAG_RDONLY);
160     uassert_int_equal(ret, RT_EOK);
161     ret = rt_device_register(&dev2, name2, RT_DEVICE_FLAG_RDONLY);
162     uassert_int_equal(ret, RT_EOK); /* Expect success if RT-Thread allows distinct names */
163     found_device = rt_device_find(name1);
164     uassert_not_null(found_device);
165     uassert_ptr_equal(found_device, &dev1);
166     uassert_str_equal(found_device->parent.name, name1);
167     found_device = rt_device_find(name2);
168     uassert_not_null(found_device);
169     uassert_ptr_equal(found_device, &dev2);
170     uassert_str_equal(found_device->parent.name, name2);
171     rt_device_unregister(&dev1);
172     rt_device_unregister(&dev2);
173 #endif
174 
175     /* Test 3: Find non-existent object */
176     const char *nonexist_name = "nonexist";
177     uassert_true(rt_strlen(nonexist_name) <= TEST_RT_NAME_MAX - 1);
178     found = rt_object_find(nonexist_name, RT_Object_Class_Thread);
179     uassert_null(found);
180 
181     /* Test 4: Find with NULL name */
182     found = rt_object_find(NULL, RT_Object_Class_Thread);
183     uassert_null(found);
184     found_thread = rt_thread_find(NULL);
185     uassert_null(found_thread);
186 
187 #ifdef RT_USING_DEVICE
188     found_device = rt_device_find(NULL);
189     uassert_null(found_device);
190 #endif
191 }
192 
test_object_info_enumeration(void)193 static void test_object_info_enumeration(void)
194 {
195     struct rt_object static_objs[3];
196     rt_object_t dyn_objs[2] = {RT_NULL, RT_NULL};
197     char names[5][TEST_RT_NAME_MAX];
198 
199     /* Generate unique names */
200     for (int i = 0; i < 5; i++)
201     {
202         uassert_true(generate_unique_name(names[i], TEST_RT_NAME_MAX, "enum") == RT_EOK);
203     }
204 
205     /* Create test objects */
206     for (int i = 0; i < 3; i++)
207     {
208         rt_object_init(&static_objs[i], RT_Object_Class_Thread, names[i]);
209     }
210     for (int i = 0; i < 2; i++)
211     {
212         dyn_objs[i] = rt_object_allocate(RT_Object_Class_Thread, names[i + 3]);
213         uassert_not_null(dyn_objs[i]);
214     }
215 
216     /* Test 1: Get object information */
217     struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Thread);
218     uassert_not_null(info);
219     uassert_int_equal(info->type, RT_Object_Class_Thread);
220 
221     /* Test 2: Get object count */
222     int count = rt_object_get_length(RT_Object_Class_Thread);
223     uassert_true(count >= 5);
224 
225     /* Test 3: Get object pointers with sufficient buffer */
226     rt_object_t *objects = (rt_object_t *)rt_malloc((count + 2) * sizeof(rt_object_t));
227     uassert_not_null(objects);
228     int ret = rt_object_get_pointers(RT_Object_Class_Thread, objects, count + 2);
229     uassert_int_equal(ret, count);
230     int found_count = 0;
231     for (int i = 0; i < ret; i++)
232     {
233         for (int j = 0; j < 3; j++)
234             if (objects[i] == &static_objs[j]) found_count++;
235         for (int j = 0; j < 2; j++)
236             if (objects[i] == dyn_objs[j]) found_count++;
237     }
238     uassert_int_equal(found_count, 5);
239     rt_free(objects);
240 
241     /* Test 4: Get object pointers with small buffer */
242     rt_object_t one_object[1];
243     ret = rt_object_get_pointers(RT_Object_Class_Thread, one_object, 1);
244     uassert_true(ret <= 1);
245 
246     /* Test 5: Empty container (Semaphore) */
247 #ifdef RT_USING_SEMAPHORE
248     int empty_count = rt_object_get_length(RT_Object_Class_Semaphore);
249     uassert_true(empty_count >= 0);
250 #endif
251 
252     /* Cleanup */
253     for (int i = 0; i < 3; i++)
254         rt_object_detach(&static_objs[i]);
255     for (int i = 0; i < 2; i++)
256         if (dyn_objs[i]) rt_object_delete(dyn_objs[i]);
257 }
258 
test_object_type_handling(void)259 static void test_object_type_handling(void)
260 {
261     struct rt_object obj;
262     char name[TEST_RT_NAME_MAX];
263     uassert_true(generate_unique_name(name, TEST_RT_NAME_MAX, "typ") == RT_EOK);
264     rt_object_init(&obj, RT_Object_Class_Thread, name);
265 
266     /* Test 1: Get object type */
267     uassert_int_equal(rt_object_get_type(&obj), RT_Object_Class_Thread);
268 
269     /* Test 2: Check system object */
270     uassert_true(rt_object_is_systemobject(&obj));
271 
272     /* Test 3: Get name with sufficient buffer */
273     char name_buf[TEST_RT_NAME_MAX];
274     rt_err_t ret = rt_object_get_name(&obj, name_buf, sizeof(name_buf));
275     uassert_int_equal(ret, RT_EOK);
276     uassert_str_equal(name_buf, name);
277 
278     /* Test 4: Get name with small buffer */
279     char small_buf[4] = {0};
280     ret = rt_object_get_name(&obj, small_buf, sizeof(small_buf));
281     uassert_int_equal(ret, RT_EOK);
282     uassert_true(rt_strncmp(small_buf, name, sizeof(small_buf) - 1) == 0);
283     uassert_true(small_buf[sizeof(small_buf) - 1] == '\0');
284 
285     /* Test 5: Get name with invalid parameters */
286     ret = rt_object_get_name(RT_NULL, name_buf, sizeof(name_buf));
287     uassert_int_equal(ret, -RT_EINVAL);
288     ret = rt_object_get_name(&obj, NULL, sizeof(name_buf));
289     uassert_int_equal(ret, -RT_EINVAL);
290     ret = rt_object_get_name(&obj, name_buf, 0);
291     uassert_int_equal(ret, -RT_EINVAL);
292 
293     rt_object_detach(&obj);
294 }
295 
testcase_init(void)296 static rt_err_t testcase_init(void)
297 {
298     if (!rt_scheduler_is_available())
299     {
300         return -RT_ERROR;
301     }
302     name_counter = 0;
303     return RT_EOK;
304 }
305 
testcase_cleanup(void)306 static rt_err_t testcase_cleanup(void)
307 {
308     return RT_EOK;
309 }
310 
test_object_suite(void)311 static void test_object_suite(void)
312 {
313 #ifdef RT_NAME_MAX < 10
314     rt_kprintf("Error: Please increase \'RT_NAME_MAX\' to be greater than 10.\n");
315     return;
316 #endif
317     UTEST_UNIT_RUN(test_object_name_handling);
318     UTEST_UNIT_RUN(test_object_find_operations);
319     UTEST_UNIT_RUN(test_object_info_enumeration);
320     UTEST_UNIT_RUN(test_object_type_handling);
321 }
322 UTEST_TC_EXPORT(test_object_suite, "testcases.kernel.object_test", testcase_init, testcase_cleanup, 20);