/*
 * FreeRTOS Integration Toolkit preview
 * Copyright (C) 2021 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.
 */

/**
 * @file transport_interface_test.c
 * @brief Integration tests for the transport interface test implementation.
 */

#include "test_execution_config.h"
#if ( TRANSPORT_INTERFACE_TEST_ENABLED == 1 )

/* Standard header includes. */
#include <string.h>
#include <stdbool.h>

/* Include for init and de-init functions. */
#include "transport_interface_test.h"

/* Include for test parameter and execution configs. */
#include "test_param_config.h"

/* Include for Unity framework. */
#include "unity.h"
#include "unity_fixture.h"

//#include "transport_mbedtls_pkcs11.h"
#include "transport_mbedtls.h"
//#include "transport_plaintext.h"


#include "network_connection.h"

struct NetworkContext
{
    TlsTransportParams_t * pParams;
};

//struct NetworkContext
//{
//    PlaintextTransportParams_t * pParams;
//};


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

/**
 * @brief Transport Interface test buffer length.
 *
 * Length of the test buffer used in tests. Guard buffers are placed before and
 * after the topic buffer to verify that transport interface APIs do not write
 * out of bounds. The memory layout is:
 *
 *     +--------------+-------------------------------+------------+
 *     |    Guard     |    Writable Topic Buffer      |   Guard    |
 *     +--------------+-------------------------------+------------+
 *
 * Both guard buffers are filled with a known pattern before each test and are
 * verified to remain unchanged after each test.
 */
#define TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH    ( 32U )
#define TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH        ( 2048U )
#define TRANSPORT_TEST_BUFFER_SUFFIX_GUARD_LENGTH    ( 32U )
#define TRANSPORT_TEST_BUFFER_TOTAL_LENGTH        \
    ( TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH + \
      TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH +     \
      TRANSPORT_TEST_BUFFER_SUFFIX_GUARD_LENGTH )

/**
 * @brief Transport Interface test buffer guard pattern.
 */
#define TRANSPORT_TEST_BUFFER_GUARD_PATTERN        ( 0xA5 )

/**
 * @brief Transport Interface send receive retry count.
 */
#define TRANSPORT_TEST_SEND_RECEIVE_RETRY_COUNT    ( 50U )

/**
 * @brief Transport Interface writev retry count.
 */
#define TRANSPORT_TEST_WRITEV_RETRY_COUNT    ( 50U )

/**
 * @brief Transport Interface delay in milliseconds.
 *
 * The Delay time for the transport interface test to wait for the data echo-ed
 * back from transport network.
 */
#define TRANSPORT_TEST_DELAY_MS                    ( 200U )

/**
 * @brief Echo server disconnect command.
 *
 * The command to inform echo server to disconnect the test connection.
 */
#define TRANSPORT_TEST_DISCONNECT_COMMAND          "DISCONNECT"

/**
 * @brief Transport Interface disconnect delay in milliseconds.
 *
 * The Delay time for the transport interface test to wait for the send operation
 * and response from the network. The delay time should be long enough to cover different
 * network environment.
 */
#define TRANSPORT_TEST_NETWORK_DELAY_MS            ( 3000U )

/**
 * @brief Number of simultaneous tasks for multithreaded tests.
 */
#define TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT     ( 2U )

/**
 * @brief The thread parameter index used in non-multithreaded test.
 */
#define TRANSPORT_TEST_INDEX                       ( 0U )

/**
 * @brief The second thread parameter index used in multithreaded test.
 */
#define TRANSPORT_TEST_SECOND_INDEX                ( 1U )

/**
 * @brief Timeout to wait thread function finish the test.
 */
#ifndef TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS
    #define TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS    ( 1000000U )
#endif

/**
 * @brief Timeout to wait no data to receive test thread.
 */
#define TRANSPORT_TEST_WAIT_THREAD_RECEIVE_TIMEOUT_MS    ( 180000U )

/**
 * @brief Print progress in SendRecvCompare test.
 *
 * This debug function should be disabled if testing with IDT.
 */
#ifndef TRANSPORT_TEST_PRINT_DEBUG_PROGRESS
    #define TRANSPORT_TEST_PRINT_DEBUG_PROGRESS    ( 0 )
#endif

/**
 * @brief Print debug error message when test fail in multi-threaded test.
 *
 * Use UnityPrint for older version of Unity.
 */
#ifndef TEST_MESSAGE
    #define TEST_MESSAGE( x )    UnityPrint( x )
#endif

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

typedef struct threadParameter
{
    NetworkContext_t * pNetworkContext;
    uint8_t transportTestBuffer[ TRANSPORT_TEST_BUFFER_TOTAL_LENGTH ];
    bool xNetworkConnected;
    bool stopFlag;
    bool xResult;
} threadParameter_t;

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

#if ( TRANSPORT_INTERFACE_TEST_ENABLED == 1 )
    #ifndef ECHO_SERVER_ENDPOINT
        #error "Please define ECHO_SERVER_ENDPOINT"
    #endif

    #ifndef ECHO_SERVER_PORT
        #error "Please define ECHO_SERVER_PORT"
    #endif
#endif /* if ( TRANSPORT_INTERFACE_TEST_ENABLED == 1 ) */

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

/**
 * @brief Struct of test parameters filled in by user.
 */
static TransportTestParam_t testParam = { 0 };

/**
 * @brief Info of the server to connect to for tests.
 */
static TestHostInfo_t testHostInfo = { 0 };

/**
 * @brief Transport Interface used in test cases.
 */
static TransportInterface_t * pTestTransport = NULL;

/**
 * @brief Thread paramter to hold the network context and test buffer.
 */
static threadParameter_t threadParameter[ TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT ];

/**
 * @brief Test group for transport interface test.
 */
TEST_GROUP( Full_TransportInterfaceTest );

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

/**
 * @brief Initialize the test data with 0,1,...,255,0,1,...
 */
static void prvInitializeTestData( uint8_t * pTransportTestBuffer,
                                   uint32_t testSize )
{
    uint32_t i;

    for( i = 0U; i < testSize; i++ )
    {
        pTransportTestBuffer[ i ] = ( uint8_t ) i;
    }
}

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

/**
 * @brief Verify the data received and buffer after testSize should remain unchanged.
 *
 * The received data is verified with the pattern initialized in prvInitializeTestData.
 * The test buffer after testSize should remain unchanged.
 */
static bool prvVerifyTestData( uint8_t * pTransportTestBuffer,
                               uint32_t testSize,
                               uint32_t maxBufferSize )
{
    uint32_t i;
    bool retValue = true;

    /* Check the data received is correct. */
    for( i = 0U; i < testSize; i++ )
    {
        if( ( ( uint8_t ) i ) != pTransportTestBuffer[ i ] )
        {
            TEST_MESSAGE( "Received data is not the same as expected." );
            retValue = false;
            break;
        }
    }

    /* Check the buffer after testSize is unchanged. */
    if( ( retValue == true ) && ( testSize < maxBufferSize ) )
    {
        for( i = 0U; i < ( maxBufferSize - testSize ); i++ )
        {
            if( pTransportTestBuffer[ testSize + i ] != TRANSPORT_TEST_BUFFER_GUARD_PATTERN )
            {
                TEST_MESSAGE( "Buffer after testSize should not be altered." );
                retValue = false;
                break;
            }
        }
    }

    return retValue;
}


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

/**
 * @brief Send the data over transport network with retry.
 *
 * The send API may return less bytes then requested. If the transport send function
 * returns zero, it should represent the send operation can be retired by calling
 * the API function. The retry operation is handled in this function.
 */
