/*
 * FreeRTOS-Cellular-Interface v1.3.0
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 */

/**
 * @file cellular_common_utest.c
 * @brief Unit tests for functions in cellular_common.h.
 */
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <limits.h>

#include "unity.h"

#include "cellular_config.h"
#include "cellular_config_defaults.h"

/* Include paths for public enums, structures, and macros. */
#include "cellular_platform.h"
#include "cellular_common_internal.h"
#include "cellular_types.h"

#include "mock_cellular_pkthandler_internal.h"
#include "mock_cellular_pktio_internal.h"

#define SIGNAL_QUALITY_CSQ_UNKNOWN                     ( 99 )
#define SIGNAL_QUALITY_CSQ_RSSI_MIN                    ( 0 )
#define SIGNAL_QUALITY_CSQ_RSSI_MAX                    ( 31 )
#define SIGNAL_QUALITY_CSQ_BER_MIN                     ( 0 )
#define SIGNAL_QUALITY_CSQ_BER_MAX                     ( 7 )
#define SIGNAL_QUALITY_CSQ_RSSI_BASE                   ( -113 )
#define SIGNAL_QUALITY_CSQ_RSSI_STEP                   ( 2 )

#define CELLULAR_URC_HANDLER_TABLE_SIZE                ( sizeof( CellularUrcHandlerTable ) / sizeof( CellularAtParseTokenMap_t ) )
#define CELLULAR_SRC_TOKEN_ERROR_TABLE_SIZE            ( sizeof( CellularSrcTokenErrorTable ) / sizeof( char * ) )
#define CELLULAR_SRC_TOKEN_SUCCESS_TABLE_SIZE          ( sizeof( CellularSrcTokenSuccessTable ) / sizeof( char * ) )
#define CELLULAR_URC_TOKEN_WO_PREFIX_TABLE_SIZE        ( sizeof( CellularUrcTokenWoPrefixTable ) / sizeof( char * ) )
#define CELLULAR_SRC_EXTRA_TOKEN_SUCCESS_TABLE_SIZE    ( sizeof( CellularSrcExtraTokenSuccessTable ) / sizeof( char * ) )

/**
 * @brief Parameters in Signal Bars Look up table for measuring Signal Bars.
 */
typedef struct signalBarsTable
{
    int8_t upperThreshold;
    uint8_t bars;
} signalBarsTable_t;

static int mallocAllocFail = 0;

static int mockPlatformMutexCreateFlag = 0;

static int eventData = 0;

static char * pData;

CellularHandle_t gCellularHandle = NULL;

CellularAtParseTokenMap_t CellularUrcHandlerTable[] =
{
    { "CEREG",             NULL },
    { "CGREG",             NULL },
    { "CREG",              NULL },
    { "NORMAL POWER DOWN", NULL },
    { "PSM POWER DOWN",    NULL },
    { "QIND",              NULL },
    { "QIOPEN",            NULL },
    { "QIURC",             NULL },
    { "QSIMSTAT",          NULL },
    { "RDY",               NULL }
};

const char * CellularSrcTokenErrorTable[] =
{ "ERROR", "BUSY", "NO CARRIER", "NO ANSWER", "NO DIALTONE", "ABORTED", "+CMS ERROR", "+CME ERROR", "SEND FAIL" };

const char * CellularSrcTokenSuccessTable[] =
{ "OK", "CONNECT", "SEND OK", ">" };

const char * CellularUrcTokenWoPrefixTable[] =
{ "NORMAL POWER DOWN", "PSM POWER DOWN", "RDY" };

const char * CellularSrcExtraTokenSuccessTable[] =
{ "EXTRA_TOKEN_1", "EXTRA_TOKEN_2", "EXTRA_TOKEN_3" };

static CellularTokenTable_t tokenTable =
{
    .pCellularUrcHandlerTable              = CellularUrcHandlerTable,
    .cellularPrefixToParserMapSize         = CELLULAR_URC_HANDLER_TABLE_SIZE,
    .pCellularSrcTokenErrorTable           = CellularSrcTokenErrorTable,
    .cellularSrcTokenErrorTableSize        = CELLULAR_SRC_TOKEN_ERROR_TABLE_SIZE,
    .pCellularSrcTokenSuccessTable         = CellularSrcTokenSuccessTable,
    .cellularSrcTokenSuccessTableSize      = CELLULAR_SRC_TOKEN_SUCCESS_TABLE_SIZE,
    .pCellularUrcTokenWoPrefixTable        = CellularUrcTokenWoPrefixTable,
    .cellularUrcTokenWoPrefixTableSize     = CELLULAR_URC_TOKEN_WO_PREFIX_TABLE_SIZE,
    .pCellularSrcExtraTokenSuccessTable    = CellularSrcExtraTokenSuccessTable,
    .cellularSrcExtraTokenSuccessTableSize = CELLULAR_SRC_EXTRA_TOKEN_SUCCESS_TABLE_SIZE
};

/* Look up table is maintained here as global scope within this file instead of
 * block scope to help developers to convert BER value. */
/* coverity[misra_c_2012_rule_8_9_violation] */
static const uint16_t rxqualValueToBerTable[] =
{
    14,  /* Assumed value 0.14%. */
    28,  /* Assumed value 0.28%.*/
    57,  /* Assumed value 0.57%. */
    113, /* Assumed value 1.13%. */
    226, /* Assumed value 2.26%. */
    453, /* Assumed value 4.53%. */
    905, /* Assumed value 9.05%. */
    1810 /* Assumed value 18.10%. */
};

/* Look up Table for Mapping Signal Bars with RSSI value in dBm of GSM Signal.
 * The upper RSSI threshold is maintained in decreasing order to return the signal Bars. */

/* Look up table is maintained here as global scope within this file instead of
 * block scope to help developers to quickly change the RSRP Upper thresholds
 * for respective Bars. */
/* coverity[misra_c_2012_rule_8_9_violation] */
static const signalBarsTable_t gsmSignalBarsTable[] =
{
    { -104, 1 },
    { -98,  2 },
    { -89,  3 },
    { -80,  4 },
    { 0,    5 },
};

