From e39c34ba7e3c146d81b05f73e272e90fa645e2e5 Mon Sep 17 00:00:00 2001 From: Ravishankar Bhagavandas Date: Tue, 30 Mar 2021 12:56:02 -0700 Subject: [PATCH] Adding unit tests for stream buffer and message buffer (#528) * Initial commit * Add more stream buffer tests * Adding message buffer tests * Adding tests to cover config assert branches Co-authored-by: alfred gedeon <28123637+alfred2g@users.noreply.github.com> --- FreeRTOS/Source | 2 +- FreeRTOS/Test/CMock/Makefile | 2 + FreeRTOS/Test/CMock/message_buffer/Makefile | 43 + .../CMock/message_buffer/message_buffer.yml | 32 + .../message_buffer/message_buffer_utest.c | 795 ++++++++++ FreeRTOS/Test/CMock/stream_buffer/Makefile | 43 + .../CMock/stream_buffer/stream_buffer.yml | 32 + .../CMock/stream_buffer/stream_buffer_utest.c | 1407 +++++++++++++++++ 8 files changed, 2355 insertions(+), 1 deletion(-) create mode 100644 FreeRTOS/Test/CMock/message_buffer/Makefile create mode 100644 FreeRTOS/Test/CMock/message_buffer/message_buffer.yml create mode 100644 FreeRTOS/Test/CMock/message_buffer/message_buffer_utest.c create mode 100644 FreeRTOS/Test/CMock/stream_buffer/Makefile create mode 100644 FreeRTOS/Test/CMock/stream_buffer/stream_buffer.yml create mode 100644 FreeRTOS/Test/CMock/stream_buffer/stream_buffer_utest.c diff --git a/FreeRTOS/Source b/FreeRTOS/Source index 23f641850d..95433d0284 160000 --- a/FreeRTOS/Source +++ b/FreeRTOS/Source @@ -1 +1 @@ -Subproject commit 23f641850d2428eac3e164d6e735e6e92dc3914a +Subproject commit 95433d02848af0d274648ae4cdd846f6eff76dde diff --git a/FreeRTOS/Test/CMock/Makefile b/FreeRTOS/Test/CMock/Makefile index a44dfe4a2b..5b27de418d 100644 --- a/FreeRTOS/Test/CMock/Makefile +++ b/FreeRTOS/Test/CMock/Makefile @@ -10,6 +10,8 @@ export LD ?= /usr/local/bin/ld UNITS += list #UNITS += queue #UNITS += timers +UNITS += stream_buffer +UNITS += message_buffer COVINFO := $(BUILD_DIR)/cmock_test.info LCOV_LIST := $(foreach unit,$(UNITS),$(GENERATED_DIR)/$(unit).info ) diff --git a/FreeRTOS/Test/CMock/message_buffer/Makefile b/FreeRTOS/Test/CMock/message_buffer/Makefile new file mode 100644 index 0000000000..a8dd6cccd0 --- /dev/null +++ b/FreeRTOS/Test/CMock/message_buffer/Makefile @@ -0,0 +1,43 @@ +# indent with spaces +.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX) + +# Do not move this line below the include +MAKEFILE_ABSPATH := $(abspath $(lastword $(MAKEFILE_LIST))) +include ../makefile.in + +# PROJECT_SRC lists the .c files under test +PROJECT_SRC := stream_buffer.c + +# PROJECT_DEPS_SRC list the .c file that are dependencies of PROJECT_SRC files +# Files in PROJECT_DEPS_SRC are excluded from coverage measurements +PROJECT_DEPS_SRC := + +# PROJECT_HEADER_DEPS: headers that should be excluded from coverage measurements. +PROJECT_HEADER_DEPS := FreeRTOS.h + +# SUITE_UT_SRC: .c files that contain test cases (must end in _utest.c) +SUITE_UT_SRC := message_buffer_utest.c + +# SUITE_SUPPORT_SRC: .c files used for testing that do not contain test cases. +# Paths are relative to PROJECT_DIR +SUITE_SUPPORT_SRC := + +# List the headers used by PROJECT_SRC that you would like to mock +MOCK_FILES_FP := $(KERNEL_DIR)/include/task.h +MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_assert.h +MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_port.h + +# List any addiitonal flags needed by the preprocessor +CPPFLAGS += -DportUSING_MPU_WRAPPERS=0 + +# List any addiitonal flags needed by the compiler +CFLAGS += + +# Try not to edit beyond this line unless necessary. + +# Project is determined based on path: $(UT_ROOT_DIR)/$(PROJECT) +PROJECT := $(lastword $(subst /, ,$(dir $(abspath $(MAKEFILE_ABSPATH))))) + +export + +include ../testdir.mk diff --git a/FreeRTOS/Test/CMock/message_buffer/message_buffer.yml b/FreeRTOS/Test/CMock/message_buffer/message_buffer.yml new file mode 100644 index 0000000000..de9ad9be42 --- /dev/null +++ b/FreeRTOS/Test/CMock/message_buffer/message_buffer.yml @@ -0,0 +1,32 @@ +:cmock: + :mock_prefix: mock_ + :when_no_prototypes: :warn + :treat_externs: :include + :enforce_strict_ordering: TRUE + :plugins: + - :ignore + - :ignore_arg + - :expect_any_args + - :array + - :callback + - :return_thru_ptr + :callback_include_count: true # include a count arg when calling the callback + :callback_after_arg_check: false # check arguments before calling the callback + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + :includes: # This will add these includes to each mock. + - + - "FreeRTOS.h" + :treat_externs: :exclude # Now the extern-ed functions will be mocked. + :weak: __attribute__((weak)) + :verbosity: 3 + :attributes: + - PRIVILEGED_FUNCTION + :strippables: + - PRIVILEGED_FUNCTION + - portDONT_DISCARD + :treat_externs: :include diff --git a/FreeRTOS/Test/CMock/message_buffer/message_buffer_utest.c b/FreeRTOS/Test/CMock/message_buffer/message_buffer_utest.c new file mode 100644 index 0000000000..2a2c00f2ab --- /dev/null +++ b/FreeRTOS/Test/CMock/message_buffer/message_buffer_utest.c @@ -0,0 +1,795 @@ +/* + * FreeRTOS V202012.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 message_buffer_utest.c */ + +/* C runtime includes. */ +#include +#include + +/* Queue includes */ +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "message_buffer.h" + +/* Test includes. */ +#include "unity.h" +#include "unity_memory.h" + +/* Mock includes. */ +#include "mock_task.h" +#include "mock_fake_assert.h" +#include "mock_fake_port.h" + +/** + * @brief Sample size in bytes of the message buffer used for test. + * Keep it short enough so that they can be allocated on stack. + */ +#define TEST_MESSAGE_BUFFER_SIZE ( 64U ) + +/** + * @brief Minimum size required to store the length of the message. + */ +#define TEST_MESSAGE_METADATA_SIZE ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) ) + +/** + * @brief Maximum size of a message that can be sent to a message buffer. + * Each message stores an associated length of the message as metadata. So maximum message + * size at any time is total length - size of the variable used to store length. + */ +#define TEST_MAX_MESSAGE_SIZE ( TEST_MESSAGE_BUFFER_SIZE - TEST_MESSAGE_METADATA_SIZE ) + +/** + * @brief Maximum unsigned long value so as to trigger an integer overflow. + */ +#define TEST_MESSAGE_BUFFER_MAX_UINT_SIZE ( ~( size_t ) ( 0UL ) ) + +/** + * @brief Ticks to wait from tests if the message buffer is full while sending data or + * below trigger level while receiveing data. + */ +#define TEST_MESSAGE_BUFFER_WAIT_TICKS ( 1000U ) + +/* ============================ GLOBAL VARIABLES =========================== */ + +/** + * @brief Global counter for the number of assertions in code. + */ +static int assertionFailed = 0; + +/** + * @brief Global counter to keep track of how many times a sender task was woken up by a task receiving from the stream buffer. + */ +static int senderTaskWoken = 0; + +/** + * @brief Global counter to keep track of how many times a receiver task was woken up by a task sending to the buffer. + */ +static int receiverTaskWoken = 0; + +/** + * @brief Dummy sender task handle to which the stream buffer receive APIs will send notification. + */ +static TaskHandle_t senderTask = ( TaskHandle_t ) ( 0xAABBCCDD ); + +/** + * @brief Dummy receiver task handle to which the stream buffer send APIs will send notifications. + */ +static TaskHandle_t receiverTask = ( TaskHandle_t ) ( 0xABCDEEFF ); + +/** + * @brief Global message buffer handle used for tests. + */ +static MessageBufferHandle_t xMessageBuffer; + +/** + * @brief Flag which denotes if test need to abort on assertion. + */ +static BaseType_t shouldAbortOnAssertion; + +/* ========================== CALLBACK FUNCTIONS =========================== */ + +void * pvPortMalloc( size_t xSize ) +{ + return unity_malloc( xSize ); +} +void vPortFree( void * pv ) +{ + return unity_free( pv ); +} + +static void vFakeAssertStub( bool x, + char * file, + int line, + int cmock_num_calls ) +{ + if( !x ) + { + assertionFailed++; + + if( shouldAbortOnAssertion == pdTRUE ) + { + TEST_ABORT(); + } + } +} + +static BaseType_t senderTaskNotificationCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( senderTask, xTaskToNotify ); + senderTaskWoken++; + return pdTRUE; +} + +static BaseType_t messageBufferReceiveFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t dataReceived = 0; + BaseType_t senderTaskWokenFromISR = pdFALSE; + + dataReceived = xMessageBufferReceiveFromISR( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, &senderTaskWokenFromISR ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, dataReceived ); + return pdTRUE; +} + +static BaseType_t messageBufferReceiveCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t dataReceived = 0; + + /* Read next message from message buffer to wake up sender task. */ + dataReceived = xMessageBufferReceive( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, 0 ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, dataReceived ); + return pdTRUE; +} + +static BaseType_t messageBufferSendFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + BaseType_t receiverTaskWokenFromISR = pdFALSE; + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t dataSent = 0; + + dataSent = xMessageBufferSendFromISR( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, &receiverTaskWokenFromISR ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, dataSent ); + return pdTRUE; +} + +static BaseType_t messageBufferSendCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t dataSent = 0; + + /* Send a single message of max message size to message buffer to wake up receiver Task. */ + dataSent = xMessageBufferSend( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, 0 ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, dataSent ); + return pdTRUE; +} + +static BaseType_t receiverTaskNotificationCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( receiverTask, xTaskToNotify ); + receiverTaskWoken++; + return pdTRUE; +} + +/******************************************************************************* + * Unity fixtures + ******************************************************************************/ +void setUp( void ) +{ + assertionFailed = 0; + xMessageBuffer = NULL; + senderTaskWoken = 0; + receiverTaskWoken = 0; + shouldAbortOnAssertion = pdTRUE; + + mock_task_Init(); + mock_fake_assert_Init(); + mock_fake_port_Init(); + + vFakePortEnterCriticalSection_Ignore(); + vFakePortExitCriticalSection_Ignore(); + ulFakePortSetInterruptMaskFromISR_IgnoreAndReturn( 0U ); + vFakePortClearInterruptMaskFromISR_Ignore(); + vFakeAssert_StubWithCallback( vFakeAssertStub ); + /* Track calls to malloc / free */ + UnityMalloc_StartTest(); +} + +/*! called before each testcase */ +void tearDown( void ) +{ + TEST_ASSERT_EQUAL_MESSAGE( 0, assertionFailed, "Assertion check failed in code." ); + UnityMalloc_EndTest(); + + mock_task_Verify(); + mock_task_Destroy(); + + mock_fake_assert_Verify(); + mock_fake_assert_Destroy(); + + mock_fake_port_Verify(); + mock_fake_port_Destroy(); +} + +/*! called at the beginning of the whole suite */ +void suiteSetUp() +{ +} + +/*! called at the end of the whole suite */ +int suiteTearDown( int numFailures ) +{ + return numFailures; +} + +static void validate_message_buffer_init_state( MessageBufferHandle_t xMessageBuffer, + size_t bufferSize ) +{ + TEST_ASSERT_TRUE( xMessageBufferIsEmpty( xMessageBuffer ) ); + TEST_ASSERT_FALSE( xMessageBufferIsFull( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( bufferSize, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( 0, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( 1, ucStreamBufferGetStreamBufferType( xMessageBuffer ) ); +} + +static void validate_and_clear_assertions( void ) +{ + TEST_ASSERT_EQUAL( 1, assertionFailed ); + assertionFailed = 0; +} + +/* ============================== Test Cases ============================== */ + +/** + * @brief Validates that message buffer of sample test size is created successfully. + */ +void test_xMessageBufferCreate_success( void ) +{ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_EQUAL( NULL, xMessageBuffer ); + validate_message_buffer_init_state( xMessageBuffer, TEST_MESSAGE_BUFFER_SIZE ); + vMessageBufferDelete( xMessageBuffer ); +} + +/** + * @brief API should fail and return null if there is an integer overflow in message buffer size passed. + */ +void test_xMessageBufferCreate_integer_overflow( void ) +{ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_MAX_UINT_SIZE ); + TEST_ASSERT_EQUAL( NULL, xMessageBuffer ); +} + +/** + * @breif API should fail and return null if the malloc fails. + */ +void test_xMessageBufferCreate_malloc_fail( void ) +{ + UnityMalloc_MakeMallocFailAfterCount( 0 ); + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NULL( xMessageBuffer ); +} + +/** + * @brief Should assert if a zero size is passed to create API. + */ +void test_xMessageBufferCreate_zero_size( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferCreate( 0 ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Should assert if the size passed is less than the minimum size requried to store metadata size. + */ +void test_xMessageBufferCreate_invalid_size( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferCreate( TEST_MESSAGE_METADATA_SIZE ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Validates message buffer creation through a static buffer allocated by the user. + */ +void test_xMessageBufferCreateStatic_success( void ) +{ + StaticMessageBuffer_t messageBufferStruct; + /* The size of message buffer array should be one greater than the required size of message buffer. */ + uint8_t messageBufferArray[ TEST_MESSAGE_BUFFER_SIZE + 1 ] = { 0 }; + + xMessageBuffer = xMessageBufferCreateStatic( sizeof( messageBufferArray ), messageBufferArray, &messageBufferStruct ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + validate_message_buffer_init_state( xMessageBuffer, TEST_MESSAGE_BUFFER_SIZE ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates message buffer creation fails if null array is passed. + */ +void test_xMessageBufferCreateStatic_null_array( void ) +{ + StaticMessageBuffer_t messageBufferStruct; + + /* Tests should abort if assertion is enabled or return NULL. */ + shouldAbortOnAssertion = pdFALSE; + + /* Returns NULL when NULL storage area is passed as a parameter. */ + xMessageBuffer = xMessageBufferCreateStatic( TEST_MESSAGE_BUFFER_SIZE, NULL, &messageBufferStruct ); + TEST_ASSERT_NULL( xMessageBuffer ); + validate_and_clear_assertions(); +} + +/** + * @brief Validates message buffer creation fails if NULL struct is passed. + */ +void test_xMessageBufferCreateStatic_null_struct( void ) +{ + /* The size of message buffer array should be one greater than the required size of message buffer. */ + uint8_t messageBufferArray[ TEST_MESSAGE_BUFFER_SIZE + 1 ] = { 0 }; + + /* Tests should abort if assertion is enabled or return NULL. */ + shouldAbortOnAssertion = pdFALSE; + + /* Returns NULL when NULL message buffer struct is passed as a parameter. */ + xMessageBuffer = xMessageBufferCreateStatic( sizeof( messageBufferArray ), messageBufferArray, NULL ); + TEST_ASSERT_NULL( xMessageBuffer ); + validate_and_clear_assertions(); +} + +/** + * @brief Validates message buffer create assert if the size passed is less than the minimum size requried to store metadata size. + */ +void test_xMessageBufferCreateStatic_invalid_size( void ) +{ + StaticMessageBuffer_t messageBufferStruct; + /* The size of message buffer array should be one greater than the required size of message buffer. */ + uint8_t messageBufferArray[ TEST_MESSAGE_BUFFER_SIZE + 1 ] = { 0 }; + + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferCreateStatic( TEST_MESSAGE_METADATA_SIZE, messageBufferArray, &messageBufferStruct ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Validate message buffer creation asserts if the size passed is zero. + */ +void test_xMessageBufferCreateStatic_zero_size( void ) +{ + StaticMessageBuffer_t messageBufferStruct; + /* The size of message buffer array should be one greater than the required size of message buffer. */ + uint8_t messageBufferArray[ TEST_MESSAGE_BUFFER_SIZE + 1 ] = { 0 }; + + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferCreateStatic( 0, messageBufferArray, &messageBufferStruct ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Validates a task is able to send upto maximum message size allowed, without blocking. + */ +void test_xMessageBufferSend_success( void ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t dataSent = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of the default test sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Sending bytes of size upto to available size should succeed. Sender task should not be in blocked state. */ + dataSent = xMessageBufferSend( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, dataSent ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( pdFALSE, xMessageBufferIsEmpty( xMessageBuffer ) ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief An integer overflow in message size to be sent should result in an + * assertion failure + */ +void test_xMessageBufferSend_message_size_integer_overflow( void ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of the default test sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferSend( xMessageBuffer, data, TEST_MESSAGE_BUFFER_MAX_UINT_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + } + validate_and_clear_assertions(); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * Sending a message of size greater than available size should return zero bytes sent. + */ +void test_xMessageBufferSend_message_larger_than_available_size( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE + 1 ] = { 0 }; + size_t sentBytes = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + sentBytes = xMessageBufferSend( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE + 1, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, sentBytes ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Sending null message should trigger an assert. + */ +void test_xMessageBufferSend_null_message( void ) +{ + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferSend( xMessageBuffer, NULL, TEST_MAX_MESSAGE_SIZE + 1, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + } + + validate_and_clear_assertions(); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Sending to a null message buffer should trigger an assert. + */ +void test_xMessageBufferSend_null_message_buffer( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE + 1 ] = { 0 }; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferSend( NULL, message, TEST_MAX_MESSAGE_SIZE + 1, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + } + + validate_and_clear_assertions(); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates that a task blocks if there is insufficient space in message buffer for a valid message to be sent. + * Task then succeeds once enough space is available by reading from the message buffer. + */ +void test_xMessageBufferSend_blocking( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t sentBytes = 0; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( senderTask ); + xTaskGenericNotifyWait_StubWithCallback( messageBufferReceiveCallback ); + xTaskGenericNotify_StubWithCallback( senderTaskNotificationCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Sending max message size so that message buffer is full. */ + sentBytes = xMessageBufferSend( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, sentBytes ); + TEST_ASSERT_EQUAL( 0, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Sending max message size again blocks the task. API succeeds after reading the current message from the buffer. */ + sentBytes = xMessageBufferSend( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, sentBytes ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( 1, senderTaskWoken ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates a task is able to receive upto maximum message size allowed, without blocking. + */ +void test_xMessageBufferReceive_success( void ) +{ + uint8_t data[ TEST_MAX_MESSAGE_SIZE ] = { 0xAA }; + size_t sent = 0; + uint8_t dataReceived[ TEST_MAX_MESSAGE_SIZE + 1 ] = { 0x00 }; + size_t received = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of the default test sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + sent = xMessageBufferSend( xMessageBuffer, data, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + + /* Receiving bytes of size less than the message length will return 0 bytes. */ + received = xMessageBufferReceive( xMessageBuffer, dataReceived, TEST_MAX_MESSAGE_SIZE - 1, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, received ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + + /* Receiving bytes of size greater than the message length will return message length bytes. */ + received = xMessageBufferReceive( xMessageBuffer, dataReceived, TEST_MAX_MESSAGE_SIZE + 1, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, received ); + TEST_ASSERT_EQUAL( 0, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates xMessageBufferReceive API with null input message. + */ +void test_xMessageBufferReceive_null_input_message( void ) +{ + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + /* Should assert if a null input mssage is passed. */ + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferReceive( xMessageBuffer, NULL, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + } + + validate_and_clear_assertions(); + vStreamBufferDelete( xMessageBuffer ); +} + + +/** + * @brief Validates xMessageBufferReceive API with null message buffer handle. + */ +void test_xMessageBufferReceive_invalid_params( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + + /* Should assert if a null message buffer handle is passed. */ + if( TEST_PROTECT() ) + { + ( void ) xMessageBufferReceive( NULL, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + } + + validate_and_clear_assertions(); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates that a task blocks if trying to read from an empty buffer. + * Task succeeds once a message is sent to message buffer. + */ +void test_xMessageBufferReceive_blocking( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t received = 0; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( receiverTask ); + xTaskGenericNotifyWait_StubWithCallback( messageBufferSendCallback ); + xTaskGenericNotify_StubWithCallback( receiverTaskNotificationCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Receive from an empty buffer causes task to block. API succeeds after a message is sent to buffer. */ + received = xMessageBufferReceive( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, received ); + TEST_ASSERT_EQUAL( 0, xStreamBufferNextMessageLengthBytes( xMessageBuffer ) ); + TEST_ASSERT_EQUAL( 1, receiverTaskWoken ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates that xMessageBufferSendFromISR API is successful and unblocks a receive task. + */ +void test_xMessageBufferSendFromISR_success( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t received = 0, sent = 0; + BaseType_t highPriorityTaskWoken = pdFALSE; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( receiverTask ); + xTaskGenericNotifyWait_StubWithCallback( messageBufferSendFromISRCallback ); + xTaskGenericNotifyFromISR_IgnoreAndReturn( pdTRUE ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( TEST_MESSAGE_BUFFER_SIZE, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Receiving from empty message buffer should block. But the task be woken up from a send from ISR. */ + received = xMessageBufferReceive( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, received ); + + + /* Sending message of max size from ISR should succeed. */ + sent = xMessageBufferSendFromISR( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, received ); + + /* Sending message of max size again from ISR should return zero instead of blocking. */ + sent = xMessageBufferSendFromISR( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 0, sent ); + + vStreamBufferDelete( xMessageBuffer ); +} + +/** + * @brief Validates that xMessageBufferSendFromISR API is successful and unblocks a receive task. + */ +void test_xMessageBufferReceiveFromISR_success( void ) +{ + uint8_t message[ TEST_MAX_MESSAGE_SIZE ] = { 0 }; + size_t sent = 0, received = 0; + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a message buffer of sample size. */ + xMessageBuffer = xMessageBufferCreate( TEST_MESSAGE_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xMessageBuffer ); + TEST_ASSERT_EQUAL( pdTRUE, xMessageBufferIsEmpty( xMessageBuffer ) ); + + /* Receiving from an empty message buffer from ISR should not block but return 0 bytes. */ + received = xMessageBufferReceiveFromISR( xMessageBuffer, message, TEST_MESSAGE_BUFFER_SIZE, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 0, received ); + + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( senderTask ); + xTaskGenericNotifyWait_StubWithCallback( messageBufferReceiveFromISRCallback ); + xTaskGenericNotifyFromISR_IgnoreAndReturn( pdTRUE ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + + /* Sending max message size so that message buffer is full. */ + sent = xMessageBufferSend( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, sent ); + TEST_ASSERT_EQUAL( 0, xMessageBufferSpacesAvailable( xMessageBuffer ) ); + + /* Receiving using a smaller buffer size from ISR should not block and return 0 bytes. */ + received = xMessageBufferReceiveFromISR( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE - 1, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 0, received ); + + /* Sending max message size again should block, but task should be woken up after receiving message from ISR. */ + sent = xMessageBufferSend( xMessageBuffer, message, TEST_MAX_MESSAGE_SIZE, TEST_MESSAGE_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_MAX_MESSAGE_SIZE, sent ); + + vStreamBufferDelete( xMessageBuffer ); +} diff --git a/FreeRTOS/Test/CMock/stream_buffer/Makefile b/FreeRTOS/Test/CMock/stream_buffer/Makefile new file mode 100644 index 0000000000..63fea62633 --- /dev/null +++ b/FreeRTOS/Test/CMock/stream_buffer/Makefile @@ -0,0 +1,43 @@ +# indent with spaces +.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX) + +# Do not move this line below the include +MAKEFILE_ABSPATH := $(abspath $(lastword $(MAKEFILE_LIST))) +include ../makefile.in + +# PROJECT_SRC lists the .c files under test +PROJECT_SRC := stream_buffer.c + +# PROJECT_DEPS_SRC list the .c file that are dependencies of PROJECT_SRC files +# Files in PROJECT_DEPS_SRC are excluded from coverage measurements +PROJECT_DEPS_SRC := + +# PROJECT_HEADER_DEPS: headers that should be excluded from coverage measurements. +PROJECT_HEADER_DEPS := FreeRTOS.h + +# SUITE_UT_SRC: .c files that contain test cases (must end in _utest.c) +SUITE_UT_SRC := stream_buffer_utest.c + +# SUITE_SUPPORT_SRC: .c files used for testing that do not contain test cases. +# Paths are relative to PROJECT_DIR +SUITE_SUPPORT_SRC := + +# List the headers used by PROJECT_SRC that you would like to mock +MOCK_FILES_FP := $(KERNEL_DIR)/include/task.h +MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_assert.h +MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_port.h + +# List any addiitonal flags needed by the preprocessor +CPPFLAGS += -DportUSING_MPU_WRAPPERS=0 + +# List any addiitonal flags needed by the compiler +CFLAGS += + +# Try not to edit beyond this line unless necessary. + +# Project is determined based on path: $(UT_ROOT_DIR)/$(PROJECT) +PROJECT := $(lastword $(subst /, ,$(dir $(abspath $(MAKEFILE_ABSPATH))))) + +export + +include ../testdir.mk diff --git a/FreeRTOS/Test/CMock/stream_buffer/stream_buffer.yml b/FreeRTOS/Test/CMock/stream_buffer/stream_buffer.yml new file mode 100644 index 0000000000..de9ad9be42 --- /dev/null +++ b/FreeRTOS/Test/CMock/stream_buffer/stream_buffer.yml @@ -0,0 +1,32 @@ +:cmock: + :mock_prefix: mock_ + :when_no_prototypes: :warn + :treat_externs: :include + :enforce_strict_ordering: TRUE + :plugins: + - :ignore + - :ignore_arg + - :expect_any_args + - :array + - :callback + - :return_thru_ptr + :callback_include_count: true # include a count arg when calling the callback + :callback_after_arg_check: false # check arguments before calling the callback + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + :includes: # This will add these includes to each mock. + - + - "FreeRTOS.h" + :treat_externs: :exclude # Now the extern-ed functions will be mocked. + :weak: __attribute__((weak)) + :verbosity: 3 + :attributes: + - PRIVILEGED_FUNCTION + :strippables: + - PRIVILEGED_FUNCTION + - portDONT_DISCARD + :treat_externs: :include diff --git a/FreeRTOS/Test/CMock/stream_buffer/stream_buffer_utest.c b/FreeRTOS/Test/CMock/stream_buffer/stream_buffer_utest.c new file mode 100644 index 0000000000..e0dda508c8 --- /dev/null +++ b/FreeRTOS/Test/CMock/stream_buffer/stream_buffer_utest.c @@ -0,0 +1,1407 @@ +/* + * FreeRTOS V202012.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 stream_buffer_utest.c */ + +/* C runtime includes. */ +#include +#include + +/* Queue includes */ +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "stream_buffer.h" + +/* Test includes. */ +#include "unity.h" +#include "unity_memory.h" + +/* Mock includes. */ +#include "mock_task.h" +#include "mock_fake_assert.h" +#include "mock_fake_port.h" + +/** + * @brief Sample size in bytes of the stream buffer used for test. + * The size is kept short enough so that the buffer can be allocated on stack. + */ +#define TEST_STREAM_BUFFER_SIZE ( 64U ) + +/** + * @brief Sample trigger level in bytes used for stream buffer tests. + * When a receiver task is blocked waiting for data, trigger level determines how much bytes should + * be available before which receiver task can be unblocked. + */ +#define TEST_STREAM_BUFFER_TRIGGER_LEVEL ( 32U ) + +/** + * @brief Maximum unsigned long value that can be passed as a stream buffer size so as to + * trigger an integer overflow. + */ +#define TEST_STREAM_BUFFER_MAX_UINT_SIZE ( ~( size_t ) ( 0UL ) ) + +/** + * @brief A value used to test setting and getting stream buffer number. + */ +#define TEST_STREAM_BUFFER_NUMBER ( 0xFFU ) + +/** + * @brief Wait ticks passed into from tests if the stream buffer is full while sending data or + * empty while receiveing data. + */ +#define TEST_STREAM_BUFFER_WAIT_TICKS ( 1000U ) + +/* ============================ GLOBAL VARIABLES =========================== */ + +/** + * @brief Global counter for the number of assertions in code. + */ +static int assertionFailed = 0; + +/** + * @brief Global counter to keep track of how many times a sender task was woken up by a task receiving from the stream buffer. + */ +static int senderTaskWoken = 0; + +/** + * @brief Global counter to keep track of how many times a receiver task was woken up by a task sending to the buffer. + */ +static int receiverTaskWoken = 0; + +/** + * @brief Dummy sender task handle to which the stream buffer receive APIs will send notification. + */ +static TaskHandle_t senderTask = ( TaskHandle_t ) ( 0xAABBCCDD ); + +/** + * @brief Dummy receiver task handle to which the stream buffer send APIs will send notifications. + */ +static TaskHandle_t receiverTask = ( TaskHandle_t ) ( 0xABCDEEFF ); + +/** + * @brief Global Stream buffer handle used for tests. + */ +static StreamBufferHandle_t xStreamBuffer; + +/** + * @brief Flag which denotes if test need to abort on assertion. + */ +static BaseType_t shouldAbortOnAssertion; + +/** + * @brief Variable used to record the total dynamic size allocated in a test. + */ +static size_t dynamicMemoryAllocated = 0; + +/* ========================== CALLBACK FUNCTIONS =========================== */ + +void * pvPortMalloc( size_t xSize ) +{ + dynamicMemoryAllocated += xSize; + return unity_malloc( xSize ); +} +void vPortFree( void * pv ) +{ + return unity_free( pv ); +} + +static void vFakeAssertStub( bool x, + char * file, + int line, + int cmock_num_calls ) +{ + if( !x ) + { + assertionFailed++; + + if( shouldAbortOnAssertion == pdTRUE ) + { + TEST_ABORT(); + } + } +} + +static BaseType_t streamBufferReceiveCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t dataReceived = 0; + + /* Receive enough bytes (full size) from stream buffer to wake up sender task. */ + dataReceived = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, 0 ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, dataReceived ); + return pdTRUE; +} + +static BaseType_t streamBufferReceiveFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t dataReceived = 0; + BaseType_t senderTaskWokenFromISR = pdFALSE; + + /* Receive enough bytes (full size) from stream buffer to wake up sender task. */ + dataReceived = xStreamBufferReceiveFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, &senderTaskWokenFromISR ); + TEST_ASSERT_EQUAL( pdTRUE, senderTaskWokenFromISR ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, dataReceived ); + return pdTRUE; +} + +static BaseType_t resetWhenBlockedCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + BaseType_t status; + + /* Reset when the task is blocked on stream buffer, should fail and return pdFAIL. */ + status = xStreamBufferReset( xStreamBuffer ); + TEST_ASSERT_EQUAL( pdFAIL, status ); + return pdTRUE; +} + +static BaseType_t sendCompletedFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + BaseType_t status = pdFALSE, highPriorityTaskWoken = pdFALSE; + + /* Executing the send completed API from ISR should unblock a high priority task waiting to receive from stream buffer. */ + status = xStreamBufferSendCompletedFromISR( xStreamBuffer, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( pdTRUE, status ); + TEST_ASSERT_EQUAL( pdTRUE, highPriorityTaskWoken ); + return pdTRUE; +} + +static BaseType_t receiveCompletedFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + BaseType_t status = pdFALSE, highPriorityTaskWoken = pdFALSE; + + /* Executing the receive completed API from ISR should unblock a high priority task waiting to send to stream buffer. */ + status = xStreamBufferReceiveCompletedFromISR( xStreamBuffer, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( pdTRUE, status ); + TEST_ASSERT_EQUAL( pdTRUE, highPriorityTaskWoken ); + return pdTRUE; +} + +static BaseType_t senderTaskNotificationCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( senderTask, xTaskToNotify ); + senderTaskWoken++; + return pdTRUE; +} + +static BaseType_t senderTaskNotificationFromISRCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( senderTask, xTaskToNotify ); + senderTaskWoken++; + *pxHigherPriorityTaskWoken = pdTRUE; + + return pdTRUE; +} + +static BaseType_t streamBufferSendCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_TRIGGER_LEVEL ] = { 0 }; + size_t dataSent = 0; + + /* Send enough (trigger level) bytes to stream buffer to wake up the receiver Task. */ + dataSent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_TRIGGER_LEVEL, 0 ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL, dataSent ); + return pdTRUE; +} + +static BaseType_t streamBufferSendFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_TRIGGER_LEVEL ] = { 0 }; + size_t dataSent = 0; + BaseType_t receiverTaskWokenFromISR = pdFALSE; + + /* Send enough (trigger level) bytes to stream buffer to wake up the receiver Task. */ + dataSent = xStreamBufferSendFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_TRIGGER_LEVEL, &receiverTaskWokenFromISR ); + TEST_ASSERT_EQUAL( pdTRUE, receiverTaskWokenFromISR ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL, dataSent ); + return pdTRUE; +} + +static BaseType_t sendLessThanTriggerLevelBytesCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_TRIGGER_LEVEL ] = { 0 }; + size_t dataSent = 0; + + /* Sending less than trigger level bytes should not wake up the receiver Task. */ + dataSent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_TRIGGER_LEVEL - 1, 0 ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL - 1, dataSent ); + return pdTRUE; +} + +static BaseType_t sendLessThanTriggerLevelBytesFromISRCallback( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait, + int cmock_num_calls ) +{ + uint8_t data[ TEST_STREAM_BUFFER_TRIGGER_LEVEL ] = { 0 }; + size_t dataSent = 0; + BaseType_t receiverTaskWokenFromISR = pdFALSE; + + /* Sending less than trigger level bytes should not wake up the receiver Task. */ + dataSent = xStreamBufferSendFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_TRIGGER_LEVEL - 1, &receiverTaskWokenFromISR ); + TEST_ASSERT_EQUAL( pdFALSE, receiverTaskWokenFromISR ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL - 1, dataSent ); + return pdTRUE; +} + +static BaseType_t receiverTaskNotificationFromISRCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( receiverTask, xTaskToNotify ); + receiverTaskWoken++; + *pxHigherPriorityTaskWoken = pdTRUE; + + return pdTRUE; +} + +static BaseType_t receiverTaskNotificationCallback( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + int cmock_num_calls ) +{ + TEST_ASSERT_EQUAL( receiverTask, xTaskToNotify ); + receiverTaskWoken++; + return pdTRUE; +} + +/******************************************************************************* + * Unity fixtures + ******************************************************************************/ +void setUp( void ) +{ + assertionFailed = 0; + xStreamBuffer = NULL; + senderTaskWoken = 0; + receiverTaskWoken = 0; + shouldAbortOnAssertion = pdTRUE; + dynamicMemoryAllocated = 0; + + + mock_task_Init(); + mock_fake_assert_Init(); + mock_fake_port_Init(); + + vFakePortEnterCriticalSection_Ignore(); + vFakePortExitCriticalSection_Ignore(); + ulFakePortSetInterruptMaskFromISR_IgnoreAndReturn( 0U ); + vFakePortClearInterruptMaskFromISR_Ignore(); + vFakeAssert_StubWithCallback( vFakeAssertStub ); + /* Track calls to malloc / free */ + UnityMalloc_StartTest(); +} + +/*! called before each testcase */ +void tearDown( void ) +{ + TEST_ASSERT_EQUAL_MESSAGE( 0, assertionFailed, "Assertion check failed in code." ); + UnityMalloc_EndTest(); + mock_task_Verify(); + mock_task_Destroy(); + mock_fake_assert_Verify(); + mock_fake_assert_Destroy(); + mock_fake_port_Verify(); + mock_fake_port_Destroy(); +} + +/*! called at the beginning of the whole suite */ +void suiteSetUp() +{ +} + +/*! called at the end of the whole suite */ +int suiteTearDown( int numFailures ) +{ + return numFailures; +} + +static void validate_stream_buffer_init_state( StreamBufferHandle_t xStreamBuffer, + size_t bufferSize ) +{ + TEST_ASSERT_TRUE( xStreamBufferIsEmpty( xStreamBuffer ) ); + TEST_ASSERT_FALSE( xStreamBufferIsFull( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( bufferSize, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 0U, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 0U, xStreamBufferNextMessageLengthBytes( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 0, ucStreamBufferGetStreamBufferType( xStreamBuffer ) ); +} + +static void validate_and_clear_assertions( void ) +{ + TEST_ASSERT_EQUAL( 1, assertionFailed ); + assertionFailed = 0; +} + +/* ============================== Test Cases ============================== */ + +/** + * @brief Validates that stream buffer of sample size is created succesfully. + */ +void test_xStreamBufferCreate_success( void ) +{ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_EQUAL( NULL, xStreamBuffer ); + validate_stream_buffer_init_state( xStreamBuffer, TEST_STREAM_BUFFER_SIZE ); + + /* Verify internal memory allocated is equal to size of the struct + buffer size + 1. */ + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE + 1U + sizeof( StaticStreamBuffer_t ), dynamicMemoryAllocated ); + + /* Set a stream buffer number and get it. */ + vStreamBufferSetStreamBufferNumber( xStreamBuffer, TEST_STREAM_BUFFER_NUMBER ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_NUMBER, uxStreamBufferGetStreamBufferNumber( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * Returns NULL if there is an integer overflow in the buffer size. + */ +void test_xStreamBufferCreate_integer_overflow( void ) +{ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_MAX_UINT_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_EQUAL( NULL, xStreamBuffer ); +} + +/** + * @brief Returns NULL if internal memory allocation of the stream buffer fails. + */ +void test_xStreamBufferCreate_malloc_fail( void ) +{ + UnityMalloc_MakeMallocFailAfterCount( 0 ); + + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_EQUAL( NULL, xStreamBuffer ); +} + +/** + * @brief Assertion fails if a zero buffer size is passed as the parameter. + */ +void test_xStreamBufferCreate_zero_buffer_size( void ) +{ + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferCreate( 0, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Assertion fails if trigger level is greater than the stream buffer size. + */ +void test_xStreamBufferCreate_invalid_trigger_level( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, ( TEST_STREAM_BUFFER_SIZE + 1 ) ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Assertion fails while trying to delete a null stream buffer. + */ +void test_xStreamBufferDelete_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + vStreamBufferDelete( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Validates stream buffer create using a static buffer is successful. + */ +void test_xStreamBufferCreateStatic_success( void ) +{ + StaticStreamBuffer_t streamBufferStruct; + + /* The size of stream buffer array should be one greater than the required size of stream buffer. */ + uint8_t streamBufferArray[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + + xStreamBuffer = xStreamBufferCreateStatic( sizeof( streamBufferArray ), TEST_STREAM_BUFFER_TRIGGER_LEVEL, streamBufferArray, &streamBufferStruct ); + + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + validate_stream_buffer_init_state( xStreamBuffer, TEST_STREAM_BUFFER_SIZE ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates stream buffer static create fails if NULL array is passed. + */ +void test_xStreamBufferCreateStatic_null_array( void ) +{ + StaticStreamBuffer_t streamBufferStruct; + + /* The size of stream buffer array should be one greater than the required size of stream buffer. */ + uint8_t streamBufferArray[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + + /* Tests should abort if assertion is enabled or return NULL. */ + shouldAbortOnAssertion = pdFALSE; + + /* Returns NULL if a NULL pointer is passed as the stream buffer storage area. */ + xStreamBuffer = xStreamBufferCreateStatic( sizeof( streamBufferArray ), TEST_STREAM_BUFFER_TRIGGER_LEVEL, NULL, &streamBufferStruct ); + TEST_ASSERT_NULL( xStreamBuffer ); + validate_and_clear_assertions(); +} + +/** + * @brief Validates stream buffer static create fails if NULL struct is passed. + */ +void test_xStreamBufferCreateStatic_invalid_null_struct( void ) +{ + /* The size of stream buffer array should be one greater than the required size of stream buffer. */ + uint8_t streamBufferArray[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + + /* Tests should abort if assertion is enabled or return NULL. */ + shouldAbortOnAssertion = pdFALSE; + + /* Returns NULL if a NULL struct is passed as a parameter. */ + xStreamBuffer = xStreamBufferCreateStatic( sizeof( streamBufferArray ), TEST_STREAM_BUFFER_TRIGGER_LEVEL, streamBufferArray, NULL ); + TEST_ASSERT_NULL( xStreamBuffer ); + validate_and_clear_assertions(); +} + +/** + * @brief Validates stream buffer static create fails on passing invalid trigger level + */ +void test_xStreamBufferCreateStatic_invalid_trigger_level( void ) +{ + StaticStreamBuffer_t streamBufferStruct; + /* The size of stream buffer array should be one greater than the required size of stream buffer. */ + uint8_t streamBufferArray[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferCreateStatic( sizeof( streamBufferArray ), TEST_STREAM_BUFFER_SIZE + 2, streamBufferArray, &streamBufferStruct ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Validates a task is able to send upto buffer space available without blocking. + */ +void test_xStreamBufferSend_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t sent = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending bytes of size upto to available size should succeed without blocking. */ + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( pdFALSE, xStreamBufferIsEmpty( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that sending data more than stream buffer size will cap it to the size of stream buffer without blocking. + */ +void test_xStreamBufferSend_more_than_buffer_size( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + size_t sent = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending bytes more than stream buffer size caps its to the size of stream buffer. */ + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE + 1, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Sending zero bytes to stream buffer should fail assertion. + */ +void test_xStreamBufferSend_zero_bytes( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE + 1 ] = { 0 }; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSend( xStreamBuffer, data, 0U, TEST_STREAM_BUFFER_WAIT_TICKS ); + } + validate_and_clear_assertions(); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates cases where stream buffer has insufficient space to send the data and sender has to block. + */ +void test_xStreamBufferSend_blocking( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t sent = 0; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( senderTask ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending upto size of stream buffer should not block. */ + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE - 1, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1, sent ); + TEST_ASSERT_EQUAL( 1, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* + * Sending beyond the stream buffer size should make task wait for upto TEST_STREAM_BUFFER_WAIT_TICKS. After the timeout + * elapses, task should return with partial bytes sent. + */ + xTaskGenericNotifyWait_ExpectAndReturn( 0, 0, 0, NULL, TEST_STREAM_BUFFER_WAIT_TICKS, pdTRUE ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 1, sent ); + TEST_ASSERT_EQUAL( pdTRUE, xStreamBufferIsFull( xStreamBuffer ) ); + + /* + * A task trying to send to a stream buffer without any space available should block for upto TEST_STREAM_BUFFER_WAIT_TICKS. + * Sender task should be notified and woken up when bytes are consumed by a receiver task during the wait period. + */ + xTaskGenericNotifyWait_StubWithCallback( streamBufferReceiveCallback ); + xTaskGenericNotify_StubWithCallback( senderTaskNotificationCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 1, senderTaskWoken ); + + /* + * A task trying to send to a stream buffer without any space available should block for upto TEST_STREAM_BUFFER_WAIT_TICKS. + * Sender task should be notified and woken up when bytes are consumed by an ISR during the wait period. + */ + xTaskGenericNotifyWait_StubWithCallback( streamBufferReceiveFromISRCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( senderTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 2, senderTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that stream buffer does not block if zero wait time is passed. + */ +void test_xStreamBufferSend_zero_wait_ticks( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t dataSent = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending data of upto stream buffer size should not block. */ + dataSent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, dataSent ); + TEST_ASSERT_EQUAL( 0, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending data beyond stream buffer size but with zero wait ticks should not block and return zero bytes sent. */ + dataSent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, 0 ); + TEST_ASSERT_EQUAL( 0, dataSent ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that a task is able to receive from a non empty stream buffer without blocking. + */ +void test_xStreamBufferReceive_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + uint8_t dataReceived[ TEST_STREAM_BUFFER_SIZE ] = { 0x00 }; + size_t sentBytes = 0, receivedBytes = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Send TEST_STREAM_BUFFER_SIZE data to the stream buffer. */ + sentBytes = xStreamBufferSend( xStreamBuffer, &data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sentBytes ); + + /* Receive the partial data from stream Buffer without blocking. */ + receivedBytes = xStreamBufferReceive( xStreamBuffer, &dataReceived, TEST_STREAM_BUFFER_SIZE - 1, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1, receivedBytes ); + TEST_ASSERT_EQUAL_HEX8_ARRAY( data, dataReceived, receivedBytes ); + TEST_ASSERT_EQUAL( 1, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* Request for full TEST_STREAM_BUFFER_SIZE bytes, but only receive what's available without blocking. */ + receivedBytes = xStreamBufferReceive( xStreamBuffer, &dataReceived, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 1, receivedBytes ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates receiving from an empty stream buffer will block untill atleast trigger level bytes are + * sent to the buffer. + */ +void test_xStreamBufferReceive_blocking( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t receivedBytes = 0; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( receiverTask ); + xTaskGenericNotify_StubWithCallback( receiverTaskNotificationCallback ); + + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* + * Receiving from an empty buffer causes the task to wait for TEST_STREAM_BUFFER_WAIT_TICKS period. + * After the timeout elapses, task returns with zero bytes received. + */ + xTaskGenericNotifyWait_ExpectAndReturn( 0, 0, 0, NULL, TEST_STREAM_BUFFER_WAIT_TICKS, pdTRUE ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, receivedBytes ); + + /* + * Sending atleast trigger level bytes, should notify and wake up the receiver task. + */ + xTaskGenericNotifyWait_StubWithCallback( streamBufferSendCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL, receivedBytes ); + TEST_ASSERT_EQUAL( 1, receiverTaskWoken ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* + * Sending atleast trigger level bytes from ISR, should notify and wake up the receiver task. + */ + xTaskGenericNotifyWait_StubWithCallback( streamBufferSendFromISRCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( receiverTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdFALSE ); + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_TRIGGER_LEVEL, receivedBytes ); + TEST_ASSERT_EQUAL( 2, receiverTaskWoken ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + + /* Sending less than trigger level bytes should not wake up the task. */ + xTaskGenericNotifyWait_StubWithCallback( sendLessThanTriggerLevelBytesCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( receiverTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 2, receiverTaskWoken ); + + /* Sending less than trigger level bytes from ISR should not wake up the task. */ + xTaskGenericNotifyWait_StubWithCallback( sendLessThanTriggerLevelBytesFromISRCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( receiverTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 2, receiverTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that receiver task does not block if zero wait ticks are passed. + */ +void test_xStreamBufferReceive_zero_wait_ticks( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t receivedBytes = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Task does not block on an empty stream buffer if zero wait ticks are passed. */ + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, 0 ); + TEST_ASSERT_EQUAL( 0, receivedBytes ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that an interrupt service routine is able to read data from stream + * buffer without blocking. + */ +void test_xStreamBufferReceiveFromISR_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + uint8_t dataReceived[ TEST_STREAM_BUFFER_SIZE ] = { 0x00 }; + size_t receivedBytes = 0, sentBytes = 0; + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Send data of atmost capacity to the stream buffer. */ + sentBytes = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sentBytes ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* Receive partial data from stream buffer without blocking. */ + receivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer, &dataReceived, ( TEST_STREAM_BUFFER_SIZE - 1U ), &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( ( TEST_STREAM_BUFFER_SIZE - 1U ), receivedBytes ); + TEST_ASSERT_EQUAL_HEX8_ARRAY( data, dataReceived, receivedBytes ); + TEST_ASSERT_EQUAL( pdFALSE, xHighPriorityTaskWoken ); + + /* Try to receive full capacity from stream buffer but only remaining data is received. */ + receivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer, &dataReceived, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 1U, receivedBytes ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Assertion fails if a null stream buffer is passed. + */ +void test_xStreamBufferReceiveFromISR_null_stream_buffer( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferReceiveFromISR( NULL, data, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Assertion fails if a null message is passed. + */ +void test_xStreamBufferReceiveFromISR_null_buffer( void ) +{ + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferReceiveFromISR( xStreamBuffer, NULL, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + } + + validate_and_clear_assertions(); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that an interrupt service routine is able to send data upto the size of stream buffer without blocking. + */ +void test_xStreamBufferSendFromISR_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t sentBytes = 0; + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Send data to an empty buffer should succeed without blocking. */ + sentBytes = xStreamBufferSendFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE - 1U, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1U, sentBytes ); + TEST_ASSERT_EQUAL( 1U, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( pdFALSE, xHighPriorityTaskWoken ); + + /* Send full capacity from ISR again should send partial bytes without blocking. */ + sentBytes = xStreamBufferSendFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 1U, sentBytes ); + TEST_ASSERT_EQUAL( 0, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( pdFALSE, xHighPriorityTaskWoken ); + + /* Send to full stream buffer should return 0 bytes sent without blocking. */ + sentBytes = xStreamBufferSendFromISR( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + TEST_ASSERT_EQUAL( 0U, sentBytes ); + TEST_ASSERT_EQUAL( pdFALSE, xHighPriorityTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Assertion fails if a null stream buffer is passed. + */ +void test_xStreamBufferSendFromISR_null_stream_buffer( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSendFromISR( NULL, data, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + } + + validate_and_clear_assertions(); +} + +/** + * @brief Assertion fails if a null message is passed. + */ +void test_xStreamBufferSendFromISR_null_message( void ) +{ + BaseType_t xHighPriorityTaskWoken = pdFALSE; + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSendFromISR( xStreamBuffer, NULL, TEST_STREAM_BUFFER_SIZE, &xHighPriorityTaskWoken ); + } + + validate_and_clear_assertions(); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates user is able to reset the stream buffer back to empty state. + */ +void test_xStreamBufferReset_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0 }; + size_t dataSent = 0; + BaseType_t status = pdFALSE; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + /* Validate stream buffer is empty initially. */ + validate_stream_buffer_init_state( xStreamBuffer, TEST_STREAM_BUFFER_SIZE ); + + /* Send full capacity to stream buffer. */ + dataSent = xStreamBufferSend( xStreamBuffer, data, sizeof( data ), TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, dataSent ); + + /* Verify that all bytes are available. */ + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* Reset the stream buffer back to empty state. */ + status = xStreamBufferReset( xStreamBuffer ); + TEST_ASSERT_EQUAL( pdTRUE, status ); + + /* Validate that stream buffer is empty. */ + validate_stream_buffer_init_state( xStreamBuffer, TEST_STREAM_BUFFER_SIZE ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Resetting a null stream buffer should fail assertion. + */ +void test_xStreamBufferReset_null_stream_buffer( void ) +{ + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferReset( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Validates that stream buffer reset fails if a sender or receiver task is blocked on it. + */ +void test_xStreamBufferReset_while_blocked( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t sentBytes = 0, receivedBytes = 0; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( senderTask ); + xTaskGenericNotifyWait_StubWithCallback( resetWhenBlockedCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* + * Perform a blocking operation to receive from an empty stream buffer. Reset stream buffer within receiver task notify + * wait callback should fail. + */ + receivedBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, receivedBytes ); + + /* + * Send full size data to stream buffer. + */ + sentBytes = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sentBytes ); + TEST_ASSERT_EQUAL( pdTRUE, xStreamBufferIsFull( xStreamBuffer ) ); + + /* + * Sending data to full stream buffer causes task to be blocked. Reset stream buffer within sender task notify + * wait callback should fail. + */ + sentBytes = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, sentBytes ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates that a receiver task is able to receive data after lowering the stream buffer trigger level. + */ +void test_xStreamBufferSetTrigerLevel_success( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + BaseType_t status; + + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( receiverTask ); + xTaskGenericNotify_StubWithCallback( receiverTaskNotificationCallback ); + xTaskGenericNotifyWait_StubWithCallback( streamBufferSendCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create stream buffer with trigger level equal to maximum stream buffer size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_SIZE ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + /* Set the trigger level to TEST_STREAM_BUFFER_TRIGGER_LEVEL. */ + status = xStreamBufferSetTriggerLevel( xStreamBuffer, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_EQUAL( pdTRUE, status ); + + /* + * Receive data from empty stream buffer, with trigger level bytes send from the callback. This should unblock + * the task. + */ + ( void ) xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 1, receiverTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validate setting trigger level with invalid parameters fails. + */ +void test_xStreamBufferSetTrigerLevel_larger_than_buffer_size( void ) +{ + BaseType_t status; + + /* Create stream buffer. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + /* Set the trigger level to ( TEST_STREAM_BUFFER_SIZE + 1) should fail. */ + status = xStreamBufferSetTriggerLevel( xStreamBuffer, ( TEST_STREAM_BUFFER_SIZE + 1 ) ); + TEST_ASSERT_EQUAL( pdFALSE, status ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Set the trigger level to 0 should pass but internally set trigger level to 1. + */ +void test_xStreamBufferSetTrigerLevel_zero( void ) +{ + BaseType_t status; + + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + status = xStreamBufferSetTriggerLevel( xStreamBuffer, 0 ); + TEST_ASSERT_EQUAL( pdTRUE, status ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Setting trigger level for a null stream buffer should fail assertion. + */ +void test_xStreamBufferSetTriggerLevel_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSetTriggerLevel( NULL, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Checking bytes available for a null stream buffer should fail assertion. + */ +void test_xStreamBufferBytesAvailable_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferBytesAvailable( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Checking if strem buffer is full for a null stream buffer should fail assertion. + */ +void test_xStreamBufferIsFull_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferIsFull( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Checking if strem buffer is empty for a null stream buffer should fail assertion. + */ +void test_xStreamBufferIsEmpty_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferIsEmpty( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Checking buffer size for a null stream buffer should fail assertion. + */ +void test_xStreamBufferSpacesAvailable_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSpacesAvailable( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Checking Next message length available for a null stream buffer fails + * assertion. + */ +void test_xStreamBufferNextMessageLengthBytes_null_stream_buffer( void ) +{ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferNextMessageLengthBytes( NULL ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Validates xStreamBufferSendCompletedFromISR function unblocks a receive task. + */ +void test_xStreamBufferSendCompletedFromISR_success( void ) +{ + BaseType_t status = pdFALSE; + BaseType_t highPriorityTaskWoken = pdFALSE; + uint8_t data[ TEST_STREAM_BUFFER_SIZE ]; + size_t receiveBytes = 0; + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + /* Send completed from ISR without receiver task waiting. */ + status = xStreamBufferSendCompletedFromISR( xStreamBuffer, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( pdFALSE, status ); + TEST_ASSERT_EQUAL( pdFALSE, highPriorityTaskWoken ); + + /* Send completed from ISR with receiver task waiting. */ + vTaskSetTimeOutState_Ignore(); + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( receiverTask ); + xTaskGenericNotifyWait_StubWithCallback( sendCompletedFromISRCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( receiverTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + receiveBytes = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, receiveBytes ); + TEST_ASSERT_EQUAL( 1, receiverTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates xStreamBufferSendCompletedFromISR fails assertion if a null stream buffer is passed. + */ +void test_xStreamBufferSendCompletedFromISR_null_stream_buffer( void ) +{ + BaseType_t highPriorityTaskWoken = pdFALSE; + + /* Send completed from ISR without receiver task waiting. */ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferSendCompletedFromISR( NULL, &highPriorityTaskWoken ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Validates xStreamBufferReceiveCompletedFromISR unblocks a sender task. + */ +void test_xStreamBufferReceiveCompletedFromISR_success( void ) +{ + BaseType_t status = pdFALSE; + BaseType_t highPriorityTaskWoken = pdFALSE; + + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t sentBytes = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + + /* Receive completed from ISR without a sender task waiting. */ + status = xStreamBufferReceiveCompletedFromISR( xStreamBuffer, &highPriorityTaskWoken ); + TEST_ASSERT_EQUAL( pdFALSE, status ); + TEST_ASSERT_EQUAL( pdFALSE, highPriorityTaskWoken ); + + + /* Send full capacity to a stream buffer so that sender task blocks. */ + sentBytes = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sentBytes ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + + /* Receive completed from ISR when a sender task is waiting. */ + xTaskGenericNotifyStateClear_IgnoreAndReturn( pdTRUE ); + xTaskGetCurrentTaskHandle_IgnoreAndReturn( senderTask ); + xTaskGenericNotifyWait_StubWithCallback( receiveCompletedFromISRCallback ); + xTaskGenericNotifyFromISR_StubWithCallback( senderTaskNotificationFromISRCallback ); + xTaskCheckForTimeOut_IgnoreAndReturn( pdTRUE ); + sentBytes = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( 0, sentBytes ); + TEST_ASSERT_EQUAL( 1, senderTaskWoken ); + + vStreamBufferDelete( xStreamBuffer ); +} + +/** + * @brief Validates xStreamBufferReceiveCompletedFromISR fails assertion if a null stream buffer is passed. + */ +void test_xStreamBufferReceiveCompletedFromISR_null_stream_buffer( void ) +{ + BaseType_t highPriorityTaskWoken = pdFALSE; + + /* Send completed from ISR without receiver task waiting. */ + if( TEST_PROTECT() ) + { + ( void ) xStreamBufferReceiveCompletedFromISR( NULL, &highPriorityTaskWoken ); + } + validate_and_clear_assertions(); +} + +/** + * @brief Validates scenario where stream buffer head and tail pointer wraps over. + */ +void test_xStreamBufferSend_WrapOver( void ) +{ + uint8_t data[ TEST_STREAM_BUFFER_SIZE ] = { 0xAA }; + size_t sent = 0, received = 0; + + vTaskSetTimeOutState_Ignore(); + vTaskSuspendAll_Ignore(); + xTaskResumeAll_IgnoreAndReturn( pdTRUE ); + + /* Create a stream buffer of the default test sample size. */ + xStreamBuffer = xStreamBufferCreate( TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_TRIGGER_LEVEL ); + TEST_ASSERT_NOT_NULL( xStreamBuffer ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Sending bytes upto max stream buffer size. */ + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 0, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Read upto max buffer size - 1 */ + received = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE - 1, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1, received ); + TEST_ASSERT_EQUAL( 1, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + /* Send upto max buffer size - 1 so that head wraps over. */ + sent = xStreamBufferSend( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE - 1, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE - 1, sent ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( 0U, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + + /* Read all bytes and verify that tail wraps over as well. */ + received = xStreamBufferReceive( xStreamBuffer, data, TEST_STREAM_BUFFER_SIZE, TEST_STREAM_BUFFER_WAIT_TICKS ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, received ); + TEST_ASSERT_EQUAL( 0, xStreamBufferBytesAvailable( xStreamBuffer ) ); + TEST_ASSERT_EQUAL( TEST_STREAM_BUFFER_SIZE, xStreamBufferSpacesAvailable( xStreamBuffer ) ); + + vStreamBufferDelete( xStreamBuffer ); +}