static bool prvTransportSendData( TransportInterface_t * pTransport,
                                  NetworkContext_t * pNetworkContext,
                                  uint8_t * pTransportTestBuffer,
                                  uint32_t sendSize )
{
    uint32_t transferTotal = 0U;
    int32_t transportResult = 0;
    uint32_t i;
    bool retValue = true;

    for( i = 0U; i < TRANSPORT_TEST_SEND_RECEIVE_RETRY_COUNT; i++ )
    {
        transportResult = pTransport->send( pNetworkContext,
                                            &pTransportTestBuffer[ transferTotal ],
                                            sendSize - transferTotal );

        /* Send should not have any error. */
        if( transportResult < 0 )
        {
            TEST_MESSAGE( "Transport send data should not have any error." );
            retValue = false;
            break;
        }

        if( ( sendSize - transferTotal ) < ( uint32_t ) transportResult )
        {
            TEST_MESSAGE( "More data is sent than expected." );
            retValue = false;
            break;
        }

        transferTotal = transferTotal + ( uint32_t ) transportResult;

        if( sendSize == transferTotal )
        {
            /* All the data is sent. Break the retry loop. */
            break;
        }
    }

    /* Check if all the data is sent. */
    if( sendSize != transferTotal )
    {
        TEST_MESSAGE( "Fail to send all the data expected." );
        retValue = false;
    }

    return retValue;
}

/*-----------------------------------------------------------*/
#ifdef TRANSPORT_TEST_EXECUTE_WRITEV_TESTS
/**
 * @brief Send the data over transport network using writev with retry.
 *
 * The writev API may return less bytes then requested. If the transport writev function
 * returns zero, it should represent that the writev operation can be retried by calling
 * the API function. The retry operation is handled in this function.
 */
static bool prvTransportWritevData( TransportInterface_t * pTransport,
                                    NetworkContext_t * pNetworkContext,
                                    TransportOutVector_t * pTransportTestVectorArray,
                                    size_t ioVecCount )
{
    int32_t transportResult = 0;
    uint32_t i;
    bool retValue = true;
    int32_t bytesToSend = 0;
    int32_t bytesSentOrError = 0;
    TransportOutVector_t * pIoVectIterator;
    size_t vectorsToBeSent = ioVecCount;

    /* Count the total number of bytes to be sent as outlined in the vector. */
    for( pIoVectIterator = pTransportTestVectorArray; pIoVectIterator <= &( pTransportTestVectorArray[ ioVecCount - 1U ] ); pIoVectIterator++ )
    {
        bytesToSend += ( int32_t ) pIoVectIterator->iov_len;
    }

    /* Reset the iterator to point to the first entry in the array. */
    pIoVectIterator = pTransportTestVectorArray;

    for( i = 0U; i < TRANSPORT_TEST_WRITEV_RETRY_COUNT; i++ )
    {
        uint32_t bytesSentThisVector = 0U;

        transportResult = pTransport->writev( pNetworkContext,
                                              pIoVectIterator,
                                              vectorsToBeSent );

        /* Send should not have any error. */
        if( transportResult < 0 )
        {
            TEST_MESSAGE( "Transport send data should not have any error." );
            retValue = false;
            break;
        }

        if( ( bytesToSend - bytesSentOrError ) < transportResult )
        {
            TEST_MESSAGE( "More data is sent than expected." );
            retValue = false;
            break;
        }

        bytesSentOrError += transportResult;
        bytesSentThisVector += transportResult;

        if( bytesToSend == bytesSentOrError )
        {
            /* All the data is sent. Break the retry loop. */
            break;
        }

        /* Increment the vector iterator based on the number of bytes sent. */
        while( ( pIoVectIterator <= &( pTransportTestVectorArray[ ioVecCount - 1U ] ) ) &&
               ( bytesSentThisVector >= pIoVectIterator->iov_len ) )
        {
            bytesSentThisVector -= ( uint32_t ) pIoVectIterator->iov_len;
            pIoVectIterator++;

            /* Update the number of vector which are yet to be sent. */
            vectorsToBeSent--;
        }

        /* Some of the bytes from the current vector pointed to by the vector iterator were sent
         * as well, update the length and the pointer to data in this vector. */
        if( ( bytesSentThisVector > 0U ) &&
            ( pIoVectIterator <= &( pTransportTestVectorArray[ ioVecCount - 1U ] ) ) )
        {
            /* Update the pointer to data to point to the first byte which needs to be sent in the next iteration. */
            pIoVectIterator->iov_base = ( const void * ) &( ( ( const uint8_t * ) pIoVectIterator->iov_base )[ bytesSentThisVector ] );
            pIoVectIterator->iov_len -= bytesSentThisVector;
        }
    }

    /* Check if all the data is sent. */
    if( bytesToSend != bytesSentOrError )
    {
        TEST_MESSAGE( "Fail to send all the data expected." );
        retValue = false;
    }

    return retValue;
}
#endif

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

/**
 * @brief Receive the data from transport network with retry.
 *
 * The receive API may return less btyes then requested. If the transport receive
 * function returns zero, it should represent the read operation can be retried
 * by calling the API function. The retry operation is handled in this function.
 */
static bool prvTransportRecvData( TransportInterface_t * pTransport,
                                  NetworkContext_t * pNetworkContext,
                                  uint8_t * pTransportTestBuffer,
                                  uint32_t recvSize )
{
    uint32_t transferTotal = 0U;
    int32_t transportResult = 0;
    uint32_t i;
    bool retValue = true;

    /* Initialize the receive buffer with TRANSPORT_TEST_BUFFER_GUARD_PATTERN. */
    memset( &( pTransportTestBuffer[ 0 ] ), TRANSPORT_TEST_BUFFER_GUARD_PATTERN, recvSize );

    for( i = 0U; i < TRANSPORT_TEST_SEND_RECEIVE_RETRY_COUNT; i++ )
    {
        transportResult = pTransport->recv( pNetworkContext,
                                            &pTransportTestBuffer[ transferTotal ],
                                            recvSize - transferTotal );

        /* Receive should not have any error. */
        if( transportResult < 0 )
        {
            TEST_MESSAGE( "Transport receive data should not have any error." );
            retValue = false;
            break;
        }

        if( ( recvSize - transferTotal ) < ( uint32_t ) transportResult )
        {
            TEST_MESSAGE( "More data is received than expected." );
            retValue = false;
            break;
        }

        transferTotal = transferTotal + transportResult;

        if( recvSize > transferTotal )
        {
            retValue = prvVerifyTestData( &pTransportTestBuffer[ transferTotal ], 0, ( recvSize - transferTotal ) );
        }
        else
        {
            /* This is test testSize == transferTotal case. All the data is received.
             * Break the retry loop. */
            break;
        }

        /* Delay to wait for the test data from the transport network. */
        FRTest_TimeDelay( TRANSPORT_TEST_DELAY_MS );
    }

    /* Check if all the data is recevied. */
    if( recvSize != transferTotal )
    {
        retValue = false;
        TEST_MESSAGE( "Fail to receive all the data expected." );
    }

    return retValue;
}

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

/**
 * @brief Verify the buffer guard of the test buffer.
 */
static void prvVerifyTestBufferGuard( uint8_t * pTransportTestBuffer )
{
    /* Prefix and Suffix guard buffers must never change. */
    TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE( TRANSPORT_TEST_BUFFER_GUARD_PATTERN,
                                         pTransportTestBuffer,
                                         TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH,
                                         "transportTestBuffer prefix guard should not be altered." );
    TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE( TRANSPORT_TEST_BUFFER_GUARD_PATTERN,
                                         &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH +
                                                                  TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH ] ),
                                         TRANSPORT_TEST_BUFFER_SUFFIX_GUARD_LENGTH,
                                         "transportTestBuffer suffix guard should not be altered." );
}

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

/**
 * @brief Thread function of the send/receive/compare test.
 */
