/*
 * FreeRTOS V202411.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
 *
 */

/* Standard includes. */
#include "stdio.h"
#include "string.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"

/* Demo app includes. */
#include "StreamBufferDemo.h"

/* The number of bytes of storage in the stream buffers used in this test. */
#define sbSTREAM_BUFFER_LENGTH_BYTES    ( ( size_t ) 30 )

/* Stream buffer length one. */
#define sbSTREAM_BUFFER_LENGTH_ONE      ( ( size_t ) 1 )

/* Start and end ASCII characters used in data sent to the buffers. */
#define sbASCII_SPACE                   32
#define sbASCII_TILDA                   126

/* Defines the number of tasks to create in this test and demo. */
#define sbNUMBER_OF_ECHO_CLIENTS        ( 2 )
#define sbNUMBER_OF_SENDER_TASKS        ( 2 )

/* Priority of the test tasks.  The send and receive go from low to high
 * priority tasks, and from high to low priority tasks. */
#define sbLOWER_PRIORITY                ( tskIDLE_PRIORITY )
#define sbHIGHER_PRIORITY               ( tskIDLE_PRIORITY + 1 )

/* Block times used when sending and receiving from the stream buffers. */
#define sbRX_TX_BLOCK_TIME              pdMS_TO_TICKS( 125UL )

/* A block time of 0 means "don't block". */
#define sbDONT_BLOCK                    ( 0 )

/* The trigger level sets the number of bytes that must be present in the
 * stream buffer before a task that is blocked on the stream buffer is moved out of
 * the Blocked state so it can read the bytes. */
#define sbTRIGGER_LEVEL_1               ( 1 )

/* The size of the stack allocated to the tasks that run as part of this demo/
 * test.  The stack size is over generous in most cases. */
#ifndef configSTREAM_BUFFER_SENDER_TASK_STACK_SIZE
    #define sbSTACK_SIZE    ( configMINIMAL_STACK_SIZE + ( configMINIMAL_STACK_SIZE >> 1 ) )
#else
    #define sbSTACK_SIZE    configSTREAM_BUFFER_SENDER_TASK_STACK_SIZE
#endif

#ifndef configSTREAM_BUFFER_SMALLER_TASK_STACK_SIZE
    #define sbSMALLER_STACK_SIZE    sbSTACK_SIZE
#else
    #define sbSMALLER_STACK_SIZE    configSTREAM_BUFFER_SMALLER_TASK_STACK_SIZE
#endif

/*-----------------------------------------------------------*/

/*
 * Performs various tests that do not require multiple tasks to interact.
 */
static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer );

/*
 * Tests sending and receiving various lengths of data via a stream buffer.
 * The echo client sends the data to the echo server, which then sends the
 * data back to the echo client, which checks it receives exactly what it
 * sent.
 */
static void prvEchoClient( void * pvParameters );
static void prvEchoServer( void * pvParameters );

/*
 * Tasks that send and receive to a stream buffer at a low priority and without
 * blocking, so the send and receive functions interleave in time as the tasks
 * are switched in and out.
 */
static void prvNonBlockingReceiverTask( void * pvParameters );
static void prvNonBlockingSenderTask( void * pvParameters );

/* Performs an assert() like check in a way that won't get removed when
 * performing a code coverage analysis. */
static void prvCheckExpectedState( BaseType_t xState );

/*
 * A task that creates a stream buffer with a specific trigger level, then
 * receives a string from an interrupt (the RTOS tick hook) byte by byte to
 * check it is only unblocked when the specified trigger level is reached.
 */
static void prvInterruptTriggerLevelTest( void * pvParameters );

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

/* This file tests both statically and dynamically allocated stream buffers.
 * Allocate the structures and buffers to be used by the statically allocated
 * objects, which get used in the echo tests. */
    static void prvReceiverTask( void * pvParameters );
    static void prvSenderTask( void * pvParameters );

    static StaticStreamBuffer_t xStaticStreamBuffers[ sbNUMBER_OF_ECHO_CLIENTS ];
    static uint32_t ulSenderLoopCounters[ sbNUMBER_OF_SENDER_TASKS ] = { 0 };
#endif /* configSUPPORT_STATIC_ALLOCATION */

/* The +1 is to make the test logic easier as the function that calculates the
 * free space will return one less than the actual free space - adding a 1 to the
 * actual length makes it appear to the tests as if the free space is returned as
 * it might logically be expected.  Returning 1 less than the actual free space is
 * fine as it can never result in an overrun. */
static uint8_t ucBufferStorage[ sbNUMBER_OF_SENDER_TASKS ][ sbSTREAM_BUFFER_LENGTH_BYTES + 1 ];

/*-----------------------------------------------------------*/

/* The buffers used by the echo client and server tasks. */
typedef struct ECHO_STREAM_BUFFERS
{
    /* Handles to the data structures that describe the stream buffers. */
    StreamBufferHandle_t xEchoClientBuffer;
    StreamBufferHandle_t xEchoServerBuffer;
} EchoStreamBuffers_t;
static volatile uint32_t ulEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };

/* The non-blocking tasks monitor their operation, and if no errors have been
 * found, increment ulNonBlockingRxCounter.  xAreStreamBufferTasksStillRunning()
 * then checks ulNonBlockingRxCounter and only returns pdPASS if
 * ulNonBlockingRxCounter is still incrementing. */
static volatile uint32_t ulNonBlockingRxCounter = 0;

/* The task that receives characters from the tick interrupt in order to test
 * different trigger levels monitors its own behaviour.  If it has not detected any
 * error then it increments ulInterruptTriggerCounter to indicate to the check task
 * that it is still operating correctly. */