/* Look up Table for Mapping Signal Bars with RSRP value in dBm of LTE CAT M1 Signal.
 * The upper RSRP threshold is maintained in decreasing order to return the signal Bars. */

/* Look up table is maintained here as global scope within this file instead of
 * block scope to help developers to quickly change the RSRP Upper thresholds
 * for respective Bars. */
/* coverity[misra_c_2012_rule_8_9_violation] */
static const signalBarsTable_t lteCATMSignalBarsTable[] =
{
    { -115, 1 },
    { -105, 2 },
    { -95,  3 },
    { -85,  4 },
    { 0,    5 },
};

/* Look up Table for Mapping Signal Bars with RSRP value in dBm of LTE CAT NB1 Signal.
 * The upper RSRP threshold is maintained in decreasing order to return the signal Bars. */

/* Look up table is maintained here as global scope within this file instead of
 * block scope to help developers to quickly change the RSRP Upper thresholds
 * for respective Bars. */
/* coverity[misra_c_2012_rule_8_9_violation] */
static const signalBarsTable_t lteNBIotSignalBarsTable[] =
{
    { -115, 1 },
    { -105, 2 },
    { -95,  3 },
    { -85,  4 },
    { 0,    5 },
};

static CellularCommInterfaceError_t _prvCommIntfOpen( CellularCommInterfaceReceiveCallback_t receiveCallback,
                                                      void * pUserData,
                                                      CellularCommInterfaceHandle_t * pCommInterfaceHandle );

static CellularCommInterfaceError_t _prvCommIntfSend( CellularCommInterfaceHandle_t commInterfaceHandle,
                                                      const uint8_t * pData,
                                                      uint32_t dataLength,
                                                      uint32_t timeoutMilliseconds,
                                                      uint32_t * pDataSentLength );

static CellularCommInterfaceError_t _prvCommIntfReceive( CellularCommInterfaceHandle_t commInterfaceHandle,
                                                         uint8_t * pBuffer,
                                                         uint32_t bufferLength,
                                                         uint32_t timeoutMilliseconds,
                                                         uint32_t * pDataReceivedLength );

static CellularCommInterfaceError_t _prvCommIntfClose( CellularCommInterfaceHandle_t commInterfaceHandle );

static CellularCommInterface_t CellularCommInterface;
/* ============================   UNITY FIXTURES ============================ */

/* Called before each test method. */
void setUp()
{
    mallocAllocFail = 0;
    eventData = 0;
    mockPlatformMutexCreateFlag = 0;
    CellularCommInterface.open = _prvCommIntfOpen;
    CellularCommInterface.send = _prvCommIntfSend;
    CellularCommInterface.recv = _prvCommIntfReceive;
    CellularCommInterface.close = _prvCommIntfClose;
}

/* Called after each test method. */
void tearDown()
{
}

/* Called at the beginning of the whole suite. */
void suiteSetUp()
{
}

/* Called at the end of the whole suite. */
int suiteTearDown( int numFailures )
{
    return numFailures;
}

/* ========================================================================== */

void dummyTaskENTER_CRITICAL( void )
{
}

void dummyTaskEXIT_CRITICAL( void )
{
}

static CellularCommInterfaceError_t _prvCommIntfOpen( CellularCommInterfaceReceiveCallback_t receiveCallback,
                                                      void * pUserData,
                                                      CellularCommInterfaceHandle_t * pCommInterfaceHandle )
{
    CellularCommInterfaceError_t commIntRet = IOT_COMM_INTERFACE_SUCCESS;

    ( void ) receiveCallback;
    CellularContext_t * pContext = ( CellularContext_t * ) pUserData;

    ( void ) pCommInterfaceHandle;

    commIntRet = receiveCallback( pContext, NULL );

    return commIntRet;
}

static CellularCommInterfaceError_t _prvCommIntfClose( CellularCommInterfaceHandle_t commInterfaceHandle )
{
    CellularCommInterfaceError_t commIntRet = IOT_COMM_INTERFACE_SUCCESS;

    ( void ) commInterfaceHandle;

    return commIntRet;
}

static CellularCommInterfaceError_t _prvCommIntfSend( CellularCommInterfaceHandle_t commInterfaceHandle,
                                                      const uint8_t * pData,
                                                      uint32_t dataLength,
                                                      uint32_t timeoutMilliseconds,
                                                      uint32_t * pDataSentLength )
{
    CellularCommInterfaceError_t commIntRet = IOT_COMM_INTERFACE_SUCCESS;

    ( void ) commInterfaceHandle;
    ( void ) pData;
    ( void ) dataLength;
    ( void ) timeoutMilliseconds;
    ( void ) pDataSentLength;

    *pDataSentLength = dataLength;

    return commIntRet;
}

static CellularCommInterfaceError_t _prvCommIntfReceive( CellularCommInterfaceHandle_t commInterfaceHandle,
                                                         uint8_t * pBuffer,
                                                         uint32_t bufferLength,
                                                         uint32_t timeoutMilliseconds,
                                                         uint32_t * pDataReceivedLength )
{
    CellularCommInterfaceError_t commIntRet = IOT_COMM_INTERFACE_SUCCESS;

    ( void ) commInterfaceHandle;
    ( void ) pBuffer;
    ( void ) bufferLength;
    ( void ) timeoutMilliseconds;
    ( void ) pDataReceivedLength;

    return commIntRet;
}

void cellularUrcNetworkRegistrationCallback( CellularUrcEvent_t urcEvent,
                                             const CellularServiceStatus_t * pServiceStatus,
                                             void * pCallbackContext )
{
    ( void ) pServiceStatus;
    ( void ) pCallbackContext;
    eventData = urcEvent;
}

void cellularUrcPdnEventCallback( CellularUrcEvent_t urcEvent,
                                  uint8_t contextId,
                                  void * pCallbackContext )
{
    ( void ) contextId;
    ( void ) pCallbackContext;
    eventData = urcEvent;
}

