/* Include Unity header */
#include "unity.h"

/* Include standard libraries */
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include "mock_FreeRTOS_IP.h"
#include "mock_FreeRTOS_IP_Timers.h"
#include "mock_FreeRTOS_Sockets.h"
#include "mock_FreeRTOS_IP_Private.h"
#include "mock_FreeRTOS_UDP_IP.h"
#include "mock_FreeRTOS_ARP.h"
#include "mock_task.h"
#include "mock_NetworkBufferManagement.h"
#include "mock_FreeRTOS_DHCP_mock.h"

#include "FreeRTOS_DHCP.h"

#include "FreeRTOS_DHCP_stubs.c"
#include "catch_assert.h"

#include "FreeRTOSIPConfig.h"

extern Socket_t xDHCPSocket;
extern DHCPData_t xDHCPData;

static const char * pcHostName = "Unit-Test";

static NetworkBufferDescriptor_t * pxGlobalNetworkBuffer[ 10 ];
static uint8_t GlobalBufferCounter = 0;
static NetworkBufferDescriptor_t * GetNetworkBuffer( size_t SizeOfEthBuf,
                                                     long unsigned int xTimeToBlock,
                                                     int callbacks )
{
    NetworkBufferDescriptor_t * pxNetworkBuffer = malloc( sizeof( NetworkBufferDescriptor_t ) );

    pxNetworkBuffer->pucEthernetBuffer = malloc( SizeOfEthBuf );

    /* Ignore the callback count. */
    ( void ) callbacks;
    /* Ignore the timeout. */
    ( void ) xTimeToBlock;

    /* Set the global network buffer so that memory can be freed later on. */
    pxGlobalNetworkBuffer[ GlobalBufferCounter++ ] = pxNetworkBuffer;

    return pxNetworkBuffer;
}

static void ReleaseNetworkBuffer( void )
{
    /* Free the ethernet buffer. */
    free( pxGlobalNetworkBuffer[ --GlobalBufferCounter ]->pucEthernetBuffer );
    /* Free the network buffer. */
    free( pxGlobalNetworkBuffer[ GlobalBufferCounter ] );
}

static void ReleaseUDPBuffer( const void * temp,
                              int callbacks )
{
    /* Should just call network buffer. */
    ReleaseNetworkBuffer();
}

#define xSizeofUDPBuffer    300
static uint8_t pucUDPBuffer[ xSizeofUDPBuffer ];

static int32_t RecvFromStub( Socket_t xSocket,
                             void * pvBuffer,
                             size_t uxBufferLength,
                             BaseType_t xFlags,
                             struct freertos_sockaddr * pxSourceAddress,
                             socklen_t * pxSourceAddressLength,
                             int callbacks )
{
    switch( callbacks )
    {
        case 0:
            memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
            break;

        case 1:
            /* Put in correct DHCP cookie. */
            ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
            /* Put incorrect DHCP opcode. */
            ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE + 10;
            break;
    }

    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    return xSizeofUDPBuffer;
}

static uint8_t DHCP_header[] =
{
    dhcpREPLY_OPCODE,       /**< Operation Code: Specifies the general type of message. */
    0x01,                   /**< Hardware type used on the local network. */
    0x06,                   /**< Hardware Address Length: Specifies how long hardware
                             * addresses are in this message. */
    0x02,                   /**< Hops. */
    0x01, 0xAB, 0xCD, 0xEF, /**< A 32-bit identification field generated by the client,
                             * to allow it to match up the request with replies received
                             * from DHCP servers. */
    0x01,                   /**< Number of seconds elapsed since a client began an attempt to acquire or renew a lease. */
    0x00,                   /**< Just one bit used to indicate broadcast. */
    0xC0, 0xA8, 0x00, 0x0A, /**< Client's IP address if it has one or 0 is put in this field. */
    0x00, 0xAA, 0xAA, 0xAA, /**< The IP address that the server is assigning to the client. */
    0x00, 0xAA, 0xAA, 0xAA, /**< The DHCP server address that the client should use. */
    0x00, 0xAA, 0xAA, 0xAA  /**< Gateway IP address in case the server client are on different subnets. */
};