static volatile uint32_t ulInterruptTriggerCounter = 0UL;

/* The stream buffer used from the tick interrupt.  This sends one byte at a time
 * to a test task to test the trigger level operation.  The variable is set to NULL
 * in between test runs. */
static volatile StreamBufferHandle_t xInterruptStreamBuffer = NULL;

/* The data sent from the tick interrupt to the task that tests the trigger
 * level functionality. */
static const char * pcDataSentFromInterrupt = "0123456789";

/* Data that is longer than the buffer that is sent to the buffers as a stream
 * of bytes.  Parts of which are written to the stream buffer to test writing
 * different lengths at different offsets, to many bytes, part streams, streams
 * that wrap, etc..  Two messages are defined to ensure left over data is not
 * accidentally read out of the buffer. */
static const char * pc55ByteString = "One two three four five six seven eight nine ten eleven";
static const char * pc54ByteString = "01234567891abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ";

/* Used to log the status of the tests contained within this file for reporting
 * to a monitoring task ('check' task). */
static BaseType_t xErrorStatus = pdPASS;

/*-----------------------------------------------------------*/

void vStartStreamBufferTasks( void )
{
    StreamBufferHandle_t xStreamBuffer;

    /* The echo servers sets up the stream buffers before creating the echo
     * client tasks.  One set of tasks has the server as the higher priority, and
     * the other has the client as the higher priority. */
    xTaskCreate( prvEchoServer, "1StrEchoServer", sbSMALLER_STACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
    xTaskCreate( prvEchoServer, "2StrEchoServer", sbSMALLER_STACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );

    /* The non blocking tasks run continuously and will interleave with each
     * other, so must be created at the lowest priority.  The stream buffer they
     * use is created and passed in using the task's parameter. */
    xStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
    xTaskCreate( prvNonBlockingReceiverTask, "StrNonBlkRx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );
    xTaskCreate( prvNonBlockingSenderTask, "StrNonBlkTx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );

    /* The task that receives bytes from an interrupt to test that it unblocks
     * at a specific trigger level must run at a high priority to minimise the risk
     * of it receiving more characters before it can execute again after being
     * unblocked. */
    xTaskCreate( prvInterruptTriggerLevelTest, "StrTrig", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL );

    #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
    {
        /* The sender tasks set up the stream buffers before creating the
         * receiver tasks.  Priorities must be 0 and 1 as the priority is used to
         * index into the xStaticStreamBuffers and ucBufferStorage arrays. */
        xTaskCreate( prvSenderTask, "Str1Sender", sbSMALLER_STACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
        xTaskCreate( prvSenderTask, "Str2Sender", sbSMALLER_STACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );
    }
    #endif /* configSUPPORT_STATIC_ALLOCATION */
}
/*-----------------------------------------------------------*/

static void prvCheckExpectedState( BaseType_t xState )
{
    configASSERT( xState );

    if( xState == pdFAIL )
    {
        xErrorStatus = pdFAIL;
    }
}
/*-----------------------------------------------------------*/

static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer )
{
    size_t xReturned, xItem, xExpected, xExpectedSpaces, xExpectedBytes;
    const size_t xMax6ByteMessages = sbSTREAM_BUFFER_LENGTH_BYTES / 6;
    const size_t xTrueSize = ( sizeof( ucBufferStorage ) / sbNUMBER_OF_SENDER_TASKS );
    const size_t x6ByteLength = 6, x17ByteLength = 17, xFullBufferSize = sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2;
    uint8_t * pucFullBuffer, * pucData, * pucReadData;
    TickType_t xTimeBeforeCall, xTimeAfterCall;
    const TickType_t xBlockTime = pdMS_TO_TICKS( 15 ), xAllowableMargin = pdMS_TO_TICKS( 3 ), xMinimalBlockTime = 2;
    UBaseType_t uxOriginalPriority;

    /* Remove warning in case configASSERT() is not defined. */
    ( void ) xAllowableMargin;

    /* To minimise stack and heap usage a full size buffer is allocated from the
     * heap, then buffers which hold smaller amounts of data are overlayed with the
     * larger buffer - just make sure not to use both at once! */
    pucFullBuffer = pvPortMalloc( xFullBufferSize );
    configASSERT( pucFullBuffer );

    pucData = pucFullBuffer;
    pucReadData = pucData + x17ByteLength;

    /* Nothing has been added or removed yet, so expect the free space to be
     * exactly as created.  Head and tail are both at 0. */
    xExpectedSpaces = sbSTREAM_BUFFER_LENGTH_BYTES;
    xExpectedBytes = 0;
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Add a single item - number of bytes available should go up by one and spaces
     * available down by one.  Head is in front of tail. */
    xExpectedSpaces--;
    xExpectedBytes++;
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == sizeof( *pucData ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Now fill the buffer by adding another 29 bytes.  Head is 30 tail is at 0. */
    xExpectedSpaces -= 29;
    xExpectedBytes += 29;
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, ( sbSTREAM_BUFFER_LENGTH_BYTES - 1 ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == ( sbSTREAM_BUFFER_LENGTH_BYTES - 1 ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );

    /* Should not be able to add another byte now. */
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == ( size_t ) 0 );

    /* Remove a byte so the tail pointer moves off 0.  Head pointer remains at the
     * end of the buffer. */
    xExpectedSpaces += 1;
    xExpectedBytes -= 1;
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == sizeof( *pucData ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Should be able to add another byte to fill the buffer again now. */
    xExpectedSpaces -= 1;
    xExpectedBytes += 1;
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == sizeof( *pucData ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );

    /* Now the head pointer is behind the tail pointer.  Read another 29 bytes so
     * the tail pointer moves to the end of the buffer. */
    xExpectedSpaces += 29;
    xExpectedBytes -= 29;
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, ( size_t ) 29, ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == ( size_t ) 29 );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Read out one more byte to wrap the tail back around to the start, to get back
     * to where we started. */
    xExpectedSpaces += 1;
    xExpectedBytes -= 1;
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
    prvCheckExpectedState( xReturned == sizeof( *pucData ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedBytes );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Try filling the message buffer in one write, blocking indefinitely.  Expect to
     * have written one byte less. */
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, xTrueSize, portMAX_DELAY );
    xExpectedSpaces = ( size_t ) 0;
    prvCheckExpectedState( xReturned == ( xTrueSize - ( size_t ) 1 ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == xExpectedSpaces );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );

    /* Empty the buffer again ready for the rest of the tests.  Again block
     *  indefinitely to ensure reading more than there can possible be won't lock this
     *  task up, so expect to actually receive one byte less than requested. */
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, xTrueSize, portMAX_DELAY );
    prvCheckExpectedState( xReturned == ( xTrueSize - ( size_t ) 1 ) );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == sbSTREAM_BUFFER_LENGTH_BYTES );
    xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == ( size_t ) 0 );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );


    /* The buffer is 30 bytes long.  6 5 byte messages should fit before the
     * buffer is completely full. */
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );

    for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
    {
        prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

        /* Generate recognizable data to write to the buffer.  This is just
         * ascii characters that shows which loop iteration the data was written
         * in. The 'FromISR' version is used to give it some exercise as a block
         * time is not used, so the call must be inside a critical section so it
         * runs with ports that don't support interrupt nesting (and therefore
         * don't have interrupt safe critical sections). */
        memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
        taskENTER_CRITICAL();
        {
            xReturned = xStreamBufferSendFromISR( xStreamBuffer, ( void * ) pucData, x6ByteLength, NULL );
        }
        taskEXIT_CRITICAL();
        prvCheckExpectedState( xReturned == x6ByteLength );

        /* The space in the buffer will have reduced by the amount of user data
         * written into the buffer. */
        xExpected -= x6ByteLength;
        xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == xExpected );
        xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
        /* +1 as it is zero indexed. */
        prvCheckExpectedState( xReturned == ( ( xItem + 1 ) * x6ByteLength ) );
    }

    /* Now the buffer should be full, and attempting to add anything should fail. */
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), sbDONT_BLOCK );
    prvCheckExpectedState( xReturned == 0 );

    /* Adding with a timeout should also fail after the appropriate time.  The
     * priority is temporarily boosted in this part of the test to keep the
     * allowable margin to a minimum. */
    uxOriginalPriority = uxTaskPriorityGet( NULL );
    vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
    xTimeBeforeCall = xTaskGetTickCount();
    xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), xBlockTime );
    xTimeAfterCall = xTaskGetTickCount();
    vTaskPrioritySet( NULL, uxOriginalPriority );
    prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) >= xBlockTime );
    prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) < ( xBlockTime + xAllowableMargin ) );
    prvCheckExpectedState( xReturned == 0 );

    /* The buffer is now full of data in the form "000000", "111111", etc.  Make
     * sure the data is read out as expected. */
    for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
    {
        /* Generate the data that is expected to be read out for this loop
         * iteration. */
        memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );

        /* Read the next 6 bytes out.  The 'FromISR' version is used to give it
         * some exercise as a block time is not used, so a it must be called from
         * a critical section so this will work on ports that don't support
         * interrupt nesting (so don't have interrupt safe critical sections). */
        taskENTER_CRITICAL();
        {
            xReturned = xStreamBufferReceiveFromISR( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, NULL );
        }
        taskEXIT_CRITICAL();
        prvCheckExpectedState( xReturned == x6ByteLength );

        /* Does the data read out match that expected? */
        prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x6ByteLength ) == 0 );

        /* The space in the buffer will have increased by the amount of user
         * data removed from the buffer. */
        xExpected += x6ByteLength;
        xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == xExpected );
        xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == ( sbSTREAM_BUFFER_LENGTH_BYTES - xExpected ) );
    }

    /* The buffer should be empty again. */
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
    prvCheckExpectedState( xExpected == sbSTREAM_BUFFER_LENGTH_BYTES );

    /* Reading with a timeout should also fail after the appropriate time.  The
     * priority is temporarily boosted in this part of the test to keep the
     * allowable margin to a minimum. */
    vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
    xTimeBeforeCall = xTaskGetTickCount();
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, xBlockTime );
    xTimeAfterCall = xTaskGetTickCount();
    vTaskPrioritySet( NULL, uxOriginalPriority );
    prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) >= xBlockTime );
    prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) < ( xBlockTime + xAllowableMargin ) );
    prvCheckExpectedState( xReturned == 0 );


    /* In the next loop 17 bytes are written to then read out on each
     * iteration.  As 30 is not divisible by 17 the data will wrap around. */
    xExpected = sbSTREAM_BUFFER_LENGTH_BYTES - x17ByteLength;

    for( xItem = 0; xItem < 100; xItem++ )
    {
        /* Generate recognizable data to write to the queue.  This is just
         * ascii characters that shows which loop iteration the data was written
         * in. */
        memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x17ByteLength );
        xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, x17ByteLength, sbDONT_BLOCK );
        prvCheckExpectedState( xReturned == x17ByteLength );

        /* The space in the buffer will have reduced by the amount of user data
         * written into the buffer. */
        xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == xExpected );
        xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == x17ByteLength );
        prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
        prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );

        /* Read the 17 bytes out again. */
        xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x17ByteLength, sbDONT_BLOCK );
        prvCheckExpectedState( xReturned == x17ByteLength );

        /* Does the data read out match that expected? */
        prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x17ByteLength ) == 0 );

        /* Full buffer space available again. */
        xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
        xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
        prvCheckExpectedState( xReturned == 0 );
        prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
        prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    }

    /* Fill the buffer with one message, check it is full, then read it back
     * again and check the correct data is received. */
    xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
    xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
    prvCheckExpectedState( memcmp( pc55ByteString, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );

    /* Fill the buffer one bytes at a time. */
    for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
    {
        /* Block time is only for test coverage, the task should never actually
         * block here. */
        xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xItem ] ), sizeof( char ), sbRX_TX_BLOCK_TIME );
    }

    /* The buffer should now be full. */
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );

    /* Read the message out in one go, even though it was written in individual
     * bytes.  Try reading much more data than is actually available to ensure only
     * the available bytes are returned (otherwise this read will write outside of
     * the memory allocated anyway!). */
    xReturned = xStreamBufferReceive( xStreamBuffer, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, sbRX_TX_BLOCK_TIME );
    prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
    prvCheckExpectedState( memcmp( ( const void * ) pc54ByteString, ( const void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );

    /* Now do the opposite, write in one go and read out in single bytes. */
    xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbRX_TX_BLOCK_TIME );
    prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferBytesAvailable( xStreamBuffer ) == sbSTREAM_BUFFER_LENGTH_BYTES );
    prvCheckExpectedState( xStreamBufferSpacesAvailable( xStreamBuffer ) == 0 );

    /* Read from the buffer one byte at a time. */
    for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
    {
        /* Block time is only for test coverage, the task should never actually
         * block here. */
        xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sizeof( char ), sbRX_TX_BLOCK_TIME );
        prvCheckExpectedState( pc55ByteString[ xItem ] == pucFullBuffer[ 0 ] );
    }

    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );

    /* Try writing more bytes than there is space. */
    vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
    xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
    vTaskPrioritySet( NULL, uxOriginalPriority );
    prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );

    /* No space now though. */
    xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
    prvCheckExpectedState( xReturned == 0 );

    /* Ensure data was written as expected even when there was an attempt to
     * write more than was available.  This also tries to read more bytes than are
     * available. */
    xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, xFullBufferSize, xMinimalBlockTime );
    prvCheckExpectedState( memcmp( ( const void * ) pucFullBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
    prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
    prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );

    /* Clean up with data in the buffer to ensure the tests that follow don't
     * see the data (the data should be discarded). */
    ( void ) xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES / ( size_t ) 2, sbDONT_BLOCK );
    vPortFree( pucFullBuffer );
    xStreamBufferReset( xStreamBuffer );
}
/*-----------------------------------------------------------*/