void cellularUrcSignalStrengthChangedCallback( CellularUrcEvent_t urcEvent,
                                               const CellularSignalInfo_t * pSignalInfo,
                                               void * pCallbackContext )
{
    ( void ) pSignalInfo;
    ( void ) pCallbackContext;
    eventData = urcEvent;
}

void cellularUrcGenericCallback( const char * pRawData,
                                 void * pCallbackContext )
{
    int len;

    ( void ) pCallbackContext;

    if( strlen( pRawData ) > 0 )
    {
        len = strlen( pRawData ) + 1;
        pData = ( char * ) malloc( sizeof( char ) * len );
        strcpy( pData, pRawData );
    }
}

void cellularModemEventCallback( CellularModemEvent_t modemEvent,
                                 void * pCallbackContext )
{
    ( void ) pCallbackContext;
    eventData = modemEvent;
}

CellularPktStatus_t cellularUndefinedRespCallback( void * pCallbackContext,
                                                   const char * pLine )
{
    ( void ) pLine;
    ( void ) pCallbackContext;
    return CELLULAR_PKT_STATUS_OK;
}

void * mock_malloc( size_t size )
{
    if( mallocAllocFail == 1 )
    {
        return NULL;
    }

    return malloc( size );
}

bool MockPlatformMutex_Create( PlatformMutex_t * pNewMutex,
                               bool recursive )
{
    bool ret = false;

    ( void ) recursive;

    if( ( mockPlatformMutexCreateFlag & 0xFF ) > 0 )
    {
        ret = true;
    }

    mockPlatformMutexCreateFlag = mockPlatformMutexCreateFlag >> 8;
    pNewMutex->created = ret;
    return ret;
}

void MockPlatformMutex_Unlock( PlatformMutex_t * pMutex )
{
    ( void ) pMutex;
}

void MockPlatformMutex_Lock( PlatformMutex_t * pMutex )
{
    ( void ) pMutex;
}

uint16_t MockvQueueDelete( QueueHandle_t queue )
{
    free( queue );
    queue = 0;
    return 1;
}

void MockPlatformMutex_Destroy( PlatformMutex_t * pMutex )
{
    pMutex->created = false;
}

/* ========================================================================== */

/**
 * @brief Test that null context case for _Cellular_CheckLibraryStatus.
 */
void test__Cellular_CheckLibraryStatus_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_CheckLibraryStatus( NULL );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that library not open case for _Cellular_CheckLibraryStatus.
 */
void test__Cellular_CheckLibraryStatus_Library_Not_Open( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    cellularStatus = _Cellular_CheckLibraryStatus( &context );

    TEST_ASSERT_EQUAL( CELLULAR_LIBRARY_NOT_OPEN, cellularStatus );
}

/**
 * @brief Test that library shutdown case for _Cellular_CheckLibraryStatus.
 */
void test__Cellular_CheckLibraryStatus_Library_Shutdown( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.bLibOpened = true;
    context.bLibShutdown = true;
    cellularStatus = _Cellular_CheckLibraryStatus( &context );

    TEST_ASSERT_EQUAL( CELLULAR_INTERNAL_FAILURE, cellularStatus );
}

/**
 * @brief Test that library closing case for _Cellular_CheckLibraryStatus.
 */
void test__Cellular_CheckLibraryStatus_Library_Closing( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.bLibOpened = true;
    context.bLibClosing = true;
    cellularStatus = _Cellular_CheckLibraryStatus( &context );

    TEST_ASSERT_EQUAL( CELLULAR_INTERNAL_FAILURE, cellularStatus );
}

/**
 * @brief Test that library happy path case for _Cellular_CheckLibraryStatus.
 */
void test__Cellular_CheckLibraryStatus_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.bLibOpened = true;
    cellularStatus = _Cellular_CheckLibraryStatus( &context );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that each status case for _Cellular_TranslatePktStatus.
 */
void test__Cellular_TranslatePktStatus_Each_Case( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_TranslatePktStatus( CELLULAR_PKT_STATUS_OK );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );

    cellularStatus = _Cellular_TranslatePktStatus( CELLULAR_PKT_STATUS_TIMED_OUT );
    TEST_ASSERT_EQUAL( CELLULAR_TIMEOUT, cellularStatus );

    cellularStatus = _Cellular_TranslatePktStatus( CELLULAR_PKT_STATUS_FAILURE );
    TEST_ASSERT_EQUAL( CELLULAR_INTERNAL_FAILURE, cellularStatus );
}

/**
 * @brief Test that each status case for _Cellular_TranslateAtCoreStatus.
 */
void test__Cellular_TranslateAtCoreStatus_Each_Case( void )
{
    CellularPktStatus_t pktStatus = CELLULAR_AT_SUCCESS;

    pktStatus = _Cellular_TranslateAtCoreStatus( CELLULAR_AT_SUCCESS );
    TEST_ASSERT_EQUAL( CELLULAR_PKT_STATUS_OK, pktStatus );

    pktStatus = _Cellular_TranslateAtCoreStatus( CELLULAR_AT_BAD_PARAMETER );
    TEST_ASSERT_EQUAL( CELLULAR_PKT_STATUS_BAD_PARAM, pktStatus );

    pktStatus = _Cellular_TranslateAtCoreStatus( CELLULAR_AT_NO_MEMORY );
    TEST_ASSERT_EQUAL( CELLULAR_PKT_STATUS_FAILURE, pktStatus );
}

/**
 * @brief Test that memory allocation failure case for _Cellular_CreateSocketData.
 */
void test__Cellular_CreateSocketData_Mem_Alloc_Fail( void )
{
    CellularPktStatus_t pktStatus = CELLULAR_AT_SUCCESS;
    CellularContext_t context;
    CellularSocketHandle_t socketHandle;

    mallocAllocFail = 1;
    memset( &context, 0, sizeof( CellularContext_t ) );
    pktStatus = _Cellular_CreateSocketData( &context, 0, CELLULAR_SOCKET_DOMAIN_AF_INET,
                                            CELLULAR_SOCKET_TYPE_DGRAM,
                                            CELLULAR_SOCKET_PROTOCOL_TCP,
                                            &socketHandle );
    TEST_ASSERT_EQUAL( CELLULAR_NO_MEMORY, pktStatus );
}