static void prvSendRecvCompareFunc( void * pParam )
{
    threadParameter_t * pThreadParameter = pParam;
    uint8_t * pTransportTestBufferStart =
        &( pThreadParameter->transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    uint32_t testBufferSize;
    NetworkContext_t * pNetworkContext = pThreadParameter->pNetworkContext;

    pThreadParameter->xResult = true;

    for( testBufferSize = 1U; testBufferSize <= TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH; testBufferSize = testBufferSize + 1 )
    {
        /* Initialize the test data buffer. */
        prvInitializeTestData( pTransportTestBufferStart, testBufferSize );

        /* Send the test data to the server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvTransportSendData( pTestTransport,
                                                              pNetworkContext,
                                                              pTransportTestBufferStart,
                                                              testBufferSize );
        }

        /* Receive the test data from server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvTransportRecvData( pTestTransport,
                                                              pNetworkContext,
                                                              pTransportTestBufferStart,
                                                              testBufferSize );
        }

        /* Compare the test data received from server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvVerifyTestData( pTransportTestBufferStart,
                                                           testBufferSize,
                                                           TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
        }

        if( ( pThreadParameter->stopFlag == true ) || ( pThreadParameter->xResult == false ) )
        {
            break;
        }

        #if ( TRANSPORT_TEST_PRINT_DEBUG_PROGRESS == 1 )
            /* Output information to indicate the test is running. */
            UNITY_OUTPUT_CHAR( '.' );
        #endif
    }
}

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

#ifdef TRANSPORT_TEST_EXECUTE_WRITEV_TESTS
/**
 * @brief Thread function of the writev/receive/compare test.
 */
static void prvWritevRecvCompareFunc( void * pParam )
{
    threadParameter_t * pThreadParameter = pParam;
    uint8_t * pTransportTestBufferStart =
        &( pThreadParameter->transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    uint32_t testBufferSize;
    NetworkContext_t * pNetworkContext = pThreadParameter->pNetworkContext;
    TransportOutVector_t pTransportTestVectorArray[ 5U ];
    size_t ioVecCount;

    pThreadParameter->xResult = true;

    for( testBufferSize = 1U; testBufferSize <= TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH; testBufferSize = testBufferSize + 1 )
    {
        if( testBufferSize % 5 == 0 )
        {
            ioVecCount = 5U;
        }
        else if( testBufferSize % 3 == 0 )
        {
            ioVecCount = 3U;
        }
        else if( testBufferSize % 2 == 0 )
        {
            ioVecCount = 2U;
        }
        else
        {
            ioVecCount = 1U;
        }

        for( size_t index = 0U; index < ioVecCount; index ++ )
        {
            pTransportTestVectorArray[ index ].iov_base = &pTransportTestBufferStart[ index * ( testBufferSize / ioVecCount ) ];;
            pTransportTestVectorArray[ index ].iov_len = testBufferSize / ioVecCount;
        }

        /* Initialize the test data buffer. */
        prvInitializeTestData( pTransportTestBufferStart, testBufferSize );

        /* Send the test data to the server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvTransportWritevData( pTestTransport, pNetworkContext, pTransportTestVectorArray,
                                                                ioVecCount );
        }

        /* Receive the test data from server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                                              testBufferSize );
        }

        /* Compare the test data received from server. */
        if( pThreadParameter->xResult == true )
        {
            pThreadParameter->xResult = prvVerifyTestData( pTransportTestBufferStart, testBufferSize,
                                                           TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
        }

        if( ( pThreadParameter->stopFlag == true ) || ( pThreadParameter->xResult == false ) )
        {
            break;
        }

        #if ( TRANSPORT_TEST_PRINT_DEBUG_PROGRESS == 1 )
            /* Output information to indicate the test is running. */
            UNITY_OUTPUT_CHAR( '.' );
        #endif
    }
}
#endif

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

static void prvRetunZeroRetryFunc( void * pParam )
{
    threadParameter_t * pThreadParameter = pParam;
    int32_t transportResult = 0;
    /* Pointer of writable test buffer. */
    uint8_t * pTransportTestBufferStart =
        &( pThreadParameter->transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = pThreadParameter->pNetworkContext;

    pThreadParameter->xResult = true;

    /* Receive from remote server. No data will be recevied since no data is sent. */
    transportResult = pTestTransport->recv( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    if( transportResult != 0 )
    {
        TEST_MESSAGE( "No data to receive should return 0." );
        pThreadParameter->xResult = false;
    }

    /* The buffer should remain unchanged when zero returned. */
    if( pThreadParameter->xResult == true )
    {
        pThreadParameter->xResult = prvVerifyTestData( pTransportTestBufferStart, 0, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    }

    /* Send some data to echo server then retry the receive operation. The receive
     * operation should be able to receive all the data. */
    /* Initialize the test data. */
    prvInitializeTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    /* Send the test data to the server. */
    if( pThreadParameter->xResult == true )
    {
        pThreadParameter->xResult = prvTransportSendData( pTestTransport, pNetworkContext,
            pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    }

    /* Receive the test data from server. */
    if( pThreadParameter->xResult == true )
    {
        pThreadParameter->xResult = prvTransportRecvData( pTestTransport, pNetworkContext,
            pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    }

    /* Compare the test data received from server. */
    if( pThreadParameter->xResult == true )
    {
        pThreadParameter->xResult = prvVerifyTestData( pTransportTestBufferStart,
            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    }
}

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

static void prvNoDataToReceiveFunc( void * pParam )
{
    threadParameter_t * pThreadParameter = pParam;
    int32_t transportResult = 0;
    /* Pointer of writable test buffer. */
    uint8_t * pTransportTestBufferStart =
        &( pThreadParameter->transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = pThreadParameter->pNetworkContext;

    pThreadParameter->xResult = true;

    /* Receive from remote server. No data will be recevied since no data is sent. */
    transportResult = pTestTransport->recv( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    if( transportResult != 0 )
    {
        TEST_MESSAGE( "No data to receive should return 0." );
        pThreadParameter->xResult = false;
    }

    /* The buffer should remain unchanged when zero returned. */
    if( pThreadParameter->xResult == true )
    {
        pThreadParameter->xResult = prvVerifyTestData( pTransportTestBufferStart, 0, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    }
}

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

/**
 * @brief Test setup function for transport interface test.
 */
TEST_SETUP( Full_TransportInterfaceTest )
{
    NetworkConnectStatus_t networkConnectResult = NETWORK_CONNECT_SUCCESS;
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    uint32_t threadIndex;

    /* Reset the threadParameter status. */
    for( threadIndex = 0U; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        threadParameter[ threadIndex ].xNetworkConnected = false;
        threadParameter[ threadIndex ].xResult = false;
        threadParameter[ threadIndex ].stopFlag = false;
    }

    /* Ensure the test parameter is valid. testParam.pNetworkCredentials is not checked here. */
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pTransport, "testParam.pTransport should not be NULL." );
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pTransport->send, "testParam.pTransport->send should not be NULL." );
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pTransport->recv, "testParam.pTransport->recv should not be NULL." );
#ifdef TRANSPORT_TEST_EXECUTE_WRITEV_TESTS
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pTransport->writev, "testParam.pTransport->writev should not be NULL." );
#endif
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pNetworkContext, "testParam.pNetworkContext should not be NULL." );
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pSecondNetworkContext, "testParam.pSecondNetworkContext should not be NULL." );
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pNetworkConnect, "testParam.pNetworkConnect should not be NULL." );
    TEST_ASSERT_NOT_NULL_MESSAGE( testParam.pNetworkDisconnect, "testParam.pNetworkDisconnect should not be NULL." );

    /* Setup the trasnport structure to use the primary network context. */
    pTestTransport = testParam.pTransport;

    /* Initialize the transport test buffer with TRANSPORT_TEST_BUFFER_GUARD_PATTERN. */
    memset( pTransportTestBuffer, TRANSPORT_TEST_BUFFER_GUARD_PATTERN, TRANSPORT_TEST_BUFFER_TOTAL_LENGTH );

    /* Call the hook function implemented by the application to initialize the transport interface. */
    networkConnectResult = testParam.pNetworkConnect( pNetworkContext,
                                                      &testHostInfo, testParam.pNetworkCredentials );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( NETWORK_CONNECT_SUCCESS, networkConnectResult, "Network connect failed." );
    threadParameter[ TRANSPORT_TEST_INDEX ].xNetworkConnected = true;
}

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

/**
 * @brief Test tear down function for transport interface test.
 */
TEST_TEAR_DOWN( Full_TransportInterfaceTest )
{
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    prvVerifyTestBufferGuard( pTransportTestBuffer );

    /* Call the hook function implemented by the application to de-initialize the transport interface. */
    if( threadParameter[ TRANSPORT_TEST_INDEX ].xNetworkConnected == true )
    {
        testParam.pNetworkDisconnect( pNetworkContext );
        threadParameter[ TRANSPORT_TEST_INDEX ].xNetworkConnected = false;
    }
}

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

/**
 * @brief Test transport interface send with NULL network context pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportSend_NetworkContextNullPtr )
{
    int32_t sendResult = 0;
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;

    /* Send with NULL network context pointer should return negative value. */
    sendResult = pTestTransport->send( NULL,
                                       &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                       TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface send with NULL NetworkContext_t "
                                                        "pointer should return negative value." );
}

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

/**
 * @brief Test transport interface send with NULL buffer pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportSend_BufferNullPtr )
{
    int32_t sendResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Send with NULL buffer pointer should return negative value. */
    sendResult = pTestTransport->send( pNetworkContext, NULL, 1 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface send with NULL buffer "
                                                        "pointer should return negative value." );
}

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

/**
 * @brief Test transport interface send with zero byte to send handling.
 */
TEST( Full_TransportInterfaceTest, TransportSend_ZeroByteToSend )
{
    int32_t sendResult = 0;
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Send with zero byte to send should return negative value. */
    sendResult = pTestTransport->send( pNetworkContext,
                                       &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                       0 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface send with zero byte "
                                                        "to send should return negative value." );
}

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

/**
 * @brief Test transport interface recv with NULL network context pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_NetworkContextNullPtr )
{
    int32_t recvResult = 0;
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;

    /* Receive with NULL network context pointer should return negative value. */
    recvResult = pTestTransport->recv( NULL,
                                       &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                       TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, recvResult, "Transport interface recv with NULL network "
                                                        "context pointer should return negative value." );

    TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE( TRANSPORT_TEST_BUFFER_GUARD_PATTERN,
                                         &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                         TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                         "transportTestBuffer should not be altered." );
}

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

/**
 * @brief Test transport interface recv with NULL buffer pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_BufferNullPtr )
{
    int32_t recvResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Receive with NULL buffer pointer should return negative value. */
    recvResult = pTestTransport->recv( pNetworkContext, NULL, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, recvResult, "Transport interface recv with NULL buffer "
                                                        "pointer should return negative value." );
}

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

/**
 * @brief Test transport interface recv zero byte to receive handling.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_ZeroByteToRecv )
{
    int32_t recvResult = 0;
    uint8_t * pTransportTestBuffer = threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Receive with zero byte to recv should return negative value. */
    recvResult = pTestTransport->recv( pNetworkContext,
                                       &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                       0 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, recvResult, "Transport interface recv with zero "
                                                        "byte to recv should return negative value." );

    TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE( TRANSPORT_TEST_BUFFER_GUARD_PATTERN,
                                         &( pTransportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ),
                                         TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                         "transportTestBuffer should not be altered." );
}

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