static uint8_t * ucGenericPtr;
static int32_t ulGenericLength;
static int32_t FreeRTOS_recvfrom_Generic( Socket_t xSocket,
                                          void * pvBuffer,
                                          size_t uxBufferLength,
                                          BaseType_t xFlags,
                                          struct freertos_sockaddr * pxSourceAddress,
                                          socklen_t * pxSourceAddressLength,
                                          int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = ucGenericPtr;
    }

    return ulGenericLength;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromLessBytesNoTimeout( Socket_t xSocket,
                                                                          void * pvBuffer,
                                                                          size_t uxBufferLength,
                                                                          BaseType_t xFlags,
                                                                          struct freertos_sockaddr * pxSourceAddress,
                                                                          socklen_t * pxSourceAddressLength,
                                                                          int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );

    return sizeof( DHCPMessage_IPv4_t ) - 1;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsFalseCookieNoTimeout( Socket_t xSocket,
                                                                                    void * pvBuffer,
                                                                                    size_t uxBufferLength,
                                                                                    BaseType_t xFlags,
                                                                                    struct freertos_sockaddr * pxSourceAddress,
                                                                                    socklen_t * pxSourceAddressLength,
                                                                                    int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsFalseOpcodeNoTimeout( Socket_t xSocket,
                                                                                    void * pvBuffer,
                                                                                    size_t uxBufferLength,
                                                                                    BaseType_t xFlags,
                                                                                    struct freertos_sockaddr * pxSourceAddress,
                                                                                    socklen_t * pxSourceAddressLength,
                                                                                    int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsCorrectCookieAndOpcodeNoTimeout( Socket_t xSocket,
                                                                                               void * pvBuffer,
                                                                                               size_t uxBufferLength,
                                                                                               BaseType_t xFlags,
                                                                                               struct freertos_sockaddr * pxSourceAddress,
                                                                                               socklen_t * pxSourceAddressLength,
                                                                                               int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put incorrect DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccessCorrectTxID( Socket_t xSocket,
                                                                          void * pvBuffer,
                                                                          size_t uxBufferLength,
                                                                          BaseType_t xFlags,
                                                                          struct freertos_sockaddr * pxSourceAddress,
                                                                          socklen_t * pxSourceAddressLength,
                                                                          int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_CorrectAddrType( Socket_t xSocket,
                                                                               void * pvBuffer,
                                                                               size_t uxBufferLength,
                                                                               BaseType_t xFlags,
                                                                               struct freertos_sockaddr * pxSourceAddress,
                                                                               socklen_t * pxSourceAddressLength,
                                                                               int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );
    /* Put in address type as ethernet. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_CorrectAddrLen( Socket_t xSocket,
                                                                              void * pvBuffer,
                                                                              size_t uxBufferLength,
                                                                              BaseType_t xFlags,
                                                                              struct freertos_sockaddr * pxSourceAddress,
                                                                              socklen_t * pxSourceAddressLength,
                                                                              int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );
    /* Put in address type as ethernet. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
    /* Put in correct address length. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulYourIPAddress_yiaddr = 0xFFFFFFFF;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_LocalHostAddr( Socket_t xSocket,
                                                                             void * pvBuffer,
                                                                             size_t uxBufferLength,
                                                                             BaseType_t xFlags,
                                                                             struct freertos_sockaddr * pxSourceAddress,
                                                                             socklen_t * pxSourceAddressLength,
                                                                             int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );
    /* Put in address type as ethernet. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
    /* Put in correct address length. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
    /* Non local host address. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulYourIPAddress_yiaddr = 0x01FFFF7F;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_NonLocalHostAddr( Socket_t xSocket,
                                                                                void * pvBuffer,
                                                                                size_t uxBufferLength,
                                                                                BaseType_t xFlags,
                                                                                struct freertos_sockaddr * pxSourceAddress,
                                                                                socklen_t * pxSourceAddressLength,
                                                                                int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId + 1 );
    /* Put in address type as ethernet. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
    /* Put in correct address length. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
    /* Non local host address. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulYourIPAddress_yiaddr = 0x01FFFF3F;

    return xSizeofUDPBuffer;
}

static int32_t FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_LocalMACAddrNotMatching( Socket_t xSocket,
                                                                                       void * pvBuffer,
                                                                                       size_t uxBufferLength,
                                                                                       BaseType_t xFlags,
                                                                                       struct freertos_sockaddr * pxSourceAddress,
                                                                                       socklen_t * pxSourceAddressLength,
                                                                                       int callbacks )
{
    if( xFlags == FREERTOS_ZERO_COPY )
    {
        *( ( uint8_t ** ) pvBuffer ) = pucUDPBuffer;
    }

    memset( pucUDPBuffer, 0, xSizeofUDPBuffer );
    /* Put in correct DHCP cookie. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulDHCPCookie = dhcpCOOKIE;
    /* Put in correct DHCP opcode. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucOpcode = dhcpREPLY_OPCODE;
    /* Put in correct DHCP Tx ID. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );
    /* Put in address type as ethernet. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
    /* Put in correct address length. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
    /* Non local host address. */
    ( ( struct xDHCPMessage_IPv4 * ) pucUDPBuffer )->ulYourIPAddress_yiaddr = 0x01FFFF3F;

    return xSizeofUDPBuffer;
}

void test_xIsDHCPSocket( void )
{
    BaseType_t xReturn;
    struct xSOCKET xTestSocket;

    xDHCPSocket = &xTestSocket;

    /************************************/
    /* Test by NOT giving DHCP socket. */
    xReturn = xIsDHCPSocket( NULL );
    TEST_ASSERT_EQUAL( pdFALSE, xReturn );

    /************************************/
    /* Test by giving DHCP socket. */
    xReturn = xIsDHCPSocket( xDHCPSocket );
    TEST_ASSERT_EQUAL( pdTRUE, xReturn );
}

void test_vDHCPSetPreferredIPAddress( void )
{
    uint32_t ulReturn;
    uint32_t ulIPAddress = 0xABCDEFAB;
    uint32_t ulSecondIP = 0xAABBCCDD;

    xDHCPData.ulPreferredIPAddress = ulSecondIP;

    ulReturn = vDHCPSetPreferredIPAddress( ulIPAddress );
    TEST_ASSERT_EQUAL( ulSecondIP, ulReturn );

    ulReturn = vDHCPSetPreferredIPAddress( ulSecondIP );
    TEST_ASSERT_EQUAL( ulIPAddress, ulReturn );
}

void test_eGetDHCPState( void )
{
    DHCPData_t xTestData;
    eDHCPState_t eReturn;
    int i;

    for( i = 0; i < sizeof( xTestData.eDHCPState ); i++ )
    {
        /* Modify the global state. */
        xDHCPData.eDHCPState = i;
        eReturn = eGetDHCPState();
        TEST_ASSERT_EQUAL( i, eReturn );
    }
}

void test_vDHCPProcess_NotResetAndIncorrectState( void )
{
    xDHCPData.eDHCPState = eSendDHCPRequest;
    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* Since the expected state is incorrect, the state
     * should remain the same. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_ResetAndIncorrectStateWithRNGFail( void )
{
    xDHCPData.eDHCPState = eSendDHCPRequest;
    /* Make random number generation fail. */
    xApplicationGetRandomNumber_ExpectAndReturn( &( xDHCPData.ulTransactionId ), pdFALSE );
    vDHCPProcess( pdTRUE, eWaitingSendFirstDiscover );

    /* Expected state is incorrect, but we are trying to reset
     * the DHCP the state machine. */
    TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_ResetAndIncorrectStateWithRNGSuccessSocketCreationFail( void )
{
    /* Test all the valid and invalid entries. */
    for( int i = 0; i < ( eNotUsingLeasedAddress * 2 ); i++ )
    {
        /* This should get assigned to a given value. */
        xDHCPSocket = NULL;
        /* Put any state. */
        xDHCPData.eDHCPState = eSendDHCPRequest;
        /* This should be reset to 0. */
        xDHCPData.xUseBroadcast = 1;
        /* This should be reset as well */
        xDHCPData.ulOfferedIPAddress = 0xAAAAAAAA;
        /* And this too. */
        xDHCPData.ulDHCPServerAddress = 0xABABABAB;


        /* Make random number generation pass. */
        xApplicationGetRandomNumber_ExpectAndReturn( &( xDHCPData.ulTransactionId ), pdTRUE );
        /* return an invalid socket. */
        FreeRTOS_socket_ExpectAndReturn( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP, FREERTOS_INVALID_SOCKET );
        /* See if the timer is reloaded. */
        vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );
        /* Try all kinds of states. */
        vDHCPProcess( pdTRUE, i );

        /* Expected state is incorrect, but we are trying to reset
         * the DHCP the state machine. */
        TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
        TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
        TEST_ASSERT_EQUAL( 0, xDHCPData.xUseBroadcast );
        /* This should be reset as well */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulOfferedIPAddress );
        /* And this too. */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulDHCPServerAddress );
    }
}

void test_vDHCPProcess_ResetAndIncorrectStateWithRNGSuccessSocketBindFail( void )
{
    struct xSOCKET xTestSocket;

    /* Test all the valid and invalid entries. */
    for( int i = 0; i < ( eNotUsingLeasedAddress * 2 ); i++ )
    {
        /* This should get assigned to a given value. */
        xDHCPSocket = NULL;
        /* Put any state. */
        xDHCPData.eDHCPState = eSendDHCPRequest;
        /* This should be reset to 0. */
        xDHCPData.xUseBroadcast = 1;
        /* This should be reset as well */
        xDHCPData.ulOfferedIPAddress = 0xAAAAAAAA;
        /* And this too. */
        xDHCPData.ulDHCPServerAddress = 0xABABABAB;


        /* Make random number generation pass. */
        xApplicationGetRandomNumber_ExpectAndReturn( &( xDHCPData.ulTransactionId ), pdTRUE );
        /* Return a valid socket. */
        FreeRTOS_socket_ExpectAndReturn( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP, &xTestSocket );
        /* Ignore the inputs to setting the socket options. */
        FreeRTOS_setsockopt_ExpectAnyArgsAndReturn( pdPASS );
        FreeRTOS_setsockopt_ExpectAnyArgsAndReturn( pdPASS );
        /* Make sure that binding fails. Return anything except zero. */
        vSocketBind_ExpectAnyArgsAndReturn( pdTRUE );
        /* Then expect the socket to be closed. */
        vSocketClose_ExpectAndReturn( &xTestSocket, NULL );
        /* See if the timer is reloaded. */
        vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );
        /* Try all kinds of states. */
        vDHCPProcess( pdTRUE, i );

        /* Expected state is incorrect, but we are trying to reset
         * the DHCP the state machine. */
        TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
        TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
        TEST_ASSERT_EQUAL( 0, xDHCPData.xUseBroadcast );
        /* This should be reset as well */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulOfferedIPAddress );
        /* And this too. */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulDHCPServerAddress );
    }
}