/**
 * @brief Test that no socket data entry case for _Cellular_CreateSocketData.
 */
void test__Cellular_CreateSocketData_No_Socket_Data_Entry_Fail( void )
{
    CellularPktStatus_t pktStatus = CELLULAR_AT_SUCCESS;
    CellularContext_t context;
    CellularSocketHandle_t socketHandle;
    uint32_t i = 0;

    memset( &context, 0, sizeof( CellularContext_t ) );

    for( i = 0; i < CELLULAR_NUM_SOCKET_MAX; i++ )
    {
        context.pSocketData[ i ] = malloc( sizeof( CellularSocketContext_t ) );
    }

    pktStatus = _Cellular_CreateSocketData( &context, 0, CELLULAR_SOCKET_DOMAIN_AF_INET,
                                            CELLULAR_SOCKET_TYPE_DGRAM,
                                            CELLULAR_SOCKET_PROTOCOL_TCP,
                                            &socketHandle );
    TEST_ASSERT_EQUAL( CELLULAR_NO_MEMORY, pktStatus );
}

/**
 * @brief Test that happy path case for _Cellular_CreateSocketData.
 */
void test__Cellular_CreateSocketData_Happy_Path( void )
{
    CellularPktStatus_t pktStatus = CELLULAR_AT_SUCCESS;
    CellularContext_t context;
    CellularSocketHandle_t socketHandle;

    memset( &context, 0, sizeof( CellularContext_t ) );
    pktStatus = _Cellular_CreateSocketData( &context, 0, CELLULAR_SOCKET_DOMAIN_AF_INET,
                                            CELLULAR_SOCKET_TYPE_DGRAM,
                                            CELLULAR_SOCKET_PROTOCOL_TCP,
                                            &socketHandle );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, pktStatus );
}

/**
 * @brief Test that null context case for _Cellular_RemoveSocketData.
 */
void test__Cellular_RemoveSocketData_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSocketContext_t socketContext;
    CellularSocketHandle_t socketHandle = &socketContext;

    cellularStatus = _Cellular_RemoveSocketData( NULL,
                                                 socketHandle );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that no match socketHandle case for _Cellular_RemoveSocketData.
 */
void test__Cellular_RemoveSocketData_No_Match_SocketHandle( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;
    CellularSocketContext_t socketContext;
    CellularSocketHandle_t socketHandle = &socketContext;
    uint32_t i = 0;

    memset( &context, 0, sizeof( CellularContext_t ) );

    for( i = 0; i < CELLULAR_NUM_SOCKET_MAX; i++ )
    {
        context.pSocketData[ i ] = malloc( sizeof( CellularSocketContext_t ) );
    }

    cellularStatus = _Cellular_RemoveSocketData( &context,
                                                 socketHandle );

    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_RemoveSocketData.
 */
void test__Cellular_RemoveSocketData_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;
    CellularSocketHandle_t socketHandle;
    int i = rand() % CELLULAR_NUM_SOCKET_MAX;

    memset( &context, 0, sizeof( CellularContext_t ) );
    socketHandle = malloc( sizeof( CellularSocketContext_t ) );
    context.pSocketData[ i ] = socketHandle;

    cellularStatus = _Cellular_RemoveSocketData( &context,
                                                 socketHandle );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that null context case for _Cellular_IsValidSocket.
 */
void test__Cellular_IsValidSocket_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_IsValidSocket( NULL,
                                              0 );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that bad socket Index parameter case for _Cellular_IsValidSocket.
 */
void test__Cellular_IsValidSocket_Bad_SocketInex_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    cellularStatus = _Cellular_IsValidSocket( &context,
                                              CELLULAR_NUM_SOCKET_MAX );

    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that bad socket parameter case for _Cellular_IsValidSocket.
 */
void test__Cellular_IsValidSocket_Bad_Socket_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );
    cellularStatus = _Cellular_IsValidSocket( &context,
                                              0 );

    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_IsValidSocket.
 */
void test__Cellular_IsValidSocket_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;
    int i = rand() % CELLULAR_NUM_SOCKET_MAX;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.pSocketData[ i ] = malloc( sizeof( CellularSocketContext_t ) );
    cellularStatus = _Cellular_IsValidSocket( &context,
                                              i );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    free( context.pSocketData[ i ] );
}

/**
 * @brief Test that out of upper bound contextId case for _Cellular_IsValidPdn.
 */
void test__Cellular_IsValidPdn_Bad_Upper_Bound_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_IsValidPdn( CELLULAR_PDN_CONTEXT_ID_MAX + 1 );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that out of lower bound contextId case for _Cellular_IsValidPdn.
 */
void test__Cellular_IsValidPdn_Bad_Lower_Bound_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_IsValidPdn( CELLULAR_PDN_CONTEXT_ID_MIN - 1 );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_IsValidPdn.
 */
void test__Cellular_IsValidPdn_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int i = rand() % CELLULAR_PDN_CONTEXT_ID_MAX;

    i = i > 0 ? i : i + 1;

    cellularStatus = _Cellular_IsValidPdn( i );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that Rssi Value case for _Cellular_ConvertCsqSignalRssi.
 */