static void prvNonBlockingSenderTask( void * pvParameters )
{
    StreamBufferHandle_t xStreamBuffer;
    size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
    const size_t xStringLength = strlen( pc54ByteString );

    /* In this case the stream buffer has already been created and is passed
     * into the task using the task's parameter. */
    xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;

    /* Keep sending the string to the stream buffer as many bytes as possible in
     * each go.  Doesn't block so calls can interleave with the non-blocking
     * receives performed by prvNonBlockingReceiverTask(). */
    for( ; ; )
    {
        /* The whole string cannot be sent at once, so xNextChar is an index to
         * the position within the string that has been sent so far.  How many
         * bytes are there left to send before the end of the string? */
        xBytesToSend = xStringLength - xNextChar;

        /* Attempt to send right up to the end of the string. */
        xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xNextChar ] ), xBytesToSend, sbDONT_BLOCK );
        prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );

        /* Move the index up the string to the next character to be sent,
         * wrapping if the end of the string has been reached. */
        xNextChar += xBytesActuallySent;
        prvCheckExpectedState( xNextChar <= xStringLength );

        if( xNextChar == xStringLength )
        {
            xNextChar = 0;
        }
    }
}
/*-----------------------------------------------------------*/

static void prvNonBlockingReceiverTask( void * pvParameters )
{
    StreamBufferHandle_t xStreamBuffer;
    size_t xNextChar = 0, xReceiveLength, xBytesToTest, xStartIndex;
    const size_t xStringLength = strlen( pc54ByteString );
    char cRxString[ 12 ]; /* Holds received characters. */
    BaseType_t xNonBlockingReceiveError = pdFALSE;

    /* In this case the stream buffer has already been created and is passed
     * into the task using the task's parameter. */
    xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;

    /* Expects to receive the pc54ByteString over and over again.  Sends and
     * receives are not blocking so will interleave. */
    for( ; ; )
    {
        /* Attempt to receive as many bytes as possible, up to the limit of the
         * Rx buffer size. */
        xReceiveLength = xStreamBufferReceive( xStreamBuffer, ( void * ) cRxString, sizeof( cRxString ), sbDONT_BLOCK );

        if( xReceiveLength > 0 )
        {
            /* xNextChar is the index into pc54ByteString that has been received
             * already.  If xReceiveLength bytes are added to that, will it go off
             * the end of the string?  If so, then first test up to the end of the
             * string, then go back to the start of pc54ByteString to test the
             * remains of the received data. */
            xBytesToTest = xReceiveLength;

            if( ( xNextChar + xBytesToTest ) > xStringLength )
            {
                /* Cap to test the received data to the end of the string. */
                xBytesToTest = xStringLength - xNextChar;

                if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) cRxString, xBytesToTest ) != 0 )
                {
                    xNonBlockingReceiveError = pdTRUE;
                }

                /* Then move back to the start of the string to test the
                 * remaining received bytes. */
                xNextChar = 0;
                xStartIndex = xBytesToTest;
                xBytesToTest = xReceiveLength - xBytesToTest;
            }
            else
            {
                /* The string didn't wrap in the buffer, so start comparing from
                 * the start of the received data. */
                xStartIndex = 0;
            }

            /* Test the received bytes are as expected, then move the index
             * along the string to the next expected char to receive. */
            if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) &( cRxString[ xStartIndex ] ), xBytesToTest ) != 0 )
            {
                xNonBlockingReceiveError = pdTRUE;
            }

            if( xNonBlockingReceiveError == pdFALSE )
            {
                /* No errors detected so increment the counter that lets the
                 *  check task know this test is still functioning correctly. */
                ulNonBlockingRxCounter++;
            }

            xNextChar += xBytesToTest;

            if( xNextChar >= xStringLength )
            {
                xNextChar = 0;
            }
        }
    }
}
/*-----------------------------------------------------------*/

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

    static void prvSenderTask( void * pvParameters )
    {
        StreamBufferHandle_t xStreamBuffer, xTempStreamBuffer;
        static uint8_t ucTempBuffer[ 10 ]; /* Just used to exercise stream buffer creating and deletion. */
        const TickType_t xTicksToWait = sbRX_TX_BLOCK_TIME, xShortDelay = pdMS_TO_TICKS( 50 );
        StaticStreamBuffer_t xStaticStreamBuffer;
        size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
        const size_t xStringLength = strlen( pc55ByteString );

        /* The task's priority is used as an index into the loop counters used to
         * indicate this task is still running. */
        UBaseType_t uxIndex = uxTaskPriorityGet( NULL );

        /* Make sure a change in priority does not inadvertently result in an
         * invalid array index. */
        prvCheckExpectedState( uxIndex < sbNUMBER_OF_ECHO_CLIENTS );

        /* Avoid compiler warnings about unused parameters. */
        ( void ) pvParameters;

        xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ) / sbNUMBER_OF_SENDER_TASKS, /* The number of bytes in each buffer in the array. */
                                                   sbTRIGGER_LEVEL_1,                                    /* The number of bytes to be in the buffer before a task blocked to wait for data is unblocked. */
                                                   &( ucBufferStorage[ uxIndex ][ 0 ] ),                 /* The address of the buffer to use within the array. */
                                                   &( xStaticStreamBuffers[ uxIndex ] ) );               /* The static stream buffer structure to use within the array. */

        /* Now the stream buffer has been created the receiver task can be
         * created.  If this sender task has the higher priority then the receiver
         * task is created at the lower priority - if this sender task has the
         * lower priority then the receiver task is created at the higher
         * priority. */
        if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
        {
            /* Here prvSingleTaskTests() performs various tests on a stream buffer
             * that was created statically. */
            prvSingleTaskTests( xStreamBuffer );
            xTaskCreate( prvReceiverTask, "StrReceiver", sbSMALLER_STACK_SIZE, ( void * ) xStreamBuffer, sbHIGHER_PRIORITY, NULL );
        }
        else
        {
            xTaskCreate( prvReceiverTask, "StrReceiver", sbSMALLER_STACK_SIZE, ( void * ) xStreamBuffer, sbLOWER_PRIORITY, NULL );
        }

        for( ; ; )
        {
            /* The whole string cannot be sent at once, so xNextChar is an index
            * to the position within the string that has been sent so far.  How
            * many bytes are there left to send before the end of the string? */
            xBytesToSend = xStringLength - xNextChar;

            /* Attempt to send right up to the end of the string. */
            xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc55ByteString[ xNextChar ] ), xBytesToSend, xTicksToWait );
            prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );

            /* Move the index up the string to the next character to be sent,
             * wrapping if the end of the string has been reached. */
            xNextChar += xBytesActuallySent;
            prvCheckExpectedState( xNextChar <= xStringLength );

            if( xNextChar == xStringLength )
            {
                xNextChar = 0;
            }

            /* Increment a loop counter so a check task can tell this task is
             * still running as expected. */
            ulSenderLoopCounters[ uxIndex ]++;

            if( uxTaskPriorityGet( NULL ) == sbHIGHER_PRIORITY )
            {
                /* Allow other tasks to run. */
                vTaskDelay( xShortDelay );
            }

            /* This stream buffer is just created and deleted to ensure no
             * issues when attempting to delete a stream buffer that was
             * created using statically allocated memory.  To save stack space
             * the buffer is set to point to the pc55ByteString, which is a const
             * string, but no data is written into the buffer so any valid address
             * will do. */
            xTempStreamBuffer = xStreamBufferCreateStatic( sizeof( ucTempBuffer ), sbTRIGGER_LEVEL_1, ucTempBuffer, &xStaticStreamBuffer );
            xStreamBufferReset( xTempStreamBuffer );
            vStreamBufferDelete( xTempStreamBuffer );
        }
    }

