/* * 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 /* 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, ¤tSubscription ) ); 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; } } /*-----------------------------------------------------------*/