void test__Cellular_ConvertCsqSignalRssi_Bad_Rssi_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_ConvertCsqSignalRssi( 0, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that Csq Rssi case for _Cellular_ConvertCsqSignalRssi.
 */
void test__Cellular_ConvertCsqSignalRssi_Bad_CsqRssi_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t rssiValue;

    cellularStatus = _Cellular_ConvertCsqSignalRssi( SIGNAL_QUALITY_CSQ_RSSI_MAX + 1, &rssiValue );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_ConvertCsqSignalRssi( SIGNAL_QUALITY_CSQ_RSSI_MIN - 1, &rssiValue );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that CSQ_UNKNOWN case for _Cellular_ConvertCsqSignalRssi.
 */
void test__Cellular_ConvertCsqSignalRssi_CSQ_UNKNOWN( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t rssiValue;

    cellularStatus = _Cellular_ConvertCsqSignalRssi( SIGNAL_QUALITY_CSQ_UNKNOWN, &rssiValue );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( CELLULAR_INVALID_SIGNAL_VALUE, rssiValue );
}

/**
 * @brief Test that happy path case for _Cellular_ConvertCsqSignalRssi.
 */
void test__Cellular_ConvertCsqSignalRssi_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t rssiValue;
    int16_t csqRssi = SIGNAL_QUALITY_CSQ_RSSI_MAX - 1;

    cellularStatus = _Cellular_ConvertCsqSignalRssi( SIGNAL_QUALITY_CSQ_RSSI_MAX - 1, &rssiValue );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( SIGNAL_QUALITY_CSQ_RSSI_BASE + ( csqRssi * SIGNAL_QUALITY_CSQ_RSSI_STEP ), rssiValue );
}

/**
 * @brief Test that Ber Value null case for _Cellular_ConvertCsqSignalBer.
 */
void test__Cellular_ConvertCsqSignalBer_Bad_BerValue_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_ConvertCsqSignalBer( 0, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that Ber Value case for _Cellular_ConvertCsqSignalBer.
 */
void test__Cellular_ConvertCsqSignalBer_Bad_CsqRssi_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t berValue;

    cellularStatus = _Cellular_ConvertCsqSignalBer( SIGNAL_QUALITY_CSQ_BER_MAX + 1, &berValue );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_ConvertCsqSignalBer( SIGNAL_QUALITY_CSQ_BER_MIN - 1, &berValue );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that invalid csqBer case for _Cellular_ConvertCsqSignalBer.
 */
void test__Cellular_ConvertCsqSignalBer_Invalid_CsqBer( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t berValue;

    cellularStatus = _Cellular_ConvertCsqSignalBer( SIGNAL_QUALITY_CSQ_UNKNOWN, &berValue );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( CELLULAR_INVALID_SIGNAL_VALUE, berValue );
}

/**
 * @brief Test that happy path case for _Cellular_ConvertCsqSignalBer.
 */
void test__Cellular_ConvertCsqSignalBer_Happy( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int16_t berValue;
    int csqBer = rand() % SIGNAL_QUALITY_CSQ_BER_MAX;

    cellularStatus = _Cellular_ConvertCsqSignalBer( csqBer, &berValue );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( rxqualValueToBerTable[ csqBer ], berValue );
}

/**
 * @brief Test that null context case for _Cellular_GetModuleContext.
 */
void test__Cellular_GetModuleContext_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_GetModuleContext( NULL, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_GetModuleContext.
 */
void test__Cellular_GetModuleContext_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;
    int32_t moduleContext;
    int32_t * pModuleContext;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.pModueContext = &moduleContext;
    cellularStatus = _Cellular_GetModuleContext( &context, ( void ** ) &pModuleContext );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( pModuleContext, context.pModueContext );
}

/**
 * @brief Test that null signal info case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_Null_SignalInfo( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_ComputeSignalBars( 0, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that unknown rat (Radio Access Technologies case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_Unknow_Rat( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_WCDMA, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_UNKNOWN, cellularStatus );
    TEST_ASSERT_EQUAL( CELLULAR_INVALID_SIGNAL_BAR_VALUE, signalInfo.bars );
}

/**
 * @brief Test that greater rat (Radio Access Technologies case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_Greater_Than_Bar( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;
    signalBarsTable_t bigSignalBars = { 1, 0 };

    signalInfo.rssi = bigSignalBars.upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_GSM, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( CELLULAR_INVALID_SIGNAL_BAR_VALUE, signalInfo.bars );
}

/**
 * @brief Test that GSM happy path case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_GSM_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    signalInfo.rssi = gsmSignalBarsTable[ 0 ].upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_GSM, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( gsmSignalBarsTable[ 0 ].bars, signalInfo.bars );
}

/**
 * @brief Test that EDGE happy path case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_EDGE_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    signalInfo.rssi = gsmSignalBarsTable[ 0 ].upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_EDGE, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( gsmSignalBarsTable[ 0 ].bars, signalInfo.bars );
}

/**
 * @brief Test that LTE happy path case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_LTE_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    signalInfo.rsrp = lteCATMSignalBarsTable[ 0 ].upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_LTE, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( lteCATMSignalBarsTable[ 0 ].bars, signalInfo.bars );
}

/**
 * @brief Test that CATM1 happy path case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_CATM1_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    signalInfo.rsrp = lteCATMSignalBarsTable[ 0 ].upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_CATM1, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( lteCATMSignalBarsTable[ 0 ].bars, signalInfo.bars );
}

/**
 * @brief Test that NBIOT happy path case for _Cellular_ComputeSignalBars.
 */
void test__Cellular_ComputeSignalBars_NBIOT_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularSignalInfo_t signalInfo;

    signalInfo.rsrp = lteNBIotSignalBarsTable[ 0 ].upperThreshold;
    cellularStatus = _Cellular_ComputeSignalBars( CELLULAR_RAT_NBIOT, &signalInfo );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( lteNBIotSignalBarsTable[ 0 ].bars, signalInfo.bars );
}

/**
 * @brief Test that null context case for _Cellular_GetCurrentRat.
 */