void test_vDHCPProcess_ResetAndIncorrectStateWithRNGSuccessSocketSuccess( void )
{
    struct xSOCKET xTestSocket;

    /* Test all the valid and invalid entries. */
    for( int i = 0; i < ( eNotUsingLeasedAddress * 2 ); i++ )
    {
        /* This should get assigned to a given value. */
        xDHCPSocket = NULL;
        /* Put any state. */
        xDHCPData.eDHCPState = eSendDHCPRequest;
        /* This should be reset to 0. */
        xDHCPData.xUseBroadcast = 1;
        /* This should be reset as well */
        xDHCPData.ulOfferedIPAddress = 0xAAAAAAAA;
        /* And this too. */
        xDHCPData.ulDHCPServerAddress = 0xABABABAB;


        /* Make random number generation pass. */
        xApplicationGetRandomNumber_ExpectAndReturn( &( xDHCPData.ulTransactionId ), pdTRUE );
        /* Return a valid socket. */
        FreeRTOS_socket_ExpectAndReturn( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP, &xTestSocket );
        /* Ignore the inputs to setting the socket options. */
        FreeRTOS_setsockopt_ExpectAnyArgsAndReturn( pdPASS );
        FreeRTOS_setsockopt_ExpectAnyArgsAndReturn( pdPASS );
        /* Make sure that binding fails. Return anything except zero. */
        vSocketBind_ExpectAnyArgsAndReturn( 0 );
        /* See if the timer is reloaded. */
        vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );
        /* Try all kinds of states. */
        vDHCPProcess( pdTRUE, i );

        /* Expected state is incorrect, but we are trying to reset
         * the DHCP the state machine. */
        TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
        TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
        TEST_ASSERT_EQUAL( 0, xDHCPData.xUseBroadcast );
        /* This should be reset as well */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulOfferedIPAddress );
        /* And this too. */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulDHCPServerAddress );
    }
}

void test_vDHCPProcess_ResetAndIncorrectStateWithSocketAlreadyCreated( void )
{
    struct xSOCKET xTestSocket;

    /* Test all the valid and invalid entries. */
    for( int i = 0; i < ( eNotUsingLeasedAddress * 2 ); i++ )
    {
        /* This should remain unchanged. */
        xDHCPSocket = &xTestSocket;
        /* Put any state. */
        xDHCPData.eDHCPState = eSendDHCPRequest;
        /* This should be reset to 0. */
        xDHCPData.xUseBroadcast = 1;
        /* This should be reset as well */
        xDHCPData.ulOfferedIPAddress = 0xAAAAAAAA;
        /* And this too. */
        xDHCPData.ulDHCPServerAddress = 0xABABABAB;
        /* And this should be updated. */
        xDHCPData.xDHCPTxPeriod = 0;

        /* Make random number generation pass. */
        xApplicationGetRandomNumber_ExpectAndReturn( &( xDHCPData.ulTransactionId ), pdTRUE );
        /* See if the timer is reloaded. */
        vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );
        /* Try all kinds of states. */
        vDHCPProcess( pdTRUE, i );

        /* Expected state is incorrect, but we are trying to reset
         * the DHCP the state machine. */
        TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
        TEST_ASSERT_EQUAL( xDHCPSocket, &xTestSocket );
        TEST_ASSERT_EQUAL( 0, xDHCPData.xUseBroadcast );
        /* This should be reset as well */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulOfferedIPAddress );
        /* And this too. */
        TEST_ASSERT_EQUAL( 0, xDHCPData.ulDHCPServerAddress );
        /* This should be updated. */
        TEST_ASSERT_EQUAL( dhcpINITIAL_DHCP_TX_PERIOD, xDHCPData.xDHCPTxPeriod );
    }
}

void test_vDHCPProcess_CorrectStateDHCPHookFailsDHCPSocketNULL( void )
{
    /* The DHCP socket is NULL. */
    xDHCPSocket = NULL;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Make sure that the local IP address is uninitialised. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;
    /* Put a verifiable value. */
    xNetworkAddressing.ulDefaultIPAddress = 0x12345678;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, ( eDHCPContinue + eDHCPUseDefaults ) << 2 );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    vIPNetworkUpCalls_Ignore();

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that we are not using leased address. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that the local IP address pointer indicates that. */
    TEST_ASSERT_EQUAL( xNetworkAddressing.ulDefaultIPAddress, *ipLOCAL_IP_ADDRESS_POINTER );
}

