/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /*! @file config_assert_utest.c */ /* C runtime includes. */ #include #include #include #include #include /* Tasks includes */ #include "FreeRTOS.h" #include "FreeRTOSConfig.h" #include "../global_vars.h" #include "task.h" /* Test includes. */ #include "unity.h" #include "unity_memory.h" #include "CException.h" /* Mock includes. */ #include "mock_timers.h" #include "mock_list.h" #include "mock_list_macros.h" #include "mock_fake_assert.h" #include "mock_fake_port.h" #include "mock_local_portable.h" /* ================================= MACROS =============================== */ /** * @brief CException code for when a configASSERT should be intercepted. */ #define configASSERT_E 0xAA101 /** * @brief simulate up to 10 tasks: add more if needed * */ #define TCB_ARRAY 10 /** * @brief Expect a configASSERT from the function called. * Break out of the called function when this occurs. * @details Use this macro when the call passed in as a parameter is expected * to cause invalid memory access. */ #define EXPECT_ASSERT_BREAK( call ) \ do \ { \ shouldAbortOnAssertion = true; \ CEXCEPTION_T e = CEXCEPTION_NONE; \ Try \ { \ call; \ TEST_FAIL(); \ } \ Catch( e ) \ { \ TEST_ASSERT_EQUAL( configASSERT_E, e ); \ } \ } while( 0 ) /* ============================ GLOBAL VARIABLES =========================== */ /** * @brief Global counter for the number of assertions in code. */ static int assertionFailed = 0; /** * @brief Flag which denotes if test need to abort on assertion. */ static BaseType_t shouldAbortOnAssertion; /* =========================== EXTERN VARIABLES =========================== */ extern void vTaskEnterCritical( void ); extern void vTaskExitCritical( void ); extern void vTaskExitCriticalFromISR( UBaseType_t xSavedInterruptStatus ); extern void prvCheckForRunStateChange( void ); extern volatile UBaseType_t uxDeletedTasksWaitingCleanUp; extern volatile BaseType_t xSchedulerRunning; extern volatile UBaseType_t uxSchedulerSuspended; extern TCB_t * volatile pxCurrentTCBs[ configNUMBER_OF_CORES ]; extern volatile BaseType_t xYieldPendings[ configNUMBER_OF_CORES ]; extern volatile UBaseType_t uxTopReadyPriority; extern List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; extern volatile TickType_t xTickCount; extern volatile TickType_t xNextTaskUnblockTime; extern TaskHandle_t xIdleTaskHandles[ configNUMBER_OF_CORES ]; /* ========================== STATIC FUNCTIONS ========================== */ static void vFakeAssertStub( bool x, char * file, int line, int cmock_num_calls ) { if( !x ) { assertionFailed++; if( shouldAbortOnAssertion == pdTRUE ) { Throw( configASSERT_E ); } } } static void validate_and_clear_assertions( void ) { TEST_ASSERT_EQUAL( 1, assertionFailed ); assertionFailed = 0; } void * pvPortMalloc( size_t xSize ) { return unity_malloc( xSize ); } void vPortFree( void * pv ) { return unity_free( pv ); } /* ============================ Unity Fixtures ============================ */ /*! called before each testcase */ void setUp( void ) { vFakeAssert_StubWithCallback( vFakeAssertStub ); } /*! called after each testcase */ void tearDown( void ) { } /*! called at the beginning of the whole suite */ void suiteSetUp() { } /*! called at the end of the whole suite */ int suiteTearDown( int numFailures ) { return numFailures; } /* =============================== Callbacks ============================== */ void port_release_task_lock_cb( int num_calls ) { pxCurrentTCBs[ 0 ]->xTaskRunState = -1; /* taskTASK_YIELDING */ } /* ============================== Test Cases ============================== */ /** * @brief This test ensures that the code asserts when the TCB's critical * nesting count is less than or equal to zero * * Coverage * @code{c} * prvYieldForTask( pxTCB ); * * configASSERT( portGET_CRITICAL_NESTING_COUNT() > 0U ); * @endcode * * configUSE_PREEMPTION == 1 * configNUMBER_OF_CORES > 1 * configUSE_CORE_AFFINITY == 1 */ void test_prvYieldForTask_assert_critical_nesting_lteq_zero( void ) { UBaseType_t uxCoreAffinityMask; TCB_t currentTCB; memset( ¤tTCB, 0x00, sizeof( TCB_t ) ); pxCurrentTCBs[ 0 ] = ¤tTCB; pxCurrentTCBs[ 0 ]->uxCoreAffinityMask = 1; pxCurrentTCBs[ 0 ]->uxCriticalNesting = 0; pxCurrentTCBs[ 0 ]->xTaskRunState = -1; /* taskTASK_NOT_RUNNING */ xSchedulerRunning = pdTRUE; /* Set the new mask to the last core. */ uxCoreAffinityMask = ( 1 << ( configNUMBER_OF_CORES - 1 ) ); vFakePortEnterCriticalSection_Expect(); vFakePortGetCoreID_ExpectAndReturn( 0 ); EXPECT_ASSERT_BREAK( vTaskCoreAffinitySet( pxCurrentTCBs[ 0 ], uxCoreAffinityMask ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when xYieldPending of the * current core is false and the task is runing * * Coverage * @code{c} * prvYieldForTask( pxTCB ); * * configASSERT( ( xYieldPendings[ portGET_CORE_ID() ] == pdTRUE ) || * ( taskTASK_IS_RUNNING( pxCurrentTCBs[ portGET_CORE_ID() ] ) == pdFALSE ) ); * @endcode * * configNUMBER_OF_CORES > 1 * configRUN_MULTIPLE_PRIORITIES = 0 * configUSE_TASK_PREEMPTION_DISABLE */ void test_prvYieldForTask_assert_yieldpending_core_is_false( void ) { TCB_t unblockedTCB[ configNUMBER_OF_CORES ] = { 0 }; ListItem_t xEventListItem; TickType_t xItemValue = 0; for( int i = 0; i < configNUMBER_OF_CORES; ++i ) { pxCurrentTCBs[ i ] = &unblockedTCB[ i ]; unblockedTCB[ i ].xTaskRunState = -1; /* taskTASK_YIELDING */ } unblockedTCB[ 0 ].uxCriticalNesting = 1; unblockedTCB[ 0 ].xTaskRunState = 0; unblockedTCB[ 0 ].uxTaskAttributes = 2; unblockedTCB[ 0 ].uxPriority = 1; unblockedTCB[ 1 ].uxTaskAttributes = 2; unblockedTCB[ 1 ].uxPriority = 0; unblockedTCB[ 1 ].xTaskRunState = 1; unblockedTCB[ 2 ].xTaskRunState = -1; unblockedTCB[ 3 ].xTaskRunState = configNUMBER_OF_CORES + 2; uxSchedulerSuspended = 3; xYieldPendings[ 1 ] = pdFALSE; listSET_LIST_ITEM_VALUE_ExpectAnyArgs(); listGET_LIST_ITEM_OWNER_ExpectAnyArgsAndReturn( &unblockedTCB[ 0 ] ); listREMOVE_ITEM_ExpectAnyArgs(); listLIST_IS_EMPTY_ExpectAnyArgsAndReturn( pdTRUE ); listREMOVE_ITEM_ExpectAnyArgs(); /* in prvAddTaskToReadyList */ listINSERT_END_ExpectAnyArgs(); /* back */ /* taskENTER_CRITICAL */ vFakePortEnterCriticalSection_Expect(); /* back */ /* prvYieldForTask */ vFakePortGetCoreID_ExpectAndReturn( 1 ); EXPECT_ASSERT_BREAK( vTaskRemoveFromUnorderedEventList( &xEventListItem, xItemValue ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when we are trying to select * the highest priority task on a specific core while the scheuler is not * running * * Coverage * @code{c} * prvSelectHighestPriorityTask( xCoreID ); * * configASSERT( xSchedulerRunning == pdTRUE ); * * @endcode * * configNUMBER_OF_CORES > 1 */ void test_prvSelectHighestPriorityTask_assert_scheduler_running_false( void ) { TCB_t unblockedTCB[ configNUMBER_OF_CORES ] = { 0 }; unblockedTCB[ 0 ].uxCriticalNesting = 0; pxCurrentTCBs[ 0 ] = &unblockedTCB[ 0 ]; xSchedulerRunning = pdFALSE; /* causes the assert */ uxSchedulerSuspended = pdFALSE; vFakePortGetTaskLock_Expect(); vFakePortGetISRLock_Expect(); EXPECT_ASSERT_BREAK( vTaskSwitchContext( 1 ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when the coreID is not equal * to the runstate of the task * * Coverage * @code{c} * prvSelectHighestPriorityTask( xCoreID ); * * configASSERT( ( pxTCB->xTaskRunState == xCoreID ) || ( pxTCB->xTaskRunState == taskTASK_YIELDING ) ); * * @endcode * * configNUMBER_OF_CORES > 1 */ void test_prvSelectHighestPriorityTask_assert_coreid_ne_runstate( void ) { TCB_t unblockedTCB[ configNUMBER_OF_CORES ] = { 0 }; unblockedTCB[ 0 ].uxCriticalNesting = 0; unblockedTCB[ 0 ].xTaskRunState = 2; /* causes the assert coreID != runstate */ pxCurrentTCBs[ 0 ] = &unblockedTCB[ 0 ]; xSchedulerRunning = pdTRUE; uxSchedulerSuspended = pdFALSE; vFakePortGetTaskLock_Expect(); vFakePortGetISRLock_Expect(); listIS_CONTAINED_WITHIN_ExpectAnyArgsAndReturn( pdFALSE ); listLIST_IS_EMPTY_ExpectAnyArgsAndReturn( pdFALSE ); listGET_LIST_ITEM_OWNER_ExpectAnyArgsAndReturn( &unblockedTCB[ 0 ] ); EXPECT_ASSERT_BREAK( vTaskSwitchContext( 0 ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts if the scheduler is not * suspended * * Coverage * @code{c} * vTaskDelete( xTaskToDelete ); * * configASSERT( uxSchedulerSuspended == 0 ); * @endcode * * configNUMBER_OF_CORES > 1 * INCLUDE_vTaskDelete */ void test_vTaskDelete_assert_scheduler_suspended_eq_1( void ) { TaskHandle_t xTaskToDelete = NULL; TCB_t * pxTCB = malloc( sizeof( TCB_t ) ); pxTCB->pxStack = malloc( 200 ); pxTCB->xTaskRunState = 1; /* task running on core 1 */ xTaskToDelete = ( TaskHandle_t ) pxTCB; uxSchedulerSuspended = 1; /* asserts the code */ xSchedulerRunning = pdTRUE; vFakePortEnterCriticalSection_Expect(); uxListRemove_ExpectAnyArgsAndReturn( pdTRUE ); listLIST_ITEM_CONTAINER_ExpectAnyArgsAndReturn( NULL ); vListInsertEnd_ExpectAnyArgs(); vPortCurrentTaskDying_ExpectAnyArgs(); vFakePortGetCoreID_ExpectAndReturn( 1 ); EXPECT_ASSERT_BREAK( vTaskDelete( xTaskToDelete ) ); validate_and_clear_assertions(); free( pxTCB->pxStack ); free( pxTCB ); } /** * @brief vTaskSuspend - scheduler suspended assertion. * * This test ensures that the code asserts when a task is suspended while * the scheduler is suspended * * Coverage * @code{c} * if( xSchedulerRunning != pdFALSE ) * { * if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() ) * { * configASSERT( uxSchedulerSuspended == 0 ); * vTaskYieldWithinAPI(); * } * else * { * prvYieldCore( pxTCB->xTaskRunState ); * } * } * @endcode * configASSERT( uxSchedulerSuspended == 0 ) is triggered. */ void test_vTaskSuspend_assert_schedulersuspended_ne_zero( void ) { TaskHandle_t xTaskToSuspend; TCB_t * pxTCB = malloc( sizeof( TCB_t ) ); xTaskToSuspend = ( TaskHandle_t ) pxTCB; xSchedulerRunning = pdTRUE; pxTCB->xTaskRunState = 1; uxSchedulerSuspended = 1; /* asserts the code */ vFakePortEnterCriticalSection_Expect(); uxListRemove_ExpectAnyArgsAndReturn( pdTRUE ); listLIST_ITEM_CONTAINER_ExpectAnyArgsAndReturn( NULL ); vListInsertEnd_ExpectAnyArgs(); listLIST_IS_EMPTY_ExpectAnyArgsAndReturn( pdTRUE ); vFakePortGetCoreID_ExpectAndReturn( 1 ); EXPECT_ASSERT_BREAK( vTaskSuspend( xTaskToSuspend ) ); validate_and_clear_assertions(); free( pxTCB ); } /** * @brief This test ensures that the code asserts when we try to switch * context with a current task that is holding a * critical section * * Coverage * @code{c} * vTaskSwitchContext( xCoreID ); * * configASSERT( portGET_CRITICAL_NESTING_COUNT() == 0 ); * * @endcode * * configNUMBER_OF_CORES > 1 */ void test_vTaskSwitchContext_assert_nexting_count_ne_zero( void ) { TCB_t currentTCB = { 0 }; currentTCB.uxCriticalNesting = 1; /* causes the assert */ pxCurrentTCBs[ 1 ] = ¤tTCB; vFakePortGetTaskLock_Expect(); vFakePortGetISRLock_Expect(); EXPECT_ASSERT_BREAK( vTaskSwitchContext( 1 ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when we try to exit a critical * section while the current tasks critical count is zero * * Coverage * @code{c} * vTaskExitCritical(); * * configASSERT( portGET_CRITICAL_NESTING_COUNT() > 0U ); * * @endcode * * configNUMBER_OF_CORES > 1 */ void test_vTaskExitCritical_assert_critical_nesting_eq_zero( void ) { TCB_t currentTCB = { 0 }; xSchedulerRunning = pdTRUE; currentTCB.uxCriticalNesting = 0; /* causes the assert */ pxCurrentTCBs[ 1 ] = ¤tTCB; vFakePortGetCoreID_ExpectAndReturn( 1 ); EXPECT_ASSERT_BREAK( vTaskExitCritical() ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when we try to exit a critical * section while the current tasks critical count is zero * * Coverage * @code{c} * vTaskExitCriticalFromISR(); * * configASSERT( portGET_CRITICAL_NESTING_COUNT() > 0U ); * * @endcode * * configNUMBER_OF_CORES > 1 */ void test_vTaskExitCriticalFromISR_assertcritical_nesting_eq_zero( void ) { TCB_t currentTCB = { 0 }; xSchedulerRunning = pdTRUE; currentTCB.uxCriticalNesting = 0; /* causes the assert */ pxCurrentTCBs[ 1 ] = ¤tTCB; vFakePortGetCoreID_ExpectAndReturn( 1 ); EXPECT_ASSERT_BREAK( vTaskExitCriticalFromISR( 1 ) ); validate_and_clear_assertions(); } /** * @brief This test ensures that the code asserts when the next unblock time is * less than the xTickCount * * Coverage * @code{c} * prvIdleTask(); * * configASSERT( xNextTaskUnblockTime >= xTickCount ); * * @endcode * * configNUMBER_OF_CORES > 1 * configUSE_TICKLESS_IDLE != 0 */ void test_prvGetExpectedIdleTime_assert_nextUnblock_lt_xTickCount( void ) { TCB_t xTCB = { 0 }; xTickCount = 250; xNextTaskUnblockTime = 240; /* expectedidletime = xNextTaskUnblockTime - xTickCount */ pxCurrentTCBs[ 0 ] = &xTCB; xTCB.uxPriority = tskIDLE_PRIORITY; /* Test Setup */ uxDeletedTasksWaitingCleanUp = 0; uxTopReadyPriority = tskIDLE_PRIORITY; /* Test Expectations */ vFakePortYield_Expect(); listCURRENT_LIST_LENGTH_ExpectAnyArgsAndReturn( 0 ); ulFakePortSetInterruptMask_ExpectAndReturn( 0 ); vFakePortGetCoreID_ExpectAndReturn( 0 ); vFakePortClearInterruptMask_Expect( 0 ); listCURRENT_LIST_LENGTH_ExpectAnyArgsAndReturn( 0 ); /* vTaskSuspendAll */ vFakePortAssertIfISR_Expect(); ulFakePortSetInterruptMask_ExpectAndReturn( 0 ); vFakePortGetCoreID_ExpectAndReturn( 0 ); vFakePortGetTaskLock_Expect(); vFakePortGetISRLock_Expect(); vFakePortReleaseISRLock_Expect(); vFakePortClearInterruptMask_Expect( 0 ); /* API Call */ portTASK_FUNCTION( prvIdleTask, args ); EXPECT_ASSERT_BREAK( prvIdleTask( NULL ) ); /* Test Verifications */ validate_and_clear_assertions(); } /** * @brief vTaskStepTick - assert if scheduler suspended. * * Coverage * @code{c} * configASSERT( uxSchedulerSuspended ); * @endcode */ void test_vTaskStepTick_assert_scheduler_not_suspended( void ) { TickType_t xTicksToJump; xTickCount = 230; xTicksToJump = 10; xNextTaskUnblockTime = 240; uxSchedulerSuspended = pdFALSE; /* API Call */ EXPECT_ASSERT_BREAK( vTaskStepTick( xTicksToJump ) ); /* Test Verifications */ validate_and_clear_assertions(); } /** * @brief vTaskStepTick - assert if scheduler suspended. * * Coverage * @code{c} * configASSERT( xTicksToJump != ( TickType_t ) 0 ); * @endcode */ void test_vTaskStepTick_assert_tick_to_jump_eq_0( void ) { TickType_t xTicksToJump; xTickCount = 240; xTicksToJump = 0; xNextTaskUnblockTime = 240; uxSchedulerSuspended = pdTRUE; /* API Call */ EXPECT_ASSERT_BREAK( vTaskStepTick( xTicksToJump ) ); /* Test Verifications */ validate_and_clear_assertions(); } /** * @brief xTaskGetIdleTaskHandleForCore - assert if xCoreID is less than 0 * * Coverage * @code{c} * configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); * @endcode * taskVALID_CORE_ID( xCoreID ) is false with xCoreID less than 0. */ void test_xTaskGetIdleTaskHandleForCore_assert_invalid_core_id_lt( void ) { /* API Call */ EXPECT_ASSERT_BREAK( xTaskGetIdleTaskHandleForCore( -1 ) ); /* Test Verifications */ validate_and_clear_assertions(); } /** * @brief xTaskGetIdleTaskHandleForCore - assert if xCoreID is greater or equal * than configNUMBER_OF_CORES * * Coverage * @code{c} * configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); * @endcode * taskVALID_CORE_ID( xCoreID ) is false with xCoreID greater or equal than configNUMBER_OF_CORES */ void test_xTaskGetIdleTaskHandleForCore_assert_invalid_core_id_ge( void ) { /* API Call */ EXPECT_ASSERT_BREAK( xTaskGetIdleTaskHandleForCore( configNUMBER_OF_CORES ) ); /* Test Verifications */ validate_and_clear_assertions(); } /** * @brief xTaskGetIdleTaskHandleForCore - assert if idle task handle is NULL due to * scheduler not started. * * Coverage * @code{c} * configASSERT( ( xIdleTaskHandles[ xCoreID ] != NULL ) ); * @endcode * ( xIdleTaskHandles[ xCoreID ] != NULL ) is false. */ void test_xTaskGetIdleTaskHandleForCore_assert_null_idle_task_handle( void ) { BaseType_t xCoreID; /* Setup the variables and structure. */ for( xCoreID = 0; xCoreID < configNUMBER_OF_CORES; xCoreID++ ) { xIdleTaskHandles[ xCoreID ] = NULL; } for( xCoreID = 0; xCoreID < configNUMBER_OF_CORES; xCoreID++ ) { /* API Call */ EXPECT_ASSERT_BREAK( xTaskGetIdleTaskHandleForCore( xCoreID ) ); /* Test Verifications */ validate_and_clear_assertions(); } }