void test__Cellular_GetCurrentRat_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_GetCurrentRat( NULL, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that null Rat case for _Cellular_GetCurrentRat.
 */
void test__Cellular_GetCurrentRat_Null_Rat( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.bLibOpened = true;

    cellularStatus = _Cellular_GetCurrentRat( &context, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_GetCurrentRat.
 */
void test__Cellular_GetCurrentRat_Happy_Path( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularContext_t context;
    CellularRat_t rat;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.bLibOpened = true;
    context.libAtData.rat = CELLULAR_RAT_WCDMA;

    cellularStatus = _Cellular_GetCurrentRat( &context, &rat );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL( CELLULAR_RAT_WCDMA, rat );
}

/**
 * @brief Test that null parameter case for _Cellular_NetworkRegistrationCallback.
 */
void test__Cellular_NetworkRegistrationCallback_Null_Parameter( void )
{
    CellularContext_t context;

    _Cellular_NetworkRegistrationCallback( NULL, CELLULAR_URC_SOCKET_OPENED, NULL );

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.networkRegistrationCallback = NULL;
    _Cellular_NetworkRegistrationCallback( &context, CELLULAR_URC_SOCKET_OPENED, NULL );
}

/**
 * @brief Test that happy path case for _Cellular_NetworkRegistrationCallback.
 */
void test__Cellular_NetworkRegistrationCallback_Happy_Path( void )
{
    CellularContext_t context;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.networkRegistrationCallback = cellularUrcNetworkRegistrationCallback;
    _Cellular_NetworkRegistrationCallback( &context, CELLULAR_URC_SOCKET_OPENED, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_URC_SOCKET_OPENED, eventData );
}

/**
 * @brief Test that null parameter case for _Cellular_PdnEventCallback.
 */
void test__Cellular_PdnEventCallback_Null_Parameter( void )
{
    CellularContext_t context;

    _Cellular_PdnEventCallback( NULL, CELLULAR_URC_SOCKET_OPENED, 0U );

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.pdnEventCallback = NULL;
    _Cellular_PdnEventCallback( &context, CELLULAR_URC_SOCKET_OPENED, 0U );
}

/**
 * @brief Test that happy path case for _Cellular_PdnEventCallback.
 */
void test__Cellular_PdnEventCallback_Happy_Path( void )
{
    CellularContext_t context;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.pdnEventCallback = cellularUrcPdnEventCallback;
    _Cellular_PdnEventCallback( &context, CELLULAR_URC_SOCKET_OPENED, 0U );

    TEST_ASSERT_EQUAL( CELLULAR_URC_SOCKET_OPENED, eventData );
}

/**
 * @brief Test that null parameter case for _Cellular_SignalStrengthChangedCallback.
 */
void test__Cellular_SignalStrengthChangedCallback_Null_Parameter( void )
{
    CellularContext_t context;

    _Cellular_SignalStrengthChangedCallback( NULL, CELLULAR_URC_SOCKET_OPENED, NULL );

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.signalStrengthChangedCallback = NULL;
    _Cellular_SignalStrengthChangedCallback( &context, CELLULAR_URC_SOCKET_OPENED, NULL );
}

/**
 * @brief Test that happy path case for _Cellular_SignalStrengthChangedCallback.
 */
void test__Cellular_SignalStrengthChangedCallback_Happy_Path( void )
{
    CellularContext_t context;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.signalStrengthChangedCallback = cellularUrcSignalStrengthChangedCallback;
    _Cellular_SignalStrengthChangedCallback( &context, CELLULAR_URC_SOCKET_OPENED, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_URC_SOCKET_OPENED, eventData );
}

/**
 * @brief Test that null parameter case for _Cellular_GenericCallback.
 */
void test__Cellular_GenericCallback_Null_Parameter( void )
{
    CellularContext_t context;
    const char pRawData[] = "cellularUrcGenericCallback test.";

    _Cellular_GenericCallback( NULL, pRawData );

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.genericCallback = NULL;
    _Cellular_GenericCallback( &context, pRawData );
}

/**
 * @brief Test that happy path case for _Cellular_GenericCallback.
 */
void test__Cellular_GenericCallback_Happy_Path( void )
{
    CellularContext_t context;
    const char pRawData[] = "cellularUrcGenericCallback test.";

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.genericCallback = cellularUrcGenericCallback;
    _Cellular_GenericCallback( &context, pRawData );

    TEST_ASSERT_EQUAL( strcmp( pRawData, pData ), 0 );
    free( pData );
}

/**
 * @brief Test that null parameter case for _Cellular_ModemEventCallback.
 */

void test__Cellular_ModemEventCallback_Null_Parameter( void )
{
    CellularContext_t context;

    _Cellular_ModemEventCallback( NULL, CELLULAR_MODEM_EVENT_POWERED_DOWN );

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.modemEventCallback = NULL;
    _Cellular_ModemEventCallback( &context, CELLULAR_MODEM_EVENT_POWERED_DOWN );
}

/**
 * @brief Test that happy path case for _Cellular_ModemEventCallback.
 */
void test__Cellular_ModemEventCallback_Happy_Path( void )
{
    CellularContext_t context;

    memset( &context, 0, sizeof( struct CellularContext ) );
    context.cbEvents.modemEventCallback = cellularModemEventCallback;
    _Cellular_ModemEventCallback( &context, CELLULAR_MODEM_EVENT_POWERED_DOWN );

    TEST_ASSERT_EQUAL( CELLULAR_MODEM_EVENT_POWERED_DOWN, eventData );
}

/**
 * @brief Test that null context case for _Cellular_GetSocketData.
 */
void test__Cellular_GetSocketData_Null_Context( void )
{
    CellularSocketContext_t * pSocketData = NULL;

    pSocketData = _Cellular_GetSocketData( NULL, 0 );
    TEST_ASSERT_EQUAL( NULL, pSocketData );
}

/**
 * @brief Test that out of range socket id case for _Cellular_GetSocketData.
 */
void test__Cellular_GetSocketData_Upper_Bound_SocketId( void )
{
    CellularSocketContext_t * pSocketData = NULL;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );

    pSocketData = _Cellular_GetSocketData( &context, CELLULAR_NUM_SOCKET_MAX );
    TEST_ASSERT_EQUAL( NULL, pSocketData );
}

/**
 * @brief Test that null socket data case for _Cellular_GetSocketData.
 */
void test__Cellular_GetSocketData_Null_SocketData( void )
{
    CellularSocketContext_t * pSocketData = NULL;
    CellularContext_t context;

    memset( &context, 0, sizeof( CellularContext_t ) );

    pSocketData = _Cellular_GetSocketData( &context, 0 );
    TEST_ASSERT_EQUAL( NULL, pSocketData );
}

/**
 * @brief Test that happy path case for _Cellular_GetSocketData.
 */
void test__Cellular_GetSocketData_Happy_Path( void )
{
    CellularSocketContext_t * pSocketData = NULL;
    CellularContext_t context;
    uint32_t i = rand() % CELLULAR_NUM_SOCKET_MAX;

    memset( &context, 0, sizeof( CellularContext_t ) );
    context.pSocketData[ i ] = malloc( sizeof( CellularSocketContext_t ) );

    pSocketData = _Cellular_GetSocketData( &context, i );
    TEST_ASSERT_EQUAL( context.pSocketData[ i ], pSocketData );
    free( context.pSocketData[ i ] );
}

/**
 * @brief Test that null context case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_LibInit( NULL, NULL, NULL );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that null comm interface member case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Null_CommInterface_Member( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_LibInit( &CellularHandle, NULL, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    CellularCommInterface.recv = NULL;
    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    CellularCommInterface.send = NULL;
    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    CellularCommInterface.close = NULL;
    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    CellularCommInterface.open = NULL;
    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that null token table case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Null_Tabletoken_Member( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    CellularTokenTable_t tokenTableNoUrcTable =
    {
        .pCellularUrcHandlerTable      = NULL,
        .cellularPrefixToParserMapSize = CELLULAR_URC_HANDLER_TABLE_SIZE,
    };

    CellularTokenTable_t tokenTableNoErrorTable =
    {
        .pCellularUrcHandlerTable       = CellularUrcHandlerTable,
        .cellularPrefixToParserMapSize  = CELLULAR_URC_HANDLER_TABLE_SIZE,
        .pCellularSrcTokenErrorTable    = NULL,
        .cellularSrcTokenErrorTableSize = CELLULAR_SRC_TOKEN_ERROR_TABLE_SIZE
    };

    CellularTokenTable_t tokenTableNoSuccessTable =
    {
        .pCellularUrcHandlerTable         = CellularUrcHandlerTable,
        .cellularPrefixToParserMapSize    = CELLULAR_URC_HANDLER_TABLE_SIZE,
        .pCellularSrcTokenErrorTable      = CellularSrcTokenErrorTable,
        .cellularSrcTokenErrorTableSize   = CELLULAR_SRC_TOKEN_ERROR_TABLE_SIZE,
        .pCellularSrcTokenSuccessTable    = NULL,
        .cellularSrcTokenSuccessTableSize = CELLULAR_SRC_TOKEN_SUCCESS_TABLE_SIZE
    };

    CellularTokenTable_t tokenTableNoWoPrefixTable =
    {
        .pCellularUrcHandlerTable          = CellularUrcHandlerTable,
        .cellularPrefixToParserMapSize     = CELLULAR_URC_HANDLER_TABLE_SIZE,
        .pCellularSrcTokenErrorTable       = CellularSrcTokenErrorTable,
        .cellularSrcTokenErrorTableSize    = CELLULAR_SRC_TOKEN_ERROR_TABLE_SIZE,
        .pCellularSrcTokenSuccessTable     = CellularSrcTokenSuccessTable,
        .cellularSrcTokenSuccessTableSize  = CELLULAR_SRC_TOKEN_SUCCESS_TABLE_SIZE,
        .pCellularUrcTokenWoPrefixTable    = NULL,
        .cellularUrcTokenWoPrefixTableSize = CELLULAR_URC_TOKEN_WO_PREFIX_TABLE_SIZE
    };

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTableNoUrcTable );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTableNoErrorTable );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTableNoSuccessTable );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTableNoWoPrefixTable );
    TEST_ASSERT_EQUAL( CELLULAR_BAD_PARAMETER, cellularStatus );
}

/**
 * @brief Test that memory allocation failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Memory_Allocation_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    mallocAllocFail = 1;

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_NO_MEMORY, cellularStatus );
}

/**
 * @brief Test that CreateLibStatusMutex failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_CreateLibStatusMutex_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_RESOURCE_CREATION_FAIL, cellularStatus );
}

/**
 * @brief Test that _Cellular_CreateAtDataMutex failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Cellular_CreateAtDataMutex_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex work. */
    mockPlatformMutexCreateFlag = 1;

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_RESOURCE_CREATION_FAIL, cellularStatus );
}