#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

    static void prvReceiverTask( void * pvParameters )
    {
        StreamBufferHandle_t const pxStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
        char cRxString[ 12 ]; /* Large enough to hold a 32-bit number in ASCII. */
        const TickType_t xTicksToWait = pdMS_TO_TICKS( 5UL );
        const size_t xStringLength = strlen( pc55ByteString );
        size_t xNextChar = 0, xReceivedLength, xBytesToReceive;

        for( ; ; )
        {
            /* Attempt to receive the number of bytes to the end of the string,
             * or the number of byte that can be placed into the rx buffer,
             * whichever is smallest. */
            xBytesToReceive = configMIN( ( xStringLength - xNextChar ), sizeof( cRxString ) );

            do
            {
                xReceivedLength = xStreamBufferReceive( pxStreamBuffer, ( void * ) cRxString, xBytesToReceive, xTicksToWait );
            } while( xReceivedLength == 0 );

            /* Ensure the received string matches the expected string. */
            prvCheckExpectedState( memcmp( ( void * ) cRxString, ( const void * ) &( pc55ByteString[ xNextChar ] ), xReceivedLength ) == 0 );

            /* Move the index into the string up to the end of the bytes
             * received so far - wrapping if the end of the string has been
             * reached. */
            xNextChar += xReceivedLength;

            if( xNextChar >= xStringLength )
            {
                xNextChar = 0;
            }
        }
    }