/**
 * @brief Test transport interface with send one byte, receive and compare
 *
 * Test send receive beharivor in the following order.
 * Send : 1 byte
 * Send : ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1 ) bytes
 * Receive : TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH bytes
 */
TEST( Full_TransportInterfaceTest, Transport_SendOneByteRecvCompare )
{
    /* Pointer of writable test buffer. */
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    bool retValue;

    /* Initialize the test data buffer. */
    prvInitializeTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    /* Send the test data to the server. */
    retValue = prvTransportSendData( pTestTransport, pNetworkContext, pTransportTestBufferStart, 1U );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

    /* Send the rest test data to the server. */
    retValue = prvTransportSendData( pTestTransport, pNetworkContext, &pTransportTestBufferStart[ 1 ],
                                     ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1U ) );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

    /* Receive the test data from server. */
    retValue = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                     TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

    /* Compare the test data received from server. */
    retValue = prvVerifyTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                  TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );
}

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

/**
 * @brief Test transport interface with send, receive one byte and compare.
 *
 * Test send receive beharivor in the following order.
 * Send : TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH bytes
 * Receive : 1 byte
 * Receive : ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1 ) bytes
 */
TEST( Full_TransportInterfaceTest, Transport_SendRecvOneByteCompare )
{
    /* Pointer of writable test buffer. */
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    bool retValue;

    /* Initialize the test data buffer. */
    prvInitializeTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    /* Send the test data to the server. */
    retValue = prvTransportSendData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                     TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

    /* Reset the buffer. The buffer after received data is verified in prvVerifyTestData. */
    memset( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_GUARD_PATTERN, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    /* Receive one byte test data from server. */
    retValue = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart, 1U );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

    /* Compare the test data received from server. */
    retValue = prvVerifyTestData( pTransportTestBufferStart, 1U, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );

    /* Receive remain test data from server. */
    retValue = prvTransportRecvData( pTestTransport, pNetworkContext, &pTransportTestBufferStart[ 1U ],
                                     ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1U ) );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

    /* Compare remain test data received from server. */
    retValue = prvVerifyTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                  TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );
}

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

/**
 * @brief Test transport interface with send, receive and compare on variable length.
 */
TEST( Full_TransportInterfaceTest, Transport_SendRecvCompare )
{
    uint32_t testBufferSize;
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    bool retValue;

    for( testBufferSize = 1U; testBufferSize <= TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH; testBufferSize = testBufferSize + 1 )
    {
        /* Initialize the test data buffer. */
        prvInitializeTestData( pTransportTestBufferStart, testBufferSize );

        /* Send the test data to the server. */
        retValue = prvTransportSendData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                         testBufferSize );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

        /* Receive the test data from server. */
        retValue = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                         testBufferSize );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

        /* Compare the test data received from server. */
        retValue = prvVerifyTestData( pTransportTestBufferStart, testBufferSize, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );

        #if ( TRANSPORT_TEST_PRINT_DEBUG_PROGRESS == 1 )
            /* Output information to indicate the test is running. */
            UNITY_OUTPUT_CHAR( '.' );
        #endif
    }
}

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

/**
 * @brief Test transport interface with send, receive and compare on variable length in multiple threads.
 */
TEST( Full_TransportInterfaceTest, Transport_SendRecvCompareMultithreaded )
{
    NetworkConnectStatus_t networkConnectResult = NETWORK_CONNECT_SUCCESS;
    int timedWaitResult = 0;
    FRTestThreadHandle_t threadHandle[ TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT ];
    uint32_t threadIndex;

    /* The primary thread parameter already setup in the test setup function. */
    for( threadIndex = 1U; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        memset( threadParameter[ threadIndex ].transportTestBuffer,
                TRANSPORT_TEST_BUFFER_GUARD_PATTERN, TRANSPORT_TEST_BUFFER_TOTAL_LENGTH );
    }

    /* Connect the secondary network context. */
    networkConnectResult = testParam.pNetworkConnect( threadParameter[ TRANSPORT_TEST_SECOND_INDEX ].pNetworkContext,
                                                      &testHostInfo, testParam.pNetworkCredentials );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( NETWORK_CONNECT_SUCCESS, networkConnectResult, "Network connect failed." );
    threadParameter[ TRANSPORT_TEST_SECOND_INDEX ].xNetworkConnected = true;

    /* Create testing threads. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        /* threadParameters are initialized in test setup function. */
        threadHandle[ threadIndex ] = FRTest_ThreadCreate( prvSendRecvCompareFunc,
                                                           &threadParameter[ threadIndex ] );
    }

    /* Waiting for all test threads complete. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        if( threadHandle[ threadIndex ] != NULL )
        {
            timedWaitResult = FRTest_ThreadTimedJoin( threadHandle[ threadIndex ],
                                                      TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS );

            if( timedWaitResult != 0 )
            {
                /* The created test thread runs over TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS. */
                threadParameter[ threadIndex ].stopFlag = true;
            }
        }
        else
        {
            /* The thread is not created. */
            threadParameter[ threadIndex ].stopFlag = true;
        }
    }

    /* The primary thread network context will be closed in the test tear down function. */
    for( threadIndex = 1; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        testParam.pNetworkDisconnect( threadParameter[ threadIndex ].pNetworkContext );
        threadParameter[ threadIndex ].xNetworkConnected = false;
    }

    /* Check if every thread finish the test in time. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        /* Check the test buffer guard. */
        prvVerifyTestBufferGuard( threadParameter[ threadIndex ].transportTestBuffer );

        /* Check if the test thread timeout or not started. */
        TEST_ASSERT_MESSAGE( ( threadParameter[ threadIndex ].stopFlag != true ), "Test thread timeout or not created." );

        /* Check the test result. */
        TEST_ASSERT_MESSAGE( ( threadParameter[ threadIndex ].xResult == true ), "Test failed in test thread." );
    }
}

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