void test_vDHCPProcess_CorrectStateDHCPHookFailsDHCPSocketNonNULL( void )
{
    struct xSOCKET xTestSocket;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Make sure that the local IP address is uninitialised. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;
    /* Put a verifiable value. */
    xNetworkAddressing.ulDefaultIPAddress = 0x12345678;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, ( eDHCPContinue + eDHCPUseDefaults ) << 2 );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    /* Ignore the call. */
    vIPNetworkUpCalls_Ignore();
    /* Expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( xDHCPSocket, NULL );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that we are not using leased address. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that the local IP address pointer indicates that. */
    TEST_ASSERT_EQUAL( xNetworkAddressing.ulDefaultIPAddress, *ipLOCAL_IP_ADDRESS_POINTER );
}

void test_vDHCPProcess_CorrectStateDHCPHookDefaultReturn( void )
{
    struct xSOCKET xTestSocket;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Make sure that the local IP address is uninitialised. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;
    /* Put a verifiable value. */
    memset( &xNetworkAddressing, 0xAA, sizeof( xNetworkAddressing ) );
    /* Put a verifiable value. */
    memset( &xDefaultAddressing, 0xBB, sizeof( xDefaultAddressing ) );

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPUseDefaults );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    /* Ignore the call. */
    vIPNetworkUpCalls_Ignore();
    /* Expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( xDHCPSocket, NULL );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that we are not using leased address. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that the network addressing struct is updated to show that. */
    TEST_ASSERT_EQUAL_MEMORY( &xDefaultAddressing, &xNetworkAddressing, sizeof( xDefaultAddressing ) );
    /* Make sure that the local IP address pointer indicates that. */
    TEST_ASSERT_EQUAL( xNetworkAddressing.ulDefaultIPAddress, *ipLOCAL_IP_ADDRESS_POINTER );
}

/* GNW = getNetworkBufferWithDescriptor */
void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnDHCPSocketNotNULLButGNWFails( void )
{
    struct xSOCKET xTestSocket;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    xTaskGetTickCount_ExpectAndReturn( 100 );
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning NULL will mean the prvSendDHCPDiscover fail. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we are not using leased address. */
    TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnDHCPSocketNULL( void )
{
    /* This should remain unchanged. */
    xDHCPSocket = NULL;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    /* Ignore the call. */
    vIPNetworkUpCalls_Ignore();

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that we are not using leased address. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnSendFailsNoBroadcast( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Not using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    /* Return the time value. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send fail. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 0 );
    /* Since the send failed, a call to release the buffer should be there. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Stub( ReleaseUDPBuffer );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
    /* The time value should be as expected. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
}

void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnSendFailsUseBroadCast( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Not using broadcast. */
    xDHCPData.xUseBroadcast = pdTRUE;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    /* Return the time value. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send fail. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 0 );
    /* Since the send failed, a call to release the buffer should be there. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Stub( ReleaseUDPBuffer );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingSendFirstDiscover, xDHCPData.eDHCPState );
    /* The time value should be as expected. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
}

void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnSendSucceedsUseBroadCast( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Using broadcast. */
    xDHCPData.xUseBroadcast = pdTRUE;
    xDHCPData.ulPreferredIPAddress = 0x00;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    /* Return the time value. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send succeed. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
    /* The time value should be as expected. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );

    /* Free the allocated memory. */
    ReleaseNetworkBuffer();
}

void test_vDHCPProcess_CorrectStateDHCPHookContinueReturnSendSucceedsUseBroadCast1( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
    /* Using broadcast. */
    xDHCPData.xUseBroadcast = pdTRUE;
    xDHCPData.ulPreferredIPAddress = 0x01;

    /* Make sure that the user indicates anything else than the desired options. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress, eDHCPContinue );
    /* Return the time value. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send succeed. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    vDHCPProcess( pdFALSE, eWaitingSendFirstDiscover );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
    /* The time value should be as expected. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );

    /* Free the allocated memory. */
    ReleaseNetworkBuffer();
}

void test_vDHCPProcess_eSendDHCPRequestCorrectStateGNWFails( void )
{
    struct xSOCKET xTestSocket;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eSendDHCPRequest;

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Return NULL network buffer. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    vDHCPProcess( pdFALSE, eSendDHCPRequest );

    /* DHCP socket should be NULL */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eSendDHCPRequestCorrectStateGNWSucceedsSendFails( void )
{
    struct xSOCKET xTestSocket;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eSendDHCPRequest;

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Send fails. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 0 );
    /* ReleaseUDPPayloadBuffer will be called. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Stub( ReleaseUDPBuffer );

    vDHCPProcess( pdFALSE, eSendDHCPRequest );

    /* DHCP socket should be still allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, xDHCPData.eDHCPState );
}


void test_vDHCPProcess_eSendDHCPRequestCorrectStateGNWSucceedsSendSucceeds( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eSendDHCPRequest;

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Send succeeds. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );
    /* Return the time value. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    vDHCPProcess( pdFALSE, eSendDHCPRequest );

    /* DHCP socket should be still allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
    TEST_ASSERT_EQUAL( dhcpINITIAL_DHCP_TX_PERIOD, xDHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eWaitingOfferRecvfromFailsNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    xDHCPData.xDHCPTxPeriod = 100;

    /* Expect these arguments. */
    FreeRTOS_recvfrom_ExpectAndReturn( xDHCPSocket, NULL, 0UL, FREERTOS_ZERO_COPY, NULL, NULL, 0 );
    /* Ignore the buffer argument though. */
    FreeRTOS_recvfrom_IgnoreArg_pvBuffer();

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );


    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be still allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromFailsTimeoutGiveUp( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we exceed the period - and give up. */
    xDHCPData.xDHCPTxPeriod = ipconfigMAXIMUM_DISCOVER_TX_PERIOD;

    /* Expect these arguments. */
    FreeRTOS_recvfrom_ExpectAndReturn( xDHCPSocket, NULL, 0UL, FREERTOS_ZERO_COPY, NULL, NULL, 0 );
    /* Ignore the buffer argument though. */
    FreeRTOS_recvfrom_IgnoreArg_pvBuffer();

    /* Make sure that there is timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference greater than the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    /* Time will be stored in DHCP state machine. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    /* Make all calls to the RNG succeed. */
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );


    /* Closing the DHCP socket. */
    vSocketClose_ExpectAndReturn( xDHCPSocket, 0 );

    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );

    /* Ignore the call to this function. */
    vARPSendGratuitous_Ignore();

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eGetLinkLayerAddress, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromFailsTimeoutDontGiveUpRNGFail( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1;

    /* Expect these arguments. Return a 0 to fail. */
    FreeRTOS_recvfrom_ExpectAndReturn( xDHCPSocket, NULL, 0UL, FREERTOS_ZERO_COPY, NULL, NULL, 0 );
    /* Ignore the buffer argument though. */
    FreeRTOS_recvfrom_IgnoreArg_pvBuffer();

    /* Make sure that there is timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference greater than the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    /* Make all calls to the RNG fail. */
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdFALSE );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
    /* make sure that the period is increased by a factor of two. */
    TEST_ASSERT_EQUAL( ( ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1 ) << 1, xDHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eWaitingOfferRecvfromFailsTimeoutDontGiveUpRNGPassUseBroadcast( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1;
    /* Using broadcast. */
    xDHCPData.xUseBroadcast = pdTRUE;

    /* Expect these arguments. Return a 0 to fail. */
    FreeRTOS_recvfrom_ExpectAndReturn( xDHCPSocket, NULL, 0UL, FREERTOS_ZERO_COPY, NULL, NULL, 0 );
    /* Ignore the buffer argument though. */
    FreeRTOS_recvfrom_IgnoreArg_pvBuffer();

    /* Make sure that there is timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference greater than the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    /* Make all calls to the RNG succeed. */
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );

    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Send succeeds. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
    /* make sure that the period is increased by a factor of two. */
    TEST_ASSERT_EQUAL( ( ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1 ) << 1, xDHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eWaitingOfferRecvfromFailsTimeoutDontGiveUpRNGPassNoBroadcast( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;

    /* Expect these arguments. Return a 0 to fail. */
    FreeRTOS_recvfrom_ExpectAndReturn( xDHCPSocket, NULL, 0UL, FREERTOS_ZERO_COPY, NULL, NULL, 0 );
    /* Ignore the buffer argument though. */
    FreeRTOS_recvfrom_IgnoreArg_pvBuffer();

    /* Make sure that there is timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference greater than the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    /* Make all calls to the RNG succeed. */
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );

    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a NULL network buffer. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eInitialWait, xDHCPData.eDHCPState );
    /* make sure that the period is increased by a factor of two. */
    TEST_ASSERT_EQUAL( ( ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) - 1 ) << 1, xDHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSucceedsFalseCookieNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsFalseCookieNoTimeout );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSucceedsFalseOpcodeNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsFalseOpcodeNoTimeout );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}