/**
 * @brief Test that _Cellular_CreatePktRequestMutex failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Cellular_CreatePktRequestMutex_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex and _Cellular_CreateAtDataMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( false );
    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_RESOURCE_CREATION_FAIL, cellularStatus );
}

/**
 * @brief Test that _Cellular_CreatePktResponseMutex failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Cellular_CreatePktResponseMutex_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( false );
    _Cellular_DestroyPktResponseMutex_Ignore();
    _Cellular_DestroyPktRequestMutex_Ignore();

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_RESOURCE_CREATION_FAIL, cellularStatus );
}

/**
 * @brief Test that pkio init failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_PkioInit_Failure( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( true );
    _Cellular_AtParseInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktHandlerInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktioInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_TIMED_OUT );
    _Cellular_PktioShutdown_Ignore();
    _Cellular_PktHandlerCleanup_Ignore();
    _Cellular_DestroyPktResponseMutex_Ignore();
    _Cellular_DestroyPktRequestMutex_Ignore();

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );

    TEST_ASSERT_EQUAL( CELLULAR_TIMEOUT, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_Happy_Path( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( true );
    _Cellular_AtParseInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktHandlerInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktioInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );
    gCellularHandle = CellularHandle;
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that null context case for _Cellular_LibCleanup.
 */