/**
 * @brief Test transport interface send function return value when disconnected.
 */
TEST( Full_TransportInterfaceTest, TransportSend_RemoteDisconnect )
{
    int32_t transportResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );

    /* Send the disconnect command to remote server. */
    transportResult = pTestTransport->send( pNetworkContext,
                                            TRANSPORT_TEST_DISCONNECT_COMMAND,
                                            strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ) );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ), transportResult,
                                     "Transport send should not have any error." );
    /* Delay to wait for the command send to server and server disconnection. */
    FRTest_TimeDelay( TRANSPORT_TEST_NETWORK_DELAY_MS );
    /* Negative value should be returned if a network disconnection has occurred. */
    transportResult = pTestTransport->send( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, transportResult,
                                         "Transport send should return negative value when disconnected." );
}

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

/**
 * @brief Test transport interface receive function return value when disconnected.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_RemoteDisconnect )
{
    int32_t transportResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );

    /* Send the disconnect command to remote server. */
    transportResult = pTestTransport->send( pNetworkContext,
                                            TRANSPORT_TEST_DISCONNECT_COMMAND,
                                            strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ) );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ), transportResult,
                                     "Transport send should not have any error." );

    /* Delay to wait for the command send to server. */
    FRTest_TimeDelay( TRANSPORT_TEST_NETWORK_DELAY_MS );

    /* Negative value should be returned if a network disconnection has occurred. */
    transportResult = pTestTransport->recv( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, transportResult,
                                         "Transport receive should return negative value when disconnected." );

    /* The buffer should remain unchanged when negative value returned. */
    TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE( TRANSPORT_TEST_BUFFER_GUARD_PATTERN,
                                         pTransportTestBufferStart,
                                         TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                         "transportTestBuffer should not be altered." );
}

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

/**
 * @brief Test transport interface receive function return value when no data to receive.
 *
 * Transport interface recv function returns zero due to no data to receive. A test
 * thread is used in this test to prevent the implementation blocked in the receive
 * function when no data to receive.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_NoDataToReceive )
{
    FRTestThreadHandle_t threadHandle;
    int timedWaitResult = 0;

    /* Create testing thread. threadParameters are initialized in test setup function. */
    threadHandle = FRTest_ThreadCreate( prvNoDataToReceiveFunc,
                                        &threadParameter[ TRANSPORT_TEST_INDEX ] );
    TEST_ASSERT_MESSAGE( threadHandle != NULL, "Create thread failed." );

    /* Waiting for the test thread complete. */
    timedWaitResult = FRTest_ThreadTimedJoin( threadHandle,
                                              TRANSPORT_TEST_WAIT_THREAD_RECEIVE_TIMEOUT_MS );

    if( timedWaitResult != 0 )
    {
        threadParameter[ TRANSPORT_TEST_INDEX ].stopFlag = true;
    }

    TEST_ASSERT_MESSAGE( timedWaitResult == 0, "Waiting for test thread receive data failed." );
}

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

/**
 * @brief Test transport interface receive return zero retry.
 *
 * Transport interface recv function returns zero due to no data to receive. The test
 * sends test data to remote server and retry the transport receive operation. Transport
 * receive function should be able to receive data. A test thread is used in this test
 * to prevent the implementation blocked in the receive function when no data to receive.
 */
TEST( Full_TransportInterfaceTest, TransportRecv_ReturnZeroRetry )
{
    FRTestThreadHandle_t threadHandle;
    int timedWaitResult = 0;

    /* Create testing thread. threadParameters are initialized in test setup function. */
    threadHandle = FRTest_ThreadCreate( prvRetunZeroRetryFunc,
                                        &threadParameter[ TRANSPORT_TEST_INDEX ] );
    TEST_ASSERT_MESSAGE( threadHandle != NULL, "Create thread failed." );

    /* Waiting for the test thread complete. */
    timedWaitResult = FRTest_ThreadTimedJoin( threadHandle,
                                              TRANSPORT_TEST_WAIT_THREAD_RECEIVE_TIMEOUT_MS );

    if( timedWaitResult != 0 )
    {
        threadParameter[ TRANSPORT_TEST_INDEX ].stopFlag = true;
    }

    TEST_ASSERT_MESSAGE( timedWaitResult == 0, "Waiting for test thread receive data failed." );
}

/*-----------------------------------------------------------*/
#ifdef TRANSPORT_TEST_EXECUTE_WRITEV_TESTS
/**
 * @brief Test transport interface writev with NULL network context pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportWritev_NetworkContextNullPtr )
{
    int32_t sendResult = 0;
    TransportOutVector_t transportTestVector = { threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH };

    /* writev with NULL network context pointer should return negative value. */
    sendResult = pTestTransport->writev( NULL,
                                         &( transportTestVector ),
                                         1U );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface writev with NULL NetworkContext_t "
                                                        "pointer should return negative value." );
}

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

/**
 * @brief Test transport interface writev with NULL buffer pointer handling.
 */
TEST( Full_TransportInterfaceTest, TransportWritev_BufferNullPtr )
{
    int32_t sendResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Writev with NULL buffer pointer should return negative value. */
    sendResult = pTestTransport->writev( pNetworkContext, NULL, 1 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface writev with NULL vector "
                                                        "pointer should return negative value." );
}

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

/**
 * @brief Test transport interface writev with zero vectors to send handling.
 */
TEST( Full_TransportInterfaceTest, TransportWritev_ZeroVectorsToSend )
{
    int32_t sendResult = 0;
    TransportOutVector_t transportTestVector = { threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH };
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    /* Send with zero byte to send should return negative value. */
    sendResult = pTestTransport->writev( pNetworkContext,
                                         &transportTestVector,
                                         0 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, sendResult, "Transport interface writev with zero vectors "
                                                        "to send should return negative value." );
}

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

/**
 * @brief Test transport interface writev when one of the vector array entries has a length
 * of 0.
 *
 * Transport interface writev should return a negative value if any member of the vector array
 * has the length 0.
 */
