diff --git a/source/include/core_mqtt.h b/source/include/core_mqtt.h index 42e0d48d..476385ab 100644 --- a/source/include/core_mqtt.h +++ b/source/include/core_mqtt.h @@ -382,6 +382,16 @@ typedef struct MQTTContext */ MQTTEventCallback_t appCallback; + /** + * @brief Application-defined data pointer. + * + * The library never reads or modifies this field; it allows the + * application to access its own state from an #MQTTEventCallback_t + * callback through the pContext parameter. As #MQTT_Init clears the + * entire context, set this field only after calling #MQTT_Init. + */ + void * pUserData; + /** * @brief Timestamp of the last packet sent by the library. */ diff --git a/test/unit-test/core_mqtt_utest.c b/test/unit-test/core_mqtt_utest.c index 415a157b..93967a1f 100644 --- a/test/unit-test/core_mqtt_utest.c +++ b/test/unit-test/core_mqtt_utest.c @@ -434,6 +434,29 @@ static bool eventCallback2( MQTTContext_t * pContext, return true; } +/** + * @brief Value of pContext->pUserData recorded by eventCallbackUserData. + */ +static void * pUserDataSeenInCallback = NULL; + +/** + * @brief Mocked MQTT event callback that records the application-defined + * pUserData pointer from the context, and otherwise behaves exactly like + * eventCallback2. + */ +static bool eventCallbackUserData( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * pSendPropsBuffer, + MQTTPropBuilder_t * pGetPropsBuffer ) +{ + pUserDataSeenInCallback = pContext->pUserData; + + return eventCallback2( pContext, pPacketInfo, pDeserializedInfo, + pReasonCode, pSendPropsBuffer, pGetPropsBuffer ); +} + static bool eventCallback3( MQTTContext_t * pContext, MQTTPacketInfo_t * pPacketInfo, MQTTDeserializedInfo_t * pDeserializedInfo, @@ -1811,6 +1834,65 @@ void test_MQTT_Init_Happy_Path( void ) TEST_ASSERT_EQUAL_MEMORY( &networkBuffer, &context.networkBuffer, sizeof( networkBuffer ) ); } +/** + * @brief Test that MQTT_Init clears pUserData and that the application can + * read it back from the event callback through the context pointer. + */ +void test_MQTT_Init_UserData_AccessibleInEventCallback( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + ProcessLoopReturns_t expectParams = { 0 }; + MQTTPubAckInfo_t pIncomingCallback[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + MQTTPublishInfo_t publishInfo = { 0 }; + int userData = 0; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + + /* Set pUserData before MQTT_Init to verify that MQTT_Init clears it. */ + context.pUserData = &userData; + + MQTT_InitConnect_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallbackUserData, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + TEST_ASSERT_NULL( context.pUserData ); + + /* The application sets its data pointer after MQTT_Init. */ + context.pUserData = &userData; + + MQTTPropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingCallback, 10, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + + context.connectStatus = MQTTConnected; + + modifyIncomingPacketStatus = MQTTSuccess; + + /* Receive a QoS 1 PUBLISH so that the event callback is invoked. */ + currentPacketType = MQTT_PACKET_TYPE_PUBLISH; + resetProcessLoopParams( &expectParams ); + expectParams.stateAfterDeserialize = MQTTPubAckSend; + expectParams.stateAfterSerialize = MQTTPublishDone; + expectParams.incomingPublish = true; + publishInfo.qos = MQTTQoS1; + expectParams.pPubInfo = &publishInfo; + expectParams.willReasonCodeBeSet = true; + expectParams.willSendPropsBeSet = true; + pUserDataSeenInCallback = NULL; + expectProcessLoopCalls( &context, &expectParams ); + + TEST_ASSERT_EQUAL_PTR( &userData, pUserDataSeenInCallback ); +} + /** * @brief Test that any NULL parameter causes MQTT_Init to return MQTTBadParameter. */