1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-02-06     tyx          first commit
9  * 2024-12-31     rbb666       Adding Test Cases
10  */
11 
12 #include "rtthread.h"
13 #include "rtdevice.h"
14 #include "utest.h"
15 
16 #ifdef RT_USING_DEVICE_IPC
17 
get_test_thread_priority(rt_int8_t pos)18 static rt_uint8_t get_test_thread_priority(rt_int8_t pos)
19 {
20     rt_int16_t priority;
21 
22     priority =  RT_SCHED_PRIV(rt_thread_self()).init_priority;
23     if (pos == 0)
24     {
25         return priority;
26     }
27     else
28     {
29         priority += pos;
30     }
31     if (priority < 0)
32     {
33         return 0;
34     }
35     else if (priority >= RT_THREAD_PRIORITY_MAX)
36     {
37         return RT_THREAD_PRIORITY_MAX - 1;
38     }
39     else
40     {
41         return (rt_uint8_t)priority;
42     }
43 }
44 
do_work_test_fun(struct rt_work * work,void * work_data)45 static void do_work_test_fun(struct rt_work *work, void *work_data)
46 {
47     *((int *)work_data) = 1;
48 }
49 
do_work_test(void)50 static void do_work_test(void)
51 {
52     struct rt_workqueue *queue;
53     rt_uint8_t curr_priority;
54     struct rt_work work;
55     volatile int work_flag = 0;
56     rt_err_t err;
57 
58     /* 1 higher priority than the current test thread */
59     curr_priority = get_test_thread_priority(-1);
60     queue = rt_workqueue_create("test", 2048, curr_priority);
61     if (queue == RT_NULL)
62     {
63         LOG_E("queue create failed, L:%d", __LINE__);
64         return;
65     }
66     rt_work_init(&work, do_work_test_fun, (void *)&work_flag);
67     err = rt_workqueue_submit_work(queue, &work, 0);
68     uassert_int_equal(err, RT_EOK);
69 
70     /* Delay 5 ticks to ensure that the task has been executed */
71     rt_thread_delay(5);
72     uassert_int_equal(work_flag, 1);
73 
74     rt_thread_delay(100);
75     rt_workqueue_destroy(queue);
76 }
77 
do_delay_work_test_fun(struct rt_work * work,void * work_data)78 static void do_delay_work_test_fun(struct rt_work *work, void *work_data)
79 {
80     *((rt_tick_t *)work_data) = rt_tick_get();
81 }
82 
do_delay_work_test(void)83 static void do_delay_work_test(void)
84 {
85     struct rt_workqueue *queue;
86     rt_uint8_t curr_priority;
87     struct rt_work work;
88     volatile rt_tick_t work_start = 0;
89     volatile rt_tick_t work_end = 0;
90     rt_err_t err;
91 
92     /* 1 higher priority than the current test thread */
93     curr_priority = get_test_thread_priority(-1);
94     queue = rt_workqueue_create("test", 2048, curr_priority);
95     if (queue == RT_NULL)
96     {
97         LOG_E("queue create failed, L:%d", __LINE__);
98         return;
99     }
100     rt_work_init(&work, do_delay_work_test_fun, (void *)&work_end);
101     work_start = rt_tick_get();
102     /* Normal delayed work submission test */
103     err = rt_workqueue_submit_work(queue, &work, 10);
104     uassert_int_equal(err, RT_EOK);
105 
106     /* Ensure that the delayed work has been executed */
107     rt_thread_delay(15);
108     /* Check if the delayed task is executed after 10 ticks */
109     if (work_end < work_start || work_end - work_start < 10)
110     {
111         uassert_false(1);
112     }
113     rt_thread_delay(100);
114     rt_workqueue_destroy(queue);
115 }
116 
cancle_work_test01_fun(struct rt_work * work,void * work_data)117 static void cancle_work_test01_fun(struct rt_work *work, void *work_data)
118 {
119     *((int *)work_data) = 1;
120 }
121 
cancle_work_test01(void)122 static void cancle_work_test01(void)
123 {
124     struct rt_workqueue *queue;
125     rt_uint8_t curr_priority;
126     struct rt_work work;
127     volatile int work_flag = 0;
128     rt_err_t err;
129 
130     /* 1 lower priority than the current test thread */
131     curr_priority = get_test_thread_priority(1);
132     queue = rt_workqueue_create("test", 2048, curr_priority);
133     if (queue == RT_NULL)
134     {
135         LOG_E("queue create failed, L:%d", __LINE__);
136         return;
137     }
138     work_flag = 0;
139     rt_work_init(&work, cancle_work_test01_fun, (void *)&work_flag);
140     /* Cancel the work before it is executed */
141     err = rt_workqueue_submit_work(queue, &work, 0);
142     uassert_int_equal(err, RT_EOK);
143 
144     /* Cancel Now */
145     err = rt_workqueue_cancel_work(queue, &work);
146     uassert_int_equal(err, RT_EOK);
147 
148     rt_thread_delay(5);
149     uassert_int_equal(work_flag, 0);
150 
151     rt_thread_delay(100);
152     rt_workqueue_destroy(queue);
153 }
154 
cancle_work_test02_fun(struct rt_work * work,void * work_data)155 static void cancle_work_test02_fun(struct rt_work *work, void *work_data)
156 {
157     rt_thread_delay(10);
158 }
159 
cancle_work_test02(void)160 static void cancle_work_test02(void)
161 {
162     struct rt_workqueue *queue;
163     rt_uint8_t curr_priority;
164     struct rt_work work;
165     rt_err_t err;
166 
167     /* 1 higher priority than the current test thread */
168     curr_priority = get_test_thread_priority(-1);
169     queue = rt_workqueue_create("test", 2048, curr_priority);
170     if (queue == RT_NULL)
171     {
172         LOG_E("queue create failed, L:%d", __LINE__);
173         return;
174     }
175     rt_work_init(&work, cancle_work_test02_fun, RT_NULL);
176     /* Cancel the work while it is in progress */
177     err = rt_workqueue_submit_work(queue, &work, 0);
178     uassert_int_equal(err, RT_EOK);
179 
180     rt_thread_delay(5);
181     err = rt_workqueue_cancel_work(queue, &work);
182     uassert_int_equal(err, -RT_EBUSY);
183 
184     rt_thread_delay(100);
185     rt_workqueue_destroy(queue);
186 }
187 
cancle_work_test03_fun(struct rt_work * work,void * work_data)188 static void cancle_work_test03_fun(struct rt_work *work, void *work_data)
189 {
190     rt_thread_delay(5);
191 }
192 
cancle_work_test03(void)193 static void cancle_work_test03(void)
194 {
195     struct rt_workqueue *queue;
196     rt_uint8_t curr_priority;
197     struct rt_work work;
198     rt_err_t err;
199 
200     /* 1 lower priority than the current test thread */
201     curr_priority = get_test_thread_priority(1);
202     queue = rt_workqueue_create("test", 2048, curr_priority);
203     if (queue == RT_NULL)
204     {
205         LOG_E("queue create failed, L:%d", __LINE__);
206         return;
207     }
208     rt_work_init(&work, cancle_work_test03_fun, RT_NULL);
209     /* Canceling a work after it has been executed */
210     err = rt_workqueue_submit_work(queue, &work, 0);
211     uassert_int_equal(err, RT_EOK);
212 
213     rt_thread_delay(10);
214     err = rt_workqueue_cancel_work(queue, &work);
215     uassert_int_equal(err, RT_EOK);
216 
217     rt_thread_delay(100);
218     rt_workqueue_destroy(queue);
219 }
220 
cancle_work_test04_fun(struct rt_work * work,void * work_data)221 static void cancle_work_test04_fun(struct rt_work *work, void *work_data)
222 {
223     rt_thread_delay(10);
224     *((int *)work_data) = 1;
225 }
226 
cancle_work_test04(void)227 static void cancle_work_test04(void)
228 {
229     struct rt_workqueue *queue;
230     rt_uint8_t curr_priority;
231     struct rt_work work;
232     volatile int work_flag = 0;
233     rt_err_t err;
234 
235     /* 1 lower priority than the current test thread */
236     curr_priority = get_test_thread_priority(1);
237     queue = rt_workqueue_create("test", 2048, curr_priority);
238     if (queue == RT_NULL)
239     {
240         LOG_E("queue create failed, L:%d", __LINE__);
241         return;
242     }
243     rt_work_init(&work, cancle_work_test04_fun, (void *)&work_flag);
244     err = rt_workqueue_submit_work(queue, &work, 0);
245     uassert_int_equal(err, RT_EOK);
246 
247     rt_thread_delay(5);
248     /* Synchronized cancellation work */
249     err = rt_workqueue_cancel_work_sync(queue, &work);
250     uassert_int_equal(err, RT_EOK);
251 
252     uassert_int_equal(work_flag, 1);
253 
254     rt_thread_delay(100);
255     rt_workqueue_destroy(queue);
256 }
257 
cancle_delay_work_test01_fun(struct rt_work * work,void * work_data)258 static void cancle_delay_work_test01_fun(struct rt_work *work, void *work_data)
259 {
260     *((int *)work_data) = 1;
261 }
262 
cancle_delay_work_test01(void)263 static void cancle_delay_work_test01(void)
264 {
265     struct rt_workqueue *queue;
266     rt_uint8_t curr_priority;
267     struct rt_work work;
268     volatile int work_flag = 0;
269     rt_err_t err;
270 
271     /* 1 lower priority than the current test thread */
272     curr_priority = get_test_thread_priority(1);
273     queue = rt_workqueue_create("test", 2048, curr_priority);
274     if (queue == RT_NULL)
275     {
276         LOG_E("queue create failed, L:%d", __LINE__);
277         return;
278     }
279     work_flag = 0;
280     rt_work_init(&work, cancle_delay_work_test01_fun, (void *)&work_flag);
281     err = rt_workqueue_submit_work(queue, &work, 20);
282     uassert_int_equal(err, RT_EOK);
283 
284     rt_thread_delay(10);
285     /* Cancel work */
286     err = rt_workqueue_cancel_work(queue, &work);
287     uassert_int_equal(err, RT_EOK);
288 
289     rt_thread_delay(15);
290     uassert_int_equal(work_flag, 0);
291 
292     rt_thread_delay(100);
293     rt_workqueue_destroy(queue);
294 }
295 
repeat_work_test01_fun(struct rt_work * work,void * work_data)296 static void repeat_work_test01_fun(struct rt_work *work, void *work_data)
297 {
298     *((int *)work_data) += 1;
299 }
300 
repeat_work_test01(void)301 static void repeat_work_test01(void)
302 {
303     struct rt_workqueue *queue;
304     rt_uint8_t curr_priority;
305     struct rt_work work;
306     volatile int work_flag = 0;
307     rt_err_t err;
308 
309     /* 1 lower priority than the current test thread */
310     curr_priority = get_test_thread_priority(1);
311     queue = rt_workqueue_create("test01", 2048, curr_priority);
312     if (queue == RT_NULL)
313     {
314         LOG_E("queue create failed, L:%d", __LINE__);
315         return;
316     }
317     work_flag = 0;
318     rt_work_init(&work, repeat_work_test01_fun, (void *)&work_flag);
319     /* Multiple submissions of the same work */
320     err = rt_workqueue_submit_work(queue, &work, 0);
321     uassert_int_equal(err, RT_EOK);
322 
323     /* The same work, before it is executed, can be submitted repeatedly and executed only once */
324     err = rt_workqueue_submit_work(queue, &work, 0);
325     if (err != RT_EOK)
326     {
327         LOG_E("L:%d err. %d", __LINE__, err);
328     }
329     rt_thread_delay(10);
330     /* Check if it was executed only once */
331     uassert_int_equal(work_flag, 1);
332 
333     rt_thread_delay(100);
334     rt_workqueue_destroy(queue);
335 }
336 
repeat_work_test02_fun(struct rt_work * work,void * work_data)337 static void repeat_work_test02_fun(struct rt_work *work, void *work_data)
338 {
339     rt_thread_delay(10);
340     *((int *)work_data) += 1;
341 }
342 
repeat_work_test02(void)343 static void repeat_work_test02(void)
344 {
345     struct rt_workqueue *queue;
346     rt_uint8_t curr_priority;
347     struct rt_work work;
348     volatile int work_flag = 0;
349     rt_err_t err;
350 
351     /* 1 priority higher than current test thread */
352     curr_priority = get_test_thread_priority(-1);
353     queue = rt_workqueue_create("test02", 2048, curr_priority);
354     if (queue == RT_NULL)
355     {
356         LOG_E("queue create failed, L:%d", __LINE__);
357         return;
358     }
359     rt_work_init(&work, repeat_work_test02_fun, (void *)&work_flag);
360     /* Submit work with high queue priority that will be executed immediately */
361     err = rt_workqueue_submit_work(queue, &work, 0);
362     uassert_int_equal(err, RT_EOK);
363 
364     rt_thread_delay(5);
365     /* Re-submission of work in progress */
366     err = rt_workqueue_submit_work(queue, &work, 0);
367     if (err != RT_EOK)
368     {
369         LOG_E("L:%d err. %d", __LINE__, err);
370     }
371     rt_thread_delay(10);
372     uassert_int_equal(work_flag, 1);
373 
374     rt_thread_delay(10);
375     uassert_int_equal(work_flag, 2);
376 
377     rt_workqueue_destroy(queue);
378 }
379 
380 static struct rt_workqueue *queue_3;
381 
repeat_work_test03_fun(struct rt_work * work,void * work_data)382 static void repeat_work_test03_fun(struct rt_work *work, void *work_data)
383 {
384     int *work_flag = (int *)work_data;
385     (*work_flag) += 1;
386     rt_kprintf("work_flag:%d\n", *work_flag);
387     if (*work_flag < 20)
388     {
389         rt_workqueue_submit_work(queue_3, work, 0);
390     }
391 }
392 
repeat_work_test03(void)393 static void repeat_work_test03(void)
394 {
395     rt_uint8_t curr_priority;
396     struct rt_work work;
397     volatile int work_flag = 0;
398     rt_err_t err;
399 
400     /* 1 priority higher than current test thread */
401     curr_priority = get_test_thread_priority(-1);
402     queue_3 = rt_workqueue_create("test03", 2048, curr_priority);
403     if (queue_3 == RT_NULL)
404     {
405         LOG_E("queue create failed, L:%d", __LINE__);
406         return;
407     }
408     rt_work_init(&work, repeat_work_test03_fun, (void *)&work_flag);
409     /* Submit work with high queue priority that will be executed immediately */
410     err = rt_workqueue_submit_work(queue_3, &work, 0);
411     uassert_int_equal(err, RT_EOK);
412 
413     /* Wait for the work to be executed 20 times with a timeout */
414     err = rt_workqueue_cancel_work_sync(queue_3, &work);
415     uassert_int_equal(err, RT_EOK);
416 
417     /* Check if the work was executed 20 times */
418     uassert_int_equal(work_flag, 20);
419 
420     rt_workqueue_destroy(queue_3);
421 }
422 
repeat_delay_work_test01_fun(struct rt_work * work,void * work_data)423 static void repeat_delay_work_test01_fun(struct rt_work *work, void *work_data)
424 {
425     *((int *)work_data) += 1;
426 }
427 
repeat_delay_work_test01(void)428 static void repeat_delay_work_test01(void)
429 {
430     struct rt_workqueue *queue;
431     rt_uint8_t curr_priority;
432     struct rt_work work;
433     volatile int work_flag = 0;
434     rt_err_t err;
435 
436     /* 1 lower priority than the current test thread */
437     curr_priority = get_test_thread_priority(1);
438     queue = rt_workqueue_create("test", 2048, curr_priority);
439     if (queue == RT_NULL)
440     {
441         LOG_E("queue create failed, L:%d", __LINE__);
442         return;
443     }
444     work_flag = 0;
445     rt_work_init(&work, repeat_delay_work_test01_fun, (void *)&work_flag);
446 
447     err = rt_workqueue_submit_work(queue, &work, 20);
448     uassert_int_equal(err, RT_EOK);
449 
450     /* At this point the delayed work has not been executed */
451     rt_thread_delay(10);
452     /* Re-submission of time-delayed work */
453     err = rt_workqueue_submit_work(queue, &work, 20);
454     uassert_int_equal(err, RT_EOK);
455 
456     rt_thread_delay(15);
457     uassert_int_equal(work_flag, 0);
458 
459     /* Waiting for delayed task execution */
460     rt_thread_delay(15);
461     uassert_int_equal(work_flag, 1);
462 
463     rt_thread_delay(100);
464     rt_workqueue_destroy(queue);
465 }
466 
repeat_delay_work_test02_fun(struct rt_work * work,void * work_data)467 static void repeat_delay_work_test02_fun(struct rt_work *work, void *work_data)
468 {
469     rt_thread_delay(10);
470     *((int *)work_data) += 1;
471 }
472 
repeat_delay_work_test02(void)473 static void repeat_delay_work_test02(void)
474 {
475     struct rt_workqueue *queue;
476     rt_uint8_t curr_priority;
477     struct rt_work work;
478     volatile int work_flag = 0;
479     rt_err_t err;
480 
481     /* 1 lower priority than the current test thread */
482     curr_priority = get_test_thread_priority(1);
483     queue = rt_workqueue_create("test", 2048, curr_priority);
484     if (queue == RT_NULL)
485     {
486         LOG_E("queue create failed, L:%d", __LINE__);
487         return;
488     }
489     work_flag = 0;
490     rt_work_init(&work, repeat_delay_work_test02_fun, (void *)&work_flag);
491 
492     err = rt_workqueue_submit_work(queue, &work, 20);
493     uassert_int_equal(err, RT_EOK);
494 
495     /* Waiting for delayed work execution */
496     rt_thread_delay(25);
497     err = rt_workqueue_submit_work(queue, &work, 20);
498     uassert_int_equal(err, RT_EOK);
499 
500     /* Check if the delayed work has been run only once */
501     rt_thread_delay(10);
502     uassert_int_equal(work_flag, 1);
503 
504     rt_thread_delay(25);
505     /* Check if the delayed work is executed twice */
506     uassert_int_equal(work_flag, 2);
507 
508     rt_thread_delay(100);
509     rt_workqueue_destroy(queue);
510 }
511 
cancel_all_work_test_fun(struct rt_work * work,void * work_data)512 static void cancel_all_work_test_fun(struct rt_work *work, void *work_data)
513 {
514     *((int *)work_data) += 1;
515 }
516 
cancel_all_work_test(void)517 static void cancel_all_work_test(void)
518 {
519     struct rt_workqueue *queue;
520     rt_uint8_t curr_priority;
521     struct rt_work work1;
522     struct rt_work work2;
523     struct rt_work work3;
524     struct rt_work work4;
525     volatile int work_flag = 0;
526     rt_err_t err;
527 
528     curr_priority = get_test_thread_priority(1);
529     queue = rt_workqueue_create("test", 2048, curr_priority);
530     if (queue == RT_NULL)
531     {
532         LOG_E("queue create failed, L:%d", __LINE__);
533         return;
534     }
535     work_flag = 0;
536     rt_work_init(&work1, cancel_all_work_test_fun, (void *)&work_flag);
537     rt_work_init(&work2, cancel_all_work_test_fun, (void *)&work_flag);
538     rt_work_init(&work3, cancel_all_work_test_fun, (void *)&work_flag);
539     rt_work_init(&work4, cancel_all_work_test_fun, (void *)&work_flag);
540 
541     err = rt_workqueue_submit_work(queue, &work1, 0);
542     uassert_int_equal(err, RT_EOK);
543 
544     err = rt_workqueue_submit_work(queue, &work2, 0);
545     uassert_int_equal(err, RT_EOK);
546 
547     err = rt_workqueue_submit_work(queue, &work3, 10);
548     uassert_int_equal(err, RT_EOK);
549 
550     err = rt_workqueue_submit_work(queue, &work4, 10);
551     uassert_int_equal(err, RT_EOK);
552 
553     err = rt_workqueue_cancel_all_work(queue);
554     uassert_int_equal(err, RT_EOK);
555 
556     rt_thread_delay(20);
557     uassert_int_equal(work_flag, 0);
558 
559     rt_thread_delay(100);
560     rt_workqueue_destroy(queue);
561 }
562 
utest_tc_init(void)563 static rt_err_t utest_tc_init(void)
564 {
565     return RT_EOK;
566 }
567 
utest_tc_cleanup(void)568 static rt_err_t utest_tc_cleanup(void)
569 {
570     return RT_EOK;
571 }
572 
testcase(void)573 static void testcase(void)
574 {
575     /* General work queue test */
576     UTEST_UNIT_RUN(do_work_test);
577     /* Delayed work queue test */
578     UTEST_UNIT_RUN(do_delay_work_test);
579     /* Cancellation of work prior to implementation */
580     UTEST_UNIT_RUN(cancle_work_test01);
581     /* Cancellation of work during execution */
582     UTEST_UNIT_RUN(cancle_work_test02);
583     /* Cancellation of work after implementation */
584     UTEST_UNIT_RUN(cancle_work_test03);
585     /* Synchronized cancellation of work during execution */
586     UTEST_UNIT_RUN(cancle_work_test04);
587     /* Cancel delayed work before execution */
588     UTEST_UNIT_RUN(cancle_delay_work_test01);
589     /* Multiple submissions of the same work prior to implementation */
590     UTEST_UNIT_RUN(repeat_work_test01);
591     /* Multiple submissions of the same work during execution */
592     UTEST_UNIT_RUN(repeat_work_test02);
593     /* Submitting the same task multiple times in a mission */
594     UTEST_UNIT_RUN(repeat_work_test03);
595     /* Multiple submissions of the same delayed task before execution */
596     UTEST_UNIT_RUN(repeat_delay_work_test01);
597     /* Multiple submissions of the same delayed task during execution */
598     UTEST_UNIT_RUN(repeat_delay_work_test02);
599     /* Cancel all works */
600     UTEST_UNIT_RUN(cancel_all_work_test);
601 }
602 UTEST_TC_EXPORT(testcase, "components.drivers.ipc.workqueue_tc", utest_tc_init, utest_tc_cleanup, 300);
603 #endif
604