1 /*
2  * FreeRTOS V202212.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26 
27 /*
28  * Contains sundry tests to exercise code that is not touched by the standard
29  * demo tasks (which are predominantly test tasks).  Some tests are included
30  * here because they can only be executed when configASSERT() is not defined.
31  */
32 
33 #include <string.h>
34 
35 #include "FreeRTOS.h"
36 #include "task.h"
37 #include "timers.h"
38 #include "event_groups.h"
39 #include "semphr.h"
40 #include "stream_buffer.h"
41 #include "message_buffer.h"
42 
43 /*-----------------------------------------------------------*/
44 
45 /*
46  * Try creating static objects with one of the mandatory parameters set to NULL.
47  * This can't be done in the standard demos as asserts() will get hit.
48  */
49 static BaseType_t prvStaticAllocationsWithNullBuffers( void );
50 
51 /*
52  * Code coverage analysis is performed with tracing turned off, so this
53  * function executes the trace specific utility functions that would not
54  * otherwise be executed..
55  */
56 static BaseType_t prvTraceUtils( void );
57 
58 /*
59  * The queue peek standard demo does not cover the case where an attempt to peek
60  * times out, so test that case.
61  */
62 static BaseType_t prvPeekTimeout( void );
63 
64 /*
65  * Calls various interrupt safe functions designed to query the state of a
66  * queue.
67  */
68 static BaseType_t prvQueueQueryFromISR( void );
69 
70 /*
71  * Hits a few paths in tasks state and status query functions not otherwise hit
72  * by standard demo and test files.
73  */
74 static BaseType_t prvTaskQueryFunctions( void );
75 
76 /*
77  * None of the standard demo tasks use the task tags - exercise them here.
78  */
79 static BaseType_t prvTaskTags( void );
80 
81 /*
82  * Exercises a few of the query functions that are not otherwise exercised in
83  * the standard demo and test functions.
84  */
85 static BaseType_t prvTimerQuery( void );
86 
87 /*-----------------------------------------------------------*/
88 
prvStaticAllocationsWithNullBuffers(void)89 static BaseType_t prvStaticAllocationsWithNullBuffers( void )
90 {
91     uintptr_t ulReturned = 0;
92     BaseType_t xReturn = pdPASS;
93     UBaseType_t uxDummy = 10;
94 
95     /* Don't expect to create any of the objects as a NULL parameter is always
96      * passed in place of a required buffer.  Hence if all passes then none of the
97      |= will be against 0, and ulReturned will still be zero at the end of this
98      * function. */
99     ulReturned |= ( uintptr_t ) xEventGroupCreateStatic( NULL );
100 
101     /* Try creating a task twice, once with puxStackBuffer NULL, and once with
102      * pxTaskBuffer NULL. */
103     ulReturned |= ( uintptr_t ) xTaskCreateStatic( NULL,    /* Task to run, not needed as the task is not created. */
104                                                    "Dummy", /* Task name. */
105                                                    configMINIMAL_STACK_SIZE,
106                                                    NULL,
107                                                    tskIDLE_PRIORITY,
108                                                    NULL,
109                                                    ( StaticTask_t * ) &xReturn ); /* Dummy value just to pass a non NULL value in - won't get used. */
110 
111     ulReturned |= ( uintptr_t ) xTaskCreateStatic( NULL,                          /* Task to run, not needed as the task is not created. */
112                                                    "Dummy",                       /* Task name. */
113                                                    configMINIMAL_STACK_SIZE,
114                                                    NULL,
115                                                    tskIDLE_PRIORITY,
116                                                    ( StackType_t * ) &xReturn, /* Dummy value just to pass a non NULL value in - won't get used. */
117                                                    NULL );
118 
119     ulReturned |= ( uintptr_t ) xQueueCreateStatic( uxDummy,
120                                                     uxDummy,
121                                                     ( uint8_t * ) &xReturn, /* Dummy value just to pass a non NULL value in - won't get used. */
122                                                     NULL );
123 
124     /* Try creating a stream buffer twice, once with pucStreamBufferStorageArea
125      * set to NULL, and once with pxStaticStreamBuffer set to NULL. */
126     ulReturned |= ( uintptr_t ) xStreamBufferCreateStatic( uxDummy,
127                                                            uxDummy,
128                                                            NULL,
129                                                            ( StaticStreamBuffer_t * ) &xReturn ); /* Dummy value just to pass a non NULL value in - won't get used. */
130 
131     ulReturned |= ( uintptr_t ) xStreamBufferCreateStatic( uxDummy,
132                                                            uxDummy,
133                                                            ( uint8_t * ) &xReturn, /* Dummy value just to pass a non NULL value in - won't get used. */
134                                                            NULL );
135 
136     if( ulReturned != 0 )
137     {
138         /* Something returned a non-NULL value. */
139         xReturn = pdFAIL;
140     }
141 
142     return xReturn;
143 }
144 /*-----------------------------------------------------------*/
145 
prvTraceUtils(void)146 static BaseType_t prvTraceUtils( void )
147 {
148     EventGroupHandle_t xEventGroup;
149     QueueHandle_t xQueue;
150     BaseType_t xReturn = pdPASS;
151     const UBaseType_t xNumber = ( UBaseType_t ) 100, xQueueLength = ( UBaseType_t ) 1;
152     UBaseType_t uxValue;
153     TaskHandle_t xTaskHandle;
154     StreamBufferHandle_t xStreamBuffer;
155     MessageBufferHandle_t xMessageBuffer;
156 
157     /* Exercise the event group trace utilities. */
158     xEventGroup = xEventGroupCreate();
159 
160     if( xEventGroup != NULL )
161     {
162         vEventGroupSetNumber( xEventGroup, xNumber );
163 
164         if( uxEventGroupGetNumber( NULL ) != 0 )
165         {
166             xReturn = pdFAIL;
167         }
168 
169         if( uxEventGroupGetNumber( xEventGroup ) != xNumber )
170         {
171             xReturn = pdFAIL;
172         }
173 
174         vEventGroupDelete( xEventGroup );
175     }
176     else
177     {
178         xReturn = pdFAIL;
179     }
180 
181     /* Exercise the queue trace utilities. */
182     xQueue = xQueueCreate( xQueueLength, ( UBaseType_t ) sizeof( uxValue ) );
183 
184     if( xQueue != NULL )
185     {
186         vQueueSetQueueNumber( xQueue, xNumber );
187 
188         if( uxQueueGetQueueNumber( xQueue ) != xNumber )
189         {
190             xReturn = pdFAIL;
191         }
192 
193         if( ucQueueGetQueueType( xQueue ) != queueQUEUE_TYPE_BASE )
194         {
195             xReturn = pdFAIL;
196         }
197 
198         vQueueDelete( xQueue );
199     }
200     else
201     {
202         xReturn = pdFAIL;
203     }
204 
205     /* Exercise the task trace utilities.  Value of 100 is arbitrary, just want
206      * to check the value that is set is also read back. */
207     uxValue = 100;
208     xTaskHandle = xTaskGetCurrentTaskHandle();
209     vTaskSetTaskNumber( xTaskHandle, uxValue );
210 
211     if( uxTaskGetTaskNumber( xTaskHandle ) != uxValue )
212     {
213         xReturn = pdFAIL;
214     }
215 
216     if( uxTaskGetTaskNumber( NULL ) != 0 )
217     {
218         xReturn = pdFAIL;
219     }
220 
221     /* Timer trace util functions are exercised in prvTimerQuery(). */
222 
223 
224     /* Exercise the stream buffer utilities.  Try creating with a trigger level
225      * of 0, it should then get capped to 1. */
226     xStreamBuffer = xStreamBufferCreate( sizeof( uint32_t ), 0 );
227 
228     if( xStreamBuffer != NULL )
229     {
230         vStreamBufferSetStreamBufferNumber( xStreamBuffer, uxValue );
231 
232         if( uxStreamBufferGetStreamBufferNumber( xStreamBuffer ) != uxValue )
233         {
234             xReturn = pdFALSE;
235         }
236 
237         if( ucStreamBufferGetStreamBufferType( xStreamBuffer ) != 0 )
238         {
239             /* "Is Message Buffer" flag should have been 0. */
240             xReturn = pdFALSE;
241         }
242 
243         vStreamBufferDelete( xStreamBuffer );
244     }
245     else
246     {
247         xReturn = pdFALSE;
248     }
249 
250     xMessageBuffer = xMessageBufferCreate( sizeof( uint32_t ) );
251 
252     if( xMessageBuffer != NULL )
253     {
254         if( ucStreamBufferGetStreamBufferType( xMessageBuffer ) == 0 )
255         {
256             /* "Is Message Buffer" flag should have been 1. */
257             xReturn = pdFALSE;
258         }
259 
260         vMessageBufferDelete( xMessageBuffer );
261     }
262     else
263     {
264         xReturn = pdFALSE;
265     }
266 
267     return xReturn;
268 }
269 /*-----------------------------------------------------------*/
270 
prvPeekTimeout(void)271 static BaseType_t prvPeekTimeout( void )
272 {
273     QueueHandle_t xHandle;
274     const UBaseType_t xQueueLength = 1;
275     BaseType_t xReturn = pdPASS;
276     TickType_t xBlockTime = ( TickType_t ) 2;
277     UBaseType_t uxReceived;
278 
279     /* Create the queue just to try peeking it while it is empty. */
280     xHandle = xQueueCreate( xQueueLength, ( UBaseType_t ) sizeof( xQueueLength ) );
281 
282     if( xHandle != NULL )
283     {
284         if( uxQueueMessagesWaiting( xHandle ) != 0 )
285         {
286             xReturn = pdFAIL;
287         }
288 
289         /* Ensure peeking from the queue times out as the queue is empty. */
290         if( xQueuePeek( xHandle, &uxReceived, xBlockTime ) != pdFALSE )
291         {
292             xReturn = pdFAIL;
293         }
294 
295         vQueueDelete( xHandle );
296     }
297     else
298     {
299         xReturn = pdFAIL;
300     }
301 
302     return xReturn;
303 }
304 /*-----------------------------------------------------------*/
305 
prvQueueQueryFromISR(void)306 static BaseType_t prvQueueQueryFromISR( void )
307 {
308     BaseType_t xReturn = pdPASS, xValue = 1;
309     const UBaseType_t xISRQueueLength = ( UBaseType_t ) 1;
310     const char * pcISRQueueName = "ISRQueue";
311     QueueHandle_t xISRQueue = NULL;
312 
313     xISRQueue = xQueueCreate( xISRQueueLength, ( UBaseType_t ) sizeof( BaseType_t ) );
314 
315     if( xISRQueue != NULL )
316     {
317         vQueueAddToRegistry( xISRQueue, pcISRQueueName );
318 
319         if( strcmp( pcQueueGetName( xISRQueue ), pcISRQueueName ) )
320         {
321             xReturn = pdFAIL;
322         }
323 
324         /* Expect the queue to be empty here. */
325         if( uxQueueMessagesWaitingFromISR( xISRQueue ) != 0 )
326         {
327             xReturn = pdFAIL;
328         }
329 
330         if( xQueueIsQueueEmptyFromISR( xISRQueue ) != pdTRUE )
331         {
332             xReturn = pdFAIL;
333         }
334 
335         if( xQueueIsQueueFullFromISR( xISRQueue ) != pdFALSE )
336         {
337             xReturn = pdFAIL;
338         }
339 
340         /* Now fill the queue - it only has one space. */
341         if( xQueueSendFromISR( xISRQueue, &xValue, NULL ) != pdPASS )
342         {
343             xReturn = pdFAIL;
344         }
345 
346         /* Check it now reports as full. */
347         if( uxQueueMessagesWaitingFromISR( xISRQueue ) != 1 )
348         {
349             xReturn = pdFAIL;
350         }
351 
352         if( xQueueIsQueueEmptyFromISR( xISRQueue ) != pdFALSE )
353         {
354             xReturn = pdFAIL;
355         }
356 
357         if( xQueueIsQueueFullFromISR( xISRQueue ) != pdTRUE )
358         {
359             xReturn = pdFAIL;
360         }
361 
362         vQueueDelete( xISRQueue );
363     }
364     else
365     {
366         xReturn = pdFAIL;
367     }
368 
369     return xReturn;
370 }
371 /*-----------------------------------------------------------*/
372 
prvTaskQueryFunctions(void)373 static BaseType_t prvTaskQueryFunctions( void )
374 {
375     static TaskStatus_t xStatus, * pxStatusArray;
376     TaskHandle_t xTimerTask, xIdleTask;
377     BaseType_t xReturn = pdPASS;
378     UBaseType_t uxNumberOfTasks, uxReturned, ux;
379     uint32_t ulTotalRunTime1, ulTotalRunTime2;
380     const uint32_t ulRunTimeTolerance = ( uint32_t ) 0xfff;
381 
382     /* Obtain task status with the stack high water mark and without the
383      * state. */
384     vTaskGetInfo( NULL, &xStatus, pdTRUE, eRunning );
385 
386     if( uxTaskGetStackHighWaterMark( NULL ) != xStatus.usStackHighWaterMark )
387     {
388         xReturn = pdFAIL;
389     }
390 
391     if( uxTaskGetStackHighWaterMark2( NULL ) != ( configSTACK_DEPTH_TYPE ) xStatus.usStackHighWaterMark )
392     {
393         xReturn = pdFAIL;
394     }
395 
396     /* Now obtain a task status without the high water mark but with the state,
397      * which in the case of the idle task should be Read. */
398     xTimerTask = xTimerGetTimerDaemonTaskHandle();
399     vTaskSuspend( xTimerTask ); /* Should never suspend Timer task normally!. */
400     vTaskGetInfo( xTimerTask, &xStatus, pdFALSE, eInvalid );
401 
402     if( xStatus.eCurrentState != eSuspended )
403     {
404         xReturn = pdFAIL;
405     }
406 
407     if( xStatus.uxBasePriority != uxTaskPriorityGetFromISR( xTimerTask ) )
408     {
409         xReturn = pdFAIL;
410     }
411 
412     if( xStatus.uxBasePriority != ( configMAX_PRIORITIES - 1 ) )
413     {
414         xReturn = pdFAIL;
415     }
416 
417     xTaskResumeFromISR( xTimerTask );
418     vTaskGetInfo( xTimerTask, &xStatus, pdTRUE, eInvalid );
419 
420     if( ( xStatus.eCurrentState != eReady ) && ( xStatus.eCurrentState != eBlocked ) )
421     {
422         xReturn = pdFAIL;
423     }
424 
425     if( uxTaskGetStackHighWaterMark( xTimerTask ) != xStatus.usStackHighWaterMark )
426     {
427         xReturn = pdFAIL;
428     }
429 
430     if( uxTaskGetStackHighWaterMark2( xTimerTask ) != ( configSTACK_DEPTH_TYPE ) xStatus.usStackHighWaterMark )
431     {
432         xReturn = pdFAIL;
433     }
434 
435     /* Attempting to abort a delay in the idle task should be guaranteed to
436      * fail as the idle task should never block. */
437     xIdleTask = xTaskGetIdleTaskHandle();
438 
439     if( xTaskAbortDelay( xIdleTask ) != pdFAIL )
440     {
441         xReturn = pdFAIL;
442     }
443 
444     /* Create an array of task status objects large enough to hold information
445      * on the number of tasks at this time - note this may change at any time if
446      * higher priority tasks are executing and creating tasks. */
447     uxNumberOfTasks = uxTaskGetNumberOfTasks();
448     pxStatusArray = ( TaskStatus_t * ) pvPortMalloc( uxNumberOfTasks * sizeof( TaskStatus_t ) );
449 
450     if( pxStatusArray != NULL )
451     {
452         /* Pass part of the array into uxTaskGetSystemState() to ensure it doesn't
453          * try using more space than there is available. */
454         uxReturned = uxTaskGetSystemState( pxStatusArray, uxNumberOfTasks / ( UBaseType_t ) 2, NULL );
455 
456         if( uxReturned != ( UBaseType_t ) 0 )
457         {
458             xReturn = pdFAIL;
459         }
460 
461         /* Now do the same but passing in the complete array size, this is done
462          * twice to check for a difference in the total run time. */
463         uxTaskGetSystemState( pxStatusArray, uxNumberOfTasks, &ulTotalRunTime1 );
464         memset( ( void * ) pxStatusArray, 0xaa, uxNumberOfTasks * sizeof( TaskStatus_t ) );
465         uxReturned = uxTaskGetSystemState( pxStatusArray, uxNumberOfTasks, &ulTotalRunTime2 );
466 
467         if( ( ulTotalRunTime2 - ulTotalRunTime1 ) > ulRunTimeTolerance )
468         {
469             xReturn = pdFAIL;
470         }
471 
472         /* Basic sanity check of array contents. */
473         for( ux = 0; ux < uxReturned; ux++ )
474         {
475             if( pxStatusArray[ ux ].eCurrentState >= ( UBaseType_t ) eInvalid )
476             {
477                 xReturn = pdFAIL;
478             }
479 
480             if( pxStatusArray[ ux ].uxCurrentPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
481             {
482                 xReturn = pdFAIL;
483             }
484         }
485 
486         vPortFree( pxStatusArray );
487     }
488     else
489     {
490         xReturn = pdFAIL;
491     }
492 
493     return xReturn;
494 }
495 /*-----------------------------------------------------------*/
496 
prvDummyTagFunction(void * pvParameter)497 static BaseType_t prvDummyTagFunction( void * pvParameter )
498 {
499     return ( BaseType_t ) pvParameter;
500 }
501 /*-----------------------------------------------------------*/
502 
prvTaskTags(void)503 static BaseType_t prvTaskTags( void )
504 {
505     BaseType_t xReturn = pdPASS, xParameter = ( BaseType_t ) 0xDEADBEEF;
506     TaskHandle_t xTask;
507 
508     /* First try with the handle of a different task.  Use the timer task for
509      * convenience. */
510     xTask = xTimerGetTimerDaemonTaskHandle();
511 
512     vTaskSetApplicationTaskTag( xTask, prvDummyTagFunction );
513 
514     if( xTaskGetApplicationTaskTag( xTask ) != prvDummyTagFunction )
515     {
516         xReturn = pdFAIL;
517     }
518     else
519     {
520         if( xTaskCallApplicationTaskHook( xTask, ( void * ) xParameter ) != xParameter )
521         {
522             xReturn = pdFAIL;
523         }
524 
525         if( xTaskCallApplicationTaskHook( xTask, ( void * ) NULL ) != pdFAIL )
526         {
527             xReturn = pdFAIL;
528         }
529     }
530 
531     /* Try FromISR version too. */
532     if( xTaskGetApplicationTaskTagFromISR( xTask ) != prvDummyTagFunction )
533     {
534         xReturn = pdFAIL;
535     }
536 
537     /* Now try with a NULL handle, so using this task. */
538     vTaskSetApplicationTaskTag( NULL, NULL );
539 
540     if( xTaskGetApplicationTaskTag( NULL ) != NULL )
541     {
542         xReturn = pdFAIL;
543     }
544 
545     if( xTaskGetApplicationTaskTagFromISR( NULL ) != NULL )
546     {
547         xReturn = pdFAIL;
548     }
549 
550     vTaskSetApplicationTaskTag( NULL, prvDummyTagFunction );
551 
552     if( xTaskGetApplicationTaskTag( NULL ) != prvDummyTagFunction )
553     {
554         xReturn = pdFAIL;
555     }
556     else
557     {
558         if( xTaskCallApplicationTaskHook( NULL, ( void * ) xParameter ) != xParameter )
559         {
560             xReturn = pdFAIL;
561         }
562 
563         if( xTaskCallApplicationTaskHook( NULL, ( void * ) NULL ) != pdFAIL )
564         {
565             xReturn = pdFAIL;
566         }
567     }
568 
569     /* Try FromISR version too. */
570     if( xTaskGetApplicationTaskTagFromISR( NULL ) != prvDummyTagFunction )
571     {
572         xReturn = pdFAIL;
573     }
574 
575     vTaskSetApplicationTaskTag( NULL, NULL );
576 
577     if( xTaskGetApplicationTaskTag( NULL ) != NULL )
578     {
579         xReturn = pdFAIL;
580     }
581 
582     return xReturn;
583 }
584 /*-----------------------------------------------------------*/
585 
prvTimerQuery(void)586 static BaseType_t prvTimerQuery( void )
587 {
588     TimerHandle_t xTimer;
589     BaseType_t xReturn = pdPASS;
590     const char * pcTimerName = "TestTimer";
591     const TickType_t xTimerPeriod = ( TickType_t ) 100;
592     const UBaseType_t uxTimerNumber = ( UBaseType_t ) 55;
593 
594     xTimer = xTimerCreate( pcTimerName,
595                            xTimerPeriod,
596                            pdFALSE,
597                            ( void * ) xTimerPeriod,
598                            NULL ); /* Not actually going to start timer so NULL callback is ok. */
599 
600     if( xTimer != NULL )
601     {
602         if( xTimerGetPeriod( xTimer ) != xTimerPeriod )
603         {
604             xReturn = pdFAIL;
605         }
606 
607         if( strcmp( pcTimerGetName( xTimer ), pcTimerName ) != 0 )
608         {
609             xReturn = pdFAIL;
610         }
611 
612         vTimerSetTimerNumber( xTimer, uxTimerNumber );
613 
614         if( uxTimerGetTimerNumber( xTimer ) != uxTimerNumber )
615         {
616             xReturn = pdFAIL;
617         }
618 
619         xTimerDelete( xTimer, portMAX_DELAY );
620     }
621     else
622     {
623         xReturn = pdFAIL;
624     }
625 
626     return xReturn;
627 }
628 /*-----------------------------------------------------------*/
629 
xRunCodeCoverageTestAdditions(void)630 BaseType_t xRunCodeCoverageTestAdditions( void )
631 {
632     BaseType_t xReturn = pdPASS;
633 
634     xReturn &= prvStaticAllocationsWithNullBuffers();
635     xReturn &= prvTraceUtils();
636     xReturn &= prvPeekTimeout();
637     xReturn &= prvQueueQueryFromISR();
638     xReturn &= prvTaskQueryFunctions();
639     xReturn &= prvTaskTags();
640     xReturn &= prvTimerQuery();
641 
642     return xReturn;
643 }
644 /*-----------------------------------------------------------*/
645