TEST( Full_TransportInterfaceTest, TransportWritev_OneVectorLengthZero )
{
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    TransportOutVector_t pTransportTestVectorArray[ 3U ];
    const size_t ioVecCount = 3U;
    int32_t transportResult;

    /* A pre-requisite for this test requires that the buffer should be at least 3 bytes long. */
    TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE( 3U,
                                                 TRANSPORT_TEST_BUFFER_TOTAL_LENGTH,
                                                 "The TRANSPORT_TEST_BUFFER_TOTAL_LENGTH must be greater than "
                                                 "or equal to 3 for this test to be run successfully." );

    /* Set the first array pointer. Set the length to 1. */
    pTransportTestVectorArray[ 0U ].iov_base = pTransportTestBufferStart;
    pTransportTestVectorArray[ 0U ].iov_len = 1U;

    /* Set the second array pointer. Set the length to 0. */
    pTransportTestVectorArray[ 1U ].iov_base = &pTransportTestBufferStart[ 1U ];
    pTransportTestVectorArray[ 1U ].iov_len = 0U;

    /* Set the third array pointer. Set the length to 1. */
    pTransportTestVectorArray[ 2U ].iov_base = &pTransportTestBufferStart[ 2U ];
    pTransportTestVectorArray[ 2U ].iov_len = 1U;

    TEST_ASSERT_FALSE_MESSAGE( prvTransportWritevData( pTestTransport, pNetworkContext, pTransportTestVectorArray, ioVecCount ),
                               "Sending of a vector array with zero length member must fail" );

    /* Negative value should be returned if a network disconnection has occurred. */
    transportResult = pTestTransport->recv( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( 1, transportResult,
                                     "Transport receive must only return one byte. Other bytes should not be sent by writev." );
}

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

/**
 * @brief Test transport interface writev when one of the vector array entries has a the data
 * pointer as NULL.
 *
 * Transport interface writev should return a negative value if any member of the vector array
 * has the data pointer pointing to NULL.
 */
TEST(Full_TransportInterfaceTest, TransportWritev_OneVectorBufferNullPtr )
{
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    TransportOutVector_t pTransportTestVectorArray[ 3U ];
    const size_t ioVecCount = 3U;
    int32_t transportResult;

    /* A pre-requisite for this test requires that the buffer should be at least 3 bytes long. */
    TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE( 3U,
                                                 TRANSPORT_TEST_BUFFER_TOTAL_LENGTH,
                                                 "The TRANSPORT_TEST_BUFFER_TOTAL_LENGTH must be greater than "
                                                 "or equal to 3 for this test to be run successfully." );

    /* Set the first array pointer. Set the length to 1. */
    pTransportTestVectorArray[ 0U ].iov_base = pTransportTestBufferStart;
    pTransportTestVectorArray[ 0U ].iov_len = 1U;

    /* Set the second array pointer to NULL. Set the length to 1. */
    pTransportTestVectorArray[ 1U ].iov_base = NULL;
    pTransportTestVectorArray[ 1U ].iov_len = 0U;

    /* Set the third array pointer. Set the length to 1. */
    pTransportTestVectorArray[ 2U ].iov_base = &pTransportTestBufferStart[ 2U ];
    pTransportTestVectorArray[ 2U ].iov_len = 1U;

    TEST_ASSERT_FALSE_MESSAGE( prvTransportWritevData( pTestTransport, pNetworkContext, pTransportTestVectorArray, ioVecCount ),
                               "Sending of a vector array with a NULL base pointer must fail." );

    /* Negative value should be returned if a network disconnection has occurred. */
    transportResult = pTestTransport->recv( pNetworkContext,
                                            pTransportTestBufferStart,
                                            TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( 1, transportResult,
                                     "Transport receive must only return one byte. Other bytes should not be sent by writev." );
}

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

/**
 * @brief Test transport interface with writev one byte, receive and compare
 *
 * Test writev receive beharivor in the following order.
 * Send : 1 byte
 * Send : ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1 ) bytes
 * Receive : TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH bytes
 */
TEST( Full_TransportInterfaceTest, Transport_WritevOneByteRecvCompare )
{
    /* Pointer of writable test buffer. */
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    bool retValue;
    TransportOutVector_t pTransportVectorArray[ 2U ];

    pTransportVectorArray[ 0U ].iov_base = pTransportTestBufferStart;
    pTransportVectorArray[ 0U ].iov_len = 1U;

    pTransportVectorArray[ 1U ].iov_base = &pTransportTestBufferStart[ 1U ];
    pTransportVectorArray[ 1U ].iov_len = ( TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH - 1U );

    /* Initialize the test data buffer. */
    prvInitializeTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );

    /* Send 1 byte of test data to the server. */
    retValue = prvTransportWritevData( pTestTransport, pNetworkContext, &pTransportVectorArray[ 0U ], 1U );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

    /* Send the rest test data to the server. */
    retValue = prvTransportWritevData( pTestTransport, pNetworkContext, &pTransportVectorArray[ 1U ], 1U );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

    /* Receive the test data from server. */
    retValue = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                     TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

    /* Compare the test data received from server. */
    retValue = prvVerifyTestData( pTransportTestBufferStart, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH,
                                  TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
    TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );
}

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

/**
 * @brief Test transport interface with writev, receive and compare on variable length.
 */
TEST( Full_TransportInterfaceTest, Transport_WritevRecvCompare )
{
    uint32_t testBufferSize;
    uint8_t * pTransportTestBufferStart =
        &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] );
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;
    bool retValue;
    TransportOutVector_t pTransportTestVectorArray[ 5U ];
    size_t ioVecCount;

    for( testBufferSize = 1U; testBufferSize <= TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH; testBufferSize = testBufferSize + 1 )
    {
        if( testBufferSize % 5 == 0 )
        {
            ioVecCount = 5U;
        }
        else if( testBufferSize % 3 == 0 )
        {
            ioVecCount = 3U;
        }
        else if( testBufferSize % 2 == 0 )
        {
            ioVecCount = 2U;
        }
        else
        {
            ioVecCount = 1U;
        }

        for( size_t index = 0U; index < ioVecCount; index ++ )
        {
            pTransportTestVectorArray[ index ].iov_base = &pTransportTestBufferStart[ index * ( testBufferSize / ioVecCount ) ];;
            pTransportTestVectorArray[ index ].iov_len = testBufferSize / ioVecCount;
        }

        /* Initialize the test data buffer. */
        prvInitializeTestData( pTransportTestBufferStart, testBufferSize );

        /* Send the test data to the server. */
        retValue = prvTransportWritevData( pTestTransport, pNetworkContext, pTransportTestVectorArray,
                                           ioVecCount );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Send test data failed." );

        /* Receive the test data from server. */
        retValue = prvTransportRecvData( pTestTransport, pNetworkContext, pTransportTestBufferStart,
                                         testBufferSize );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Receive test data failed." );

        /* Compare the test data received from server. */
        retValue = prvVerifyTestData( pTransportTestBufferStart, testBufferSize, TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH );
        TEST_ASSERT_MESSAGE( ( retValue == true ), "Verify test data failed." );

        #if ( TRANSPORT_TEST_PRINT_DEBUG_PROGRESS == 1 )
            /* Output information to indicate the test is running. */
            UNITY_OUTPUT_CHAR( '.' );
        #endif
    }
}

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

/**
 * @brief Test transport interface with writev, receive and compare on variable length in multiple threads.
 */
TEST( Full_TransportInterfaceTest, Transport_WritevRecvCompareMultithreaded )
{
    NetworkConnectStatus_t networkConnectResult = NETWORK_CONNECT_SUCCESS;
    int timedWaitResult = 0;
    FRTestThreadHandle_t threadHandle[ TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT ];
    uint32_t threadIndex;

    /* The primary thread parameter already setup in the test setup function. */
    for( threadIndex = 1U; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        memset( threadParameter[ threadIndex ].transportTestBuffer,
                TRANSPORT_TEST_BUFFER_GUARD_PATTERN, TRANSPORT_TEST_BUFFER_TOTAL_LENGTH );
    }

    /* Connect the secondary network context. */
    networkConnectResult = testParam.pNetworkConnect( threadParameter[ TRANSPORT_TEST_SECOND_INDEX ].pNetworkContext,
                                                      &testHostInfo, testParam.pNetworkCredentials );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( NETWORK_CONNECT_SUCCESS, networkConnectResult, "Network connect failed." );
    threadParameter[ TRANSPORT_TEST_SECOND_INDEX ].xNetworkConnected = true;

    /* Create testing threads. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        /* threadParameters are initialized in test setup function. */
        threadHandle[ threadIndex ] = FRTest_ThreadCreate( prvWritevRecvCompareFunc,
                                                           &threadParameter[ threadIndex ] );
    }

    /* Waiting for all test threads complete. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        if( threadHandle[ threadIndex ] != NULL )
        {
            timedWaitResult = FRTest_ThreadTimedJoin( threadHandle[ threadIndex ],
                                                      TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS );

            if( timedWaitResult != 0 )
            {
                /* The created test thread runs over TRANSPORT_TEST_WAIT_THREAD_TIMEOUT_MS. */
                threadParameter[ threadIndex ].stopFlag = true;
            }
        }
        else
        {
            /* The thread is not created. */
            threadParameter[ threadIndex ].stopFlag = true;
        }
    }

    /* The primary thread network context will be closed in the test tear down function. */
    for( threadIndex = 1; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        testParam.pNetworkDisconnect( threadParameter[ threadIndex ].pNetworkContext );
        threadParameter[ threadIndex ].xNetworkConnected = false;
    }

    /* Check if every thread finish the test in time. */
    for( threadIndex = 0; threadIndex < TRANSPORT_TEST_MULTI_THREAD_TASK_COUNT; threadIndex++ )
    {
        /* Check the test buffer guard. */
        prvVerifyTestBufferGuard( threadParameter[ threadIndex ].transportTestBuffer );

        /* Check if the test thread timeout or not started. */
        TEST_ASSERT_MESSAGE( ( threadParameter[ threadIndex ].stopFlag != true ), "Test thread timeout or not created." );

        /* Check the test result. */
        TEST_ASSERT_MESSAGE( ( threadParameter[ threadIndex ].xResult == true ), "Test failed in test thread." );
    }
}

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

