1
0
mirror of https://github.com/FreeRTOS/coreMQTT synced 2025-07-04 10:44:44 +08:00
coreMQTT/test/unit/iot_tests_mqtt_receive.c
Muneeb Ahmed 94887b2f35 Reduce complexity in MQTT serialize and API (#701)
* Refactor Wait and PublishAsync API functions

* Refactor _IotMqtt_DeserializePublish

* Move publish setup checks to iot_mqtt_validate.c
2020-01-09 11:23:42 -08:00

1828 lines
72 KiB
C

/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_tests_mqtt_receive.c
* @brief Tests for the function @ref mqtt_function_receivecallback.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* SDK initialization include. */
#include "iot_init.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Test framework includes. */
#include "unity_fixture.h"
/* MQTT test access include. */
#include "iot_test_access_mqtt.h"
/**
* @brief Determine which MQTT server mode to test (AWS IoT or Mosquitto).
*/
#if !defined( IOT_TEST_MQTT_MOSQUITTO ) || IOT_TEST_MQTT_MOSQUITTO == 0
#define AWS_IOT_MQTT_SERVER true
#else
#define AWS_IOT_MQTT_SERVER false
#endif
/** @brief Default CONNACK packet for the receive tests. */
static const uint8_t _pConnackTemplate[] = { 0x20, 0x02, 0x00, 0x00 };
/** @brief Default PUBLISH packet for the receive tests. */
static const uint8_t _pPublishTemplate[] =
{
0x30, 0x8d, 0x02, 0x00, 0x0b, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
/** @brief Default PUBACK packet for the receive tests. */
static const uint8_t _pPubackTemplate[] = { 0x40, 0x02, 0x00, 0x01 };
/** @brief Default SUBACK packet for the receive tests. */
static const uint8_t _pSubackTemplate[] = { 0x90, 0x05, 0x00, 0x01, 0x00, 0x01, 0x02 };
/** @brief Default UNSUBACK packet for the receive tests. */
static const uint8_t _pUnsubackTemplate[] = { 0xb0, 0x02, 0x00, 0x01 };
/** @brief Default PINGRESP packet for the receive tests. */
static const uint8_t _pPingrespTemplate[] = { 0xd0, 0x00 };
/*-----------------------------------------------------------*/
/**
* @brief Topic name and filter used in the tests.
*/
#define TEST_TOPIC_NAME "/test/topic"
/**
* @brief Length of #TEST_TOPIC_NAME.
*/
#define TEST_TOPIC_LENGTH ( ( uint16_t ) ( sizeof( TEST_TOPIC_NAME ) - 1 ) )
/**
* @brief Timeout for waiting on a PUBLISH callback.
*/
#define PUBLISH_CALLBACK_TIMEOUT ( 1000 )
/**
* @brief Declare a buffer holding a packet and its size.
*/
#define DECLARE_PACKET( pTemplate, bufferName, sizeName ) \
uint8_t bufferName[ sizeof( pTemplate ) ] = { 0 }; \
const size_t sizeName = sizeof( pTemplate ); \
( void ) memcpy( bufferName, pTemplate, sizeName );
/**
* @brief Initializer for operations in the tests.
*/
#define INITIALIZE_OPERATION( name ) \
{ \
.link = { 0 }, .incomingPublish = false, .pMqttConnection = NULL, \
.jobStorage = IOT_TASKPOOL_JOB_STORAGE_INITIALIZER, .job = IOT_TASKPOOL_JOB_INITIALIZER, \
.u.operation = \
{ \
.jobReference = 1, .type = name, .flags = IOT_MQTT_FLAG_WAITABLE, \
.packetIdentifier = 1, .pMqttPacket = NULL, .packetSize = 0, \
.notify = { .callback = { 0 } }, .status = IOT_MQTT_STATUS_PENDING, \
.periodic = { .retry = { 0 } } \
} \
}
/*-----------------------------------------------------------*/
/**
* @brief Context for calls to the network receive function.
*/
typedef struct _receiveContext
{
const uint8_t * pData; /**< @brief The data to receive. */
size_t dataLength; /**< @brief Length of data. */
size_t dataIndex; /**< @brief Next byte of data to read. */
} _receiveContext_t;
/*-----------------------------------------------------------*/
/**
* @brief The MQTT connection shared by all the tests.
*/
static _mqttConnection_t * _pMqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
/**
* @brief The network interface shared by all the tests.
*/
static IotNetworkInterface_t _networkInterface = { 0 };
/**
* @brief The subscription shared by all the tests.
*/
static IotMqttSubscription_t _subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
/**
* @brief Tracks whether a deserializer override was called for a test.
*/
static bool _deserializeOverrideCalled = false;
/**
* @brief Tracks whether #_getPacketType has been called.
*/
static bool _getPacketTypeCalled = false;
/**
* @brief Tracks whether #_getRemainingLength has been called.
*/
static bool _getRemainingLengthCalled = false;
/**
* @brief Tracks whether #_close has been called.
*/
static bool _networkCloseCalled = false;
/**
* @brief Tracks whether #_disconnectCallback has been called.
*/
static bool _disconnectCallbackCalled = false;
/*-----------------------------------------------------------*/
/**
* @brief Get packet type function override.
*/
static uint8_t _getPacketType( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface )
{
_getPacketTypeCalled = true;
return _IotMqtt_GetPacketType( pNetworkConnection, pNetworkInterface );
}
/*-----------------------------------------------------------*/
/**
* @brief Get remaining length function override.
*/
static size_t _getRemainingLength( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface )
{
_getRemainingLengthCalled = true;
return _IotMqtt_GetRemainingLength( pNetworkConnection, pNetworkInterface );
}
/*-----------------------------------------------------------*/
/**
* @brief Serializer override for PUBACK.
*/
static IotMqttError_t _serializePuback( uint16_t packetIdentifier,
uint8_t ** pPubackPacket,
size_t * pPacketSize )
{
return _IotMqtt_SerializePuback( packetIdentifier,
pPubackPacket,
pPacketSize );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for CONNACK.
*/
static IotMqttError_t _deserializeConnack( _mqttPacket_t * pConnack )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializeConnack( pConnack );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for PUBLISH.
*/
static IotMqttError_t _deserializePublish( _mqttPacket_t * pPublish )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializePublish( pPublish );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for PUBACK.
*/
static IotMqttError_t _deserializePuback( _mqttPacket_t * pPuback )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializePuback( pPuback );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for SUBACK.
*/
static IotMqttError_t _deserializeSuback( _mqttPacket_t * pSuback )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializeSuback( pSuback );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for UNSUBACK.
*/
static IotMqttError_t _deserializeUnsuback( _mqttPacket_t * pUnsuback )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializeUnsuback( pUnsuback );
}
/*-----------------------------------------------------------*/
/**
* @brief Deserializer override for PINGRESP.
*/
static IotMqttError_t _deserializePingresp( _mqttPacket_t * pPingresp )
{
_deserializeOverrideCalled = true;
return _IotMqtt_DeserializePingresp( pPingresp );
}
/*-----------------------------------------------------------*/
/**
* @brief Reset the status of an #_mqttOperation_t and push it to the list of
* MQTT operations awaiting network response.
*/
static void _operationResetAndPush( _mqttOperation_t * pOperation )
{
pOperation->u.operation.status = IOT_MQTT_STATUS_PENDING;
pOperation->u.operation.jobReference = 1;
IotListDouble_InsertHead( &( _pMqttConnection->pendingResponse ), &( pOperation->link ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Process a non-PUBLISH buffer and check the result.
*/
static bool _processBuffer( const _mqttOperation_t * pOperation,
const uint8_t * pBuffer,
size_t bufferSize,
IotMqttError_t expectedResult )
{
bool status = true;
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pBuffer;
receiveContext.dataLength = bufferSize;
/* Call the receive callback on pBuffer. */
IotMqtt_ReceiveCallback( ( IotNetworkConnection_t ) &receiveContext,
_pMqttConnection );
/* Check expected result if operation is given. */
if( pOperation != NULL )
{
status = ( expectedResult == pOperation->u.operation.status );
}
return status;
}
/*-----------------------------------------------------------*/
/**
* @brief Process a PUBLISH message and check the result.
*/
static bool _processPublish( const uint8_t * pPublish,
size_t publishSize,
uint32_t expectedInvokeCount )
{
IotSemaphore_t invokeCount;
uint32_t finalInvokeCount = 0, i = 0;
bool waitResult = true;
_receiveContext_t receiveContext = { 0 };
/* Create a semaphore that counts how many times the publish callback is invoked. */
if( expectedInvokeCount > 0 )
{
if( IotSemaphore_Create( &invokeCount, 0, expectedInvokeCount ) == false )
{
return false;
}
}
/* Set the subscription parameter. */
if( IotListDouble_IsEmpty( &( _pMqttConnection->subscriptionList ) ) == false )
{
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
IotListDouble_PeekHead( &( _pMqttConnection->subscriptionList ) ),
link );
pSubscription->callback.pCallbackContext = &invokeCount;
}
/* Set the members of the receive context. */
receiveContext.pData = pPublish;
receiveContext.dataLength = publishSize;
/* Call the receive callback on pPublish. */
IotMqtt_ReceiveCallback( ( IotNetworkConnection_t ) &receiveContext,
_pMqttConnection );
/* Check how many times the publish callback is invoked. */
for( i = 0; i < expectedInvokeCount; i++ )
{
waitResult = IotSemaphore_TimedWait( &invokeCount,
PUBLISH_CALLBACK_TIMEOUT );
if( waitResult == false )
{
break;
}
}
/* Ensure that the invoke count semaphore has a value of 0, then destroy the
* semaphore. */
if( expectedInvokeCount > 0 )
{
finalInvokeCount = IotSemaphore_GetCount( &invokeCount );
IotSemaphore_Destroy( &invokeCount );
}
/* Check results against expected values. */
return ( finalInvokeCount == 0 ) &&
( waitResult == true );
}
/*-----------------------------------------------------------*/
/**
* @brief Called when a PUBLISH message is "received".
*/
static void _publishCallback( void * pCallbackContext,
IotMqttCallbackParam_t * pPublish )
{
IotSemaphore_t * pInvokeCount = ( IotSemaphore_t * ) pCallbackContext;
/* QoS 2 is valid for these tests, but currently unsupported by the MQTT library.
* Change the QoS to 0 so that QoS validation passes. */
pPublish->u.message.info.qos = IOT_MQTT_QOS_0;
/* Check that the parameters to this function are valid. */
if( ( _IotMqtt_ValidatePublish( AWS_IOT_MQTT_SERVER,
&( pPublish->u.message.info ),
0,
NULL,
NULL ) == true ) &&
( pPublish->u.message.info.topicNameLength == TEST_TOPIC_LENGTH ) &&
( strncmp( TEST_TOPIC_NAME, pPublish->u.message.info.pTopicName, TEST_TOPIC_LENGTH ) == 0 ) )
{
IotSemaphore_Post( pInvokeCount );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Simulates a network receive function.
*/
static size_t _receive( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bytesRequested )
{
size_t bytesReceived = 0;
_receiveContext_t * pReceiveContext = ( _receiveContext_t * ) pConnection;
if( pReceiveContext->dataIndex != pReceiveContext->dataLength )
{
TEST_ASSERT_NOT_EQUAL( 0, bytesRequested );
TEST_ASSERT_LESS_THAN( pReceiveContext->dataLength, pReceiveContext->dataIndex );
/* Calculate how much data to copy. */
const size_t dataAvailable = pReceiveContext->dataLength - pReceiveContext->dataIndex;
if( bytesRequested > dataAvailable )
{
bytesReceived = dataAvailable;
}
else
{
bytesReceived = bytesRequested;
}
/* Copy data into given buffer. */
if( bytesReceived > 0 )
{
( void ) memcpy( pBuffer,
pReceiveContext->pData + pReceiveContext->dataIndex,
bytesReceived );
pReceiveContext->dataIndex += bytesReceived;
}
}
return bytesReceived;
}
/*-----------------------------------------------------------*/
/**
* @brief A network send function that checks the message is a PUBACK.
*/
static size_t _checkPuback( IotNetworkConnection_t pConnection,
const uint8_t * pMessage,
size_t messageLength )
{
_mqttPacket_t pubackPacket = { .u = { 0 } };
IotMqttError_t deserializeStatus = IOT_MQTT_STATUS_PENDING;
/* Silence warnings about unused parameters. */
( void ) pConnection;
/* All PUBACK packets should be 4 bytes long. */
TEST_ASSERT_EQUAL( 4, messageLength );
/* Deserialize the PUBACK. */
pubackPacket.type = MQTT_PACKET_TYPE_PUBACK;
pubackPacket.remainingLength = 2;
pubackPacket.pRemainingData = ( uint8_t * ) ( pMessage + 2 );
deserializeStatus = _IotMqtt_DeserializePuback( &pubackPacket );
/* Check the results of the PUBACK deserialization. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, deserializeStatus );
TEST_ASSERT_EQUAL( 1, pubackPacket.packetIdentifier );
return messageLength;
}
/*-----------------------------------------------------------*/
/**
* @brief A network close function that reports if it was invoked.
*/
static IotNetworkError_t _close( IotNetworkConnection_t pConnection )
{
/* Silence warnings about unused parameters. */
( void ) pConnection;
/* Report that this function was called. */
_networkCloseCalled = true;
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/
/**
* @brief A disconnect callback function that checks for a "bad packet" reason
* and reports if it was invoked.
*/
static void _disconnectCallback( void * pCallbackContext,
IotMqttCallbackParam_t * pCallbackParam )
{
/* Silence warnings about unused parameters. */
( void ) pCallbackContext;
if( pCallbackParam->u.disconnectReason == IOT_MQTT_BAD_PACKET_RECEIVED )
{
_disconnectCallbackCalled = true;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Test group for MQTT Receive tests.
*/
TEST_GROUP( MQTT_Unit_Receive );
/*-----------------------------------------------------------*/
/**
* @brief Test setup for MQTT Receive tests.
*/
TEST_SETUP( MQTT_Unit_Receive )
{
static IotMqttSerializer_t serializer = IOT_MQTT_SERIALIZER_INITIALIZER;
IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;
/* Initialize SDK. */
TEST_ASSERT_EQUAL_INT( true, IotSdk_Init() );
/* Initialize the MQTT library. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, IotMqtt_Init() );
/* Set the deserializer overrides. */
serializer.serialize.puback = _serializePuback;
serializer.deserialize.connack = _deserializeConnack;
serializer.deserialize.publish = _deserializePublish;
serializer.deserialize.puback = _deserializePuback;
serializer.deserialize.suback = _deserializeSuback;
serializer.deserialize.unsuback = _deserializeUnsuback;
serializer.deserialize.pingresp = _deserializePingresp;
serializer.getPacketType = _getPacketType;
serializer.getRemainingLength = _getRemainingLength;
_networkInterface.send = _checkPuback;
_networkInterface.receive = _receive;
_networkInterface.close = _close;
networkInfo.pNetworkInterface = &_networkInterface;
networkInfo.disconnectCallback.function = _disconnectCallback;
/* Initialize the MQTT connection used by the tests. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the MQTT serializer overrides. */
_pMqttConnection->pSerializer = &serializer;
/* Set the members of the subscription. */
_subscription.pTopicFilter = TEST_TOPIC_NAME;
_subscription.topicFilterLength = TEST_TOPIC_LENGTH;
_subscription.callback.function = _publishCallback;
/* Add the subscription to the MQTT connection. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, _IotMqtt_AddSubscriptions( _pMqttConnection,
1,
&_subscription,
1 ) );
/* Clear functions called flags. */
_deserializeOverrideCalled = false;
_getPacketTypeCalled = false;
_getRemainingLengthCalled = false;
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/*-----------------------------------------------------------*/
/**
* @brief Test tear down for MQTT Receive tests.
*/
TEST_TEAR_DOWN( MQTT_Unit_Receive )
{
/* Clean up resources taken in test setup. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
IotMqtt_Cleanup();
IotSdk_Cleanup();
/* Check that the tests used a deserializer override. */
TEST_ASSERT_EQUAL_INT( true, _deserializeOverrideCalled );
TEST_ASSERT_EQUAL_INT( true, _getPacketTypeCalled );
TEST_ASSERT_EQUAL_INT( true, _getRemainingLengthCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Test group runner for MQTT Receive tests.
*/
TEST_GROUP_RUNNER( MQTT_Unit_Receive )
{
RUN_TEST_CASE( MQTT_Unit_Receive, DecodeRemainingLength );
RUN_TEST_CASE( MQTT_Unit_Receive, InvalidPacket );
RUN_TEST_CASE( MQTT_Unit_Receive, ReceiveMallocFail );
RUN_TEST_CASE( MQTT_Unit_Receive, ConnackValid );
RUN_TEST_CASE( MQTT_Unit_Receive, ConnackInvalid );
RUN_TEST_CASE( MQTT_Unit_Receive, PublishValid );
RUN_TEST_CASE( MQTT_Unit_Receive, PublishInvalid );
RUN_TEST_CASE( MQTT_Unit_Receive, PubackValid );
RUN_TEST_CASE( MQTT_Unit_Receive, PubackInvalid );
RUN_TEST_CASE( MQTT_Unit_Receive, SubackValid );
RUN_TEST_CASE( MQTT_Unit_Receive, SubackInvalid );
RUN_TEST_CASE( MQTT_Unit_Receive, UnsubackValid );
RUN_TEST_CASE( MQTT_Unit_Receive, UnsubackInvalid );
RUN_TEST_CASE( MQTT_Unit_Receive, Pingresp );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the function for decoding MQTT remaining length.
*/
TEST( MQTT_Unit_Receive, DecodeRemainingLength )
{
/* Decode 0, which has a 1-byte representation. */
{
uint8_t pRemainingLength[ 4 ] = { 0 };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( 0,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Decode 129, which has a 2-byte representation. */
{
uint8_t pRemainingLength[ 4 ] = { 0x81, 0x01, 0x00, 0x00 };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( 129,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Decode 16,386, which has a 3-byte representation. */
{
uint8_t pRemainingLength[ 4 ] = { 0x82, 0x80, 0x01, 0x00 };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( 16386,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Decode 268,435,455, which has a 4-byte representation. This value is the
* largest representable value. */
{
uint8_t pRemainingLength[ 4 ] = { 0xff, 0xff, 0xff, 0x7f };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( 268435455,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Attempt to decode an invalid value where all continuation bits are set. */
{
uint8_t pRemainingLength[ 4 ] = { 0xff, 0xff, 0xff, 0x8f };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( MQTT_REMAINING_LENGTH_INVALID,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Attempt to decode a 4-byte representation of 0. According to the spec,
* this representation is not valid. */
{
uint8_t pRemainingLength[ 4 ] = { 0x80, 0x80, 0x80, 0x00 };
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = pRemainingLength;
receiveContext.dataLength = 4;
TEST_ASSERT_EQUAL( MQTT_REMAINING_LENGTH_INVALID,
_IotMqtt_GetRemainingLength( &receiveContext,
&_networkInterface ) );
}
/* Test tear down for this test group checks that deserializer overrides
* were called. However, this test does not use any deserializer overrides;
* set these values to true so that the checks pass. */
_deserializeOverrideCalled = true;
_getPacketTypeCalled = true;
_getRemainingLengthCalled = true;
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with an
* invalid control packet type.
*/
TEST( MQTT_Unit_Receive, InvalidPacket )
{
uint8_t invalidPacket = 0xf0;
_receiveContext_t receiveContext = { 0 };
/* Set the members of the receive context. */
receiveContext.pData = &invalidPacket;
receiveContext.dataLength = 1;
/* Processing a control packet 0xf is a protocol violation. */
IotMqtt_ReceiveCallback( ( IotNetworkConnection_t ) &receiveContext,
_pMqttConnection );
/* Processing an invalid packet should cause the network connection to be closed. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
/* This test should not have called any deserializer. Set the deserialize
* override called flag to true so that the check for it passes. */
TEST_ASSERT_EQUAL_INT( false, _deserializeOverrideCalled );
_deserializeOverrideCalled = true;
TEST_ASSERT_EQUAL_INT( false, _getRemainingLengthCalled );
_getRemainingLengthCalled = true;
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback when memory
* allocation fails.
*/
TEST( MQTT_Unit_Receive, ReceiveMallocFail )
{
/* Logging uses malloc and will interfere with this test. Only run if logging
* is disabled. */
#if ( LIBRARY_LOG_LEVEL == IOT_LOG_NONE )
_receiveContext_t receiveContext = { 0 };
/* Data stream to process. Contains 2 SUBACKs. */
const uint8_t pDataStream[] =
{
0x90, 0x05, 0x00, 0x01, 0x00, 0x01, 0x02,
0x90, 0x05, 0x00, 0x01, 0x00, 0x01, 0x02
};
/* Set the members of the receive context. */
receiveContext.pData = pDataStream;
receiveContext.dataLength = sizeof( pDataStream );
/* Set malloc to fail and process the first SUBACK. */
UnityMalloc_MakeMallocFailAfterCount( 0 );
IotMqtt_ReceiveCallback( ( IotNetworkConnection_t ) &receiveContext,
_pMqttConnection );
/* Allow the use of malloc and process the second SUBACK. */
UnityMalloc_MakeMallocFailAfterCount( -1 );
IotMqtt_ReceiveCallback( ( IotNetworkConnection_t ) &receiveContext,
_pMqttConnection );
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
#else
/* Test tear down for this test group checks that deserializer overrides
* were called. Set these values to true so that the checks pass. */
_deserializeOverrideCalled = true;
_getPacketTypeCalled = true;
_getRemainingLengthCalled = true;
#endif /* if LIBRARY_LOG_LEVEL != IOT_LOG_NONE */
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a spec-compliant
* CONNACK.
*/
TEST( MQTT_Unit_Receive, ConnackValid )
{
uint8_t i = 0;
_mqttOperation_t connect = INITIALIZE_OPERATION( IOT_MQTT_CONNECT );
connect.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( connect.u.operation.notify.waitSemaphore ),
0,
10 ) );
/* Even though no CONNECT is in the receive queue, 4 bytes should still be
* processed (should not crash). */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_STATUS_PENDING ) );
}
/* Process a valid, successful CONNACK with no SP flag. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_SUCCESS ) );
}
/* Process a valid, successful CONNACK with SP flag set. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 2 ] = 0x01;
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_SUCCESS ) );
}
/* Check each of the CONNACK failure codes, which range from 1 to 5. */
for( i = 0x01; i < 0x06; i++ )
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
/* Set the CONNECT state and code. */
_operationResetAndPush( &connect );
pConnack[ 3 ] = i;
/* Push a CONNECT operation to the queue and process a CONNACK. */
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_SERVER_REFUSED ) );
}
IotSemaphore_Destroy( &( connect.u.operation.notify.waitSemaphore ) );
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a
* CONNACK that doesn't comply to MQTT spec.
*/
TEST( MQTT_Unit_Receive, ConnackInvalid )
{
_mqttOperation_t connect = INITIALIZE_OPERATION( IOT_MQTT_CONNECT );
connect.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( connect.u.operation.notify.waitSemaphore ),
0,
10 ) );
/* An incomplete CONNACK should not be processed, and no status should be set. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize - 1,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The CONNACK control packet type must be 0x20. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 0 ] = 0x21;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* A CONNACK must have a remaining length of 2. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 1 ] = 0x03;
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The reserved bits in CONNACK must be 0. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 2 ] = 0x80;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The fourth byte of CONNACK must be 0 if the SP flag is set. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 2 ] = 0x01;
pConnack[ 3 ] = 0x01;
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* CONNACK return codes cannot be above 5. */
{
DECLARE_PACKET( _pConnackTemplate, pConnack, connackSize );
pConnack[ 3 ] = 0x06;
_operationResetAndPush( &connect );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &connect,
pConnack,
connackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
IotSemaphore_Destroy( &( connect.u.operation.notify.waitSemaphore ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a spec-compliant
* PUBLISH.
*/
TEST( MQTT_Unit_Receive, PublishValid )
{
/* Process a valid QoS 0 PUBLISH. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
1 ) );
}
/* Process a valid QoS 1 PUBLISH. Prevent an attempt to send PUBACK by
* making no memory available for the PUBACK. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 0 ] = 0x32;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
1 ) );
}
/* Process a valid QoS 2 PUBLISH. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 0 ] = 0x34;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
1 ) );
}
/* Process a valid PUBLISH with DUP flag set. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 0 ] = 0x38;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
1 ) );
}
/* Remove the subscription. Even though there is no matching subscription,
* all bytes of the PUBLISH should still be processed (should not crash). */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
_IotMqtt_RemoveSubscriptionByTopicFilter( _pMqttConnection,
&_subscription,
1 );
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
}
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a PUBLISH
* that doesn't comply to MQTT spec.
*/
TEST( MQTT_Unit_Receive, PublishInvalid )
{
/* Attempting to process a packet smaller than 5 bytes should result in no
* bytes processed. 5 bytes is the minimum size of a PUBLISH. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
4,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The PUBLISH cannot have a QoS of 3. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 0 ] = 0x36;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a PUBLISH with an invalid "Remaining length". */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 1 ] = 0xff;
pPublish[ 2 ] = 0xff;
pPublish[ 3 ] = 0xff;
pPublish[ 4 ] = 0xff;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a PUBLISH where some bytes could not be received. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 2 ] = 0x03;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a PUBLISH with a "Remaining length" smaller than the
* spec allows. */
{
/* QoS 0. */
DECLARE_PACKET( _pPublishTemplate, pPublish0, publish0Size );
pPublish0[ 1 ] = 0x02;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish0,
publish0Size,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
_networkCloseCalled = false;
/* QoS 1. */
DECLARE_PACKET( _pPublishTemplate, pPublish1, publish1Size );
pPublish1[ 0 ] = 0x32;
pPublish1[ 1 ] = 0x04;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish1,
publish1Size,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a PUBLISH with a "Remaining length" smaller than the
* end of the variable header. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 1 ] = 0x0a;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a PUBLISH with packet identifier 0. */
{
DECLARE_PACKET( _pPublishTemplate, pPublish, publishSize );
pPublish[ 0 ] = 0x32;
pPublish[ 17 ] = 0x00;
TEST_ASSERT_EQUAL_INT( true, _processPublish( pPublish,
publishSize,
0 ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a
* spec-compliant PUBACK.
*/
TEST( MQTT_Unit_Receive, PubackValid )
{
_mqttOperation_t publish = INITIALIZE_OPERATION( IOT_MQTT_PUBLISH_TO_SERVER );
publish.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( publish.u.operation.notify.waitSemaphore ),
0,
10 ) );
/* Even though no PUBLISH is in the receive queue, 4 bytes should still be
* processed (should not crash). */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize,
IOT_MQTT_STATUS_PENDING ) );
}
/* Process a valid PUBACK. */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
_operationResetAndPush( &publish );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize,
IOT_MQTT_SUCCESS ) );
}
IotSemaphore_Destroy( &( publish.u.operation.notify.waitSemaphore ) );
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a PUBACK
* that doesn't comply to MQTT spec.
*/
TEST( MQTT_Unit_Receive, PubackInvalid )
{
_mqttOperation_t publish = INITIALIZE_OPERATION( IOT_MQTT_PUBLISH_TO_SERVER );
publish.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( publish.u.operation.notify.waitSemaphore ),
0,
10 ) );
_operationResetAndPush( &publish );
/* An incomplete PUBACK should not be processed, and no status should be set. */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize - 1,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The PUBACK control packet type must be 0x40. */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
pPuback[ 0 ] = 0x41;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
_operationResetAndPush( &publish );
/* A PUBACK must have a remaining length of 2. */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
pPuback[ 1 ] = 0x03;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The packet identifier in PUBACK cannot be 0. No status should be set if
* packet identifier 0 is received. */
{
DECLARE_PACKET( _pPubackTemplate, pPuback, pubackSize );
pPuback[ 3 ] = 0x00;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &publish,
pPuback,
pubackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Remove unprocessed PUBLISH if present. */
if( IotLink_IsLinked( &( publish.link ) ) == true )
{
IotDeQueue_Remove( &( publish.link ) );
}
IotSemaphore_Destroy( &( publish.u.operation.notify.waitSemaphore ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a
* spec-compliant SUBACK.
*/
TEST( MQTT_Unit_Receive, SubackValid )
{
_mqttSubscription_t * pNewSubscription = NULL;
_mqttOperation_t subscribe = INITIALIZE_OPERATION( IOT_MQTT_SUBSCRIBE );
IotMqttSubscription_t pSubscriptions[ 2 ] = { IOT_MQTT_SUBSCRIPTION_INITIALIZER };
subscribe.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( subscribe.u.operation.notify.waitSemaphore ),
0,
10 ) );
/* Add 2 additional subscriptions to the MQTT connection. */
pSubscriptions[ 0 ].qos = IOT_MQTT_QOS_1;
pSubscriptions[ 0 ].callback.function = _publishCallback;
pSubscriptions[ 0 ].pTopicFilter = TEST_TOPIC_NAME "1";
pSubscriptions[ 0 ].topicFilterLength = TEST_TOPIC_LENGTH + 1;
pSubscriptions[ 1 ].qos = IOT_MQTT_QOS_1;
pSubscriptions[ 1 ].callback.function = _publishCallback;
pSubscriptions[ 1 ].pTopicFilter = TEST_TOPIC_NAME "2";
pSubscriptions[ 1 ].topicFilterLength = TEST_TOPIC_LENGTH + 1;
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, _IotMqtt_AddSubscriptions( _pMqttConnection,
1,
pSubscriptions,
2 ) );
/* Set orders 2 and 1 for the new subscriptions. */
pNewSubscription = IotLink_Container( _mqttSubscription_t,
IotListDouble_PeekHead( &( _pMqttConnection->subscriptionList ) ),
link );
pNewSubscription->packetInfo.order = 2;
pNewSubscription = IotLink_Container( _mqttSubscription_t,
pNewSubscription->link.pNext,
link );
pNewSubscription->packetInfo.order = 1;
/* Even though no SUBSCRIBE is in the receive queue, all bytes of the SUBACK
* should still be processed (should not crash). */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_STATUS_PENDING ) );
}
/* Process a valid SUBACK where all subscriptions are successful. */
{
IotMqttSubscription_t currentSubscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
_operationResetAndPush( &subscribe );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_SUCCESS ) );
/* Test the subscription check function. QoS is not tested. */
TEST_ASSERT_EQUAL_INT( true, IotMqtt_IsSubscribed( _pMqttConnection,
pSubscriptions[ 0 ].pTopicFilter,
pSubscriptions[ 0 ].topicFilterLength,
&currentSubscription ) );
TEST_ASSERT_EQUAL_UINT16( currentSubscription.topicFilterLength,
pSubscriptions[ 0 ].topicFilterLength );
TEST_ASSERT_EQUAL_STRING_LEN( currentSubscription.pTopicFilter,
pSubscriptions[ 0 ].pTopicFilter,
currentSubscription.topicFilterLength );
TEST_ASSERT_EQUAL_PTR( currentSubscription.callback.function,
pSubscriptions[ 0 ].callback.function );
TEST_ASSERT_EQUAL_PTR( currentSubscription.callback.pCallbackContext,
pSubscriptions[ 0 ].callback.pCallbackContext );
}
/* Process a valid SUBACK where some subscriptions were rejected. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 4 ] = 0x80;
pSuback[ 6 ] = 0x80;
_operationResetAndPush( &subscribe );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_SERVER_REFUSED ) );
/* Check that rejected subscriptions were removed from the subscription
* list. */
TEST_ASSERT_EQUAL_INT( false, IotMqtt_IsSubscribed( _pMqttConnection,
TEST_TOPIC_NAME,
TEST_TOPIC_LENGTH,
NULL ) );
TEST_ASSERT_EQUAL_INT( false, IotMqtt_IsSubscribed( _pMqttConnection,
pSubscriptions[ 1 ].pTopicFilter,
pSubscriptions[ 1 ].topicFilterLength,
NULL ) );
}
IotSemaphore_Destroy( &( subscribe.u.operation.notify.waitSemaphore ) );
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a
* SUBACK that doesn't comply to MQTT spec.
*/
TEST( MQTT_Unit_Receive, SubackInvalid )
{
_mqttOperation_t subscribe = INITIALIZE_OPERATION( IOT_MQTT_SUBSCRIBE );
subscribe.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( subscribe.u.operation.notify.waitSemaphore ),
0,
10 ) );
_operationResetAndPush( &subscribe );
/* Attempting to process a packet smaller than 5 bytes should result in no
* bytes processed. 5 bytes is the minimum size of a SUBACK. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
4,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a SUBACK with an invalid "Remaining length". */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 1 ] = 0xff;
pSuback[ 2 ] = 0xff;
pSuback[ 3 ] = 0xff;
pSuback[ 4 ] = 0xff;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a SUBACK larger than the size of the data stream. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 1 ] = 0x52;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a SUBACK with a "Remaining length" smaller than the
* spec allows. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 1 ] = 0x02;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Attempt to process a SUBACK with a bad return code. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 6 ] = 0xff;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The SUBACK control packet type must be 0x90. */
{
DECLARE_PACKET( _pSubackTemplate, pSuback, subackSize );
pSuback[ 0 ] = 0x91;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &subscribe,
pSuback,
subackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
IotSemaphore_Destroy( &( subscribe.u.operation.notify.waitSemaphore ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with a
* spec-compliant UNSUBACK.
*/
TEST( MQTT_Unit_Receive, UnsubackValid )
{
_mqttOperation_t unsubscribe = INITIALIZE_OPERATION( IOT_MQTT_UNSUBSCRIBE );
unsubscribe.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( unsubscribe.u.operation.notify.waitSemaphore ),
0,
10 ) );
/* Even though no UNSUBSCRIBE is in the receive queue, 4 bytes should still be
* processed (should not crash). */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize,
IOT_MQTT_STATUS_PENDING ) );
}
/* Process a valid UNSUBACK. */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
_operationResetAndPush( &unsubscribe );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize,
IOT_MQTT_SUCCESS ) );
}
IotSemaphore_Destroy( &( unsubscribe.u.operation.notify.waitSemaphore ) );
/* Network close function should not have been invoked. */
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback with an
* UNSUBACK that doesn't comply to MQTT spec.
*/
TEST( MQTT_Unit_Receive, UnsubackInvalid )
{
_mqttOperation_t unsubscribe = INITIALIZE_OPERATION( IOT_MQTT_UNSUBSCRIBE );
unsubscribe.pMqttConnection = _pMqttConnection;
/* Create the wait semaphore so notifications don't crash. The value of
* this semaphore will not be checked, so the maxValue argument is arbitrary. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &( unsubscribe.u.operation.notify.waitSemaphore ),
0,
10 ) );
_operationResetAndPush( &unsubscribe );
/* An incomplete UNSUBACK should not be processed, and no status should be set. */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize - 1,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The UNSUBACK control packet type must be 0xb0. */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
pUnsuback[ 0 ] = 0xb1;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize,
IOT_MQTT_BAD_RESPONSE ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
_operationResetAndPush( &unsubscribe );
/* An UNSUBACK must have a remaining length of 2. */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
pUnsuback[ 1 ] = 0x03;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The packet identifier in UNSUBACK cannot be 0. No status should be set if
* packet identifier 0 is received. */
{
DECLARE_PACKET( _pUnsubackTemplate, pUnsuback, unsubackSize );
pUnsuback[ 3 ] = 0x00;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( &unsubscribe,
pUnsuback,
unsubackSize,
IOT_MQTT_STATUS_PENDING ) );
/* Network close should have been called for invalid packet. */
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* Remove unprocessed UNSUBSCRIBE if present. */
if( IotLink_IsLinked( &( unsubscribe.link ) ) == true )
{
IotDeQueue_Remove( &( unsubscribe.link ) );
}
IotSemaphore_Destroy( &( unsubscribe.u.operation.notify.waitSemaphore ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_receivecallback when receiving
* a PINGRESP packet (both compliant and non-compliant packets).
*/
TEST( MQTT_Unit_Receive, Pingresp )
{
/* Even though no PINGREQ is expected, the keep-alive failure flag should
* be cleared (should not crash). */
{
_pMqttConnection->pingreq.u.operation.periodic.ping.failure = 0;
DECLARE_PACKET( _pPingrespTemplate, pPingresp, pingrespSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( NULL,
pPingresp,
pingrespSize,
IOT_MQTT_SUCCESS ) );
TEST_ASSERT_EQUAL_INT( 0, _pMqttConnection->pingreq.u.operation.periodic.ping.failure );
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/* Process a valid PINGRESP. */
{
_pMqttConnection->pingreq.u.operation.periodic.ping.failure = 1;
DECLARE_PACKET( _pPingrespTemplate, pPingresp, pingrespSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( NULL,
pPingresp,
pingrespSize,
IOT_MQTT_SUCCESS ) );
TEST_ASSERT_EQUAL_INT( 0, _pMqttConnection->pingreq.u.operation.periodic.ping.failure );
TEST_ASSERT_EQUAL_INT( false, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( false, _disconnectCallbackCalled );
}
/* An incomplete PINGRESP should not be processed, and the keep-alive failure
* flag should not be cleared. */
{
_pMqttConnection->pingreq.u.operation.periodic.ping.failure = 1;
DECLARE_PACKET( _pPingrespTemplate, pPingresp, pingrespSize );
TEST_ASSERT_EQUAL_INT( true, _processBuffer( NULL,
pPingresp,
pingrespSize - 1,
IOT_MQTT_SUCCESS ) );
TEST_ASSERT_EQUAL_INT( 1, _pMqttConnection->pingreq.u.operation.periodic.ping.failure );
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* A PINGRESP should have a remaining length of 0. */
{
_pMqttConnection->pingreq.u.operation.periodic.ping.failure = 1;
DECLARE_PACKET( _pPingrespTemplate, pPingresp, pingrespSize );
pPingresp[ 1 ] = 0x01;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( NULL,
pPingresp,
pingrespSize,
IOT_MQTT_SUCCESS ) );
TEST_ASSERT_EQUAL_INT( 1, _pMqttConnection->pingreq.u.operation.periodic.ping.failure );
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
/* The PINGRESP control packet type must be 0xd0. */
{
_pMqttConnection->pingreq.u.operation.periodic.ping.failure = 1;
DECLARE_PACKET( _pPingrespTemplate, pPingresp, pingrespSize );
pPingresp[ 0 ] = 0xd1;
TEST_ASSERT_EQUAL_INT( true, _processBuffer( NULL,
pPingresp,
pingrespSize,
IOT_MQTT_SUCCESS ) );
TEST_ASSERT_EQUAL_INT( 1, _pMqttConnection->pingreq.u.operation.periodic.ping.failure );
TEST_ASSERT_EQUAL_INT( true, _networkCloseCalled );
TEST_ASSERT_EQUAL_INT( true, _disconnectCallbackCalled );
_networkCloseCalled = false;
_disconnectCallbackCalled = false;
}
}
/*-----------------------------------------------------------*/