1
0
mirror of https://github.com/FreeRTOS/coreMQTT synced 2025-06-09 04:44:54 +08:00
coreMQTT/utest/mqtt_utest.c
Muneeb Ahmed 0884ff0f4d Clear old records for clean sessions (#1148)
* Clear state records when a CONNACK's session present flag is false

Co-authored-by: Gary Wicker <14828980+gkwicker@users.noreply.github.com>
2020-08-24 13:33:45 -07:00

2676 lines
113 KiB
C

#include <string.h>
#include <stdbool.h>
#include "unity.h"
/* Include paths for public enums, structures, and macros. */
#include "mqtt.h"
#include "mock_mqtt_lightweight.h"
#include "mock_mqtt_state.h"
/**
* @brief A valid starting packet ID per MQTT spec. Start from 1.
*/
#define MQTT_FIRST_VALID_PACKET_ID ( 1 )
/**
* @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 3.1.1 spec.
*/
#define MQTT_PACKET_PINGREQ_SIZE ( 2U )
/**
* @brief A packet type not handled by MQTT_ProcessLoop.
*/
#define MQTT_PACKET_TYPE_INVALID ( 0U )
/**
* @brief Number of milliseconds in a second.
*/
#define MQTT_ONE_SECOND_TO_MS ( 1000U )
/**
* @brief Length of the MQTT network buffer.
*/
#define MQTT_TEST_BUFFER_LENGTH ( 128 )
/**
* @brief Sample keep-alive interval that should be greater than 0.
*/
#define MQTT_SAMPLE_KEEPALIVE_INTERVAL_S ( 1U )
/**
* @brief Length of time spent for single test case with
* multiple iterations spent in the process loop for coverage.
*/
#define MQTT_SAMPLE_PROCESS_LOOP_TIMEOUT_MS ( 1U )
/**
* @brief Zero timeout in the process loop implies one iteration.
*/
#define MQTT_NO_TIMEOUT_MS ( 0U )
/**
* @brief Sample length of remaining serialized data.
*/
#define MQTT_SAMPLE_REMAINING_LENGTH ( 64 )
/**
* @brief Subtract this value from max value of global entry time
* for the timer overflow test.
*/
#define MQTT_OVERFLOW_OFFSET ( 3 )
/**
* @brief Subtract this value from max value of global entry time
* for the timer overflow test.
*/
#define MQTT_TIMER_CALLS_PER_ITERATION ( 4 )
/**
* @brief Timeout for the timer overflow test.
*/
#define MQTT_TIMER_OVERFLOW_TIMEOUT_MS ( 10 )
/**
* @brief A sample network context that we set to NULL.
*/
#define MQTT_SAMPLE_NETWORK_CONTEXT ( NULL )
/**
* @brief Sample topic filter to subscribe to.
*/
#define MQTT_SAMPLE_TOPIC_FILTER "iot"
/**
* @brief Length of sample topic filter.
*/
#define MQTT_SAMPLE_TOPIC_FILTER_LENGTH ( sizeof( MQTT_SAMPLE_TOPIC_FILTER ) - 1 )
/**
* @brief The packet type to be received by the process loop.
* IMPORTANT: Make sure this is set before calling expectProcessLoopCalls(...).
*/
static uint8_t currentPacketType = MQTT_PACKET_TYPE_INVALID;
/**
* @brief The return value of modifyIncomingPacket(...) CMock callback that
* replaces a call to MQTT_GetIncomingPacketTypeAndLength.
* IMPORTANT: Make sure this is set before calling expectProcessLoopCalls(...).
*/
static MQTTStatus_t modifyIncomingPacketStatus = MQTTSuccess;
/**
* @brief Time at the beginning of each test. Note that this is not updated with
* a real clock. Instead, we simply increment this variable.
*/
static uint32_t globalEntryTime = 0;
/**
* @brief A static buffer used by the MQTT library for storing packet data.
*/
static uint8_t mqttBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 };
/**
* @brief A flag to indicate whether event callback is called from
* MQTT_ProcessLoop.
*/
static bool isEventCallbackInvoked = false;
/* ============================ UNITY FIXTURES ============================ */
/* Called before each test method. */
void setUp()
{
memset( ( void * ) mqttBuffer, 0x0, sizeof( mqttBuffer ) );
MQTT_State_strerror_IgnoreAndReturn( "DUMMY_MQTT_STATE" );
globalEntryTime = 0;
}
/* 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;
}
/* ========================================================================== */
/**
* @brief Mock successful transport send, and write data into buffer for
* verification.
*/
static int32_t mockSend( NetworkContext_t * pNetworkContext,
const void * pMessage,
size_t bytesToSend )
{
const uint8_t * buffer = ( const uint8_t * ) pMessage;
/* Treat network context as pointer to buffer for mocking. */
uint8_t * mockNetwork = *( pNetworkContext->buffer );
size_t bytesSent = 0;
while( bytesSent++ < bytesToSend )
{
/* Write single byte and advance buffer. */
*mockNetwork++ = *buffer++;
}
/* Move stream by bytes sent. */
*( pNetworkContext->buffer ) = mockNetwork;
return bytesToSend;
}
/**
* @brief Initialize pNetworkBuffer using static buffer.
*
* @param[in] pNetworkBuffer Network buffer provided for the context.
*/
static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer )
{
pNetworkBuffer->pBuffer = mqttBuffer;
pNetworkBuffer->size = MQTT_TEST_BUFFER_LENGTH;
}
/**
* @brief Mocked MQTT event callback.
*
* @param[in] pContext MQTT context pointer.
* @param[in] pPacketInfo Packet Info pointer for the incoming packet.
* @param[in] pDeserializedInfo Deserialized information from the incoming packet.
*/
static void eventCallback( MQTTContext_t * pContext,
MQTTPacketInfo_t * pPacketInfo,
MQTTDeserializedInfo_t * pDeserializedInfo )
{
( void ) pContext;
( void ) pPacketInfo;
( void ) pDeserializedInfo;
/* Update the global state to indicate that event callback is invoked. */
isEventCallbackInvoked = true;
}
/**
* @brief A mocked timer query function that increments on every call. This
* guarantees that only a single iteration runs in the ProcessLoop for ease
* of testing.
*/
static uint32_t getTime( void )
{
return globalEntryTime++;
}
/**
* @brief A mocked timer function that could be used on a device with no system
* time.
*/
static uint32_t getTimeDummy( void )
{
return 0;
}
/**
* @brief Mocked successful transport send.
*
* @param[in] tcpSocket TCP socket.
* @param[in] pMessage Data to send.
* @param[in] bytesToWrite Length of data to send.
*
* @return Number of bytes sent; negative value on error;
* 0 for timeout or 0 bytes sent.
*/
static int32_t transportSendSuccess( NetworkContext_t * pNetworkContext,
const void * pBuffer,
size_t bytesToWrite )
{
TEST_ASSERT_EQUAL( MQTT_SAMPLE_NETWORK_CONTEXT, pNetworkContext );
( void ) pBuffer;
return bytesToWrite;
}
/**
* @brief Mocked failed transport send.
*/
static int32_t transportSendFailure( NetworkContext_t * pNetworkContext,
const void * pBuffer,
size_t bytesToWrite )
{
( void ) pNetworkContext;
( void ) pBuffer;
( void ) bytesToWrite;
return -1;
}
/**
* @brief Mocked transport send that succeeds then fails.
*/
static int32_t transportSendSucceedThenFail( NetworkContext_t * pNetworkContext,
const void * pMessage,
size_t bytesToSend )
{
int32_t retVal = bytesToSend;
static int counter = 0;
( void ) pNetworkContext;
( void ) pMessage;
if( counter++ )
{
retVal = -1;
counter = 0;
}
return retVal;
}
/**
* @brief Mocked successful transport read.
*
* @param[in] tcpSocket TCP socket.
* @param[out] pBuffer Buffer for receiving data.
* @param[in] bytesToRead Size of pBuffer.
*
* @return Number of bytes received; negative value on error.
*/
static int32_t transportRecvSuccess( NetworkContext_t * pNetworkContext,
void * pBuffer,
size_t bytesToRead )
{
TEST_ASSERT_EQUAL( MQTT_SAMPLE_NETWORK_CONTEXT, pNetworkContext );
( void ) pBuffer;
return bytesToRead;
}
/**
* @brief Mocked failed transport read.
*/
static int32_t transportRecvFailure( NetworkContext_t * pNetworkContext,
void * pBuffer,
size_t bytesToRead )
{
( void ) pNetworkContext;
( void ) pBuffer;
( void ) bytesToRead;
return -1;
}
/**
* @brief Mocked transport reading one byte at a time.
*/
static int32_t transportRecvOneByte( NetworkContext_t * pNetworkContext,
void * pBuffer,
size_t bytesToRead )
{
( void ) pNetworkContext;
( void ) pBuffer;
( void ) bytesToRead;
return 1;
}
/**
* @brief Initialize the transport interface with the mocked functions for
* send and receive.
*
* @brief param[in] pTransport The transport interface to use with the context.
*/
static void setupTransportInterface( TransportInterface_t * pTransport )
{
pTransport->pNetworkContext = MQTT_SAMPLE_NETWORK_CONTEXT;
pTransport->send = transportSendSuccess;
pTransport->recv = transportRecvSuccess;
}
/**
* @brief Initialize pSubscribeInfo using test-defined macros.
*
* @param[in] pSubscribeInfo Pointer to MQTT subscription info.
*/
static void setupSubscriptionInfo( MQTTSubscribeInfo_t * pSubscribeInfo )
{
pSubscribeInfo->qos = MQTTQoS1;
pSubscribeInfo->pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER;
pSubscribeInfo->topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH;
}
/**
* @brief This helper function is used to expect any calls from the process loop
* to mocked functions belonging to an external header file. Its parameters
* are used to provide return values for these mocked functions.
*/
static void expectProcessLoopCalls( MQTTContext_t * const pContext,
MQTTStatus_t deserializeStatus,
MQTTPublishState_t stateAfterDeserialize,
MQTTStatus_t updateStateStatus,
MQTTStatus_t serializeStatus,
MQTTPublishState_t stateAfterSerialize,
MQTTStatus_t processLoopStatus,
bool incomingPublish,
MQTTPublishInfo_t * pPubInfo )
{
MQTTStatus_t mqttStatus = MQTTSuccess;
MQTTPacketInfo_t incomingPacket = { 0 };
size_t pingreqSize = MQTT_PACKET_PINGREQ_SIZE;
bool expectMoreCalls = true;
/* Modify incoming packet depending on type to be tested. */
incomingPacket.type = currentPacketType;
incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( modifyIncomingPacketStatus );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
/* More calls are expected only with the following packet types. */
if( ( currentPacketType != MQTT_PACKET_TYPE_PUBLISH ) &&
( currentPacketType != MQTT_PACKET_TYPE_PUBACK ) &&
( currentPacketType != MQTT_PACKET_TYPE_PUBREC ) &&
( currentPacketType != MQTT_PACKET_TYPE_PUBREL ) &&
( currentPacketType != MQTT_PACKET_TYPE_PUBCOMP ) &&
( currentPacketType != MQTT_PACKET_TYPE_PINGRESP ) &&
( currentPacketType != MQTT_PACKET_TYPE_SUBACK ) &&
( currentPacketType != MQTT_PACKET_TYPE_UNSUBACK ) )
{
expectMoreCalls = false;
}
/* When no data is available, the process loop tries to send a PINGREQ. */
if( modifyIncomingPacketStatus == MQTTNoDataAvailable )
{
if( ( pContext->waitingForPingResp == false ) &&
( pContext->keepAliveIntervalSec != 0U ) )
{
MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Replace pointer parameter being passed to the method. */
MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize );
MQTT_SerializePingreq_ExpectAnyArgsAndReturn( serializeStatus );
}
expectMoreCalls = false;
}
/* Deserialize based on the packet type (PUB or ACK) being received. */
if( expectMoreCalls )
{
if( incomingPublish )
{
MQTT_DeserializePublish_ExpectAnyArgsAndReturn( deserializeStatus );
if( pPubInfo != NULL )
{
MQTT_DeserializePublish_ReturnThruPtr_pPublishInfo( pPubInfo );
}
}
else
{
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( deserializeStatus );
}
if( ( deserializeStatus != MQTTSuccess ) ||
( currentPacketType == MQTT_PACKET_TYPE_PINGRESP ) ||
( currentPacketType == MQTT_PACKET_TYPE_SUBACK ) ||
( currentPacketType == MQTT_PACKET_TYPE_UNSUBACK ) )
{
expectMoreCalls = false;
}
}
/* Update state based on the packet type (PUB or ACK) being received. */
if( expectMoreCalls )
{
if( incomingPublish )
{
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( updateStateStatus );
MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &stateAfterDeserialize );
}
else
{
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( updateStateStatus );
MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
}
if( stateAfterDeserialize == MQTTPublishDone )
{
expectMoreCalls = false;
}
else
{
switch( updateStateStatus )
{
case MQTTSuccess:
expectMoreCalls = true;
break;
case MQTTStateCollision:
expectMoreCalls = pPubInfo->dup;
if( pPubInfo->dup == true )
{
MQTT_CalculateStatePublish_ExpectAnyArgsAndReturn( stateAfterDeserialize );
}
break;
default:
expectMoreCalls = false;
}
}
}
/* Serialize the packet to be sent in response to the received packet. */
if( expectMoreCalls )
{
MQTT_SerializeAck_ExpectAnyArgsAndReturn( serializeStatus );
if( serializeStatus != MQTTSuccess )
{
expectMoreCalls = false;
}
}
/* Update the state based on the sent packet. */
if( expectMoreCalls )
{
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( ( stateAfterSerialize == MQTTStateNull ) ?
MQTTIllegalState : MQTTSuccess );
MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize );
}
/* Expect the above calls when running MQTT_ProcessLoop. */
mqttStatus = MQTT_ProcessLoop( pContext, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( processLoopStatus, mqttStatus );
/* Any final assertions to end the test. */
if( mqttStatus == MQTTSuccess )
{
if( currentPacketType == MQTT_PACKET_TYPE_PUBLISH )
{
TEST_ASSERT_TRUE( pContext->controlPacketSent );
}
if( currentPacketType == MQTT_PACKET_TYPE_PINGRESP )
{
TEST_ASSERT_FALSE( pContext->waitingForPingResp );
}
}
}
/* ========================================================================== */
/**
* @brief Test that MQTT_Init is able to update the context object correctly.
*/
void test_MQTT_Init_Happy_Path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
TEST_ASSERT_EQUAL( MQTTNotConnected, context.connectStatus );
TEST_ASSERT_EQUAL( MQTT_FIRST_VALID_PACKET_ID, context.nextPacketId );
TEST_ASSERT_EQUAL_PTR( getTime, context.getTime );
TEST_ASSERT_EQUAL_PTR( eventCallback, context.appCallback );
/* These Unity assertions take pointers and compare their contents. */
TEST_ASSERT_EQUAL_MEMORY( &transport, &context.transportInterface, sizeof( transport ) );
TEST_ASSERT_EQUAL_MEMORY( &networkBuffer, &context.networkBuffer, sizeof( networkBuffer ) );
}
/**
* @brief Test that any NULL parameter causes MQTT_Init to return MQTTBadParameter.
*/
void test_MQTT_Init_Invalid_Params( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupTransportInterface( &transport );
/* Check that MQTTBadParameter is returned if any NULL parameters are passed. */
mqttStatus = MQTT_Init( NULL, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
mqttStatus = MQTT_Init( &context, NULL, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, NULL );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Test if NULL is passed for any of the function pointers. */
mqttStatus = MQTT_Init( &context, &transport, NULL, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
mqttStatus = MQTT_Init( &context, &transport, getTime, NULL, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
transport.recv = NULL;
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
transport.recv = transportRecvSuccess;
transport.send = NULL;
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/* ========================================================================== */
/**
* @brief Test MQTT_Connect, except for receiving the CONNACK.
*/
void test_MQTT_Connect_sendConnect( void )
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
uint32_t timeout = 2;
bool sessionPresent;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
size_t remainingLength, packetSize;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Check parameters */
status = MQTT_Connect( NULL, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
status = MQTT_Connect( &mqttContext, NULL, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, NULL );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Empty connect info fails. */
MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
memset( ( void * ) &connectInfo, 0x0, sizeof( connectInfo ) );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
connectInfo.pClientIdentifier = MQTT_CLIENT_IDENTIFIER;
connectInfo.clientIdentifierLength = sizeof( MQTT_CLIENT_IDENTIFIER ) - 1;
MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializeConnect_ExpectAnyArgsAndReturn( MQTTNoMemory );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status );
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
/* Transport send failed when sending CONNECT. */
/* Choose 10 bytes variable header + 1 byte payload for the remaining
* length of the CONNECT. The packet size needs to be nonzero for this test
* as that is the amount of bytes used in the call to send the packet. */
packetSize = 13;
remainingLength = 11;
mqttContext.transportInterface.send = transportSendFailure;
MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreArg_pPacketSize();
MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength();
MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
/* Send the CONNECT successfully. This provides branch coverage for sendPacket. */
mqttContext.transportInterface.send = transportSendSuccess;
MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
/* We know the send was successful if MQTT_GetIncomingPacketTypeAndLength()
* is called. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
}
/**
* @brief Test CONNACK reception in MQTT_Connect.
*/
void test_MQTT_Connect_receiveConnack( void )
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
uint32_t timeout = 0;
bool sessionPresent, sessionPresentExpected;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
transport.recv = transportRecvFailure;
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Everything before receiving the CONNACK should succeed. */
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
/* Nothing received from transport interface. Set timeout to 2 for branch coverage. */
timeout = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status );
/* Did not receive a CONNACK. */
incomingPacket.type = MQTT_PACKET_TYPE_PINGRESP;
incomingPacket.remainingLength = 0;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
/* Transport receive failure when receiving rest of packet. */
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
timeout = 2;
mqttContext.transportInterface.recv = transportRecvFailure;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
/* Bad response when deserializing CONNACK. */
mqttContext.transportInterface.recv = transportRecvSuccess;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTBadResponse );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
/* Test case when broker sends session present flag in response to a
* clean session connection request. */
mqttContext.transportInterface.recv = transportRecvSuccess;
connectInfo.cleanSession = true;
sessionPresentExpected = true;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
}
/**
* @brief Test CONNACK reception in MQTT_Connect.
*/
void test_MQTT_Connect_receiveConnack_retries( void )
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
bool sessionPresent;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket;
/* Same set of tests with retries. MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT is 2*/
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
transport.recv = transportRecvFailure;
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Everything before receiving the CONNACK should succeed. */
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
/* Test with retries. MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT is 2.
* Nothing received from transport interface. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
/* 2 retries. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status );
/* Did not receive a CONNACK. */
incomingPacket.type = MQTT_PACKET_TYPE_PINGRESP;
incomingPacket.remainingLength = 0;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
/* Transport receive failure when receiving rest of packet. */
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
/* Bad response when deserializing CONNACK. */
mqttContext.transportInterface.recv = transportRecvSuccess;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTBadResponse );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
}
/**
* @brief Test error cases for MQTT_Connect when a timeout occurs or the packet
* needs to be discarded in MQTT_Connect.
*/
void test_MQTT_Connect_partial_receive()
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
uint32_t timeout = 0;
bool sessionPresent;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
transport.recv = transportRecvOneByte;
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Everything before receiving the CONNACK should succeed. */
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
/* Not enough time to receive entire packet, for branch coverage. This is due
* to the fact the mocked receive function reads only one byte at a time. */
timeout = 1;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
timeout = 10;
/* Not enough space for packet, discard it. */
mqttContext.networkBuffer.size = 2;
incomingPacket.remainingLength = 3;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status );
/* Discard packet, no timeout provided. This should fail since multiple
* iterations of the discard loop are required to discard the packet, but only
* one will run. */
mqttContext.transportInterface.recv = transportRecvSuccess;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
/* Timeout while discarding packet. */
/* (Mocked) read only one byte at a time to ensure timeout will occur. */
mqttContext.transportInterface.recv = transportRecvOneByte;
incomingPacket.remainingLength = 20;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
/* Receive failure while discarding packet. */
mqttContext.transportInterface.recv = transportRecvFailure;
/* Test with dummy get time function to make sure there are no infinite loops. */
mqttContext.getTime = getTimeDummy;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, MQTT_NO_TIMEOUT_MS, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
}
/**
* @brief Test resend of pending acks in MQTT_Connect.
*/
void test_MQTT_Connect_resendPendingAcks( void )
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
uint32_t timeout = 2;
bool sessionPresent, sessionPresentResult;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket;
uint16_t packetIdentifier = 1;
MQTTPublishState_t pubRelState = MQTTPubRelSend;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
memset( ( void * ) &connectInfo, 0x00, sizeof( connectInfo ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
/* Test 1. No packets to resend reestablishing a session. */
/* successful receive CONNACK packet. */
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
/* Return with a session present flag. */
sessionPresent = true;
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresent );
/* No packets to resend. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
TEST_ASSERT_TRUE( sessionPresentResult );
/* Test 2. One packet found in ack pending state, but Sending ack failed. */
sessionPresentResult = false;
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresent );
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack failure. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTBadParameter );
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult );
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
TEST_ASSERT_EQUAL_INT( MQTTNotConnected, mqttContext.connectStatus );
TEST_ASSERT_TRUE( sessionPresentResult );
/* Test 3. One packet found in ack pending state, Sent
* PUBREL successfully. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresent );
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack successful. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Query for any remaining packets pending to ack. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_ID_INVALID );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
/* Test 4. Three packets found in ack pending state. Sent PUBREL successfully
* for first and failed for second and no attempt for third. */
mqttContext.keepAliveIntervalSec = 0;
mqttContext.connectStatus = MQTTNotConnected;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresent );
/* First packet. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack successful. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Second packet. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier + 1 );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack failed. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTBadParameter );
/* Query for any remaining packets pending to ack. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier + 2 );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
TEST_ASSERT_EQUAL_INT( MQTTNotConnected, mqttContext.connectStatus );
/* Test 5. Two packets found in ack pending state. Sent PUBREL successfully
* for first and failed for second. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresent );
/* First packet. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack successful. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Second packet. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier + 1 );
MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState );
/* Serialize Ack successful. */
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Query for any remaining packets pending to ack. */
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_ID_INVALID );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
}
/**
* @brief Test success case for MQTT_Connect().
*/
void test_MQTT_Connect_happy_path()
{
MQTTContext_t mqttContext;
MQTTConnectInfo_t connectInfo;
MQTTPublishInfo_t willInfo;
uint32_t timeout = 2;
bool sessionPresent, sessionPresentExpected;
MQTTStatus_t status;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket;
MQTTPubAckInfo_t cleanRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 };
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
/* Success. */
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
/* With non-NULL Will. */
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
willInfo.pTopicName = "test";
willInfo.topicNameLength = 4;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
/* Request to establish a clean session. */
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
connectInfo.cleanSession = true;
sessionPresentExpected = false;
/* Populate some state records to make sure they are cleared since a clean session
* will be established. */
mqttContext.outgoingPublishRecords[ 0 ].packetId = 1;
mqttContext.outgoingPublishRecords[ 0 ].qos = MQTTQoS2;
mqttContext.outgoingPublishRecords[ 0 ].publishState = MQTTPublishSend;
mqttContext.incomingPublishRecords[ MQTT_STATE_ARRAY_MAX_COUNT - 1 ].packetId = 1;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
TEST_ASSERT_FALSE( sessionPresent );
/* Test old records were cleared. */
TEST_ASSERT_EQUAL_MEMORY( cleanRecords, mqttContext.outgoingPublishRecords, sizeof( cleanRecords ) );
TEST_ASSERT_EQUAL_MEMORY( cleanRecords, mqttContext.incomingPublishRecords, sizeof( cleanRecords ) );
/* Request to establish a session if present and session present is received
* from broker. */
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
connectInfo.cleanSession = false;
sessionPresentExpected = true;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected );
MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
TEST_ASSERT_TRUE( sessionPresent );
/* CONNACK receive with timeoutMs=0. Retry logic will be used. */
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
sessionPresent = false;
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
/* CONNACK receive with timeoutMs=0. Retry logic will be used.
* #MQTTNoDataAvailable for first #MQTT_GetIncomingPacketTypeAndLength
* and success in the second time. */
mqttContext.connectStatus = MQTTNotConnected;
mqttContext.keepAliveIntervalSec = 0;
incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
incomingPacket.remainingLength = 2;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess );
status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec );
}
/* ========================================================================== */
/**
* @brief Test that MQTT_Publish works as intended.
*/
void test_MQTT_Publish( void )
{
MQTTContext_t mqttContext;
MQTTPublishInfo_t publishInfo;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTStatus_t status;
size_t headerSize;
MQTTPublishState_t expectedState;
const uint16_t PACKET_ID = 1;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
transport.send = transportSendFailure;
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
memset( ( void * ) &publishInfo, 0x0, sizeof( publishInfo ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Verify parameters. */
status = MQTT_Publish( NULL, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
status = MQTT_Publish( &mqttContext, NULL, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
publishInfo.qos = MQTTQoS1;
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
publishInfo.payloadLength = 1;
publishInfo.pPayload = NULL;
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
memset( ( void * ) &publishInfo, 0x0, sizeof( publishInfo ) );
/* Bad Parameter when getting packet size. */
publishInfo.qos = MQTTQoS0;
MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Always return success from now on. */
MQTT_GetPublishPacketSize_IgnoreAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTNoMemory );
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status );
/* The transport interface will fail. */
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
/* We need sendPacket to be called with at least 1 byte to send, so that
* it can return failure. This argument is the output of serializing the
* publish header. */
headerSize = 1;
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
/* We want to test the first call to sendPacket within sendPublish succeeding,
* and the second one failing. */
mqttContext.transportInterface.send = transportSendSucceedThenFail;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
publishInfo.pPayload = "Test";
publishInfo.payloadLength = 4;
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
mqttContext.transportInterface.send = transportSendSuccess;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
/* Test that sending a publish without a payload succeeds. */
publishInfo.pPayload = NULL;
publishInfo.payloadLength = 0;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
status = MQTT_Publish( &mqttContext, &publishInfo, 0 );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
/* Restore the test payload and length. */
publishInfo.pPayload = "Test";
publishInfo.payloadLength = 4;
/* Now for non zero QoS, which uses state engine. */
publishInfo.qos = MQTTQoS2;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTBadParameter );
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
publishInfo.qos = MQTTQoS1;
expectedState = MQTTPublishSend;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState );
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
/* Duplicate publish. dup flag is not marked by application. */
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision );
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTStateCollision, status );
/* Duplicate publish. dup flag is marked by application. */
publishInfo.dup = true;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision );
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState );
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
/* Duplicate publish. dup flag is marked by application.
* State record is not present. */
publishInfo.dup = true;
MQTT_SerializePublishHeader_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_SerializePublishHeader_ReturnThruPtr_pHeaderSize( &headerSize );
MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState );
status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
}
/* ========================================================================== */
/**
* @brief Test that MQTT_Disconnect works as intended.
*/
void test_MQTT_Disconnect( void )
{
MQTTContext_t mqttContext;
MQTTStatus_t status;
uint8_t buffer[ 10 ];
uint8_t * bufPtr = buffer;
NetworkContext_t networkContext;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
size_t disconnectSize = 2;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
networkContext.buffer = &bufPtr;
transport.pNetworkContext = &networkContext;
transport.recv = transportRecvSuccess;
transport.send = transportSendFailure;
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
mqttContext.connectStatus = MQTTConnected;
/* Verify parameters. */
status = MQTT_Disconnect( NULL );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Send failure with network error. */
MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize );
MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess );
status = MQTT_Disconnect( &mqttContext );
TEST_ASSERT_EQUAL( MQTTSendFailed, status );
/* Successful send. */
mqttContext.transportInterface.send = mockSend;
MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize );
MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Write a disconnect packet into the buffer. */
mqttBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
status = MQTT_Disconnect( &mqttContext );
TEST_ASSERT_EQUAL( MQTTSuccess, status );
TEST_ASSERT_EQUAL( MQTTNotConnected, mqttContext.connectStatus );
TEST_ASSERT_EQUAL_MEMORY( mqttBuffer, buffer, 2 );
}
/* ========================================================================== */
/**
* @brief Test that MQTT_GetPacketId works as intended.
*/
void test_MQTT_GetPacketId( void )
{
MQTTContext_t mqttContext;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
uint16_t packetId;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
memset( ( void * ) &mqttContext, 0x0, sizeof( mqttContext ) );
MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
/* Verify parameters. */
packetId = MQTT_GetPacketId( NULL );
TEST_ASSERT_EQUAL_INT( 0, packetId );
packetId = MQTT_GetPacketId( &mqttContext );
TEST_ASSERT_EQUAL_INT( 1, packetId );
TEST_ASSERT_EQUAL_INT( 2, mqttContext.nextPacketId );
mqttContext.nextPacketId = UINT16_MAX;
packetId = MQTT_GetPacketId( &mqttContext );
TEST_ASSERT_EQUAL_INT( UINT16_MAX, packetId );
TEST_ASSERT_EQUAL_INT( 1, mqttContext.nextPacketId );
}
/* ========================================================================== */
/**
* @brief Test that NULL pContext causes MQTT_ProcessLoop to return MQTTBadParameter.
*/
void test_MQTT_ProcessLoop_Invalid_Params( void )
{
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTStatus_t mqttStatus = MQTT_ProcessLoop( NULL, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
setupTransportInterface( &transport );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Get time function cannot be NULL. */
context.getTime = NULL;
mqttStatus = MQTT_ProcessLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Restore the time function for the next test. */
context.getTime = getTime;
/* The fixed network buffer cannot be NULL. */
context.networkBuffer.pBuffer = NULL;
mqttStatus = MQTT_ProcessLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/**
* @brief This test case covers all calls to the private method,
* handleIncomingPublish(...),
* that result in the process loop returning successfully.
*/
void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPublishInfo_t pubInfo;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
modifyIncomingPacketStatus = MQTTSuccess;
/* Assume QoS = 1 so that a PUBACK will be sent after receiving PUBLISH.
* That is, expectProcessLoopCalls will take on the following parameters:
* incomingPublish=true and stateAfterDeserialize=MQTTPubAckSend. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubAckSend,
MQTTSuccess, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, true, NULL );
/* Assume QoS = 2 so that a PUBREC will be sent after receiving PUBLISH.
* That is, expectProcessLoopCalls will take on the following parameters:
* incomingPublish=true and stateAfterDeserialize=MQTTPubRecSend. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubRecSend,
MQTTSuccess, MQTTSuccess, MQTTPubRelPending,
MQTTSuccess, true, NULL );
/* Duplicate QoS1 publish received.
* expectProcessLoopCalls will take on the following parameters:
* incomingPublish=true, stateAfterDeserialize=MQTTPubAckSend,
* updateStateStatus=MQTTStateCollision and pPubInfo is passed with
* dup flag set. The event callback should not be invoked. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
pubInfo.dup = true;
pubInfo.qos = MQTTQoS1;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubAckSend,
MQTTStateCollision, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, true, &pubInfo );
TEST_ASSERT_FALSE( isEventCallbackInvoked );
/* Duplicate QoS2 publish received.
* expectProcessLoopCalls will take on the following parameters:
* incomingPublish=true, stateAfterDeserialize=MQTTPubRecSend,
* updateStateStatus=MQTTStateCollision and pPubInfo is passed with
* dup flag set. The event callback should not be invoked. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
pubInfo.qos = MQTTQoS2;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubAckSend,
MQTTStateCollision, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, true, &pubInfo );
TEST_ASSERT_FALSE( isEventCallbackInvoked );
/* Duplicate QoS2 publish received with no collision.
* expectProcessLoopCalls will take on the following parameters:
* incomingPublish=true, stateAfterDeserialize=MQTTPubRecSend,
* updateStateStatus=MQTTStateCollision and pPubInfo is passed with
* dup flag set. The event callback should be invoked. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubAckSend,
MQTTSuccess, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, true, &pubInfo );
TEST_ASSERT_TRUE( isEventCallbackInvoked );
}
/**
* @brief This test case covers all calls to the private method,
* handleIncomingPublish(...),
* that result in the process loop returning an error.
*/
void test_MQTT_ProcessLoop_handleIncomingPublish_Error_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPublishInfo_t publishInfo = { 0 };
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
modifyIncomingPacketStatus = MQTTSuccess;
/* Verify that an error is propagated when deserialization fails by returning
* MQTTBadResponse. Any parameters beyond that are actually irrelevant
* because they are only used as return values for non-expected calls. */
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
expectProcessLoopCalls( &context, MQTTBadResponse, MQTTStateNull,
MQTTIllegalState, MQTTBadResponse, MQTTStateNull,
MQTTBadResponse, true, NULL );
/* A publish is received when already a state record exists, but dup
* flag is not set. */
publishInfo.dup = false;
publishInfo.qos = MQTTQoS0;
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubRecSend,
MQTTStateCollision, MQTTSuccess, MQTTPublishDone,
MQTTStateCollision, true, &publishInfo );
TEST_ASSERT_FALSE( isEventCallbackInvoked );
/* A publish is received and dup flag is set, but state update failed. */
publishInfo.dup = true;
publishInfo.qos = MQTTQoS2;
currentPacketType = MQTT_PACKET_TYPE_PUBLISH;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubAckSend,
MQTTIllegalState, MQTTSuccess, MQTTPublishDone,
MQTTIllegalState, true, &publishInfo );
TEST_ASSERT_FALSE( isEventCallbackInvoked );
}
/**
* @brief This test case covers all calls to the private method,
* handleIncomingAck(...),
* that result in the process loop returning successfully.
*/
void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
modifyIncomingPacketStatus = MQTTSuccess;
/* Mock the receiving of a PUBACK packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_PUBACK;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, false, NULL );
/* Mock the receiving of a PUBREC packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_PUBREC;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubRelSend,
MQTTSuccess, MQTTSuccess, MQTTPubCompPending,
MQTTSuccess, false, NULL );
/* Mock the receiving of a PUBREL packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_PUBREL;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubCompSend,
MQTTSuccess, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, false, NULL );
/* Duplicate PUBREL, but no record exists. */
currentPacketType = MQTT_PACKET_TYPE_PUBREL;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTStateNull,
MQTTBadParameter, MQTTSuccess, MQTTPublishDone,
MQTTBadParameter, false, NULL );
/* Mock the receiving of a PUBCOMP packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_PUBCOMP;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, MQTTSuccess, MQTTPublishDone,
MQTTSuccess, false, NULL );
/* Mock the receiving of a PINGRESP packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_PINGRESP;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTStateNull,
MQTTSuccess, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
/* Mock the receiving of a SUBACK packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_SUBACK;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTStateNull,
MQTTSuccess, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
/* Verify that process loop is still successful when SUBACK indicates a
* server refusal. */
currentPacketType = MQTT_PACKET_TYPE_SUBACK;
isEventCallbackInvoked = false;
expectProcessLoopCalls( &context, MQTTServerRefused, MQTTStateNull,
MQTTSuccess, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
TEST_ASSERT_TRUE( isEventCallbackInvoked );
/* Mock the receiving of an UNSUBACK packet type and expect the appropriate
* calls made from the process loop. */
currentPacketType = MQTT_PACKET_TYPE_UNSUBACK;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTStateNull,
MQTTSuccess, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
}
/**
* @brief This test case covers all calls to the private method,
* handleIncomingAck(...),
* that result in the process loop returning an error.
*/
void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
modifyIncomingPacketStatus = MQTTSuccess;
/* Verify that MQTTBadResponse is propagated when deserialization fails upon
* receiving an unknown packet type. */
currentPacketType = MQTT_PACKET_TYPE_INVALID;
expectProcessLoopCalls( &context, MQTTBadResponse, MQTTStateNull,
MQTTIllegalState, MQTTBadResponse, MQTTStateNull,
MQTTBadResponse, false, NULL );
/* Verify that MQTTSendFailed is propagated when receiving a PUBREC
* then failing when serializing a PUBREL to send in response. */
currentPacketType = MQTT_PACKET_TYPE_PUBREC;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubRelSend,
MQTTSuccess, MQTTNoMemory, MQTTStateNull,
MQTTSendFailed, false, NULL );
/* Verify that MQTTBadResponse is propagated when deserialization fails upon
* receiving a PUBACK. */
currentPacketType = MQTT_PACKET_TYPE_PUBACK;
expectProcessLoopCalls( &context, MQTTBadResponse, MQTTStateNull,
MQTTIllegalState, MQTTBadResponse, MQTTStateNull,
MQTTBadResponse, false, NULL );
/* Verify that MQTTBadResponse is propagated when deserialization fails upon
* receiving a PINGRESP. */
currentPacketType = MQTT_PACKET_TYPE_PINGRESP;
expectProcessLoopCalls( &context, MQTTBadResponse, MQTTStateNull,
MQTTIllegalState, MQTTBadResponse, MQTTStateNull,
MQTTBadResponse, false, NULL );
/* Verify that MQTTBadResponse is propagated when deserialization fails upon
* receiving a SUBACK. */
currentPacketType = MQTT_PACKET_TYPE_SUBACK;
expectProcessLoopCalls( &context, MQTTBadResponse, MQTTStateNull,
MQTTIllegalState, MQTTBadResponse, MQTTStateNull,
MQTTBadResponse, false, NULL );
/* Verify that MQTTIllegalState is returned if MQTT_UpdateStateAck(...)
* provides an unknown state such as MQTTStateNull to sendPublishAcks(...). */
currentPacketType = MQTT_PACKET_TYPE_PUBREC;
expectProcessLoopCalls( &context, MQTTSuccess, MQTTPubRelSend,
MQTTSuccess, MQTTSuccess, MQTTStateNull,
MQTTIllegalState, false, NULL );
}
/**
* @brief This test case covers all calls to the private method,
* handleKeepAlive(...),
* that result in the process loop returning successfully.
*/
void test_MQTT_ProcessLoop_handleKeepAlive_Happy_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupNetworkBuffer( &networkBuffer );
setupTransportInterface( &transport );
modifyIncomingPacketStatus = MQTTNoDataAvailable;
globalEntryTime = MQTT_ONE_SECOND_TO_MS;
/* Coverage for the branch path where keep alive interval is 0. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.waitingForPingResp = false;
context.keepAliveIntervalSec = 0;
expectProcessLoopCalls( &context, MQTTStateNull, MQTTStateNull,
MQTTIllegalState, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
/* Coverage for the branch path where keep alive interval is greater than 0,
* and the interval has expired. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.waitingForPingResp = true;
context.keepAliveIntervalSec = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
context.lastPacketTime = getTime();
expectProcessLoopCalls( &context, MQTTStateNull, MQTTStateNull,
MQTTIllegalState, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
/* Coverage for the branch path where PINGRESP timeout interval hasn't expired. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.waitingForPingResp = true;
context.keepAliveIntervalSec = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
context.lastPacketTime = 0;
context.pingReqSendTimeMs = MQTT_ONE_SECOND_TO_MS;
expectProcessLoopCalls( &context, MQTTStateNull, MQTTStateNull,
MQTTIllegalState, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
/* Coverage for the branch path where a PINGRESP hasn't been sent out yet. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.waitingForPingResp = false;
context.keepAliveIntervalSec = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
context.lastPacketTime = 0;
expectProcessLoopCalls( &context, MQTTStateNull, MQTTStateNull,
MQTTIllegalState, MQTTSuccess, MQTTStateNull,
MQTTSuccess, false, NULL );
}
/**
* @brief This test case covers all calls to the private method,
* handleKeepAlive(...),
* that result in the process loop returning an error.
*/
void test_MQTT_ProcessLoop_handleKeepAlive_Error_Paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupTransportInterface( &transport );
modifyIncomingPacketStatus = MQTTNoDataAvailable;
globalEntryTime = MQTT_ONE_SECOND_TO_MS;
/* Coverage for the branch path where PINGRESP timeout interval has expired. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.keepAliveIntervalSec = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S;
context.lastPacketTime = 0;
context.pingReqSendTimeMs = 0;
context.waitingForPingResp = true;
expectProcessLoopCalls( &context, MQTTStateNull, MQTTStateNull,
MQTTIllegalState, MQTTSuccess, MQTTStateNull,
MQTTKeepAliveTimeout, false, NULL );
}
/**
* @brief This test mocks a failing transport receive and runs multiple
* iterations of the process loop, resulting in returning MQTTRecvFailed.
*/
void test_MQTT_ProcessLoop_Receive_Failed( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
setupNetworkBuffer( &networkBuffer );
setupTransportInterface( &transport );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
mqttStatus = MQTT_ProcessLoop( &context, MQTT_SAMPLE_PROCESS_LOOP_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTRecvFailed, mqttStatus );
}
/**
* @brief Set the initial entry time close to the maximum value, causing
* an overflow. This test then checks that the process loop still runs for the
* expected number of iterations in spite of this.
*/
void test_MQTT_ProcessLoop_Timer_Overflow( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket = { 0 };
MQTTPublishState_t publishState = MQTTPubAckSend;
MQTTPublishState_t ackState = MQTTPublishDone;
uint8_t i = 0;
uint8_t numIterations = ( MQTT_TIMER_OVERFLOW_TIMEOUT_MS / MQTT_TIMER_CALLS_PER_ITERATION ) + 1;
uint32_t expectedFinalTime;
setupTransportInterface( &transport );
networkBuffer.size = 1000;
incomingPacket.type = MQTT_PACKET_TYPE_PUBLISH;
incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
globalEntryTime = UINT32_MAX - MQTT_OVERFLOW_OFFSET;
expectedFinalTime = MQTT_TIMER_CALLS_PER_ITERATION * numIterations - MQTT_OVERFLOW_OFFSET;
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify that we run the expected number of iterations despite overflowing. */
for( ; i < numIterations; i++ )
{
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
/* Assume QoS = 1 so that a PUBACK will be sent after receiving PUBLISH. */
MQTT_DeserializePublish_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &publishState );
MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &ackState );
}
mqttStatus = MQTT_ProcessLoop( &context, MQTT_TIMER_OVERFLOW_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
TEST_ASSERT_EQUAL( expectedFinalTime, globalEntryTime );
}
/* ========================================================================== */
/**
* @brief Test that MQTT_ReceiveLoop() works as intended. Since the only difference
* between this and the process loop is keep alive, we only need to test the
* differences for coverage.
*/
void test_MQTT_ReceiveLoop( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTPacketInfo_t incomingPacket = { 0 };
setupTransportInterface( &transport );
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify that a NULL Context returns an error. */
mqttStatus = MQTT_ReceiveLoop( NULL, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Verify that a NULL time function returns an error. */
context.getTime = NULL;
mqttStatus = MQTT_ReceiveLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
context.getTime = getTime;
/* Verify that a null fixed network buffer returns an error. */
context.networkBuffer.pBuffer = NULL;
mqttStatus = MQTT_ReceiveLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
setupNetworkBuffer( &( context.networkBuffer ) );
/* Error case, for branch coverage. */
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
mqttStatus = MQTT_ReceiveLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTRecvFailed, mqttStatus );
/* Keep Alive should not trigger.*/
context.keepAliveIntervalSec = 1;
MQTT_GetIncomingPacketTypeAndLength_IgnoreAndReturn( MQTTNoDataAvailable );
mqttStatus = MQTT_ReceiveLoop( &context, 2000 );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
TEST_ASSERT_FALSE( context.controlPacketSent );
/* Test with a dummy getTime to ensure there's no infinite loops. */
context.getTime = getTimeDummy;
mqttStatus = MQTT_ReceiveLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
context.getTime = getTime;
MQTT_GetIncomingPacketTypeAndLength_StopIgnore();
/* Receive a PINGRESP. */
incomingPacket.type = MQTT_PACKET_TYPE_PINGRESP;
incomingPacket.remainingLength = 0U;
MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
mqttStatus = MQTT_ReceiveLoop( &context, MQTT_NO_TIMEOUT_MS );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
}
/* ========================================================================== */
/**
* @brief This test case verifies that MQTT_Subscribe returns MQTTBadParameter
* with an invalid parameter. This test case also gives us coverage over
* the private method, validateSubscribeUnsubscribeParams(...).
*/
void test_MQTT_Subscribe_invalid_params( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
MQTTSubscribeInfo_t subscribeInfo;
/* Call subscribe with a NULL context. */
mqttStatus = MQTT_Subscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Call subscribe with a NULL subscription list. */
mqttStatus = MQTT_Subscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Call subscribe with 0 subscriptions. */
mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Packet ID cannot be 0 per MQTT 3.1.1 spec. */
mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, 0 );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/**
* @brief This test case verifies that MQTT_Subscribe returns successfully
* when valid parameters are passed and all bytes are sent.
*/
void test_MQTT_Subscribe_happy_path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTSubscribeInfo_t subscribeInfo;
size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
setupSubscriptionInfo( &subscribeInfo );
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSuccess is returned with the following mocks. */
MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
MQTT_SerializeSubscribe_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Subscribe. */
mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
}
/**
* @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed
* if transport interface send returns an error.
*/
void test_MQTT_Subscribe_error_paths( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTSubscribeInfo_t subscribeInfo;
size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH;
/* Verify that an error is propagated when transport interface returns an error. */
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
setupSubscriptionInfo( &subscribeInfo );
transport.send = transportSendFailure;
transport.recv = transportRecvFailure;
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSendFailed is propagated when transport interface returns an error. */
MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
MQTT_SerializeSubscribe_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Subscribe. */
mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus );
}
/* ========================================================================== */
/**
* @brief This test case verifies that MQTT_Unsubscribe returns MQTTBadParameter
* with an invalid parameter. This test case also gives us coverage over
* the private method, validateSubscribeUnsubscribeParams(...).
*/
void test_MQTT_Unsubscribe_invalid_params( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
MQTTSubscribeInfo_t subscribeInfo;
/* Call subscribe with a NULL context. */
mqttStatus = MQTT_Unsubscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Call subscribe with a NULL subscription list. */
mqttStatus = MQTT_Unsubscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Call subscribe with 0 subscriptions. */
mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
/* Packet ID cannot be 0 per MQTT 3.1.1 spec. */
mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, 0 );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/**
* @brief This test case verifies that MQTT_Unsubscribe returns successfully
* when valid parameters are passed and all bytes are sent.
*/
void test_MQTT_Unsubscribe_happy_path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTSubscribeInfo_t subscribeInfo;
size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
setupSubscriptionInfo( &subscribeInfo );
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSuccess is returned with the following mocks. */
MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
MQTT_SerializeUnsubscribe_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Unsubscribe. */
mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
}
/**
* @brief This test case verifies that MQTT_Unsubscribe returns MQTTSendFailed
* if transport interface send returns an error.
*/
void test_MQTT_Unsubscribe_error_path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
MQTTSubscribeInfo_t subscribeInfo;
size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH;
/* Verify that an error is propagated when transport interface returns an error. */
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
setupSubscriptionInfo( &subscribeInfo );
transport.send = transportSendFailure;
transport.recv = transportRecvFailure;
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSendFailed is propagated when transport interface returns an error. */
MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize );
MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength );
MQTT_SerializeUnsubscribe_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Unsubscribe. */
mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID );
TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus );
}
/* ========================================================================== */
/**
* @brief This test case verifies that MQTT_Ping returns MQTTBadParameter
* with context parameter is NULL.
*/
void test_MQTT_Ping_invalid_params( void )
{
MQTTStatus_t mqttStatus;
/* Call ping with a NULL context. */
mqttStatus = MQTT_Ping( NULL );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/**
* @brief This test case verifies that MQTT_Ping returns successfully
* when valid parameters are passed and all bytes are sent.
*/
void test_MQTT_Ping_happy_path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
size_t pingreqSize = MQTT_PACKET_PINGREQ_SIZE;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSuccess is returned. */
MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize );
MQTT_SerializePingreq_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Ping. */
mqttStatus = MQTT_Ping( &context );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
TEST_ASSERT_EQUAL( context.lastPacketTime, context.pingReqSendTimeMs );
TEST_ASSERT_TRUE( context.waitingForPingResp );
}
/**
* @brief This test case verifies that MQTT_Ping returns MQTTSendFailed
* if transport interface send returns an error.
*/
void test_MQTT_Ping_error_path( void )
{
MQTTStatus_t mqttStatus;
MQTTContext_t context;
TransportInterface_t transport;
MQTTFixedBuffer_t networkBuffer;
size_t pingreqSize = MQTT_PACKET_PINGREQ_SIZE;
setupTransportInterface( &transport );
setupNetworkBuffer( &networkBuffer );
/* Test a network error is returned from sending the PING packet over the
* transport send . */
transport.send = transportSendFailure;
transport.recv = transportRecvFailure;
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTSendFailed is propagated when transport interface returns an error. */
MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize );
MQTT_SerializePingreq_ExpectAnyArgsAndReturn( MQTTSuccess );
/* Expect the above calls when running MQTT_Ping. */
mqttStatus = MQTT_Ping( &context );
TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus );
/* Initialize context. */
mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
/* Verify MQTTBadParameter is propagated when getting PINGREQ packet size fails. */
MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize );
/* Expect the above calls when running MQTT_Ping. */
mqttStatus = MQTT_Ping( &context );
TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus );
}
/* ========================================================================== */
/**
* @brief Test MQTT_MatchTopic for invalid input parameters.
*/
void test_MQTT_MatchTopic_InvalidInput( void )
{
bool matchResult = false;
/* NULL topic name. */
TEST_ASSERT_EQUAL( MQTTBadParameter, MQTT_MatchTopic( NULL,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid topic name length. */
TEST_ASSERT_EQUAL( MQTTBadParameter, MQTT_MatchTopic( MQTT_SAMPLE_TOPIC_FILTER,
0u,
MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* NULL topic filter. */
TEST_ASSERT_EQUAL( MQTTBadParameter, MQTT_MatchTopic( MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
NULL,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid topic filter length. */
TEST_ASSERT_EQUAL( MQTTBadParameter, MQTT_MatchTopic( MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
MQTT_SAMPLE_TOPIC_FILTER,
0u,
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid output parameter. */
TEST_ASSERT_EQUAL( MQTTBadParameter, MQTT_MatchTopic( MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
NULL ) );
}
/**
* @brief Verifies that MQTT_MatchTopic is able to determine an exact match between the
* topic name and topic filter.
*/
void test_MQTT_MatchTopic_ExactMatch( void )
{
const char * pTopicFilter = NULL;
const char * pTopicName = NULL;
bool matchResult = false;
/* Test for topic filter and topic name having exact match. */
pTopicName = "/test/match";
pTopicFilter = "/test/match";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test for exact match when topic name and filter start with '$' .*/
pTopicName = "$///";
pTopicFilter = "$///";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test for no match (with no wildcard in the topic filter). */
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
MQTT_SAMPLE_TOPIC_FILTER,
MQTT_SAMPLE_TOPIC_FILTER_LENGTH,
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Test for match at end with no wildcards. */
pTopicName = "/test/match/";
pTopicFilter = "/test/match/a";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
}
/**
* @brief Verifies that MQTT_MatchTopic meets the MQTT 3.1.1 specification of all
* cases of matching topic filters that contain the single-level '+' wildcard.
*/
void test_MQTT_MatchTopic_Wildcard_SingleLevel( void )
{
const char * pTopicName = NULL;
const char * pTopicFilter = NULL;
bool matchResult = false;
/* Nominal case of topic filter ending with '+' .*/
pTopicName = "/test/match/level1";
pTopicFilter = "/test/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test for match with a topic name starting with '$' .*/
pTopicName = "$test/match/level1";
pTopicFilter = "$test/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test that match fails when topic name has more levels than topic filter. */
pTopicName = "/test/match/level1/level2";
pTopicFilter = "/test/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
pTopicName = "/";
pTopicFilter = "+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Test with '+' as the topic filter. */
pTopicName = "test";
pTopicFilter = "+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test that '+' in topic filter matches topic name containing consecutive
* level separators, "//" in the corresponding level. */
pTopicName = "/test//level1";
pTopicFilter = "/test/+/level1";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test with multiple placements of wildcard in topic filter. */
pTopicName = "/test/match/level1";
pTopicFilter = "/+/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
pTopicName = "/test/match/level1";
pTopicFilter = "+/+/+/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
pTopicName = "/test///level1";
pTopicFilter = "/test/+/+/level1";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Edge case where filter ending with '/+' matches topic ending with '/'. */
pTopicName = "/test/match/";
pTopicFilter = "/test/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Edge case where filter ending with '/+' should not match a topic ending with
* at parent level. */
pTopicName = "/test/match";
pTopicFilter = "/test/match/+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Edge case where topic filter starts with '+' and topic name starts with '$'. */
pTopicName = "$/test/match";
pTopicFilter = "+/test/match";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Topic name matches all characters with topic filter, but topic filter is invalid. */
pTopicName = "test/match/level";
pTopicFilter = "test/match/level+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid topic filter where non-starting '+' is not placed after '/'.*/
pTopicName = "test/match/level1";
pTopicFilter = "test/match/level+";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
}
/**
* @brief Verifies that MQTT_MatchTopic meets the MQTT 3.1.1 specification of all
* cases of matching topic filters that contain the multi-level '#' wildcard.
*/
void test_MQTT_MatchTopic_Wildcard_MultiLevel_Match_Cases( void )
{
const char * pTopicName = NULL;
const char * pTopicFilter = NULL;
bool matchResult = false;
/* Match topic filter ending with '#' with a single level .*/
pTopicName = "/test/match/level1";
pTopicFilter = "/test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Match topic filter ending with '#' with multiple levels in topic name.*/
pTopicName = "/test/match/level1/level2/level3";
pTopicFilter = "/test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
pTopicName = "/test/match/level1/level2/level3";
pTopicFilter = "/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Match when topic filter is "#" */
pTopicName = "test/match/level";
pTopicFilter = "#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test for match with a topic name starting with '$' .*/
pTopicName = "$test/match/level1";
pTopicFilter = "$test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Match '#' with topic name ending at the parent level. */
pTopicName = "/test/match";
pTopicFilter = "/test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Edge case where filter ending with '/#' matches topic ending with '/'. */
pTopicName = "/test/match/";
pTopicFilter = "/test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
/* Test for topic filters containing both '+' and '#' wildcard characters. */
pTopicName = "/test/match";
pTopicFilter = "+/test/match/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
pTopicName = "/test/match/level";
pTopicFilter = "+/+/+/#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( true, matchResult );
}
/**
* @brief Verifies that MQTT_MatchTopic meets the MQTT 3.1.1 specification for
* cases of where topic filter containing '#' wildcard do not match topic name.
*/
void test_MQTT_MatchTopic_Wildcard_MultiLevel_No_Match_Cases( void )
{
const char * pTopicName = NULL;
const char * pTopicFilter = NULL;
bool matchResult = false;
/* Edge case where topic filter starts with '#' and topic name starts with '$'. */
pTopicName = "$/test/match";
pTopicFilter = "#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid topic filter where non-starting '#' is not placed after '/'.*/
pTopicName = "test/match/level1";
pTopicFilter = "test/match/level#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Topic name matches all characters with topic filter, but topic filter is invalid. */
pTopicName = "test/match/level";
pTopicFilter = "test/match/level?#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
pTopicName = "test/match/level";
pTopicFilter = "test/match/level#";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
/* Invalid topic filters that contain '#' at a non-ending position .*/
pTopicName = "test/match/level1/level2";
pTopicFilter = "test/match/#/level2";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
pTopicName = "test/match/level2";
pTopicFilter = "#/match/level2";
TEST_ASSERT_EQUAL( MQTTSuccess, MQTT_MatchTopic( pTopicName,
strlen( pTopicName ),
pTopicFilter,
strlen( pTopicFilter ),
&matchResult ) );
TEST_ASSERT_EQUAL( false, matchResult );
}
/* ========================================================================== */
/**
* @brief Tests that MQTT_GetSubAckStatusCodes works as expected in parsing the
* payload information of a SUBACK packet.
*/
void test_MQTT_GetSubAckStatusCodes( void )
{
MQTTPacketInfo_t mqttPacketInfo;
size_t payloadSize;
uint8_t * pPayloadStart;
MQTTStatus_t status = MQTTSuccess;
uint8_t buffer[ 10 ] = { 0 };
buffer[ 0 ] = 0;
buffer[ 1 ] = 1;
buffer[ 2 ] = 0x00;
buffer[ 3 ] = 0x01;
buffer[ 4 ] = 0x02;
buffer[ 5 ] = 0x80;
/* Process a valid SUBACK packet containing whole range of server response codes. */
mqttPacketInfo.type = MQTT_PACKET_TYPE_SUBACK;
mqttPacketInfo.pRemainingData = buffer;
mqttPacketInfo.remainingLength = 6;
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, &pPayloadStart, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
TEST_ASSERT_EQUAL_PTR( &buffer[ 2 ], pPayloadStart );
TEST_ASSERT_EQUAL_INT( MQTTSubAckSuccessQos0, pPayloadStart[ 0 ] );
TEST_ASSERT_EQUAL_INT( MQTTSubAckSuccessQos1, pPayloadStart[ 1 ] );
TEST_ASSERT_EQUAL_INT( MQTTSubAckSuccessQos2, pPayloadStart[ 2 ] );
TEST_ASSERT_EQUAL_INT( MQTTSubAckFailure, pPayloadStart[ 3 ] );
TEST_ASSERT_EQUAL_INT( 4, payloadSize );
/* Packet is NULL. */
status = MQTT_GetSubAckStatusCodes( NULL, &pPayloadStart, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Output parameter, pPayloadStart, is NULL. */
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, NULL, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Output parameter, pPayloadSize, is NULL. */
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, &pPayloadStart, NULL );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Remaining Data is NULL. */
mqttPacketInfo.pRemainingData = NULL;
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, &pPayloadStart, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* non-SUBACK packet type. */
mqttPacketInfo.type = MQTT_PACKET_TYPE_CONNACK;
mqttPacketInfo.pRemainingData = buffer;
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, &pPayloadStart, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
/* Invalid remaining length value in packet. */
mqttPacketInfo.remainingLength = 0;
mqttPacketInfo.type = MQTT_PACKET_TYPE_SUBACK;
status = MQTT_GetSubAckStatusCodes( &mqttPacketInfo, &pPayloadStart, &payloadSize );
TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
}
/* ========================================================================== */
/**
* @brief Test MQTT_Status_strerror returns correct strings.
*/
void test_MQTT_Status_strerror( void )
{
MQTTStatus_t status;
const char * str = NULL;
status = MQTTSuccess;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTSuccess", str );
status = MQTTBadParameter;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTBadParameter", str );
status = MQTTNoMemory;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTNoMemory", str );
status = MQTTSendFailed;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTSendFailed", str );
status = MQTTRecvFailed;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTRecvFailed", str );
status = MQTTBadResponse;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTBadResponse", str );
status = MQTTServerRefused;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTServerRefused", str );
status = MQTTNoDataAvailable;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTNoDataAvailable", str );
status = MQTTIllegalState;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTIllegalState", str );
status = MQTTStateCollision;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTStateCollision", str );
status = MQTTKeepAliveTimeout;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "MQTTKeepAliveTimeout", str );
status = MQTTKeepAliveTimeout + 1;
str = MQTT_Status_strerror( status );
TEST_ASSERT_EQUAL_STRING( "Invalid MQTT Status code", str );
}
/* ========================================================================== */