void test_vDHCPProcess_eWaitingOfferRecvfromSucceedsCorrectCookieAndOpcodeNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which won't match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSucceedsCorrectCookieAndOpcodeNoTimeout );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromLessBytesNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromLessBytesNoTimeout );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccessCorrectTxID( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccessCorrectTxID );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccess_CorrectAddrType( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_CorrectAddrType );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccess_CorrectAddrLen_BroadcastAddress( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_CorrectAddrLen );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccess_CorrectAddrLen_LocalHostAddress( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_LocalHostAddr );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccess_CorrectAddrLen_NonLocalHostAddress( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_NonLocalHostAddr );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferRecvfromSuccess_CorrectAddrLen_LocalMACNotmatching( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;
    MACAddress_t xBackup;

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    memcpy( &xBackup, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );

    memset( ipLOCAL_MAC_ADDRESS, 0xAA, sizeof( MACAddress_t ) );

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_eWaitingOfferRecvfromSuccess_LocalMACAddrNotMatching );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( pucUDPBuffer );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    memcpy( ipLOCAL_MAC_ADDRESS, &xBackup, sizeof( MACAddress_t ) );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageWithoutOptionsNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;
    uint8_t DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}


void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageIncorrectOptionsNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;
    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 3U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Now add options which will be processed. */
    /* Add a closing flag at the end. */
    DHCPMsg[ xTotalLength - 1U ] = 0xFF;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageMissingLengthByteNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U + 1U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageIncorrectLengthByteNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U + 3U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add incorrect length. */
    DHCPOption[ 1 ] = 100;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageGetNACKNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U + 3U + 1U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_NACK;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageGetACKNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U + 3U + 1U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageOneOptionNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U + 3U + 1U;
    uint8_t DHCPMsg[ xTotalLength ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_OFFER;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );

    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that we still in the state from where we started. */
    TEST_ASSERT_EQUAL( eWaitingOffer, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageTwoOptionsSendFails( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    xDHCPData.ulOfferedIPAddress = DHCPServerAddress;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_OFFER;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );
    /* Return continue. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreRequest, ulClientIPAddress, eDHCPContinue );
    /* Make the hook return correct value. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning NULL will mean the prvSendDHCPRequest fails. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that sending failed. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, xDHCPData.eDHCPState );
}


void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageTwoOptionsSendSucceeds( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    xDHCPData.ulOfferedIPAddress = DHCPServerAddress;

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_OFFER;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );
    /* Return continue. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreRequest, ulClientIPAddress, eDHCPContinue );
    /* Make the hook return correct value. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send succeed. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* The state should indicate that sending failed. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, xDHCPData.eDHCPState );
    /* The time should be updated. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageTwoOptionsDHCPHookReturnDefaultSendSucceeds( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    xDHCPData.ulOfferedIPAddress = DHCPServerAddress;

    /* Rest the network addressing values. */
    memset( &( xNetworkAddressing ), 0, sizeof( xNetworkAddressing ) );

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_OFFER;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );
    /* Return continue. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreRequest, ulClientIPAddress, eDHCPUseDefaults );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    vIPNetworkUpCalls_Ignore();
    /* Expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( xDHCPSocket, NULL );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that sending failed. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL_MEMORY( &( xNetworkAddressing ), &( xDefaultAddressing ), sizeof( xNetworkAddressing ) );
}

void test_vDHCPProcess_eWaitingOfferCorrectDHCPMessageTwoOptionsDHCPHookReturnErrorSendSucceeds( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint8_t testMemory[ sizeof( xNetworkAddressing ) ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    xDHCPData.ulOfferedIPAddress = DHCPServerAddress;

    /* Rest the network addressing values. */
    memset( &( xNetworkAddressing ), 0, sizeof( xNetworkAddressing ) );
    memset( &( testMemory ), 0, sizeof( xNetworkAddressing ) );

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_OFFER;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingOffer;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );
    /* Return continue. */
    xApplicationDHCPHook_ExpectAndReturn( eDHCPPhasePreRequest, ulClientIPAddress, ( eDHCPContinue + eDHCPUseDefaults ) << 1 );
    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );
    vIPNetworkUpCalls_Ignore();
    /* Expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( xDHCPSocket, NULL );

    vDHCPProcess( pdFALSE, eWaitingOffer );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* The state should indicate that sending failed. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL_MEMORY( &( xNetworkAddressing ), &( testMemory ), sizeof( xNetworkAddressing ) );
}


void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsIncorrectServerNoTimeout( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint8_t testMemory[ sizeof( xNetworkAddressing ) ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Rest the network addressing values. */
    memset( &( xNetworkAddressing ), 0, sizeof( xNetworkAddressing ) );
    memset( &( testMemory ), 0, sizeof( xNetworkAddressing ) );

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put incorrect address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress + 1234;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Still waiting on acknowledge. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL_MEMORY( &( xNetworkAddressing ), &( testMemory ), sizeof( xNetworkAddressing ) );
}

