From ac0cbebb896ee7bc181204940af39acc76c4600e Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 28 Sep 2021 16:06:55 +0800 Subject: [PATCH] ringbuf: Fix bug where comparision between a signed and unsigned operand resulted in incorrect free size for no-split/allow-split buffers This commit fixes a bug in no-split and allow-split ring buffers free buffer size calculation. When the free size available in the buffers less than the size of one item header, the function prvGetCurMaxSizeNoSplit/AllowSplit() incorrectly returned the maxItemSize instead of 0. This is due to the comparision between a negative and a positive value where both operands are treated as unsigned during the comparision operation, thereby treating the negative operand as a large integer. Also added new unit tests to test buffer-full and almost-full conditions where this scenario is likely to be hit. Closes https://github.com/espressif/esp-idf/issues/7344 Closes https://github.com/espressif/esp-idf/pull/7371 --- components/esp_ringbuf/ringbuf.c | 65 +++- components/esp_ringbuf/test/test_ringbuf.c | 379 ++++++++++++++++++++- tools/ci/check_copyright_ignore.txt | 1 - 3 files changed, 418 insertions(+), 27 deletions(-) diff --git a/components/esp_ringbuf/ringbuf.c b/components/esp_ringbuf/ringbuf.c index c0a7f42147..d8d3cbe6ee 100644 --- a/components/esp_ringbuf/ringbuf.c +++ b/components/esp_ringbuf/ringbuf.c @@ -26,7 +26,7 @@ #define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite #define rbITEM_DUMMY_DATA_FLAG ( ( UBaseType_t ) 2 ) //Data from here to end of the ring buffer is dummy data. Restart reading at start of head of the buffer #define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around -#define rbITEM_WRITTEN_FLAG ( ( UBaseType_t ) 8 ) //Item has been written to by the application, thus it is free to be read +#define rbITEM_WRITTEN_FLAG ( ( UBaseType_t ) 8 ) //Item has been written to by the application, thus can be read //Static allocation related #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -135,16 +135,41 @@ static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xI //Checks if an item will currently fit in a byte buffer static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize); -//Copies an item to a no-split ring buffer. Only call this function after calling prvCheckItemFitsDefault() +/* +Copies an item to a no-split ring buffer +Entry: + - Must have already guaranteed there is sufficient space for item by calling prvCheckItemFitsDefault() +Exit: + - New item copied into ring buffer + - pucAcquire and pucWrite updated. + - Dummy item added if necessary +*/ static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); -//Copies an item to a allow-split ring buffer. Only call this function after calling prvCheckItemFitsDefault() +/* +Copies an item to a allow-split ring buffer +Entry: + - Must have already guaranteed there is sufficient space for item by calling prvCheckItemFitsDefault() +Exit: + - New item copied into ring buffer + - pucAcquire and pucWrite updated + - Item may be split +*/ static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); //Copies an item to a byte buffer. Only call this function after calling prvCheckItemFitsByteBuffer() static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); //Retrieve item from no-split/allow-split ring buffer. *pxIsSplit is set to pdTRUE if the retrieved item is split +/* +Entry: + - Must have already guaranteed that there is an item available for retrieval by calling prvCheckItemAvail() + - Guaranteed that pucREAD points to a valid item (i.e., not a dummy item) +Exit: + - Item is returned. Only first half returned if split + - pucREAD updated to point to next valid item to read, or equals to pucWrite if there are no more valid items to read + - pucREAD update must skip over dummy items +*/ static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, @@ -156,7 +181,12 @@ static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, size_t xMaxSize, size_t *pxItemSize); -//Return an item to a split/no-split ring buffer +/* +Return an item to a split/no-split ring buffer +Exit: + - Item is marked free rbITEM_FREE_FLAG + - pucFree is progressed as far as possible, skipping over already freed items or dummy items +*/ static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem); //Return data to a byte buffer @@ -384,7 +414,7 @@ static void prvSendItemDoneNoSplit(Ringbuffer_t *pxRingbuffer, uint8_t* pucItem) //Redundancy check to ensure write pointer has not overshot buffer bounds configASSERT(pxRingbuffer->pucWrite <= pxRingbuffer->pucHead + pxRingbuffer->xSize); } - //Check if pucAcquire requires wrap around + //Check if pucWrite requires wrap around if ((pxRingbuffer->pucTail - pxRingbuffer->pucWrite) < rbHEADER_SIZE) { pxRingbuffer->pucWrite = pxRingbuffer->pucHead; } @@ -525,7 +555,7 @@ static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, //Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail); } else { - //Exclusive of pucTali if length is larger than zero, pcReturn should never point to pucTail + //Exclusive of pucTail if length is larger than zero, pcReturn should never point to pucTail configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail); } *pxItemSize = pxHeader->xItemLen; //Get length of item @@ -615,7 +645,7 @@ static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem) //Redundancy check to ensure free pointer has not overshot buffer bounds configASSERT(pxRingbuffer->pucFree <= pxRingbuffer->pucHead + pxRingbuffer->xSize); } - //Check if pucRead requires wrap around + //Check if pucFree requires wrap around if ((pxRingbuffer->pucTail - pxRingbuffer->pucFree) < rbHEADER_SIZE) { pxRingbuffer->pucFree = pxRingbuffer->pucHead; } @@ -666,12 +696,15 @@ static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer) //No-split ring buffer items need space for a header xFreeSize -= rbHEADER_SIZE; - //Limit free size to be within bounds - if (xFreeSize > pxRingbuffer->xMaxItemSize) { - xFreeSize = pxRingbuffer->xMaxItemSize; - } else if (xFreeSize < 0) { + + //Check for xFreeSize < 0 before checking xFreeSize > pxRingbuffer->xMaxItemSize + //to avoid incorrect comparison operation when xFreeSize is negative + if (xFreeSize < 0) { //Occurs when free space is less than header size xFreeSize = 0; + } else if (xFreeSize > pxRingbuffer->xMaxItemSize) { + //Limit free size to be within bounds + xFreeSize = pxRingbuffer->xMaxItemSize; } return xFreeSize; } @@ -696,11 +729,13 @@ static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer) (rbHEADER_SIZE * 2); } - //Limit free size to be within bounds - if (xFreeSize > pxRingbuffer->xMaxItemSize) { - xFreeSize = pxRingbuffer->xMaxItemSize; - } else if (xFreeSize < 0) { + //Check for xFreeSize < 0 before checking xFreeSize > pxRingbuffer->xMaxItemSize + //to avoid incorrect comparison operation when xFreeSize is negative + if (xFreeSize < 0) { xFreeSize = 0; + } else if (xFreeSize > pxRingbuffer->xMaxItemSize) { + //Limit free size to be within bounds + xFreeSize = pxRingbuffer->xMaxItemSize; } return xFreeSize; } diff --git a/components/esp_ringbuf/test/test_ringbuf.c b/components/esp_ringbuf/test/test_ringbuf.c index 29554b13a2..46a39b2f66 100644 --- a/components/esp_ringbuf/test/test_ringbuf.c +++ b/components/esp_ringbuf/test/test_ringbuf.c @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + #include #include #include "freertos/FreeRTOS.h" @@ -17,7 +23,8 @@ #define NO_OF_RB_TYPES 3 #define ITEM_HDR_SIZE 8 #define SMALL_ITEM_SIZE 8 -#define LARGE_ITEM_SIZE (2 * SMALL_ITEM_SIZE) +#define MEDIUM_ITEM_SIZE ((3 * SMALL_ITEM_SIZE) >> 1) //12 bytes +#define LARGE_ITEM_SIZE (2 * SMALL_ITEM_SIZE) //16 bytes #define BUFFER_SIZE 160 //4Byte aligned size static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; @@ -37,6 +44,17 @@ static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item, si TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to send item"); } +static void send_item_and_check_failure(RingbufHandle_t handle, const uint8_t *item, size_t item_size, TickType_t ticks_to_wait, bool in_isr) +{ + BaseType_t ret; + if (in_isr) { + ret = xRingbufferSendFromISR(handle, (void *)item, item_size, NULL); + } else { + ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait); + } + TEST_ASSERT_MESSAGE(ret == pdFALSE, "Sent an item to a full buffer"); +} + static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr) { //Receive item from no-split buffer @@ -73,7 +91,6 @@ static void receive_check_and_return_item_allow_split(RingbufHandle_t handle, co } else { ret = xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait); } - //= xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait); TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive item"); TEST_ASSERT_MESSAGE(item1 != NULL, "Failed to receive item"); @@ -159,19 +176,37 @@ static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, co } /* ----------------- Basic ring buffer behavior tests cases -------------------- - * The following test cases will test basic send, receive, and wrap around - * behavior of each type of ring buffer. Each test case will do the following - * 1) Send multiple items (nearly fill the buffer) - * 2) Receive and check the sent items (also prepares the buffer for a wrap around - * 3) Send a final item that causes a wrap around - * 4) Receive and check the wrapped item + * The following set of test cases will test basic send, receive, wrap around and buffer full + * behavior of each type of ring buffer. + * Test Case 1: + * 1) Send multiple items (nearly fill the buffer) + * 2) Receive and check the sent items (also prepares the buffer for a wrap around) + * 3) Send a final item that causes a wrap around + * 4) Receive and check the wrapped item + * + * Test Case 2: + * 1) Send multiple items (completely fill the buffer) + * 2) Send another item and verify that send failure occurs + * 3) Receive one item and verify that space is freed up in the buffer + * 4) Receive and check the remaining sent items + * + * Test Case 3: + * 1) Send multiple items (nearly fill the buffer) + * 2) Send another small sized item to fill the buffer without causing a wrap around (not needed in case of byte buffer) + * 2) Send another item and verify that send failure occurs + * 4) Receive and check the sent items */ -TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]") +TEST_CASE("TC#1: No-Split", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT); TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE/2 - ITEM_HDR_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect max item size received"); + //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE); @@ -179,11 +214,21 @@ TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]") for (int i = 0; i < no_of_items; i++) { send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items, "Incorrect items waiting"); + //Test receiving items for (int i = 0; i < no_of_items; i++) { receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + //Write pointer should be near the end, test wrap around UBaseType_t write_pos_before, write_pos_after; vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL); @@ -198,11 +243,112 @@ TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]") vRingbufferDelete(buffer_handle); } -TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]") +TEST_CASE("TC#2: No-Split", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE/2 - ITEM_HDR_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect max item size received"); + + //Calculate number of items to send. Aim to fill the buffer + int no_of_items = (BUFFER_SIZE) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE); + + //Test sending items + for (int i = 0; i < no_of_items; i++) { + send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items, "Incorrect items waiting"); + + //At this point, the buffer should be full. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == 0, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Receive one item + receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //At this point, the buffer should not be full any more + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) > 0, "Buffer should have free space"); + + //Test receiving remaining items + for (int i = 0; i < no_of_items - 1; i++) { + receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + +TEST_CASE("TC#3: No-Split", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE/2 - ITEM_HDR_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == ((BUFFER_SIZE >> 1) - ITEM_HDR_SIZE), "Incorrect max item size received"); + + //Calculate number of medium items to send. Aim to almost fill the buffer + int no_of_medium_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + MEDIUM_ITEM_SIZE)) / (ITEM_HDR_SIZE + MEDIUM_ITEM_SIZE); + + //Test sending items + for (int i = 0; i < no_of_medium_items; i++) { + send_item_and_check(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of medium items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_medium_items, "Incorrect items waiting"); + + //Send one small sized item. This will ensure that the item fits at the end of the buffer without causing the write pointer to wrap around. + send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //The buffer should not have any free space as the number of bytes remaining should be < ITEM_HDR_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == 0, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Test receiving medium items + for (int i = 0; i < no_of_medium_items; i++) { + receive_check_and_return_item_no_split(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Test receiving small item + receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + +TEST_CASE("TC#1: Allow-Split", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT); TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE - (ITEM_HDR_SIZE * 2). + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect max item size received"); + //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE); @@ -210,11 +356,21 @@ TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]") for (int i = 0; i < no_of_items; i++) { send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items, "Incorrect items waiting"); + //Test receiving items for (int i = 0; i < no_of_items; i++) { receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + //Write pointer should be near the end, test wrap around UBaseType_t write_pos_before, write_pos_after; vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL); @@ -229,11 +385,112 @@ TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]") vRingbufferDelete(buffer_handle); } -TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]") +TEST_CASE("TC#2: Allow-Split", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE - (ITEM_HDR_SIZE * 2). + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect max item size received"); + + //Calculate number of items to send. Aim to fill the buffer + int no_of_items = (BUFFER_SIZE) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE); + + //Test sending items + for (int i = 0; i < no_of_items; i++) { + send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items, "Incorrect items waiting"); + + //At this point, the buffer should be full. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == 0, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Receive one item + receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //At this point, the buffer should not be full any more + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) > 0, "Buffer should have free space"); + + //Test receiving remaining items + for (int i = 0; i < no_of_items - 1; i++) { + receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + +TEST_CASE("TC#3: Allow-Split", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE - (ITEM_HDR_SIZE * 2). + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == (BUFFER_SIZE - (ITEM_HDR_SIZE * 2)), "Incorrect max item size received"); + + //Calculate number of medium items to send. Aim to almost fill the buffer + int no_of_medium_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + MEDIUM_ITEM_SIZE)) / (ITEM_HDR_SIZE + MEDIUM_ITEM_SIZE); + + //Test sending items + for (int i = 0; i < no_of_medium_items; i++) { + send_item_and_check(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of medium items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_medium_items, "Incorrect items waiting"); + + //Send one small sized item. This will ensure that the item fits at the end of the buffer without causing the write pointer to wrap around. + send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //The buffer should not have any free space as the number of bytes remaining should be < ITEM_HDR_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == 0, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Test receiving medium items + for (int i = 0; i < no_of_medium_items; i++) { + receive_check_and_return_item_allow_split(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Test receiving small item + receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + +TEST_CASE("TC#1: Byte buffer", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF); TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == BUFFER_SIZE, "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == BUFFER_SIZE, "Incorrect max item size received"); + //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around int no_of_items = (BUFFER_SIZE - SMALL_ITEM_SIZE) / SMALL_ITEM_SIZE; @@ -241,11 +498,21 @@ TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]") for (int i = 0; i < no_of_items; i++) { send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items * SMALL_ITEM_SIZE, "Incorrect number of bytes waiting"); + //Test receiving items for (int i = 0; i < no_of_items; i++) { receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); } + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect number of bytes waiting"); + //Write pointer should be near the end, test wrap around UBaseType_t write_pos_before, write_pos_after; vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL); @@ -260,6 +527,96 @@ TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]") vRingbufferDelete(buffer_handle); } +TEST_CASE("TC#2: Byte buffer", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == BUFFER_SIZE, "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == BUFFER_SIZE, "Incorrect max item size received"); + + //Calculate number of items to send. Aim to fill the buffer + int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE; + + //Test sending items + for (int i = 0; i < no_of_items; i++) { + send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_items * SMALL_ITEM_SIZE, "Incorrect number of bytes waiting"); + + //At this point, the buffer should be full. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == 0, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Receive one item + receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //At this point, the buffer should not be full any more + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) > 0, "Buffer should have free space"); + + //Test receiving remaining items + for (int i = 0; i < no_of_items - 1; i++) { + receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + +TEST_CASE("TC#3: Byte buffer", "[esp_ringbuf]") +{ + //Create buffer + RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF); + TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer"); + + //Check buffer free size and max item size upon buffer creation. Should be BUFFER_SIZE. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) == BUFFER_SIZE, "Incorrect buffer free size received"); + TEST_ASSERT_MESSAGE(xRingbufferGetMaxItemSize(buffer_handle) == BUFFER_SIZE, "Incorrect max item size received"); + + //Calculate number of medium items to send. Aim to almost fill the buffer + int no_of_medium_items = BUFFER_SIZE / MEDIUM_ITEM_SIZE; + + //Test sending items + for (int i = 0; i < no_of_medium_items; i++) { + send_item_and_check(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify items waiting matches with the number of medium items sent + UBaseType_t items_waiting; + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == no_of_medium_items * MEDIUM_ITEM_SIZE, "Incorrect number of bytes waiting"); + + //The buffer should not have any free space for one small item. + TEST_ASSERT_MESSAGE(xRingbufferGetCurFreeSize(buffer_handle) < SMALL_ITEM_SIZE, "Buffer full not achieved"); + + //Send an item. The item should not be sent to a full buffer. + send_item_and_check_failure(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false); + + //Test receiving medium items + for (int i = 0; i < no_of_medium_items; i++) { + receive_check_and_return_item_byte_buffer(buffer_handle, large_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS, false); + } + + //Verify that no items are waiting + vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting); + TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting"); + + //Cleanup + vRingbufferDelete(buffer_handle); +} + /* ----------------------- Ring buffer queue sets test ------------------------ * The following test case will test receiving from ring buffers that have been * added to a queue set. The test case will do the following... diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 97be9a450a..96058137d5 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -877,7 +877,6 @@ components/esp_pm/pm_locks.c components/esp_pm/pm_trace.c components/esp_pm/test/test_pm.c components/esp_ringbuf/include/freertos/ringbuf.h -components/esp_ringbuf/test/test_ringbuf.c components/esp_rom/esp32/esp_rom_caps.h components/esp_rom/esp32/ld/esp32.rom.api.ld components/esp_rom/esp32/ld/esp32.rom.eco3.ld