#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/

static void prvEchoClient( void * pvParameters )
{
    size_t xSendLength = 0, ux;
    char * pcStringToSend, * pcStringReceived, cNextChar = sbASCII_SPACE;
    const TickType_t xTicksToWait = pdMS_TO_TICKS( 50 );
    StreamBufferHandle_t xTempStreamBuffer;

/* The task's priority is used as an index into the loop counters used to
 * indicate this task is still running. */
    UBaseType_t uxIndex = uxTaskPriorityGet( NULL );

/* Pointers to the client and server stream buffers are passed into this task
 * using the task's parameter. */
    EchoStreamBuffers_t * pxStreamBuffers = ( EchoStreamBuffers_t * ) pvParameters;

    /* Prevent compiler warnings. */
    ( void ) pvParameters;

    /* Create the buffer into which strings to send to the server will be
     * created, and the buffer into which strings echoed back from the server will
     * be copied. */
    pcStringToSend = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
    pcStringReceived = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );

    configASSERT( pcStringToSend );
    configASSERT( pcStringReceived );

    for( ; ; )
    {
        /* Generate the length of the next string to send. */
        xSendLength++;

        /* The stream buffer is being used to hold variable length data, so
         * each data item requires sizeof( size_t ) bytes to hold the data's
         * length, hence the sizeof() in the if() condition below. */
        if( xSendLength > ( sbSTREAM_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) )
        {
            /* Back to a string length of 1. */
            xSendLength = sizeof( char );
        }

        memset( pcStringToSend, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );

        for( ux = 0; ux < xSendLength; ux++ )
        {
            pcStringToSend[ ux ] = cNextChar;

            cNextChar++;

            if( cNextChar > sbASCII_TILDA )
            {
                cNextChar = sbASCII_SPACE;
            }
        }

        /* Send the generated string to the buffer. */
        do
        {
            ux = xStreamBufferSend( pxStreamBuffers->xEchoClientBuffer, ( void * ) pcStringToSend, xSendLength, xTicksToWait );
        } while( ux == 0 );

        /* Wait for the string to be echoed back. */
        memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
        xStreamBufferReceive( pxStreamBuffers->xEchoServerBuffer, ( void * ) pcStringReceived, xSendLength, portMAX_DELAY );

        prvCheckExpectedState( strcmp( pcStringToSend, pcStringReceived ) == 0 );

        /* Maintain a count of the number of times this code executes so a
         * check task can determine if this task is still functioning as
         * expected or not.  As there are two client tasks, and the priorities
         * used are 0 and 1, the task's priority is used as an index into the
         * loop count array. */
        ulEchoLoopCounters[ uxIndex ]++;

        /* This stream buffer is just created and deleted to ensure no memory
         * leaks. */
        xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
        vStreamBufferDelete( xTempStreamBuffer );

        /* The following are tests for a stream buffer of size one. */
        /* Create a buffer of size one. */
        xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_ONE, sbTRIGGER_LEVEL_1 );
        /* Ensure that the buffer was created successfully. */
        configASSERT( xTempStreamBuffer );

        /* Send one byte to the buffer. */
        ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
        /* Ensure that the byte was sent successfully. */
        configASSERT( ux == 1 );
        /* Try sending another byte to the buffer. */
        ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
        /* Make sure that send failed as the buffer is full. */
        configASSERT( ux == 0 );

        /* Receive one byte from the buffer. */
        memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
        ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
        /* Ensure that the receive was successful. */
        configASSERT( ux == 1 );
        /* Ensure that the correct data was received. */
        configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );
        /* Try receiving another byte from the buffer. */
        ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
        /* Ensure that the receive failed as the buffer is empty. */
        configASSERT( ux == 0 );

        /* Try sending two bytes to the buffer. Since the size of the
         * buffer is one, we must not be able to send more than one. */
        ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 2, sbDONT_BLOCK );
        /* Ensure that only one byte was sent. */
        configASSERT( ux == 1 );

        /* Try receiving two bytes from the buffer. Since the size of the
         * buffer is one, we must not be able to get more than one. */
        memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
        ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 2, sbDONT_BLOCK );
        /* Ensure that only one byte was received. */
        configASSERT( ux == 1 );
        /* Ensure that the correct data was received. */
        configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );

        /* Delete the buffer. */
        vStreamBufferDelete( xTempStreamBuffer );
    }
}
/*-----------------------------------------------------------*/