/**
 * @brief Test transport interface writev function return value when disconnected.
 */
TEST( Full_TransportInterfaceTest, TransportWritev_RemoteDisconnect )
{
    int32_t transportResult = 0;
    NetworkContext_t * pNetworkContext = threadParameter[ TRANSPORT_TEST_INDEX ].pNetworkContext;

    TransportOutVector_t transportTestVectorDisconnect = { TRANSPORT_TEST_DISCONNECT_COMMAND, strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ) };
    TransportOutVector_t transportTestVectorbuffer = { &( threadParameter[ TRANSPORT_TEST_INDEX ].transportTestBuffer[ TRANSPORT_TEST_BUFFER_PREFIX_GUARD_LENGTH ] ), TRANSPORT_TEST_BUFFER_WRITABLE_LENGTH };

    /* Send the disconnect command to remote server. */
    transportResult = pTestTransport->writev( pNetworkContext,
                                              &transportTestVectorDisconnect,
                                              1U );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( strlen( TRANSPORT_TEST_DISCONNECT_COMMAND ), transportResult,
                                     "Transport writev should not have any error." );

    /* Delay to wait for the command send to server and server disconnection. */
    FRTest_TimeDelay( TRANSPORT_TEST_NETWORK_DELAY_MS );

    /* Negative value should be returned if a network disconnection has occurred. */
    transportResult = pTestTransport->writev( pNetworkContext,
                                              &transportTestVectorbuffer,
                                              1U );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, transportResult,
                                         "Transport writev should return negative value when disconnected." );
}

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

/**
 * @brief Test group runner for transport interface test against echo server.
 */
TEST_GROUP_RUNNER( Full_TransportInterfaceTest )
{
    /* Invalid parameter test. Disable or replace assert may be required to run these tests. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportSend_NetworkContextNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportSend_BufferNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportSend_ZeroByteToSend );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_NetworkContextNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_BufferNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_ZeroByteToRecv );

    /* Send and receive correctness test. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_SendOneByteRecvCompare );
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_SendRecvOneByteCompare );
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_SendRecvCompare );
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_SendRecvCompareMultithreaded );

    /* Disconnect test. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportSend_RemoteDisconnect );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_RemoteDisconnect );

    /* Transport behavior test. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_NoDataToReceive );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportRecv_ReturnZeroRetry );

#ifdef TRANSPORT_TEST_EXECUTE_WRITEV_TESTS
    /* Invalid parameter test. Disable or replace assert may be required to run these tests. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_NetworkContextNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_BufferNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_ZeroVectorsToSend );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_OneVectorBufferNullPtr );
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_OneVectorLengthZero );

    /* Send and receive correctness test. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_WritevOneByteRecvCompare );
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_WritevRecvCompare );
    RUN_TEST_CASE( Full_TransportInterfaceTest, Transport_WritevRecvCompareMultithreaded );

    /* Disconnect test. */
    RUN_TEST_CASE( Full_TransportInterfaceTest, TransportWritev_RemoteDisconnect );
#endif
}


//typedef struct NetworkContext NetworkContext_t;
//tls
/*-----------------------------------------------------------*/
static NetworkCredentials_t xNetworkCredentials = {0};
static TransportInterface_t xTransport = {0};

static NetworkContext_t xNetworkContext = {0};
static NetworkContext_t xSecondNetworkContext = {0};

#define mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS       (5000)

/*-----------------------------------------------------------*/
static NetworkConnectStatus_t prvTransportNetworkConnect(void *pvNetworkContext,
                                                        TestHostInfo_t *pxHostInfo,
                                                        void *pvNetworkCredentials)
{
    TlsTransportStatus_t xStatus;

    xStatus = TLS_FreeRTOS_Connect(pvNetworkContext,
                                    pxHostInfo->pHostName,
                                    pxHostInfo->port,
                                    pvNetworkCredentials,
                                    mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS,
                                    mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS);
    return xStatus;
}

/*-----------------------------------------------------------*/
static void prvTransportNetworkDisconnect(void *pvNetworkContext)
{
    TLS_FreeRTOS_Disconnect(pvNetworkContext);
}

#if (MQTT_TEST_ENABLED == 1)
uint32_t MqttTestGetTimeMs(void)
{
    TickType_t xTickCount = 0;
    uint32_t ulTimeMs = 0UL;

    /* Get the current tick count. */
    xTickCount = xTaskGetTickCount();

    /* Convert the ticks to milliseconds. */
    ulTimeMs = (uint32_t) (xTickCount * 1000 / configTICK_RATE_HZ);

    /* Reduce ulGlobalEntryTimeMs from obtained time so as to always return
     * the elapsed time in the application. */
    ulTimeMs = (uint32_t)(ulTimeMs - ulGlobalEntryTimeMs);

    return ulTimeMs;
}

void SetupMqttTestParam( MqttTestParam_t * pTestParam )
{
    configASSERT(pTestParam);

    /* Initialization of timestamp for MQTT */
    ulGlobalEntryTimeMs = MqttTestGetTimeMs();

     /* Setup the transport interface */
    xTransport.send = TLS_FreeRTOS_send;
    xTransport.recv = TLS_FreeRTOS_recv;
    xTransport.writev = NULL;

    /* Set the credentials for establishing a TLS connection. */
    xNetworkCredentials.pRootCa = (const unsigned char *)ECHO_SERVER_ROOT_CA;
    xNetworkCredentials.rootCaSize = sizeof(ECHO_SERVER_ROOT_CA);

#ifdef democonfigCLIENT_CERTIFICATE_PEM
    xNetworkCredentials.pClientCert = ( const unsigned char * ) democonfigCLIENT_CERTIFICATE_PEM;
    xNetworkCredentials.clientCertSize = sizeof( democonfigCLIENT_CERTIFICATE_PEM );
    xNetworkCredentials.pPrivateKey = ( const unsigned char * ) democonfigCLIENT_PRIVATE_KEY_PEM;
    xNetworkCredentials.privateKeySize = sizeof( democonfigCLIENT_PRIVATE_KEY_PEM );
#endif
    xNetworkCredentials.disableSni = pdFALSE;

    pTestParam->pTransport = &xTransport;
    pTestParam->pNetworkContext = &xNetworkContext;
    pTestParam->pSecondNetworkContext = &xSecondNetworkContext;
    pTestParam->pNetworkConnect = prvTransportNetworkConnect;
    pTestParam->pNetworkDisconnect = prvTransportNetworkDisconnect;
    pTestParam->pNetworkCredentials = &xNetworkCredentials;
    pTestParam->pGetTimeMs = MqttTestGetTimeMs;
}
#endif

