1
0
mirror of https://github.com/FreeRTOS/coreMQTT synced 2025-07-05 11:00:18 +08:00
coreMQTT/test/unit/iot_tests_mqtt_api.c
abhidixi11 7b075bf0b8 fix: Increase MQTT test code coverage. (#713)
* fix: Increase MQTT test code coverage.

Code coverage for file: iot_mqtt_network.c
2020-01-10 15:40:05 -08:00

2485 lines
95 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_api.c
* @brief Tests for the user-facing API functions (declared in iot_mqtt.h).
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* SDK initialization include. */
#include "iot_init.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_clock.h"
#include "platform/iot_threads.h"
/* Test framework includes. */
#include "unity_fixture.h"
/* MQTT test access include. */
#include "iot_test_access_mqtt.h"
/* MQTT serializer API include */
#include "iot_mqtt_serialize.h"
/* MQTT mock include. */
#include "iot_tests_mqtt_mock.h"
/* Atomics include. */
#include "iot_atomic.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 Timeout to use for the tests. This can be short, but should allow time
* for other threads to run.
*/
#define TIMEOUT_MS ( 400 )
/**
* @brief A short keep-alive interval to use for the keep-alive tests. It may be
* shorter than the minimum 1 second specified by the MQTT spec.
*/
#define SHORT_KEEP_ALIVE_MS ( 100 )
/**
* @brief The number of times the periodic keep-alive should run.
*/
#define KEEP_ALIVE_COUNT ( 10 )
/*
* Client identifier and length to use for the MQTT API tests.
*/
#define CLIENT_IDENTIFIER ( "test" ) /**< @brief Client identifier. */
#define CLIENT_IDENTIFIER_LENGTH ( ( uint16_t ) ( sizeof( CLIENT_IDENTIFIER ) - 1 ) ) /**< @brief Length of client identifier. */
/*
* Will topic name and length to use for the MQTT API tests.
*/
#define TEST_TOPIC_NAME ( "/test/topic" ) /**< @brief An arbitrary topic name. */
#define TEST_TOPIC_NAME_LENGTH ( ( uint16_t ) ( sizeof( TEST_TOPIC_NAME ) - 1 ) ) /**< @brief Length of topic name. */
/**
* @brief A non-NULL function pointer to use for subscription callback. This
* "function" should cause a crash if actually called.
*/
#define SUBSCRIPTION_CALLBACK \
( ( void ( * )( void *, \
IotMqttCallbackParam_t * ) ) 0x01 )
/**
* @brief How many times #TEST_MQTT_Unit_API_DisconnectMallocFail_ will test
* malloc failures.
*
* The DISCONNECT function provides no mechanism to wait on its successful
* completion. Therefore, this function simply uses the value below as an estimate
* for the maximum number of times DISCONNECT will use malloc.
*/
#define DISCONNECT_MALLOC_LIMIT ( 20 )
/*
* Constants that affect the behavior of #TEST_MQTT_Unit_API_PublishDuplicates.
*/
#define DUP_CHECK_RETRY_MS ( 100 ) /**< @brief When to start sending duplicate packets. */
#define DUP_CHECK_RETRY_LIMIT ( 3 ) /**< @brief How many duplicate packets to send. */
#define DUP_CHECK_TIMEOUT ( 3000 ) /**< @brief Total time allowed to send all duplicate packets.
* Duplicates are sent using an exponential backoff strategy. */
/** @brief The minimum amount of time the test can take. */
#define DUP_CHECK_MINIMUM_WAIT \
( DUP_CHECK_RETRY_MS + \
2 * DUP_CHECK_RETRY_MS + \
4 * DUP_CHECK_RETRY_MS + \
IOT_MQTT_RESPONSE_WAIT_MS )
/*-----------------------------------------------------------*/
/**
* @brief Tracks whether #_publishSetDup has been called.
*/
static bool _publishSetDupCalled = false;
/**
* @brief Counts how many time #_sendPingreq has been called.
*/
static uint32_t _pingreqSendCount = 0;
/**
* @brief Counts how many times #_close has been called.
*/
static uint32_t _closeCount = 0;
/**
* @brief Counts how many times #_disconnectCallback has been called.
*/
static uint32_t _disconnectCallbackCount = 0;
/**
* @brief An MQTT connection to share among the tests.
*/
static _mqttConnection_t * _pMqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
/**
* @brief An #IotMqttNetworkInfo_t to share among the tests.
*/
static IotMqttNetworkInfo_t _networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;
/**
* @brief An #IotNetworkInterface_t to share among the tests.
*/
static IotNetworkInterface_t _networkInterface = { 0 };
/*-----------------------------------------------------------*/
/**
* @brief A thread routine that simulates an incoming PINGRESP.
*/
static void _incomingPingresp( void * pArgument )
{
/* Silence warnings about unused parameters. */
( void ) pArgument;
/* This test will not work if the response wait time is too small. */
#if IOT_MQTT_RESPONSE_WAIT_MS < ( 2 * SHORT_KEEP_ALIVE_MS + 100 )
#error "IOT_MQTT_RESPONSE_WAIT_MS too small for keep-alive tests."
#endif
static int32_t invokeCount = 0;
static uint64_t lastInvokeTime = 0;
uint64_t currentTime = IotClock_GetTimeMs();
/* Increment invoke count for this function. */
invokeCount++;
/* Sleep to simulate the network round-trip time. */
IotClock_SleepMs( 2 * SHORT_KEEP_ALIVE_MS );
/* Respond with a PINGRESP. */
if( invokeCount <= KEEP_ALIVE_COUNT )
{
/* Log a status with Unity, as this test may take a while. */
UnityPrint( "KeepAlivePeriodic " );
UnityPrintNumber( ( UNITY_INT ) invokeCount );
UnityPrint( " of " );
UnityPrintNumber( ( UNITY_INT ) KEEP_ALIVE_COUNT );
UnityPrint( " DONE at " );
UnityPrintNumber( ( UNITY_INT ) IotClock_GetTimeMs() );
UnityPrint( " ms" );
if( invokeCount > 1 )
{
UnityPrint( " (+" );
UnityPrintNumber( ( UNITY_INT ) ( currentTime - lastInvokeTime ) );
UnityPrint( " ms)." );
}
else
{
UnityPrint( "." );
}
UNITY_PRINT_EOL();
lastInvokeTime = currentTime;
IotMqtt_ReceiveCallback( NULL,
_pMqttConnection );
}
}
/*-----------------------------------------------------------*/
/**
* @brief PUBLISH set DUP function override.
*/
static void _publishSetDup( uint8_t * pPublishPacket,
uint8_t * pPacketIdentifierHigh,
uint16_t * pNewPacketIdentifier )
{
_publishSetDupCalled = true;
_IotMqtt_PublishSetDup( pPublishPacket,
pPacketIdentifierHigh,
pNewPacketIdentifier );
}
/*-----------------------------------------------------------*/
/**
* @brief A send function that always "succeeds". May report that it was invoked
* through a semaphore.
*/
static size_t _sendSuccess( IotNetworkConnection_t pSendContext,
const uint8_t * pMessage,
size_t messageLength )
{
IotSemaphore_t * pWaitSem = ( IotSemaphore_t * ) pSendContext;
/* Silence warnings about unused parameters. */
( void ) pMessage;
/* Post to the wait semaphore if given. */
if( pWaitSem != NULL )
{
IotSemaphore_Post( pWaitSem );
/* Yield the processor to context switch. */
IotClock_SleepMs( 10 );
}
/* This function returns the message length to simulate a successful send. */
return messageLength;
}
/*-----------------------------------------------------------*/
/**
* @brief A send function for PINGREQ that responds with a PINGRESP.
*/
static size_t _sendPingreq( IotNetworkConnection_t pSendContext,
const uint8_t * pMessage,
size_t messageLength )
{
/* Silence warnings about unused parameters. */
( void ) pSendContext;
( void ) pMessage;
/* Create a thread that responds with PINGRESP, then increment the PINGREQ
* send counter if successful. */
if( Iot_CreateDetachedThread( _incomingPingresp,
NULL,
IOT_THREAD_DEFAULT_PRIORITY,
IOT_THREAD_DEFAULT_STACK_SIZE ) == true )
{
_pingreqSendCount++;
}
/* This function returns the message length to simulate a successful send. */
return messageLength;
}
/*-----------------------------------------------------------*/
/**
* @brief A send function that delays.
*/
static size_t _sendDelay( IotNetworkConnection_t pSendContext,
const uint8_t * pMessage,
size_t messageLength )
{
IotSemaphore_t * pWaitSem = ( IotSemaphore_t * ) pSendContext;
/* Silence warnings about unused parameters. */
( void ) pMessage;
/* Post to the wait semaphore. */
IotSemaphore_Post( pWaitSem );
/* Delay for 2 seconds. */
IotClock_SleepMs( 2000 );
/* This function returns the message length to simulate a successful send. */
return messageLength;
}
/*-----------------------------------------------------------*/
/**
* @brief A create function that fails when server info is not supplied.
*/
static IotNetworkError_t _createMock( IotNetworkServerInfo_t pServerInfo,
IotNetworkCredentials_t pCredentialInfo,
IotNetworkConnection_t * pConnection )
{
( void ) pCredentialInfo;
( void ) pConnection;
if( pServerInfo == NULL )
{
return IOT_NETWORK_FAILURE;
}
else
{
return IOT_NETWORK_SUCCESS;
}
}
/*-----------------------------------------------------------*/
/**
* @brief An empty function for task pool job.
*/
static void _testTaskRoutine( IotTaskPool_t pTaskPool,
IotTaskPoolJob_t pJob,
void * pUserContext )
{
/* Do Nothing */
( void ) pTaskPool;
( void ) pJob;
( void ) pUserContext;
}
/*-----------------------------------------------------------*/
/**
* @brief This send function checks that a duplicate outgoing message differs from
* the original.
*/
static size_t _dupChecker( IotNetworkConnection_t pSendContext,
const uint8_t * pMessage,
size_t messageLength )
{
static int32_t runCount = 0;
static bool status = true;
bool * pDupCheckResult = ( bool * ) pSendContext;
uint8_t publishFlags = *pMessage;
/* Declare the remaining variables required to check packet identifier
* for the AWS IoT MQTT server. */
#if AWS_IOT_MQTT_SERVER == true
static uint16_t lastPacketIdentifier = 0;
_mqttPacket_t publishPacket = { .u.pMqttConnection = NULL };
_mqttOperation_t publishOperation = { .link = { 0 } };
publishPacket.type = publishFlags;
publishPacket.u.pIncomingPublish = &publishOperation;
publishPacket.remainingLength = 8 + TEST_TOPIC_NAME_LENGTH;
publishPacket.pRemainingData = ( uint8_t * ) pMessage + ( messageLength - publishPacket.remainingLength );
#endif
/* Ignore any MQTT packet that's not a PUBLISH. */
if( ( publishFlags & 0xf0 ) != MQTT_PACKET_TYPE_PUBLISH )
{
return messageLength;
}
runCount++;
/* Check how many times this function has been called. */
if( runCount == 1 )
{
#if AWS_IOT_MQTT_SERVER == true
/* Deserialize the PUBLISH to read the packet identifier. */
if( _IotMqtt_DeserializePublish( &publishPacket ) != IOT_MQTT_SUCCESS )
{
status = false;
}
else
{
lastPacketIdentifier = publishPacket.packetIdentifier;
}
#else /* if AWS_IOT_MQTT_SERVER == true */
/* DUP flag should not be set on this function's first run. */
if( ( publishFlags & 0x08 ) == 0x08 )
{
status = false;
}
#endif /* if AWS_IOT_MQTT_SERVER == true */
}
else
{
/* Only check the packet again if the previous run checks passed. */
if( status == true )
{
#if AWS_IOT_MQTT_SERVER == true
/* Deserialize the PUBLISH to read the packet identifier. */
if( _IotMqtt_DeserializePublish( &publishPacket ) != IOT_MQTT_SUCCESS )
{
status = false;
}
else
{
/* Check that the packet identifier is different. */
status = ( publishPacket.packetIdentifier != lastPacketIdentifier );
lastPacketIdentifier = publishPacket.packetIdentifier;
}
#else /* if AWS_IOT_MQTT_SERVER == true */
/* DUP flag should be set when this function runs again. */
if( ( publishFlags & 0x08 ) != 0x08 )
{
status = false;
}
#endif /* if AWS_IOT_MQTT_SERVER == true */
}
/* Write the check result on the last expected run of this function. */
if( runCount == DUP_CHECK_RETRY_LIMIT )
{
*pDupCheckResult = status;
}
}
/* Return the message length to simulate a successful send. */
return messageLength;
}
/*-----------------------------------------------------------*/
/**
* @brief A network receive function that simulates receiving a PINGRESP.
*/
static size_t _receivePingresp( IotNetworkConnection_t pReceiveContext,
uint8_t * pBuffer,
size_t bytesRequested )
{
size_t bytesReceived = 0;
static size_t receiveIndex = 0;
const uint8_t pPingresp[ 2 ] = { MQTT_PACKET_TYPE_PINGRESP, 0x00 };
/* Silence warnings about unused parameters. */
( void ) pReceiveContext;
/* Receive of PINGRESP should only ever request 1 byte. */
if( bytesRequested == 1 )
{
/* Write a byte of PINGRESP. */
*pBuffer = pPingresp[ receiveIndex ];
bytesReceived = 1;
/* Alternate the byte of PINGRESP to write. */
receiveIndex = ( receiveIndex + 1 ) % 2;
}
return bytesReceived;
}
/*-----------------------------------------------------------*/
/**
* @brief A function for setting the receive callback that just returns success.
*/
static IotNetworkError_t _setReceiveCallback( IotNetworkConnection_t pConnection,
IotNetworkReceiveCallback_t receiveCallback,
void * pReceiveContext )
{
/* Silence warnings about unused parameters. */
( void ) pConnection;
( void ) receiveCallback;
( void ) pReceiveContext;
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/
/**
* @brief A network close function that counts how many times it was invoked.
*/
static IotNetworkError_t _close( IotNetworkConnection_t pCloseContext )
{
/* Silence warnings about unused parameters. */
( void ) pCloseContext;
Atomic_Increment_u32( &_closeCount );
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/
/**
* @brief An MQTT disconnect callback that counts how many times it was invoked.
*/
static void _disconnectCallback( void * pCallbackContext,
IotMqttCallbackParam_t * pCallbackParam )
{
IotMqttDisconnectReason_t * pExpectedReason = ( IotMqttDisconnectReason_t * ) pCallbackContext;
/* Only increment counter if the reasons match. */
if( pCallbackParam->u.disconnectReason == *pExpectedReason )
{
Atomic_Increment_u32( &_disconnectCallbackCount );
}
}
/*-----------------------------------------------------------*/
/**
* @brief A task pool job routine that decrements an MQTT operation's job
* reference count.
*/
static void _decrementReferencesJob( IotTaskPool_t pTaskPool,
IotTaskPoolJob_t pJob,
void * pContext )
{
_mqttOperation_t * pOperation = ( _mqttOperation_t * ) pContext;
/* Silence warnings about unused parameters. */
( void ) pTaskPool;
( void ) pJob;
/* Decrement an operation's reference count. */
if( _IotMqtt_DecrementOperationReferences( pOperation, false ) == false )
{
/* Unblock the main test thread. */
IotSemaphore_Post( &( pOperation->u.operation.notify.waitSemaphore ) );
}
}
/*-----------------------------------------------------------*/
/**
* @brief get next byte mock function to test MQTT serializer API
*/
static IotMqttError_t _getNextByte( void * pNetworkInterface,
uint8_t * nextByte )
{
uint8_t * buffer;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Treat network interface as pointer to buffer for mocking */
/* Send next byte */
if( ( pNetworkInterface != NULL ) && ( nextByte != NULL ) )
{
buffer = ( *( uint8_t ** ) pNetworkInterface );
/* read single byte */
*nextByte = *buffer;
/* Move stream by 1 byte */
( *( uint8_t ** ) pNetworkInterface ) = ++buffer;
}
else
{
status = IOT_MQTT_NETWORK_ERROR;
}
return status;
}
/*-----------------------------------------------------------*/
/**
* @brief Test group for MQTT API tests.
*/
TEST_GROUP( MQTT_Unit_API );
/*-----------------------------------------------------------*/
/**
* @brief Test setup for MQTT API tests.
*/
TEST_SETUP( MQTT_Unit_API )
{
_publishSetDupCalled = false;
_pingreqSendCount = 0;
/* Reset the network info and interface. */
( void ) memset( &_networkInfo, 0x00, sizeof( IotMqttNetworkInfo_t ) );
( void ) memset( &_networkInterface, 0x00, sizeof( IotNetworkInterface_t ) );
_networkInterface.setReceiveCallback = _setReceiveCallback;
_networkInfo.pNetworkInterface = &_networkInterface;
/* Reset the counters. */
_pingreqSendCount = 0;
_closeCount = 0;
_disconnectCallbackCount = 0;
/* Initialize libraries. */
TEST_ASSERT_EQUAL_INT( true, IotSdk_Init() );
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, IotMqtt_Init() );
}
/*-----------------------------------------------------------*/
/**
* @brief Test tear down for MQTT API tests.
*/
TEST_TEAR_DOWN( MQTT_Unit_API )
{
IotMqtt_Cleanup();
IotSdk_Cleanup();
}
/*-----------------------------------------------------------*/
/**
* @brief Test group runner for MQTT API tests.
*/
TEST_GROUP_RUNNER( MQTT_Unit_API )
{
RUN_TEST_CASE( MQTT_Unit_API, Init );
RUN_TEST_CASE( MQTT_Unit_API, StringCoverage );
RUN_TEST_CASE( MQTT_Unit_API, OperationCreateDestroy );
RUN_TEST_CASE( MQTT_Unit_API, OperationWaitTimeout );
RUN_TEST_CASE( MQTT_Unit_API, ConnectParameters );
RUN_TEST_CASE( MQTT_Unit_API, ConnectMallocFail );
RUN_TEST_CASE( MQTT_Unit_API, ConnectRestoreSessionMallocFail );
RUN_TEST_CASE( MQTT_Unit_API, DisconnectMallocFail );
RUN_TEST_CASE( MQTT_Unit_API, DisconnectAlreadyDisconnected );
RUN_TEST_CASE( MQTT_Unit_API, PublishQoS0Parameters );
RUN_TEST_CASE( MQTT_Unit_API, PublishQoS0MallocFail );
RUN_TEST_CASE( MQTT_Unit_API, PublishQoS1 );
RUN_TEST_CASE( MQTT_Unit_API, PublishDuplicates );
RUN_TEST_CASE( MQTT_Unit_API, SubscribeUnsubscribeParameters );
RUN_TEST_CASE( MQTT_Unit_API, SubscribeMallocFail );
RUN_TEST_CASE( MQTT_Unit_API, UnsubscribeMallocFail );
RUN_TEST_CASE( MQTT_Unit_API, SingleThreaded );
RUN_TEST_CASE( MQTT_Unit_API, KeepAlivePeriodic );
RUN_TEST_CASE( MQTT_Unit_API, KeepAliveJobCleanup );
RUN_TEST_CASE( MQTT_Unit_API, GetConnectPacketSizeChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializeConnectChecks );
RUN_TEST_CASE( MQTT_Unit_API, GetSubscribePacketSizeChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializeSubscribeChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializeUnsubscribeChecks );
RUN_TEST_CASE( MQTT_Unit_API, GetPublishPacketSizeChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializePublishChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializeDisconnectChecks );
RUN_TEST_CASE( MQTT_Unit_API, SerializePingReqChecks );
RUN_TEST_CASE( MQTT_Unit_API, DeserializeResponseChecks );
RUN_TEST_CASE( MQTT_Unit_API, DeserializePublishChecks );
RUN_TEST_CASE( MQTT_Unit_API, GetIncomingMQTTPacketTypeAndLengthChecks );
RUN_TEST_CASE( MQTT_Unit_API, MqttOperationTryDestroy );
RUN_TEST_CASE( MQTT_Unit_API, CreateNetworkConnectionCheck );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the function @ref mqtt_function_init.
*/
TEST( MQTT_Unit_API, Init )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttOperation_t operation = IOT_MQTT_OPERATION_INITIALIZER;
/* Initialization was done in test set up. Clean up here before running this test. */
IotMqtt_Cleanup();
/* Calling cleanup twice should not crash. */
IotMqtt_Cleanup();
/* Calling API functions without calling IotMqtt_Init should fail. */
connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH;
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_NOT_INITIALIZED, status );
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
status = IotMqtt_SubscribeAsync( _pMqttConnection, &subscription, 1, 0, NULL, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_NOT_INITIALIZED, status );
status = IotMqtt_UnsubscribeAsync( _pMqttConnection, &subscription, 1, 0, NULL, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_NOT_INITIALIZED, status );
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, 0, NULL, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_NOT_INITIALIZED, status );
status = IotMqtt_Wait( operation, TIMEOUT_MS );
TEST_ASSERT_EQUAL( IOT_MQTT_NOT_INITIALIZED, status );
IotMqtt_Disconnect( _pMqttConnection, 0 );
/* Reinitialize for test cleanup. Calling init twice should not crash. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, IotMqtt_Init() );
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, IotMqtt_Init() );
}
/*-----------------------------------------------------------*/
/**
* @brief Provides code coverage of the MQTT enum-to-string functions,
* @ref mqtt_function_strerror and @ref mqtt_function_operationtype.
*/
TEST( MQTT_Unit_API, StringCoverage )
{
int32_t i = 0;
const char * pMessage = NULL;
/* For each MQTT Error, check the returned string. */
const char * pExitString = "INVALID STATUS";
size_t exitStringLength = strlen( pExitString );
while( true )
{
pMessage = IotMqtt_strerror( ( IotMqttError_t ) i );
TEST_ASSERT_NOT_NULL( pMessage );
if( strncmp( pExitString, pMessage, exitStringLength ) == 0 )
{
break;
}
i++;
}
/* For each MQTT Operation Type, check the returned string. */
i = 0;
pExitString = "INVALID OPERATION";
exitStringLength = strlen( pExitString );
while( true )
{
pMessage = IotMqtt_OperationType( ( IotMqttOperationType_t ) i );
TEST_ASSERT_NOT_NULL( pMessage );
if( strncmp( pExitString, pMessage, exitStringLength ) == 0 )
{
break;
}
i++;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Test reference counts as MQTT operations are created and destroyed.
*/
TEST( MQTT_Unit_API, OperationCreateDestroy )
{
_mqttOperation_t * pOperation = NULL;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Adjustment to reference count based on keep-alive status. */
const int32_t keepAliveReference = 1 + ( ( _pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs != 0 ) ? 1 : 0 );
/* A new MQTT connection should only have a possible reference for keep-alive. */
TEST_ASSERT_EQUAL_INT32( keepAliveReference, _pMqttConnection->references );
/* Create a new operation referencing the MQTT connection. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, _IotMqtt_CreateOperation( _pMqttConnection,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&pOperation ) );
/* Check reference counts and list placement. */
TEST_ASSERT_EQUAL_INT32( 1 + keepAliveReference, _pMqttConnection->references );
TEST_ASSERT_EQUAL_INT32( 2, pOperation->u.operation.jobReference );
TEST_ASSERT_EQUAL_PTR( &( pOperation->link ), IotListDouble_FindFirstMatch( &( _pMqttConnection->pendingProcessing ),
NULL,
NULL,
&( pOperation->link ) ) );
/* Schedule a job that destroys the operation. */
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS, IotTaskPool_CreateJob( _decrementReferencesJob,
pOperation,
&( pOperation->jobStorage ),
&( pOperation->job ) ) );
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS, IotTaskPool_Schedule( IOT_SYSTEM_TASKPOOL,
pOperation->job,
0 ) );
/* Wait for the job to complete. */
IotSemaphore_Wait( &( pOperation->u.operation.notify.waitSemaphore ) );
/* Check reference counts after job completion. */
TEST_ASSERT_EQUAL_INT32( 1 + keepAliveReference, _pMqttConnection->references );
TEST_ASSERT_EQUAL_INT32( 1, pOperation->u.operation.jobReference );
TEST_ASSERT_EQUAL_PTR( &( pOperation->link ), IotListDouble_FindFirstMatch( &( _pMqttConnection->pendingProcessing ),
NULL,
NULL,
&( pOperation->link ) ) );
/* Disconnect the MQTT connection, then call Wait to clean up the operation. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
IotMqtt_Wait( pOperation, 0 );
}
/*-----------------------------------------------------------*/
/**
* @brief Test that an operation is correctly cleaned up if @ref mqtt_function_wait
* times out while its job is executing.
*/
TEST( MQTT_Unit_API, OperationWaitTimeout )
{
_mqttOperation_t * pOperation = NULL;
IotSemaphore_t waitSem;
/* An arbitrary MQTT packet for this test. */
static uint8_t pPacket[ 2 ] = { MQTT_PACKET_TYPE_PINGREQ, 0x00 };
/* Create the wait semaphore. */
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &waitSem, 0, 1 ) );
if( TEST_PROTECT() )
{
/* Set the network interface send function. */
_networkInterface.send = _sendDelay;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set parameter to network send function. */
_pMqttConnection->pNetworkConnection = &waitSem;
/* Create a new operation referencing the MQTT connection. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, _IotMqtt_CreateOperation( _pMqttConnection,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&pOperation ) );
/* Set an arbitrary MQTT packet for the operation. */
pOperation->u.operation.type = IOT_MQTT_PINGREQ;
pOperation->u.operation.pMqttPacket = pPacket;
pOperation->u.operation.packetSize = 2;
/* Schedule the send job. */
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, _IotMqtt_ScheduleOperation( pOperation,
_IotMqtt_ProcessSend,
0 ) );
/* Wait for the send job to begin. */
IotSemaphore_Wait( &waitSem );
/* Wait on the MQTT operation with a short timeout. This should cause a
* timeout while the send job is still executing. */
TEST_ASSERT_EQUAL( IOT_MQTT_TIMEOUT, IotMqtt_Wait( pOperation, 10 ) );
/* Wait with an invalid operation */
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, IotMqtt_Wait( NULL, 10 ) );
/* Check reference count after a timed out wait. */
IotMutex_Lock( &( _pMqttConnection->referencesMutex ) );
TEST_ASSERT_EQUAL_INT32( 1, pOperation->u.operation.jobReference );
IotMutex_Unlock( &( _pMqttConnection->referencesMutex ) );
/* Disconnect the MQTT connection. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
/* Clean up the MQTT library, which waits for the send job to finish. The
* library must be re-initialized so that test tear down does not crash. */
IotMqtt_Cleanup();
IotMqtt_Init();
}
IotSemaphore_Destroy( &waitSem );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_connect with various
* invalid parameters.
*/
TEST( MQTT_Unit_API, ConnectParameters )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
IotMqttPublishInfo_t willInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
_networkInterface.send = _sendSuccess;
_networkInterface.close = _close;
/* Check that the network interface is validated. */
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
/* Check that the connection info is validated. */
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH;
/* Connect with bad previous session subscription. */
connectInfo.cleanSession = false;
connectInfo.pPreviousSubscriptions = &subscription;
connectInfo.previousSubscriptionCount = 1;
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
/* Connect with bad subscription count. */
connectInfo.previousSubscriptionCount = 0;
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
connectInfo.previousSubscriptionCount = 1;
/* Check that the will info is validated when it's provided. */
connectInfo.pWillInfo = &willInfo;
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
willInfo.pTopicName = TEST_TOPIC_NAME;
willInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
/* Check that a will message longer than 65535 is not allowed. */
willInfo.pPayload = "";
willInfo.payloadLength = 65536;
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
willInfo.payloadLength = 0;
/* Check connect returns error if network info is invalid. */
status = IotMqtt_Connect( NULL,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_NETWORK_ERROR, status );
/* Check that passing a wait time of 0 returns immediately. */
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
0,
&_pMqttConnection );
TEST_ASSERT_EQUAL( IOT_MQTT_TIMEOUT, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_connect when memory
* allocation fails at various points.
*/
TEST( MQTT_Unit_API, ConnectMallocFail )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
_networkInterface.close = _close;
connectInfo.keepAliveSeconds = 100;
connectInfo.cleanSession = true;
connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH;
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call CONNECT. Memory allocation will fail at various times during
* this call. */
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
/* If the return value is timeout, then all memory allocation succeeded
* and the loop can exit. The expected return value is timeout (and not
* success) because the receive callback is never invoked. */
if( status == IOT_MQTT_TIMEOUT )
{
break;
}
/* If the return value isn't timeout, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_connect when memory
* allocation fails at various points for a persistent session.
*/
TEST( MQTT_Unit_API, ConnectRestoreSessionMallocFail )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
_networkInterface.close = _close;
connectInfo.cleanSession = false;
connectInfo.keepAliveSeconds = 100;
connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH;
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
connectInfo.pPreviousSubscriptions = &subscription;
connectInfo.previousSubscriptionCount = 1;
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call CONNECT with a previous session. Memory allocation will fail at
* various times during this call. */
status = IotMqtt_Connect( &_networkInfo,
&connectInfo,
TIMEOUT_MS,
&_pMqttConnection );
/* If the return value is timeout, then all memory allocation succeeded
* and the loop can exit. The expected return value is timeout (and not
* success) because the receive callback is never invoked. */
if( status == IOT_MQTT_TIMEOUT )
{
break;
}
/* If the return value isn't timeout, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_disconnect when memory
* allocation fails at various points.
*/
TEST( MQTT_Unit_API, DisconnectMallocFail )
{
int32_t i = 0;
IotMqttDisconnectReason_t expectedReason = IOT_MQTT_DISCONNECT_CALLED;
/* Set the members of the network interface. */
_networkInterface.send = _sendSuccess;
_networkInterface.close = _close;
_networkInfo.createNetworkConnection = false;
_networkInfo.disconnectCallback.pCallbackContext = &expectedReason;
_networkInfo.disconnectCallback.function = _disconnectCallback;
for( i = 0; i < DISCONNECT_MALLOC_LIMIT; i++ )
{
/* Allow unlimited use of malloc during connection initialization. */
UnityMalloc_MakeMallocFailAfterCount( -1 );
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set malloc to eventually fail. */
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call DISCONNECT; this function should always perform cleanup regardless
* of memory allocation errors. */
IotMqtt_Disconnect( _pMqttConnection, 0 );
TEST_ASSERT_EQUAL_INT( 1, _closeCount );
TEST_ASSERT_EQUAL_INT( 1, _disconnectCallbackCount );
_closeCount = 0;
_disconnectCallbackCount = 0;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_disconnect when
* disconnected mqtt connection is passed.
*/
TEST( MQTT_Unit_API, DisconnectAlreadyDisconnected )
{
IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
/* Set up a mocked MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotTest_MqttMockInit( &mqttConnection ) );
TEST_ASSERT_EQUAL_INT( 1, mqttConnection->references );
/* Increase reference count to 3 so the subsequent disconnect
* calls do not free the connection. */
mqttConnection->references += 2;
/* Call Disconnect, reference count should decrement. */
IotMqtt_Disconnect( mqttConnection, 0 );
TEST_ASSERT_EQUAL_INT( 2, mqttConnection->references );
/* 'disconnected' flag should be set */
TEST_ASSERT_EQUAL( true, mqttConnection->disconnected );
/* Make sure reference count is decremented when 'disconnected'
* connection is passed without any attempts to close socket again. */
IotMqtt_Disconnect( mqttConnection, 0 );
TEST_ASSERT_EQUAL_INT( 1, mqttConnection->references );
/* One final disconnect to bring reference count to zero and free
* the connection */
IotMqtt_Disconnect( mqttConnection, 0 );
/* mqttConnection should be freed after above call.
* Test should not fail with any Unity memory leak asserts. */
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_publishasync (QoS 0) with various
* valid and invalid parameters.
*/
TEST( MQTT_Unit_API, PublishQoS0Parameters )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;
IotMqttCallbackInfo_t callbackInfo = IOT_MQTT_CALLBACK_INFO_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
if( TEST_PROTECT() )
{
/* Check that the publish info is validated. */
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, 0, NULL, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
/* Check that a QoS 0 publish is refused if a notification is requested. */
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, IOT_MQTT_FLAG_WAITABLE, NULL, &publishOperation );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, 0, &callbackInfo, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
/* If valid parameters are passed, QoS 0 publish should always return success. */
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, 0, 0, &publishOperation );
TEST_ASSERT_EQUAL( IOT_MQTT_SUCCESS, status );
}
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_publishasync (QoS 0) when memory
* allocation fails at various points.
*/
TEST( MQTT_Unit_API, PublishQoS0MallocFail )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the necessary members of publish info. */
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
if( TEST_PROTECT() )
{
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call PUBLISH. Memory allocation will fail at various times during
* this call. */
status = IotMqtt_PublishAsync( _pMqttConnection, &publishInfo, 0, NULL, NULL );
/* Once PUBLISH succeeds, the loop can exit. */
if( status == IOT_MQTT_SUCCESS )
{
break;
}
/* If the return value isn't success, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
}
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_publishasync (QoS 1) with various
* invalid parameters. Also tests the behavior of @ref mqtt_function_publishasync
* (QoS 1) when memory allocation fails at various points.
*/
TEST( MQTT_Unit_API, PublishQoS1 )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;
IotMqttCallbackInfo_t callbackInfo = IOT_MQTT_CALLBACK_INFO_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the publish info. */
publishInfo.qos = IOT_MQTT_QOS_1;
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
if( TEST_PROTECT() )
{
/* Setting the waitable flag with no reference is not allowed. */
status = IotMqtt_PublishAsync( _pMqttConnection,
&publishInfo,
IOT_MQTT_FLAG_WAITABLE,
NULL,
NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
/* Setting both the waitable flag and callback info is not allowed. */
status = IotMqtt_PublishAsync( _pMqttConnection,
&publishInfo,
IOT_MQTT_FLAG_WAITABLE,
&callbackInfo,
&publishOperation );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
/* Check QoS 1 PUBLISH behavior with malloc failures. */
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call PUBLISH. Memory allocation will fail at various times during
* this call. */
status = IotMqtt_PublishAsync( _pMqttConnection,
&publishInfo,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&publishOperation );
/* If the PUBLISH succeeded, the loop can exit after waiting for the QoS
* 1 PUBLISH to be cleaned up. */
if( status == IOT_MQTT_STATUS_PENDING )
{
TEST_ASSERT_EQUAL( IOT_MQTT_TIMEOUT, IotMqtt_Wait( publishOperation, TIMEOUT_MS ) );
break;
}
/* If the return value isn't success, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
}
/* Clean up MQTT connection. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that duplicate QoS 1 PUBLISH packets are different from the
* original.
*
* For non-AWS IoT MQTT servers, checks that the DUP flag is set. For
* AWS IoT MQTT servers, checks that the packet identifier is different.
*/
TEST( MQTT_Unit_API, PublishDuplicates )
{
static IotMqttSerializer_t serializer = IOT_MQTT_SERIALIZER_INITIALIZER;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;
bool dupCheckResult = false;
uint64_t startTime = 0;
/* Initializer parameters. */
serializer.serialize.publishSetDup = _publishSetDup;
_networkInterface.send = _dupChecker;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the serializers and parameter to the send function. */
_pMqttConnection->pNetworkConnection = &dupCheckResult;
_pMqttConnection->pSerializer = &serializer;
/* Set the publish info. */
publishInfo.qos = IOT_MQTT_QOS_1;
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
publishInfo.pPayload = "test";
publishInfo.payloadLength = 4;
publishInfo.retryMs = DUP_CHECK_RETRY_MS;
publishInfo.retryLimit = DUP_CHECK_RETRY_LIMIT;
startTime = IotClock_GetTimeMs();
if( TEST_PROTECT() )
{
/* Send a PUBLISH with retransmissions enabled. */
TEST_ASSERT_EQUAL( IOT_MQTT_STATUS_PENDING,
IotMqtt_PublishAsync( _pMqttConnection,
&publishInfo,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&publishOperation ) );
/* Since _dupChecker doesn't actually transmit a PUBLISH, no PUBACK is
* expected. */
TEST_ASSERT_EQUAL( IOT_MQTT_RETRY_NO_RESPONSE,
IotMqtt_Wait( publishOperation, DUP_CHECK_TIMEOUT ) );
/* Check the result of the DUP check. */
TEST_ASSERT_EQUAL_INT( true, dupCheckResult );
/* Check that at least the minimum wait time elapsed. */
TEST_ASSERT_TRUE( startTime + DUP_CHECK_MINIMUM_WAIT <= IotClock_GetTimeMs() );
}
/* Clean up MQTT connection. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
/* Check that the set DUP override was called. */
if( TEST_PROTECT() )
{
TEST_ASSERT_EQUAL_INT( true, _publishSetDupCalled );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_subscribeasync and
* @ref mqtt_function_unsubscribeasync with various invalid parameters.
*/
TEST( MQTT_Unit_API, SubscribeUnsubscribeParameters )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
IotMqttOperation_t subscribeOperation = IOT_MQTT_OPERATION_INITIALIZER;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Check that subscription info is validated. */
status = IotMqtt_SubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&subscribeOperation );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_UnsubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&subscribeOperation );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
/* A reference must be provided for a waitable SUBSCRIBE. */
status = IotMqtt_SubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_UnsubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_PARAMETER, status );
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_subscribeasync when memory allocation
* fails at various points.
*/
TEST( MQTT_Unit_API, SubscribeMallocFail )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
IotMqttOperation_t subscribeOperation = IOT_MQTT_OPERATION_INITIALIZER;
/* Initializer parameters. */
_networkInterface.send = _sendSuccess;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the necessary members of the subscription. */
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
if( TEST_PROTECT() )
{
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call SUBSCRIBE. Memory allocation will fail at various times during
* this call. */
status = IotMqtt_SubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&subscribeOperation );
/* If the SUBSCRIBE succeeded, the loop can exit after waiting for
* the SUBSCRIBE to be cleaned up. */
if( status == IOT_MQTT_STATUS_PENDING )
{
TEST_ASSERT_EQUAL( IOT_MQTT_TIMEOUT, IotMqtt_Wait( subscribeOperation, TIMEOUT_MS ) );
break;
}
/* If the return value isn't success, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
/* No lingering subscriptions should be in the MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotListDouble_IsEmpty( &( _pMqttConnection->subscriptionList ) ) );
}
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests the behavior of @ref mqtt_function_unsubscribeasync when memory
* allocation fails at various points.
*/
TEST( MQTT_Unit_API, UnsubscribeMallocFail )
{
int32_t i = 0;
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
IotMqttOperation_t unsubscribeOperation = IOT_MQTT_OPERATION_INITIALIZER;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
0 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the necessary members of the subscription. */
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
if( TEST_PROTECT() )
{
for( i = 0; ; i++ )
{
UnityMalloc_MakeMallocFailAfterCount( i );
/* Call UNSUBSCRIBE. Memory allocation will fail at various times during
* this call. */
status = IotMqtt_UnsubscribeAsync( _pMqttConnection,
&subscription,
1,
IOT_MQTT_FLAG_WAITABLE,
NULL,
&unsubscribeOperation );
/* If the UNSUBSCRIBE succeeded, the loop can exit after waiting for
* the UNSUBSCRIBE to be cleaned up. */
if( status == IOT_MQTT_STATUS_PENDING )
{
TEST_ASSERT_EQUAL( IOT_MQTT_TIMEOUT, IotMqtt_Wait( unsubscribeOperation, TIMEOUT_MS ) );
break;
}
/* If the return value isn't success, check that it is memory allocation
* failure. */
TEST_ASSERT_EQUAL( IOT_MQTT_NO_MEMORY, status );
}
/* No lingering subscriptions should be in the MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotListDouble_IsEmpty( &( _pMqttConnection->subscriptionList ) ) );
}
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
/*-----------------------------------------------------------*/
/**
* @brief Test that MQTT can work in a single thread without the task pool.
*/
TEST( MQTT_Unit_API, SingleThreaded )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotTaskPoolInfo_t taskPoolInfo = IOT_TASKPOOL_INFO_INITIALIZER_SMALL;
/* Shut down the system task pool to test if MQTT works without it. */
IotTaskPool_Destroy( IOT_SYSTEM_TASKPOOL );
/* Set the members of the subscription. */
subscription.pTopicFilter = TEST_TOPIC_NAME;
subscription.topicFilterLength = TEST_TOPIC_NAME_LENGTH;
subscription.callback.function = SUBSCRIPTION_CALLBACK;
/* Set the members of the publish info. */
publishInfo.pTopicName = TEST_TOPIC_NAME;
publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
publishInfo.pPayload = "test";
publishInfo.payloadLength = 4;
publishInfo.qos = IOT_MQTT_QOS_1;
if( TEST_PROTECT() )
{
/* Set up a mocked MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotTest_MqttMockInit( &mqttConnection ) );
/* Add a subscription. */
status = IotMqtt_SubscribeSync( mqttConnection, &subscription, 1, 0, DUP_CHECK_TIMEOUT );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Transmit a message with no retry. */
status = IotMqtt_PublishSync( mqttConnection, &publishInfo, 0, DUP_CHECK_TIMEOUT );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Remove the subscription. */
status = IotMqtt_UnsubscribeSync( mqttConnection, &subscription, 1, 0, DUP_CHECK_TIMEOUT );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Re-initialize the system task pool. The task pool must be available to
* send messages with a retry. */
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS, IotTaskPool_CreateSystemTaskPool( &taskPoolInfo ) );
/* Transmit a message with a retry. */
publishInfo.retryLimit = DUP_CHECK_RETRY_LIMIT;
publishInfo.retryMs = DUP_CHECK_RETRY_MS;
status = IotMqtt_PublishSync( mqttConnection, &publishInfo, 0, DUP_CHECK_TIMEOUT );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
IotTest_MqttMockCleanup();
}
else
{
/* Re-initialize the system task pool for test tear down. */
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS, IotTaskPool_CreateSystemTaskPool( &taskPoolInfo ) );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Tests keep-alive handling and ensures that it is periodic.
*/
TEST( MQTT_Unit_API, KeepAlivePeriodic )
{
/* The expected disconnect reason for this test's disconnect callback. */
IotMqttDisconnectReason_t expectedReason = IOT_MQTT_KEEP_ALIVE_TIMEOUT;
/* An estimate for the amount of time this test requires. */
const uint32_t sleepTimeMs = ( KEEP_ALIVE_COUNT * SHORT_KEEP_ALIVE_MS ) +
( IOT_MQTT_RESPONSE_WAIT_MS * KEEP_ALIVE_COUNT ) + 2500;
/* Print a newline so this test may log its status. */
UNITY_PRINT_EOL();
/* Initialize parameters. */
_networkInterface.send = _sendPingreq;
_networkInterface.receive = _receivePingresp;
_networkInterface.close = _close;
_networkInfo.disconnectCallback.pCallbackContext = &expectedReason;
_networkInfo.disconnectCallback.function = _disconnectCallback;
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
1 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set a short keep-alive interval so this test runs faster. */
_pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs = SHORT_KEEP_ALIVE_MS;
_pMqttConnection->pingreq.u.operation.periodic.ping.nextPeriodMs = SHORT_KEEP_ALIVE_MS;
/* Schedule the initial PINGREQ. */
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS,
IotTaskPool_ScheduleDeferred( IOT_SYSTEM_TASKPOOL,
_pMqttConnection->pingreq.job,
_pMqttConnection->pingreq.u.operation.periodic.ping.nextPeriodMs ) );
/* Sleep to allow ample time for periodic PINGREQ sends and PINGRESP responses. */
IotClock_SleepMs( sleepTimeMs );
/* Disconnect the connection. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
/* Check the counters for PINGREQ send and close. */
TEST_ASSERT_EQUAL_INT32( KEEP_ALIVE_COUNT + 1, _pingreqSendCount );
TEST_ASSERT_EQUAL_INT32( 2, _closeCount );
/* Check that the disconnect callback was invoked once (with reason
* "keep-alive timeout"). */
TEST_ASSERT_EQUAL_INT32( 1, Atomic_Add_u32( &_disconnectCallbackCount, 0 ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that the keep-alive job cleans up the MQTT connection after a call
* to @ref mqtt_function_disconnect.
*/
TEST( MQTT_Unit_API, KeepAliveJobCleanup )
{
IotSemaphore_t waitSem;
/* Initialize parameters. */
_networkInterface.send = _sendSuccess;
TEST_ASSERT_EQUAL_INT( true, IotSemaphore_Create( &waitSem, 0, 1 ) );
if( TEST_PROTECT() )
{
/* Create a new MQTT connection. */
_pMqttConnection = IotTestMqtt_createMqttConnection( AWS_IOT_MQTT_SERVER,
&_networkInfo,
1 );
TEST_ASSERT_NOT_NULL( _pMqttConnection );
/* Set the parameter to the send function. */
_pMqttConnection->pNetworkConnection = &waitSem;
/* Set a short keep-alive interval so this test runs faster. */
_pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs = SHORT_KEEP_ALIVE_MS;
_pMqttConnection->pingreq.u.operation.periodic.ping.nextPeriodMs = SHORT_KEEP_ALIVE_MS;
/* Schedule the initial PINGREQ. */
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS,
IotTaskPool_ScheduleDeferred( IOT_SYSTEM_TASKPOOL,
_pMqttConnection->pingreq.job,
_pMqttConnection->pingreq.u.operation.periodic.ping.nextPeriodMs ) );
/* Wait for the keep-alive job to send a PINGREQ. */
IotSemaphore_Wait( &waitSem );
/* Immediately disconnect the connection. */
IotMqtt_Disconnect( _pMqttConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );
}
IotSemaphore_Destroy( &waitSem );
}
/*-----------------------------------------------------------*/
/* Tests for public serializer API */
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_GetConnectPacketSize works as intended.
* to @ref mqtt_function_getconnectpacketsize.
*/
TEST( MQTT_Unit_API, GetConnectPacketSizeChecks )
{
IotMqttConnectInfo_t connectInfo;
size_t remainingLength = 0;
size_t packetSize = 0;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Call IotMqtt_GetConnectPacketSize() with various combinations of
* incorrect paramters */
status = IotMqtt_GetConnectPacketSize( NULL, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetConnectPacketSize( &connectInfo, NULL, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetConnectPacketSize( &connectInfo, &remainingLength, NULL );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Verify empty connect info fails. */
memset( ( void * ) &connectInfo, 0x0, sizeof( connectInfo ) );
status = IotMqtt_GetConnectPacketSize( &connectInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Verify good case */
memset( ( void * ) &connectInfo, 0x0, sizeof( connectInfo ) );
connectInfo.cleanSession = true;
connectInfo.pClientIdentifier = "TEST";
connectInfo.clientIdentifierLength = 4;
status = IotMqtt_GetConnectPacketSize( &connectInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure remaining size returned is 16. */
TEST_ASSERT_EQUAL_INT( 16, remainingLength );
/* Make sure packet size is 18. */
TEST_ASSERT_EQUAL_INT( 18, packetSize );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_SerializeConnect works as intended.
* to @ref mqtt_function_serializeconnect.
*/
TEST( MQTT_Unit_API, SerializeConnectChecks )
{
IotMqttConnectInfo_t connectInfo;
size_t remainingLength = 0;
uint8_t buffer[ 20 ];
size_t bufferSize = sizeof( buffer );
size_t packetSize = bufferSize;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Verify bad parameter errors. */
status = IotMqtt_SerializeConnect( NULL, remainingLength, buffer, packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializeConnect( &connectInfo, remainingLength, NULL, packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
memset( ( void * ) &connectInfo, 0x0, sizeof( connectInfo ) );
/* Make sure greater remaining length returns error. */
remainingLength = 120;
status = IotMqtt_SerializeConnect( &connectInfo, 120, buffer, packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds */
/* Calculate packet size. */
memset( ( void * ) &connectInfo, 0x0, sizeof( connectInfo ) );
connectInfo.cleanSession = true;
connectInfo.pClientIdentifier = "TEST";
connectInfo.clientIdentifierLength = 4;
status = IotMqtt_GetConnectPacketSize( &connectInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure buffer has enough space */
TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
/* Make sure test succeeds. */
status = IotMqtt_SerializeConnect( &connectInfo, remainingLength, buffer, packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* For this example, IotMqtt_GetConnectPacketSize() will return
* packetSize = remainingLength +2 (two byte fixed header).
* Make sure IotMqtt_SerializeConnect()
* fails when remaining length is more than packet size. */
status = IotMqtt_SerializeConnect( &connectInfo, remainingLength + 4, buffer, packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_GetSubscribePacketSize works as intended.
* to @ref mqtt_function_getsubscriptionpacketsize.
*/
TEST( MQTT_Unit_API, GetSubscribePacketSizeChecks )
{
IotMqttSubscription_t subscriptionList;
size_t subscriptionCount = 0;
size_t remainingLength = 0;
size_t packetSize = 0;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Verify parameters. */
status = IotMqtt_GetSubscriptionPacketSize( 100,
&subscriptionList,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
NULL,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
&subscriptionList,
subscriptionCount,
NULL,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
&subscriptionList,
subscriptionCount,
&remainingLength,
NULL );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Verify empty subscription list fails. */
memset( ( void * ) &subscriptionList, 0x0, sizeof( subscriptionList ) );
subscriptionCount = 0;
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
&subscriptionList,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Verify good case. */
memset( ( void * ) &subscriptionList, 0x0, sizeof( subscriptionList ) );
subscriptionList.qos = IOT_MQTT_QOS_0;
subscriptionList.pTopicFilter = "/example/topic";
subscriptionList.topicFilterLength = sizeof( "/example/topic" );
subscriptionCount = sizeof( subscriptionList ) / sizeof( IotMqttSubscription_t );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
&subscriptionList,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
TEST_ASSERT_GREATER_THAN( remainingLength, packetSize );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_SerializeSubscribe works as intended.
* to @ref mqtt_function_serializesubscribe.
*/
TEST( MQTT_Unit_API, SerializeSubscribeChecks )
{
IotMqttSubscription_t subscriptionList;
size_t subscriptionCount = 0;
size_t remainingLength = 0;
uint16_t packetIdentifier;
uint8_t buffer[ 25 ];
size_t bufferSize = sizeof( buffer );
size_t packetSize = bufferSize;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Verify bad parameters fail. */
status = IotMqtt_SerializeSubscribe( NULL,
subscriptionCount,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializeSubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
NULL,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializeSubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
&packetIdentifier,
NULL,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Get correct values of packet size and remaining length. */
memset( ( void * ) &subscriptionList, 0x0, sizeof( subscriptionList ) );
subscriptionList.qos = IOT_MQTT_QOS_0;
subscriptionList.pTopicFilter = "/example/topic";
subscriptionList.topicFilterLength = sizeof( "/example/topic" );
subscriptionCount = sizeof( subscriptionList ) / sizeof( IotMqttSubscription_t );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
&subscriptionList,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure buffer has enough space */
TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
/* Make sure subscription count of zero fails. */
status = IotMqtt_SerializeSubscribe( &subscriptionList,
0,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Make sure success is returned for good case. */
status = IotMqtt_SerializeSubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* For this example, IotMqtt_GetSubscriptionPacketSize() will return
* packetSize = remainingLength +2 (two byte fixed header).
* Make sure IotMqtt_SerializeSubscribe()
* fails when remaining length is more than packet size. */
status = IotMqtt_SerializeSubscribe( &subscriptionList,
subscriptionCount,
remainingLength + 4,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_SerializeUnsubscribe works as intended.
* to @ref mqtt_function_serializeunsubscribe.
*/
TEST( MQTT_Unit_API, SerializeUnsubscribeChecks )
{
IotMqttSubscription_t subscriptionList;
size_t subscriptionCount = 0;
size_t remainingLength = 0;
uint16_t packetIdentifier;
uint8_t buffer[ 25 ];
size_t bufferSize = sizeof( buffer );
size_t packetSize = bufferSize;
IotMqttError_t status = IOT_MQTT_SUCCESS;
status = IotMqtt_SerializeUnsubscribe( NULL,
subscriptionCount,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializeUnsubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
NULL,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializeUnsubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
&packetIdentifier,
NULL,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Get correct values of packetsize and remaining length. */
memset( ( void * ) &subscriptionList, 0x0, sizeof( subscriptionList ) );
subscriptionList.qos = IOT_MQTT_QOS_0;
subscriptionList.pTopicFilter = "/example/topic";
subscriptionList.topicFilterLength = sizeof( "/example/topic" );
subscriptionCount = sizeof( subscriptionList ) / sizeof( IotMqttSubscription_t );
status = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_UNSUBSCRIBE,
&subscriptionList,
subscriptionCount,
&remainingLength,
&packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure buffer has enough space */
TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
/* Make sure subscription count of zero fails. */
status = IotMqtt_SerializeUnsubscribe( &subscriptionList,
0,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Make sure success it returned for good case. */
status = IotMqtt_SerializeUnsubscribe( &subscriptionList,
subscriptionCount,
remainingLength,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* For this example, IotMqtt_GetSubscriptionPacketSize() will return
* packetSize = remainingLength +2, make sure IotMqtt_SerializeUnsubscribe()
* fails when remaining length is more than packet size. */
status = IotMqtt_SerializeUnsubscribe( &subscriptionList,
subscriptionCount,
remainingLength + 4,
&packetIdentifier,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_GetPublishPacketSize works as intended.
* to @ref mqtt_function_getpublishpacketsize.
*/
TEST( MQTT_Unit_API, GetPublishPacketSizeChecks )
{
IotMqttPublishInfo_t publishInfo;
size_t remainingLength = 0;
size_t packetSize;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Verify bad paramameters fail. */
status = IotMqtt_GetPublishPacketSize( NULL, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetPublishPacketSize( &publishInfo, NULL, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_GetPublishPacketSize( &publishInfo, &remainingLength, NULL );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Empty topic must fail. */
memset( ( void * ) &publishInfo, 0x00, sizeof( publishInfo ) );
status = IotMqtt_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds. */
publishInfo.pTopicName = "/test/topic";
publishInfo.topicNameLength = sizeof( "/test/topic" );
status = IotMqtt_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_GetPublishPacketSize works as intended.
* to @ref mqtt_function_serializepublish.
*/
TEST( MQTT_Unit_API, SerializePublishChecks )
{
IotMqttPublishInfo_t publishInfo;
size_t remainingLength = 98;
uint16_t packetIdentifier;
uint8_t * pPacketIdentifierHigh;
uint8_t buffer[ 100 ];
size_t bufferSize = sizeof( buffer );
size_t packetSize = bufferSize;
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Verify bad parameters fail. */
memset( ( void * ) &publishInfo, 0x00, sizeof( publishInfo ) );
publishInfo.pTopicName = "/test/topic";
publishInfo.topicNameLength = sizeof( "/test/topic" );
status = IotMqtt_SerializePublish( &publishInfo,
remainingLength,
NULL,
&pPacketIdentifierHigh,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializePublish( NULL,
remainingLength,
&packetIdentifier,
&pPacketIdentifierHigh,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
status = IotMqtt_SerializePublish( &publishInfo,
remainingLength,
&packetIdentifier,
&pPacketIdentifierHigh,
NULL,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* NULL topic fails. */
publishInfo.pTopicName = NULL;
status = IotMqtt_SerializePublish( &publishInfo,
remainingLength,
&packetIdentifier,
&pPacketIdentifierHigh,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds */
publishInfo.pTopicName = "/test/topic";
publishInfo.topicNameLength = sizeof( "/test/topic" );
/* Calculate exact packet size and remaining length. */
status = IotMqtt_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure buffer has enough space */
TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
status = IotMqtt_SerializePublish( &publishInfo,
remainingLength,
&packetIdentifier,
&pPacketIdentifierHigh,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_SerializeDisconnect works as intended.
* to @ref mqtt_function_serializedisconnect.
*/
TEST( MQTT_Unit_API, SerializeDisconnectChecks )
{
uint8_t buffer[ 10 ];
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Buffer size less than disconnect request fails. */
status = IotMqtt_SerializeDisconnect( buffer, 1 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* NULL buffer fails. */
status = IotMqtt_SerializeDisconnect( NULL, 10 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds. */
status = IotMqtt_SerializeDisconnect( buffer, 2 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_SerializePingReq works as intended.
* to @ref mqtt_function_serializepingreq.
*/
TEST( MQTT_Unit_API, SerializePingReqChecks )
{
uint8_t buffer[ 10 ];
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Buffer size less than disconnect request fails. */
status = IotMqtt_SerializePingreq( buffer, 1 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* NULL buffer fails. */
status = IotMqtt_SerializePingreq( NULL, 10 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds. */
status = IotMqtt_SerializePingreq( buffer, 2 );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_GetIncomingMQTTPacketTypeAndLength works as intended.
* to @ref mqtt_function_getincomingmqttpackettypeandlength.
*/
TEST( MQTT_Unit_API, GetIncomingMQTTPacketTypeAndLengthChecks )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
IotMqttPacketInfo_t mqttPacket;
uint8_t buffer[ 10 ];
uint8_t * bufPtr = buffer;
/* Dummy network interface - pointer to pointer to a buffer. */
void * pNetworkInterface = ( void * ) &bufPtr;
buffer[ 0 ] = 0x20; /* CONN ACK */
buffer[ 1 ] = 0x02; /* Remaining length. */
status = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &mqttPacket, _getNextByte, pNetworkInterface );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
TEST_ASSERT_EQUAL_INT( 0x20, mqttPacket.type );
TEST_ASSERT_EQUAL_INT( 0x02, mqttPacket.remainingLength );
/* Test with NULL network interface */
status = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &mqttPacket, _getNextByte, NULL );
TEST_ASSERT_EQUAL( IOT_MQTT_NETWORK_ERROR, status );
/* Test with incorrect packet type. */
buffer[ 0 ] = 0x10; /* INVALID */
status = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &mqttPacket, _getNextByte, pNetworkInterface );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_RESPONSE, status );
/* Test with invalid remaining length. */
buffer[ 0 ] = 0x20; /* CONN ACK */
buffer[ 1 ] = 0xFF; /* Remaining length. */
status = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &mqttPacket, _getNextByte, pNetworkInterface );
TEST_ASSERT_EQUAL( IOT_MQTT_BAD_RESPONSE, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_DeserializeResponse works as intended.
* to @ref mqtt_function_deserializeresponse.
*/
TEST( MQTT_Unit_API, DeserializeResponseChecks )
{
IotMqttPacketInfo_t mqttPacketInfo;
IotMqttError_t status = IOT_MQTT_SUCCESS;
uint8_t buffer[ 10 ];
/* Verify parameters */
status = IotMqtt_DeserializeResponse( NULL );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
memset( ( void * ) &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) );
mqttPacketInfo.type = 0x01;
mqttPacketInfo.pRemainingData = buffer;
status = IotMqtt_DeserializeResponse( &mqttPacketInfo );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds - Test for CONN ACK */
/* Set conn ack variable portion */
buffer[ 0 ] = 0x00;
buffer[ 1 ] = 0x00;
/* Set type, remaining length and remaining data. */
mqttPacketInfo.type = 0x20; /* CONN ACK */
mqttPacketInfo.pRemainingData = buffer;
mqttPacketInfo.remainingLength = 0x02; /* CONN ACK Remaining Length. */
status = IotMqtt_DeserializeResponse( &mqttPacketInfo );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests that IotMqtt_DeserializePublish works as intended.
* to @ref mqtt_function_deserializepublish.
*/
TEST( MQTT_Unit_API, DeserializePublishChecks )
{
IotMqttPacketInfo_t mqttPacketInfo;
IotMqttPublishInfo_t publishInfo;
IotMqttError_t status = IOT_MQTT_SUCCESS;
uint8_t buffer[ 100 ];
size_t bufferSize = sizeof( buffer );
size_t packetSize = bufferSize;
size_t remainingLength = 0;
uint16_t packetIdentifier;
uint8_t * pPacketIdentifierHigh;
uint8_t * pNetworkInterface;
/* Verify parameters. */
status = IotMqtt_DeserializePublish( NULL );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
memset( ( void * ) &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) );
/* Bad Packet Type. */
mqttPacketInfo.type = 0x01;
mqttPacketInfo.pRemainingData = buffer;
status = IotMqtt_DeserializePublish( &mqttPacketInfo );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_BAD_PARAMETER, status );
/* Good case succeeds - Test for Publish. */
/* 1. Find out length of the packet .*/
memset( &publishInfo, 0x00, sizeof( publishInfo ) );
publishInfo.pTopicName = "/test/topic";
publishInfo.topicNameLength = ( uint16_t ) strlen( publishInfo.pTopicName );
publishInfo.pPayload = "Hello World";
publishInfo.payloadLength = ( uint16_t ) strlen( publishInfo.pPayload );
publishInfo.qos = IOT_MQTT_QOS_0;
/* Calculate exact packet size and remaining length */
status = IotMqtt_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Make sure buffer has enough space */
TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
/* 2. Serialize packet in the buffer. */
status = IotMqtt_SerializePublish( &publishInfo,
remainingLength,
&packetIdentifier,
&pPacketIdentifierHigh,
buffer,
packetSize );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* 3. Deserialize - get type and length. */
pNetworkInterface = buffer;
status = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &mqttPacketInfo, _getNextByte, ( void * ) &pNetworkInterface );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
/* Remaining data points to byte 3. */
mqttPacketInfo.pRemainingData = &buffer[ 2 ];
/* 4. Deserialize publish. */
status = IotMqtt_DeserializePublish( &mqttPacketInfo );
TEST_ASSERT_EQUAL_INT( IOT_MQTT_SUCCESS, status );
}
/*-----------------------------------------------------------*/
/**
* @brief Tests internal function _mqttOperation_tryDestroy works
* as intended.
* @note: Uses access function.
*/
TEST( MQTT_Unit_API, MqttOperationTryDestroy )
{
_mqttOperation_t * pMqttOperation = NULL;
/* _mqttOperation_t mqttOperation ; */
IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
IotTaskPoolJob_t pTaskPoolJob = IOT_TASKPOOL_JOB_INITIALIZER;
IotTaskPoolError_t taskPoolError = IOT_TASKPOOL_SUCCESS;
IotTaskPoolJobStorage_t _testJobStorage = IOT_TASKPOOL_JOB_STORAGE_INITIALIZER;
/* Create Task pool Job */
taskPoolError = IotTaskPool_CreateJob( _testTaskRoutine, NULL, &_testJobStorage, &pTaskPoolJob );
TEST_ASSERT_EQUAL( IOT_TASKPOOL_SUCCESS, taskPoolError );
/* Allocate operation */
pMqttOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );
TEST_ASSERT_NOT_NULL( pMqttOperation )
/* Set up a mocked MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotTest_MqttMockInit( &mqttConnection ) );
void * pData = ( void * ) pMqttOperation;
memset( pData, 0, sizeof( _mqttOperation_t ) );
/* Non Publish operation */
pMqttOperation->incomingPublish = false;
pMqttOperation->pMqttConnection = mqttConnection;
pMqttOperation->job = pTaskPoolJob;
pMqttOperation->u.operation.jobReference = 2;
IotTestMqtt_mqttOperation_tryDestroy( pData );
/* Job reference must be decremented, Operation must be still allocated */
TEST_ASSERT_EQUAL_INT( 1, pMqttOperation->u.operation.jobReference );
/* Publish Operation */
/* reset mocked MQTT connection. */
TEST_ASSERT_EQUAL_INT( true, IotTest_MqttMockInit( &mqttConnection ) );
pMqttOperation->incomingPublish = true;
pMqttOperation->u.publish.pReceivedData = IotMqtt_MallocMessage( 10 );
/*
* pOperation will be destroyed after this call and the test
* should not report any memory leaks because the destroy
* Operation should free all memory.
*/
IotTestMqtt_mqttOperation_tryDestroy( pData );
/* The call should not assert, operation should be destroyed */
IotTest_MqttMockCleanup();
}
/*-----------------------------------------------------------*/
/**
* @brief Tests internal function _createNetworkConnection works
* as intended.
* @note: Uses access function.
*/
TEST( MQTT_Unit_API, CreateNetworkConnectionCheck )
{
IotMqttNetworkInfo_t * pNetworkInfo = NULL;
IotNetworkConnection_t * pNetworkConnection = { 0 };
bool createdNetworkConnection = false;
/* Test for parameter validation */
TEST_ASSERT_EQUAL( IOT_NETWORK_BAD_PARAMETER, IotTestMqtt_createNetworkConnection( pNetworkInfo,
pNetworkConnection,
&createdNetworkConnection ) );
/* Setup correct network info */
pNetworkInfo = &_networkInfo;
pNetworkInfo->createNetworkConnection = true;
pNetworkInfo->pNetworkInterface = &_networkInterface;
_networkInterface.create = _createMock;
/* Invalid server info */
pNetworkInfo->u.setup.pNetworkServerInfo = NULL;
TEST_ASSERT_NOT_EQUAL( IOT_MQTT_SUCCESS, IotTestMqtt_createNetworkConnection( pNetworkInfo,
pNetworkConnection,
&createdNetworkConnection ) );
}
/*-----------------------------------------------------------*/