static void prvEchoServer( void * pvParameters )
{
    size_t xReceivedLength;
    char * pcReceivedString;
    EchoStreamBuffers_t xStreamBuffers;
    TickType_t xTimeOnEntering;
    const TickType_t xTicksToBlock = pdMS_TO_TICKS( 350UL );

    /* Prevent compiler warnings about unused parameters. */
    ( void ) pvParameters;

    /* Create the stream buffer used to send data from the client to the server,
     * and the stream buffer used to echo the data from the server back to the
     * client. */
    xStreamBuffers.xEchoClientBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
    xStreamBuffers.xEchoServerBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
    configASSERT( xStreamBuffers.xEchoClientBuffer );
    configASSERT( xStreamBuffers.xEchoServerBuffer );

    /* Create the buffer into which received strings will be copied. */
    pcReceivedString = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
    configASSERT( pcReceivedString );

    /* Don't expect to receive anything yet! */
    xTimeOnEntering = xTaskGetTickCount();
    xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, xTicksToBlock );
    prvCheckExpectedState( ( ( TickType_t ) ( xTaskGetTickCount() - xTimeOnEntering ) ) >= xTicksToBlock );
    prvCheckExpectedState( xReceivedLength == 0 );

    /* Now the stream buffers have been created the echo client task can be
     * created.  If this server task has the higher priority then the client task
     * is created at the lower priority - if this server task has the lower
     * priority then the client task is created at the higher priority. */
    if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
    {
        xTaskCreate( prvEchoClient, "EchoClient", sbSMALLER_STACK_SIZE, ( void * ) &xStreamBuffers, sbHIGHER_PRIORITY, NULL );
    }
    else
    {
        /* Here prvSingleTaskTests() performs various tests on a stream buffer
         * that was created dynamically. */
        prvSingleTaskTests( xStreamBuffers.xEchoClientBuffer );
        xTaskCreate( prvEchoClient, "EchoClient", sbSMALLER_STACK_SIZE, ( void * ) &xStreamBuffers, sbLOWER_PRIORITY, NULL );
    }

    for( ; ; )
    {
        memset( pcReceivedString, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );

        /* Has any data been sent by the client? */
        xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, portMAX_DELAY );

        /* Should always receive data as max delay was used. */
        prvCheckExpectedState( xReceivedLength > 0 );

        /* Echo the received data back to the client. */
        xStreamBufferSend( xStreamBuffers.xEchoServerBuffer, ( void * ) pcReceivedString, xReceivedLength, portMAX_DELAY );
    }
}
/*-----------------------------------------------------------*/