void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsIncorrectServerTimeoutGNBfails( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint8_t testMemory[ sizeof( xNetworkAddressing ) ];
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    /* Rest the network addressing values. */
    memset( &( xNetworkAddressing ), 0, sizeof( xNetworkAddressing ) );
    memset( &( testMemory ), 0, sizeof( xNetworkAddressing ) );

    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put incorrect address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress + 1234;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );
    /* Return time second time which can be verified. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a NULL so that prvSendDHCPRequest fails. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Still waiting on acknowledge. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, xDHCPData.eDHCPState );
    /* The time value should be stored in the state machine. */
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
    TEST_ASSERT_EQUAL_MEMORY( &( xNetworkAddressing ), &( testMemory ), sizeof( xNetworkAddressing ) );
}

void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsIncorrectServerTimeoutGNBsucceeds( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put incorrect address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress + 1234;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );
    /* Return time second time which can be verified. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Send succeeds. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Still waiting on acknowledge. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( xTimeValue, xDHCPData.xDHCPTxTime );
}

void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsIncorrectServerTimeoutPeriodLess( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure we exceed the period - and thus, give up. */
    xDHCPData.xDHCPTxPeriod = ( ipconfigMAXIMUM_DISCOVER_TX_PERIOD >> 1 ) + 1;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put incorrect address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress + 1234;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Period exceeded. We should now be in initial state. */
    TEST_ASSERT_EQUAL( eInitialWait, xDHCPData.eDHCPState );
    /* Period exceeded, should have initial value */
    TEST_ASSERT_EQUAL( 100, xDHCPData.xDHCPTxTime );
}


void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsCorrectServerLeaseTimeZero( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;

    /* Reset the lease time so that it will be set to default
     * value later. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Expect( dhcpDEFAULT_LEASE_TIME );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
}


void test_vDHCPProcess_eWaitingAcknowledgeTwoOptionsCorrectServerLeaseTimeLessThanMinConfig( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;

    /* Reset the lease time so that it will be set to minimum
     * value later. */
    xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME - 10;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Expect( dhcpMINIMUM_LEASE_TIME );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( dhcpMINIMUM_LEASE_TIME, xDHCPData.ulLeaseTime );
}


void test_vDHCPProcess_eWaitingAcknowledge_TwoOptions_CorrectServer_AptLeaseTime( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time to an appropriate value. */
    xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME + 10;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Expect( dhcpMINIMUM_LEASE_TIME + 10 );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( dhcpMINIMUM_LEASE_TIME + 10, xDHCPData.ulLeaseTime );
}

void test_vDHCPProcess_eWaitingAcknowledge_TwoOptions_NACK( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */ + 3U /* DHCP offer */ + 6U /* Server IP address */ + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_NACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time to an appropriate value. */
    xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME + 10;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Should now be reset after NACK. */
    TEST_ASSERT_EQUAL( eInitialWait, xDHCPData.eDHCPState );
}


void test_vDHCPProcess_eWaitingAcknowledge_AllOptionsCorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 6U                                    /* DNS server */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    uint32_t ulDNSServer = 0xC0010101;       /* 192.1.1.1 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SUBNET_MASK_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulSubnetMask;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_GATEWAY_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulGateway;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_LEASE_TIME_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulLeaseTime;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_DNS_SERVER_OPTIONS_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulDNSServer;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Expect( configTICK_RATE_HZ * ( FreeRTOS_ntohl( ulLeaseTime ) >> 1 ) );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( configTICK_RATE_HZ * ( FreeRTOS_ntohl( ulLeaseTime ) >> 1 ), xDHCPData.ulLeaseTime );
    TEST_ASSERT_EQUAL( EP_IPv4_SETTINGS.ulGatewayAddress, ulGateway );
    TEST_ASSERT_EQUAL( EP_IPv4_SETTINGS.ulNetMask, ulSubnetMask );
}


void test_vDHCPProcess_eWaitingAcknowledge_DNSIncorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 6U                                    /* DNS server */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    uint32_t ulDNSServer = 0xC0010101;       /* 192.1.1.1 */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SUBNET_MASK_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulSubnetMask;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_GATEWAY_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulGateway;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_LEASE_TIME_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulLeaseTime;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_DNS_SERVER_OPTIONS_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 3;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulDNSServer;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Expect( configTICK_RATE_HZ * ( FreeRTOS_ntohl( ulLeaseTime ) >> 1 ) );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( configTICK_RATE_HZ * ( FreeRTOS_ntohl( ulLeaseTime ) >> 1 ), xDHCPData.ulLeaseTime );
    TEST_ASSERT_EQUAL( EP_IPv4_SETTINGS.ulGatewayAddress, ulGateway );
    TEST_ASSERT_EQUAL( EP_IPv4_SETTINGS.ulNetMask, ulSubnetMask );
}

void test_vDHCPProcess_eWaitingAcknowledge_IPv4ServerIncorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 3;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Ignore();

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( dhcpDEFAULT_LEASE_TIME, xDHCPData.ulLeaseTime );
}


void test_vDHCPProcess_eWaitingAcknowledge_SubnetMaskIncorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SUBNET_MASK_OPTION_CODE;
    /* Add incorrect length. */
    DHCPOption[ 1 ] = 3;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulSubnetMask;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Ignore();

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( dhcpDEFAULT_LEASE_TIME, xDHCPData.ulLeaseTime );
}

void test_vDHCPProcess_eWaitingAcknowledge_GatewayIncorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SUBNET_MASK_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulSubnetMask;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_GATEWAY_OPTION_CODE;
    /* Add incorrect length. */
    DHCPOption[ 1 ] = 2;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulGateway;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Ignore();

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( dhcpDEFAULT_LEASE_TIME, xDHCPData.ulLeaseTime );
}