void test__Cellular_LibCleanup_Null_Context( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    cellularStatus = _Cellular_LibCleanup( NULL );

    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_LibCleanup.
 */
void test__Cellular_LibCleanup_Happy_Path( void )
{
    CellularContext_t * pContext = ( CellularContext_t * ) gCellularHandle;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int i = rand() % CELLULAR_NUM_SOCKET_MAX;
    CellularSocketContext_t * pSocketData = ( CellularSocketContext_t * ) malloc( sizeof( CellularSocketContext_t ) );

    pContext->pSocketData[ i ] = pSocketData;

    _Cellular_PktioShutdown_Ignore();
    _Cellular_PktHandlerCleanup_Ignore();
    _Cellular_DestroyPktRequestMutex_Ignore();
    _Cellular_DestroyPktResponseMutex_Ignore();

    cellularStatus = _Cellular_LibCleanup( pContext );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that PktHandlerInit failure case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_PktHandlerInit_Fail( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( true );
    _Cellular_AtParseInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktHandlerInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_FAILURE );
    _Cellular_DestroyPktResponseMutex_Ignore();
    _Cellular_DestroyPktRequestMutex_Ignore();

    cellularStatus = _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );
    TEST_ASSERT_EQUAL( CELLULAR_INTERNAL_FAILURE, cellularStatus );
}

/**
 * @brief Test that no pktio shutdown case for _Cellular_LibCleanup.
 */
void test__Cellular_LibCleanup_No_Pktio_Shutdown( void )
{
    CellularContext_t * pContext = ( CellularContext_t * ) malloc( sizeof( struct CellularContext ) );
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int i = rand() % CELLULAR_NUM_SOCKET_MAX;
    CellularSocketContext_t * pSocketData = ( CellularSocketContext_t * ) malloc( sizeof( CellularSocketContext_t ) );

    pContext->pSocketData[ i ] = pSocketData;
    pContext->bLibOpened = false;

    _Cellular_PktioShutdown_Ignore();
    _Cellular_PktHandlerCleanup_Ignore();
    _Cellular_DestroyPktRequestMutex_Ignore();
    _Cellular_DestroyPktResponseMutex_Ignore();

    cellularStatus = _Cellular_LibCleanup( pContext );

    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
}

/**
 * @brief Test that happy path case for _shutdownCallback.
 */
void test__shutdownCallback_Happy_Path( void )
{
    CellularHandle_t CellularHandle = NULL;
    CellularContext_t * pContext;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( true );
    _Cellular_AtParseInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktHandlerInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktioInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );

    ( void ) _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );
    pContext = ( CellularContext_t * ) CellularHandle;
    pContext->pPktioShutdownCB( pContext );
    TEST_ASSERT_EQUAL( true, pContext->bLibShutdown );
}

/**
 * @brief Test that happy path case for _Cellular_AtcmdRequestWithCallback.
 */
void test__Cellular_AtcmdRequestWithCallback_Happy_Path( void )
{
    CellularAtReq_t atReqGetResult = { 0 };
    CellularContext_t context;
    CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK;

    _Cellular_PktHandler_AtcmdRequestWithCallback_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    pktStatus = _Cellular_AtcmdRequestWithCallback( &context, atReqGetResult );

    TEST_ASSERT_EQUAL( CELLULAR_PKT_STATUS_OK, pktStatus );
}

/**
 * @brief Test that double allocate context case for _Cellular_LibInit.
 */
void test__Cellular_LibInit_PktHandlerInit_Double_Allocate_Context( void )
{
    CellularHandle_t CellularHandle = NULL;

    /* Mock CreateLibStatusMutex, _Cellular_CreateAtDataMutex and _Cellular_CreatePktRequestMutex work. */
    mockPlatformMutexCreateFlag = 0x0101;
    _Cellular_CreatePktRequestMutex_IgnoreAndReturn( true );
    _Cellular_CreatePktResponseMutex_IgnoreAndReturn( true );
    _Cellular_AtParseInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );
    _Cellular_PktHandlerInit_IgnoreAndReturn( CELLULAR_PKT_STATUS_OK );

    _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );
    CellularHandle = NULL;
    _Cellular_LibInit( &CellularHandle, &CellularCommInterface, &tokenTable );
}

/**
 * @brief Test that null parameter case for _Cellular_RegisterUndefinedRespCallback.
 */
void test__Cellular_RegisterUndefinedRespCallback_Null_Parameter( void )
{
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int32_t customDefinedContext;

    /* Pass NULL cellular context pointer to _Cellular_RegisterUndefinedRespCallback. */
    cellularStatus = _Cellular_RegisterUndefinedRespCallback( NULL, cellularUndefinedRespCallback, &customDefinedContext );
    TEST_ASSERT_EQUAL( CELLULAR_INVALID_HANDLE, cellularStatus );
}

/**
 * @brief Test that happy path case for _Cellular_RegisterUndefinedRespCallback.
 */
void test__Cellular_RegisterUndefinedRespCallback_Happy_Path( void )
{
    CellularContext_t context;
    CellularError_t cellularStatus = CELLULAR_SUCCESS;
    int32_t customDefinedContext;

    /* Register the cellular undefined response callback. */
    cellularStatus = _Cellular_RegisterUndefinedRespCallback( &context, cellularUndefinedRespCallback, &customDefinedContext );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL_PTR( cellularUndefinedRespCallback, context.undefinedRespCallback );
    TEST_ASSERT_EQUAL_PTR( &customDefinedContext, context.pUndefinedRespCBContext );

    /* unregister the cellular undefined response callback. */
    cellularStatus = _Cellular_RegisterUndefinedRespCallback( &context, NULL, &customDefinedContext );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_NULL( context.undefinedRespCallback );
    /* Undefined callback context is cleared if the undefined callback is NULL. */
    TEST_ASSERT_NULL( context.pUndefinedRespCBContext );

    /* Register the cellular undefined response callback without callback context. */
    cellularStatus = _Cellular_RegisterUndefinedRespCallback( &context, cellularUndefinedRespCallback, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_EQUAL_PTR( cellularUndefinedRespCallback, context.undefinedRespCallback );
    TEST_ASSERT_NULL( context.pUndefinedRespCBContext );

    /* unregister the cellular undefined response callback without callback context. */
    cellularStatus = _Cellular_RegisterUndefinedRespCallback( &context, NULL, NULL );
    TEST_ASSERT_EQUAL( CELLULAR_SUCCESS, cellularStatus );
    TEST_ASSERT_NULL( context.undefinedRespCallback );
    TEST_ASSERT_NULL( context.pUndefinedRespCBContext );
}