void vPeriodicStreamBufferProcessing( void )
{
    static size_t xNextChar = 0;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* Called from the tick interrupt hook.  If the global stream buffer
     * variable is not NULL then the prvInterruptTriggerTest() task expects a byte
     * to be sent to the stream buffer on each tick interrupt. */
    if( xInterruptStreamBuffer != NULL )
    {
        /* One character from the pcDataSentFromInterrupt string is sent on each
         * interrupt.  The task blocked on the stream buffer should not be
         * unblocked until the defined trigger level is hit. */
        xStreamBufferSendFromISR( xInterruptStreamBuffer, ( const void * ) &( pcDataSentFromInterrupt[ xNextChar ] ), sizeof( char ), &xHigherPriorityTaskWoken );

        if( xNextChar < strlen( pcDataSentFromInterrupt ) )
        {
            xNextChar++;
        }
    }
    else
    {
        /* Start at the beginning of the string being sent again. */
        xNextChar = 0;
    }
}
/*-----------------------------------------------------------*/

static void prvInterruptTriggerLevelTest( void * pvParameters )
{
    StreamBufferHandle_t xStreamBuffer;
    size_t xTriggerLevel = 1, xBytesReceived;
    const size_t xStreamBufferSizeBytes = ( size_t ) 9, xMaxTriggerLevel = ( size_t ) 7, xMinTriggerLevel = ( size_t ) 2;
    const TickType_t xReadBlockTime = 5, xCycleBlockTime = pdMS_TO_TICKS( 100 );
    uint8_t ucRxData[ 9 ];
    BaseType_t xErrorDetected = pdFALSE;

    #ifndef configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN
        const size_t xAllowableMargin = ( size_t ) 0;
    #else
        const size_t xAllowableMargin = ( size_t ) configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN;
    #endif

    /* Remove compiler warning about unused parameter. */
    ( void ) pvParameters;

    for( ; ; )
    {
        for( xTriggerLevel = xMinTriggerLevel; xTriggerLevel < xMaxTriggerLevel; xTriggerLevel++ )
        {
            /* This test is very time sensitive so delay at the beginning to ensure
             * the rest of the system is up and running before starting.  Delay between
             * each loop to ensure the interrupt that sends to the stream buffer
             * detects it needs to start sending from the start of the string again.. */
            vTaskDelay( xCycleBlockTime );

            /* Create the stream buffer that will be used from inside the tick
             * interrupt. */
            memset( ucRxData, 0x00, sizeof( ucRxData ) );
            xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
            configASSERT( xStreamBuffer );

            /* Now the stream buffer has been created it can be assigned to the
             * file scope variable, which will allow the tick interrupt to start
             * using it. */
            taskENTER_CRITICAL();
            {
                xInterruptStreamBuffer = xStreamBuffer;
            }
            taskEXIT_CRITICAL();

            xBytesReceived = xStreamBufferReceive( xStreamBuffer, ( void * ) ucRxData, sizeof( ucRxData ), xReadBlockTime );

            /* Set the file scope variable back to NULL so the interrupt doesn't
             * try to use it again. */
            taskENTER_CRITICAL();
            {
                xInterruptStreamBuffer = NULL;
            }
            taskEXIT_CRITICAL();

            /* Now check the number of bytes received equals the trigger level,
             * except in the case that the read timed out before the trigger level
             * was reached. */
            if( xTriggerLevel > xReadBlockTime )
            {
                /* Trigger level was greater than the block time so expect to
                 * time out having received xReadBlockTime bytes. */
                if( xBytesReceived > xReadBlockTime )
                {
                    /* Received more bytes than expected.  That could happen if
                     * this task unblocked at the right time, but an interrupt
                     * added another byte to the stream buffer before this task was
                     * able to run. */
                    if( ( xBytesReceived - xReadBlockTime ) > xAllowableMargin )
                    {
                        xErrorDetected = pdTRUE;
                    }
                }
                else if( xReadBlockTime != xBytesReceived )
                {
                    /* It is possible the interrupt placed an item in the stream
                     * buffer before this task called xStreamBufferReceive(), but
                     * if that is the case then xBytesReceived will only every be
                     * 0 as the interrupt will only have executed once. */
                    if( xBytesReceived != 1 )
                    {
                        xErrorDetected = pdTRUE;
                    }
                }
            }
            else if( xTriggerLevel < xReadBlockTime )
            {
                /* Trigger level was less than the block time so we expect to
                 * have received the trigger level number of bytes - could be more
                 * though depending on other activity between the task being
                 * unblocked and the task reading the number of bytes received.  It
                 * could also be less if the interrupt already put something in the
                 * stream buffer before this task attempted to read it - in which
                 * case the task would have returned the available bytes immediately
                 * without ever blocking - in that case the bytes received will
                 * only ever be 1 as the interrupt would not have executed more
                 * than one in that time unless this task has too low a priority. */
                if( xBytesReceived < xTriggerLevel )
                {
                    if( xBytesReceived != 1 )
                    {
                        xErrorDetected = pdTRUE;
                    }
                }
                else if( ( xBytesReceived - xTriggerLevel ) > xAllowableMargin )
                {
                    xErrorDetected = pdTRUE;
                }
            }
            else
            {
                /* The trigger level equalled the block time, so expect to
                 * receive no greater than the block time.  It could also be less
                 * if the interrupt already put something in the stream buffer
                 * before this task attempted to read it - in which case the task
                 * would have returned the available bytes immediately without ever
                 * blocking - in that case the bytes received would only ever be 1
                 * because the interrupt is not going to execute twice in that time
                 * unless this task is running a too low a priority. */
                if( xBytesReceived < xReadBlockTime )
                {
                    if( xBytesReceived != 1 )
                    {
                        xErrorDetected = pdTRUE;
                    }
                }
                else if( ( xBytesReceived - xReadBlockTime ) > xAllowableMargin )
                {
                    xErrorDetected = pdTRUE;
                }
            }

            if( xBytesReceived > sizeof( ucRxData ) )
            {
                xErrorDetected = pdTRUE;
            }
            else if( memcmp( ( void * ) ucRxData, ( const void * ) pcDataSentFromInterrupt, xBytesReceived ) != 0 )
            {
                /* Received data didn't match that expected. */
                xErrorDetected = pdTRUE;
            }

            if( xErrorDetected == pdFALSE )
            {
                /* Increment the cycle counter so the 'check' task knows this test
                 * is still running without error. */
                ulInterruptTriggerCounter++;
            }

            /* Tidy up ready for the next loop. */
            vStreamBufferDelete( xStreamBuffer );
        }
    }
}
/*-----------------------------------------------------------*/