/*-----------------------------------------------------------*/
#if (TRANSPORT_INTERFACE_TEST_ENABLED == 1)
#if 0    //pkcs11_mbedtls
void SetupTransportTestParam( TransportTestParam_t * pTestParam )
{
    configASSERT(pTestParam != NULL);

    /* Setup the transport interface */
    xTransport.send = TLS_FreeRTOS_send;
    xTransport.recv = TLS_FreeRTOS_recv;
    xTransport.writev = NULL;

    xNetworkContext.pParams = pvPortMalloc(sizeof(TlsTransportParams_t));
    xSecondNetworkContext.pParams = pvPortMalloc(sizeof(TlsTransportParams_t));


    /* Set the credentials for establishing a TLS connection. */
    xNetworkCredentials.pRootCa = (const unsigned char *)ECHO_SERVER_ROOT_CA;
    xNetworkCredentials.rootCaSize = sizeof(ECHO_SERVER_ROOT_CA);

//    xNetworkCredentials.pUserName = AWS_IOT_METRICS_STRING;
//    xNetworkCredentials.userNameSize = ( uint16_t ) strlen( AWS_IOT_METRICS_STRING );

    xNetworkCredentials.pUserName = NULL;
    xNetworkCredentials.userNameSize = 0U;

    /* Password for authentication is not used. */
    xNetworkCredentials.pPassword = NULL;
    xNetworkCredentials.passwordSize = 0U;

    xNetworkCredentials.pClientCertLabel = PKCS11_TEST_LABEL_DEVICE_CERTIFICATE_FOR_TLS;
    xNetworkCredentials.pPrivateKeyLabel = PKCS11_TEST_LABEL_DEVICE_PRIVATE_KEY_FOR_TLS;

    xNetworkCredentials.disableSni = pdFALSE;

    pTestParam->pTransport = &xTransport;
    pTestParam->pNetworkContext = &xNetworkContext;
    pTestParam->pSecondNetworkContext = &xSecondNetworkContext;
    pTestParam->pNetworkConnect = prvTransportNetworkConnect;
    pTestParam->pNetworkDisconnect = prvTransportNetworkDisconnect;
    pTestParam->pNetworkCredentials = &xNetworkCredentials;
}
#endif
void SetupTransportTestParam( TransportTestParam_t * pTestParam )
{
    configASSERT(pTestParam != NULL);

    /* Setup the transport interface */
    xTransport.send = TLS_FreeRTOS_send;
    xTransport.recv = TLS_FreeRTOS_recv;
    xTransport.writev = NULL;

    xNetworkContext.pParams = pvPortMalloc(sizeof(TlsTransportParams_t));
    xSecondNetworkContext.pParams = pvPortMalloc(sizeof(TlsTransportParams_t));

    /* Set the credentials for establishing a TLS connection. */
    xNetworkCredentials.pRootCa = (const unsigned char *)ECHO_SERVER_ROOT_CA;
    xNetworkCredentials.rootCaSize = sizeof(ECHO_SERVER_ROOT_CA);
    xNetworkCredentials.pClientCert = ( const unsigned char * ) TRANSPORT_CLIENT_CERTIFICATE;
    xNetworkCredentials.clientCertSize = sizeof( TRANSPORT_CLIENT_CERTIFICATE );
    xNetworkCredentials.pPrivateKey = ( const unsigned char * ) TRANSPORT_CLIENT_PRIVATE_KEY;
    xNetworkCredentials.privateKeySize = sizeof( TRANSPORT_CLIENT_PRIVATE_KEY );

    xNetworkCredentials.disableSni = pdFALSE;

    pTestParam->pTransport = &xTransport;
    pTestParam->pNetworkContext = &xNetworkContext;
    pTestParam->pSecondNetworkContext = &xSecondNetworkContext;
    pTestParam->pNetworkConnect = prvTransportNetworkConnect;
    pTestParam->pNetworkDisconnect = prvTransportNetworkDisconnect;
    pTestParam->pNetworkCredentials = &xNetworkCredentials;
}

#endif

int RunTransportInterfaceTest( void )
{
    int status = -1;

    /* Assign the TransportInterface_t pointer used in test cases. */
    SetupTransportTestParam( &testParam );
    threadParameter[ 0 ].pNetworkContext = testParam.pNetworkContext;
    threadParameter[ 1 ].pNetworkContext = testParam.pSecondNetworkContext;
    testHostInfo.pHostName = ECHO_SERVER_ENDPOINT;
    testHostInfo.port = ECHO_SERVER_PORT;

    /* Initialize unity. */
    UnityFixture.Verbose = 1;
    UnityFixture.GroupFilter = 0;
    UnityFixture.NameFilter = 0;
    UnityFixture.RepeatCount = 1;
    UNITY_BEGIN();

    /* Run the test group. */
    RUN_TEST_GROUP( Full_TransportInterfaceTest );

    status = UNITY_END();

    return status;
}

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

#if 0                    //plaintext
//static NetworkCredentials_t xNetworkCredentials = {0};
static TransportInterface_t xTransport = {0};

static NetworkContext_t xNetworkContext = {0};
static NetworkContext_t xSecondNetworkContext = {0};

#define mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS       (750)

/*-----------------------------------------------------------*/
static NetworkConnectStatus_t prvTransportNetworkConnect(void *pvNetworkContext,
                                                        TestHostInfo_t *pxHostInfo,
                                                        void *pvNetworkCredentials)
{
    PlaintextTransportStatus_t xStatus;

    xStatus = Plaintext_FreeRTOS_Connect(pvNetworkContext,
                                    pxHostInfo->pHostName,
                                    pxHostInfo->port,
                                    mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS,
                                    mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS);
    return xStatus;
}

/*-----------------------------------------------------------*/
static NetworkConnectStatus_t prvTransportNetworkDisconnect(void *pvNetworkContext)
{
    Plaintext_FreeRTOS_Disconnect(pvNetworkContext);
}

#if (TRANSPORT_INTERFACE_TEST_ENABLED == 1)

void SetupTransportTestParam( TransportTestParam_t * pTestParam )
{
    configASSERT(pTestParam != NULL);

    /* Setup the transport interface */
    xTransport.send = Plaintext_FreeRTOS_send;
    xTransport.recv = Plaintext_FreeRTOS_recv;
    xTransport.writev = NULL;

    xNetworkContext.pParams = pvPortMalloc(sizeof(PlaintextTransportParams_t));
    xSecondNetworkContext.pParams = pvPortMalloc(sizeof(PlaintextTransportParams_t));

//    /* Set the credentials for establishing a TLS connection. */
//    xNetworkCredentials.pRootCa = (const unsigned char *)ECHO_SERVER_ROOT_CA;
//    xNetworkCredentials.rootCaSize = sizeof(ECHO_SERVER_ROOT_CA);
//
//    xNetworkCredentials.pClientCert = ( const unsigned char * ) TRANSPORT_CLIENT_CERTIFICATE;
//    xNetworkCredentials.clientCertSize = sizeof( TRANSPORT_CLIENT_CERTIFICATE );
//    xNetworkCredentials.pPrivateKey = ( const unsigned char * ) TRANSPORT_CLIENT_PRIVATE_KEY;
//    xNetworkCredentials.privateKeySize = sizeof( TRANSPORT_CLIENT_PRIVATE_KEY );
//
//    xNetworkCredentials.disableSni = pdFALSE;

    pTestParam->pTransport = &xTransport;
    pTestParam->pNetworkContext = &xNetworkContext;
    pTestParam->pSecondNetworkContext = &xSecondNetworkContext;
    pTestParam->pNetworkConnect = prvTransportNetworkConnect;
    pTestParam->pNetworkDisconnect = prvTransportNetworkDisconnect;
    pTestParam->pNetworkCredentials = NULL;
}
#endif
#endif
#endif /* if ( TRANSPORT_INTERFACE_TEST_ENABLED == 1 ) */