void test_vDHCPProcess_eWaitingAcknowledge_LeaseTimeIncorrectLength( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 1U /* Padding */
                                    + 3U                                    /* DHCP offer */
                                    + 6U                                    /* Server IP address */
                                    + 6U                                    /* Subnet Mask */
                                    + 6U                                    /* Gateway */
                                    + 6U                                    /* Lease time */
                                    + 1U /* End */;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Leave one byte for the padding. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) + 1 ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 1;
    /* Add the offer byte. */
    DHCPOption[ 2 ] = dhcpMESSAGE_TYPE_ACK;

    DHCPOption += 4;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = DHCPServerAddress;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_SUBNET_MASK_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulSubnetMask;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_GATEWAY_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 4;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulGateway;

    DHCPOption += 6;
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_LEASE_TIME_OPTION_CODE;
    /* Add incorrect length. */
    DHCPOption[ 1 ] = 3;
    /* Add the offer byte. */
    *( ( uint32_t * ) &DHCPOption[ 2 ] ) = ulLeaseTime;


    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Expect this function to be called since we now have
     * successfully acquired an IP address. */
    vIPNetworkUpCalls_Expect();

    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );

    /* Expect ARP to begin. */
    vARPSendGratuitous_Expect();

    /* Expect the timer to be reloaded. */
    vDHCPTimerReload_Ignore();

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be unallocated */
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
    /* Should now be using leased address. */
    TEST_ASSERT_EQUAL( eLeasedAddress, xDHCPData.eDHCPState );
    /* Make sure that this is not changed. */
    TEST_ASSERT_EQUAL( dhcpDEFAULT_LEASE_TIME, xDHCPData.ulLeaseTime );
}


void test_vDHCPProcess_eWaitingAcknowledge_IncorrectLengthofpacket( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    /* Create a bit longer DHCP message but keep it empty. */
    const BaseType_t xTotalLength = sizeof( struct xDHCPMessage_IPv4 ) + 2U;
    uint8_t DHCPMsg[ xTotalLength ];
    uint32_t DHCPServerAddress = 0xC0A80001; /* 192.168.0.1 */
    uint32_t ulClientIPAddress = 0xC0A8000A; /* 192.168.0.10 */
    uint32_t ulSubnetMask = 0xFFFFF100;      /* 255.255.241.0 */
    uint32_t ulGateway = 0xC0A80001;         /* 192.168.0.1 */
    uint32_t ulLeaseTime = 0x00000096;       /* 150 seconds */
    DHCPMessage_IPv4_t * pxDHCPMessage = ( DHCPMessage_IPv4_t * ) DHCPMsg;

    DHCPMsg[ xTotalLength - 1U ] = 0xFF;


    /* Set the header - or at least the start of DHCP message. */
    memset( DHCPMsg, 0, sizeof( DHCPMsg ) );
    /* Copy the header here. */
    memcpy( DHCPMsg, DHCP_header, sizeof( DHCP_header ) );
    /* Make sure that the address matches. */
    memcpy( pxDHCPMessage->ucClientHardwareAddress, ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
    /* Add the expected cookie. */
    pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;

    /* Set the client IP address. */
    pxDHCPMessage->ulYourIPAddress_yiaddr = ulClientIPAddress;

    /* Get pointer to the end. */
    uint8_t * DHCPOption = &DHCPMsg[ sizeof( struct xDHCPMessage_IPv4 ) ];
    /* Add Message type code. */
    DHCPOption[ 0 ] = dhcpIPv4_MESSAGE_TYPE_OPTION_CODE;
    /* Add length. */
    DHCPOption[ 1 ] = 0;

    /* Put the information in global variables to be returned by
     * the FreeRTOS_recvrom. */
    ucGenericPtr = DHCPMsg;
    ulGenericLength = sizeof( DHCPMsg );

    /* This should remain unchanged. */
    xDHCPSocket = &xTestSocket;
    /* Put the required state. */
    xDHCPData.eDHCPState = eWaitingAcknowledge;
    /* Not Using broadcast. */
    xDHCPData.xUseBroadcast = pdFALSE;
    /* Set the transaction ID which will match. */
    xDHCPData.ulTransactionId = 0x01ABCDEF;
    /* Put correct address. */
    xDHCPData.ulDHCPServerAddress = DHCPServerAddress;
    /* Reset the lease time. */
    xDHCPData.ulLeaseTime = 0;
    /* Put some time values. */
    xDHCPData.xDHCPTxTime = 100;
    /* Make sure that we don't exceed the period - and thus, don't give up. */
    xDHCPData.xDHCPTxPeriod = 100;

    /* Reset this value so that it can be verified later. */
    *ipLOCAL_IP_ADDRESS_POINTER = 0;

    /* Get a stub. */
    FreeRTOS_recvfrom_Stub( FreeRTOS_recvfrom_Generic );
    /* Release the UDP buffer. */
    FreeRTOS_ReleaseUDPPayloadBuffer_Expect( DHCPMsg );

    /* Make sure that there is no timeout. The expression is: xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod  */
    /* Return a value which makes the difference just equal to the period. */
    xTaskGetTickCount_ExpectAndReturn( xDHCPData.xDHCPTxTime + xDHCPData.xDHCPTxPeriod + 100 );
    /* Return time second time which can be verified. */
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );

    /* Get the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Send succeeds. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    vDHCPProcess( pdFALSE, eWaitingAcknowledge );

    /* DHCP socket should be allocated */
    TEST_ASSERT_EQUAL( &xTestSocket, xDHCPSocket );
    /* Should still be stuck in waiting for ack state. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, xDHCPData.eDHCPState );
}

void test_vDHCPProcess_eGetLinkLayerAddress_Timeout_NoARPIPClash( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    EP_DHCPData.xDHCPTxTime = 100;
    EP_DHCPData.xDHCPTxPeriod = 100;
    /* Put the required state. */
    xDHCPData.eDHCPState = eGetLinkLayerAddress;

    xARPHadIPClash = pdFALSE;

    xTaskGetTickCount_ExpectAndReturn( EP_DHCPData.xDHCPTxPeriod + EP_DHCPData.xDHCPTxTime + 100 );

    vIPNetworkUpCalls_Expect();

    vDHCPProcess( pdFALSE, eGetLinkLayerAddress );

    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, EP_DHCPData.eDHCPState );
}