BaseType_t xAreStreamBufferTasksStillRunning( void )
{
    static uint32_t ulLastEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
    static uint32_t ulLastNonBlockingRxCounter = 0;
    static uint32_t ulLastInterruptTriggerCounter = 0;
    BaseType_t x;

    for( x = 0; x < sbNUMBER_OF_ECHO_CLIENTS; x++ )
    {
        if( ulLastEchoLoopCounters[ x ] == ulEchoLoopCounters[ x ] )
        {
            xErrorStatus = pdFAIL;
        }
        else
        {
            ulLastEchoLoopCounters[ x ] = ulEchoLoopCounters[ x ];
        }
    }

    if( ulNonBlockingRxCounter == ulLastNonBlockingRxCounter )
    {
        xErrorStatus = pdFAIL;
    }
    else
    {
        ulLastNonBlockingRxCounter = ulNonBlockingRxCounter;
    }

    if( ulLastInterruptTriggerCounter == ulInterruptTriggerCounter )
    {
        xErrorStatus = pdFAIL;
    }
    else
    {
        ulLastInterruptTriggerCounter = ulInterruptTriggerCounter;
    }

    #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
    {
        static uint32_t ulLastSenderLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };

        for( x = 0; x < sbNUMBER_OF_SENDER_TASKS; x++ )
        {
            if( ulLastSenderLoopCounters[ x ] == ulSenderLoopCounters[ x ] )
            {
                xErrorStatus = pdFAIL;
            }
            else
            {
                ulLastSenderLoopCounters[ x ] = ulSenderLoopCounters[ x ];
            }
        }
    }
    #endif /* configSUPPORT_STATIC_ALLOCATION */

    return xErrorStatus;
}
/*-----------------------------------------------------------*/