void test_vDHCPProcess_eGetLinkLayerAddress_Timeout_ARPIPClash( void )
{
    struct xSOCKET xTestSocket;
    TickType_t xTimeValue = 1234;

    EP_DHCPData.xDHCPTxTime = 100;
    EP_DHCPData.xDHCPTxPeriod = 100;
    /* Put the required state. */
    xDHCPData.eDHCPState = eGetLinkLayerAddress;
    /* This should be nullified. */
    xDHCPSocket = &xTestSocket;

    xARPHadIPClash = pdTRUE;

    xTaskGetTickCount_ExpectAndReturn( EP_DHCPData.xDHCPTxPeriod + EP_DHCPData.xDHCPTxTime + 100 );
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );
    /* Then expect the socket to be closed. */
    vSocketClose_ExpectAndReturn( &xTestSocket, NULL );
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );
    vARPSendGratuitous_Expect();

    vDHCPProcess( pdFALSE, eGetLinkLayerAddress );

    TEST_ASSERT_EQUAL( eGetLinkLayerAddress, EP_DHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
}

void test_vDHCPProcess_eGetLinkLayerAddress_NoTimeout( void )
{
    EP_DHCPData.xDHCPTxTime = 100;
    EP_DHCPData.xDHCPTxPeriod = 100;
    /* Put the required state. */
    xDHCPData.eDHCPState = eGetLinkLayerAddress;

    xARPHadIPClash = pdTRUE;

    /* Make it so that there is no timeout. */
    xTaskGetTickCount_ExpectAndReturn( EP_DHCPData.xDHCPTxPeriod + EP_DHCPData.xDHCPTxTime );

    vDHCPProcess( pdFALSE, eGetLinkLayerAddress );

    TEST_ASSERT_EQUAL( eGetLinkLayerAddress, EP_DHCPData.eDHCPState );
}


void test_vDHCPProcess_eLeasedAddress_NetworkDown( void )
{
    /* Put the required state. */
    xDHCPData.eDHCPState = eLeasedAddress;

    /* The network is not up. */
    FreeRTOS_IsNetworkUp_ExpectAndReturn( 0 );
    /* Expect the DHCP timer to be reloaded. */
    vDHCPTimerReload_Expect( pdMS_TO_TICKS( 5000U ) );

    vDHCPProcess( pdFALSE, eLeasedAddress );

    /* Still in this phase. */
    TEST_ASSERT_EQUAL( eLeasedAddress, EP_DHCPData.eDHCPState );
}

void test_vDHCPProcess_eLeasedAddress_NetworkUp_SokcetCreated_RNGPass_GNBfail( void )
{
    struct xSOCKET xTestSocket;
    BaseType_t xTimeValue = 300;

    /* Socket is already created. */
    xDHCPSocket = &xTestSocket;

    /* Put the required state. */
    xDHCPData.eDHCPState = eLeasedAddress;

    /* The network is up. */
    FreeRTOS_IsNetworkUp_ExpectAndReturn( 1 );

    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdTRUE );
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Return the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning NULL will mean the prvSendDHCPDiscover fail. */
    pxGetNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( NULL );

    /* Expect the timer to be set. */
    vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );

    vDHCPProcess( pdFALSE, eLeasedAddress );

    /* Need to send DHCP request. */
    TEST_ASSERT_EQUAL( eSendDHCPRequest, EP_DHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( xTimeValue, EP_DHCPData.xDHCPTxTime );
    TEST_ASSERT_EQUAL( dhcpINITIAL_DHCP_TX_PERIOD, EP_DHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eLeasedAddress_NetworkUp_SokcetCreated_RNGFail( void )
{
    struct xSOCKET xTestSocket;
    BaseType_t xTimeValue = 300;

    /* Socket is already created. */
    xDHCPSocket = &xTestSocket;

    /* Put the required state. */
    xDHCPData.eDHCPState = eLeasedAddress;

    /* The network is up. */
    FreeRTOS_IsNetworkUp_ExpectAndReturn( 1 );

    /* Make RNG fail. */
    xApplicationGetRandomNumber_ExpectAnyArgsAndReturn( pdFALSE );
    xTaskGetTickCount_ExpectAndReturn( xTimeValue );
    /* Return the hostname. */
    pcApplicationHostnameHook_ExpectAndReturn( pcHostName );
    /* Returning a proper network buffer. */
    pxGetNetworkBufferWithDescriptor_Stub( GetNetworkBuffer );
    /* Make the call to FreeRTOS_send succeed. */
    FreeRTOS_sendto_ExpectAnyArgsAndReturn( 1 );

    /* Expect the timer to be set. */
    vDHCPTimerReload_Expect( dhcpINITIAL_TIMER_PERIOD );

    vDHCPProcess( pdFALSE, eLeasedAddress );

    /* Sent DHCP request - waiting ACK. */
    TEST_ASSERT_EQUAL( eWaitingAcknowledge, EP_DHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( xTimeValue, EP_DHCPData.xDHCPTxTime );
    TEST_ASSERT_EQUAL( dhcpINITIAL_DHCP_TX_PERIOD, EP_DHCPData.xDHCPTxPeriod );
}

void test_vDHCPProcess_eLeasedAddress_NetworkUp_SocketNotCreated_RNGPass_GNBfail( void )
{
    /* Socket not created. */
    xDHCPSocket = NULL;

    /* Put the required state. */
    xDHCPData.eDHCPState = eLeasedAddress;

    /* The network is up. */
    FreeRTOS_IsNetworkUp_ExpectAndReturn( 1 );

    /* Return invalid socket. */
    FreeRTOS_socket_ExpectAnyArgsAndReturn( FREERTOS_INVALID_SOCKET );

    vDHCPProcess( pdFALSE, eLeasedAddress );

    /* Still here. */
    TEST_ASSERT_EQUAL( eLeasedAddress, EP_DHCPData.eDHCPState );
    TEST_ASSERT_EQUAL( NULL, xDHCPSocket );
}

void test_vDHCPProcess_eNotUsingLeasedAddress( void )
{
    /* Put the required state. */
    xDHCPData.eDHCPState = eNotUsingLeasedAddress;

    /* Expect the timer to be disabled. */
    vIPSetDHCPTimerEnableState_Expect( pdFALSE );

    vDHCPProcess( pdFALSE, eNotUsingLeasedAddress );

    /* Continue not using DHCP. */
    TEST_ASSERT_EQUAL( eNotUsingLeasedAddress, EP_DHCPData.eDHCPState );
}

void test_vDHCPProcess_IncorrectState( void )
{
    /* Put a non-existent state. */
    xDHCPData.eDHCPState = ( eNotUsingLeasedAddress << 1 );

    vDHCPProcess( pdFALSE, ( eNotUsingLeasedAddress << 1 ) );

    /* Continue not using DHCP. */
    TEST_ASSERT_EQUAL( ( eNotUsingLeasedAddress << 1 ), EP_DHCPData.eDHCPState );
}
