// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifdef __cplusplus #include #include #include #else #include #include #include #include #endif #include "azure_macro_utils/macro_utils.h" #include "testrunnerswitcher.h" #include "umock_c/umock_c.h" #include "iothub_account.h" #include "iothubtest.h" #include "iothub_messaging.h" #include "serializer.h" #include "azure_c_shared_utility/buffer_.h" #include "azure_c_shared_utility/strings.h" #include "azure_c_shared_utility/lock.h" #include "azure_c_shared_utility/crt_abstractions.h" #include "azure_c_shared_utility/threadapi.h" #include "iothubtransportamqp.h" #include "iothubtransporthttp.h" #include "iothubtransportmqtt.h" #include "MacroE2EModelAction.h" #include "iothub_client.h" #include "iothub_client_options.h" #include "iothub_devicemethod.h" #include "parson.h" #include "methodreturn.h" #include "azure_c_shared_utility/platform.h" #include "iothub_service_client_auth.h" #include "iothub_devicetwin.h" static TEST_MUTEX_HANDLE g_testByTest; static IOTHUB_ACCOUNT_INFO_HANDLE g_iothubAcctInfo = NULL; /*the following time expressed in seconds denotes the maximum time to read all the events available in an event hub*/ #define MAX_DRAIN_TIME 100.0 /*the following time expressed in seconds denotes the maximum "cloud" travel time - the time from a moment some data reaches the cloud until that data is available for a consumer*/ #define MAX_CLOUD_TRAVEL_TIME 60.0 #define MAX_TWIN_CLOUD_TRAVEL_TIME 120.0 #define TIME_DATA_LENGTH 32 TEST_DEFINE_ENUM_TYPE(IOTHUB_TEST_CLIENT_RESULT, IOTHUB_TEST_CLIENT_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_TEST_CLIENT_RESULT, IOTHUB_TEST_CLIENT_RESULT_VALUES); TEST_DEFINE_ENUM_TYPE(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_RESULT_VALUES); TEST_DEFINE_ENUM_TYPE(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES); TEST_DEFINE_ENUM_TYPE(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES); TEST_DEFINE_ENUM_TYPE(IOTHUB_DEVICE_METHOD_RESULT, IOTHUB_DEVICE_METHOD_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_DEVICE_METHOD_RESULT, IOTHUB_DEVICE_METHOD_RESULT_VALUES); TEST_DEFINE_ENUM_TYPE(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_RESULT_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_RESULT_VALUES); static const char* TEST_SEND_DATA_FMT = "{\"ExampleData\": { \"SendDate\": \"%.24s\", \"UniqueId\":%d} }"; static const char* TEST_RECV_DATA_FMT = "{\\\"Name\\\": \\\"testaction\\\", \\\"Parameters\\\": { \\\"property1\\\": \\\"%.24s\\\", \\\"UniqueId\\\":%d}}"; static const char* TEST_CMP_DATA_FMT = "{\"Name\": \"testaction\", \"Parameters\": { \"property1\": \"%.24s\", \"UniqueId\":%d} }"; static const char* TEST_MACRO_CMP_DATA_FMT = "{\"UniqueId\":%d, \"property1\":\"%.24s\"}"; static const char* TEST_MACRO_RECV_DATA_FMT = "{\"Name\":\"dataMacroCallback\", \"Parameters\":{\"property1\":\"%.24s\", \"UniqueId\": %d}}"; static size_t g_uniqueTestId = 0; typedef struct _tagEXPECTED_SEND_DATA { const char* expectedString; bool wasFound; bool dataWasSent; LOCK_HANDLE lock; /*needed to protect this structure*/ } EXPECTED_SEND_DATA; typedef struct _tagEXPECTED_RECEIVE_DATA { const char* toBeSend; size_t toBeSendSize; const char* compareData; size_t compareDataSize; bool wasFound; LOCK_HANDLE lock; /*needed to protect this structure*/ } EXPECTED_RECEIVE_DATA; static EXPECTED_RECEIVE_DATA* g_recvMacroData; static LOCK_HANDLE lock; /*this protects the data written in the below callback from the main thread that is polling it*/ static int was_my_IOTHUB_CLIENT_REPORTED_STATE_CALLBACK_called; void my_IOTHUB_CLIENT_REPORTED_STATE_CALLBACK(int status_code, void* userContextCallback) { (void)(userContextCallback); ASSERT_ARE_EQUAL(int, status_code, 204); (void)Lock(lock); was_my_IOTHUB_CLIENT_REPORTED_STATE_CALLBACK_called = 1; Unlock(lock); } static void openCompleteCallback(void* context) { (void)printf("Open completed, context: %s\n", (char*)context); } static void sendCompleteCallback(void* context, IOTHUB_MESSAGING_RESULT messagingResult) { (void)context; if (messagingResult == IOTHUB_MESSAGING_OK) { (void)printf("Message has been sent successfully\n"); } else { (void)printf("Send failed\n"); } } // This is a Static function called from the macro EXECUTE_COMMAND_RESULT dataMacroCallback(deviceModel* device, ascii_char_ptr property1, int UniqueId) { (void)device; if (g_recvMacroData != NULL) { if (Lock(g_recvMacroData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock"); } else { if ( (g_uniqueTestId == (size_t)UniqueId) && (strcmp(g_recvMacroData->compareData, property1) == 0) ) { g_recvMacroData->wasFound = true; } (void)Unlock(g_recvMacroData->lock); } } return EXECUTE_COMMAND_SUCCESS; } /*this function "links" IoTHub to the serialization library*/ static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) { const unsigned char* buffer; size_t size; if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) { ASSERT_FAIL("unable to IoTHubMessage_GetByteArray"); } else { /*buffer is not zero terminated*/ char* buffer_string = (char*)malloc(size+1); ASSERT_IS_NOT_NULL(buffer_string); if (memcpy(buffer_string, buffer, size) == 0) { ASSERT_FAIL("memcpy failed for buffer"); } else { buffer_string[size] = '\0'; EXECUTE_COMMAND(userContextCallback, buffer_string); } free(buffer_string); } return IOTHUBMESSAGE_ACCEPTED; } METHODRETURN_HANDLE theSumOfThings(deviceModel* device, int a, int b) { (void)(device); int a_plus_b = a + b; size_t needed = snprintf(NULL, 0, "%u", a_plus_b); char* jsonValue = (char*)malloc(needed + 1); ASSERT_IS_NOT_NULL(jsonValue); (void)snprintf(jsonValue, needed + 1, "%u", a_plus_b); METHODRETURN_HANDLE result = MethodReturn_Create(10, jsonValue); ASSERT_IS_NOT_NULL(result); free(jsonValue); return result; } /*AT NO TIME, CAN THERE BE 2 TRANSPORTS OVER OPENSSL IN THE SAME TIME.*/ /*If there are, nasty Linux bugs will happen*/ BEGIN_TEST_SUITE(serializer_e2e) static void iotHubMacroCallBack(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) { (void)result; EXPECTED_SEND_DATA* expectedData = (EXPECTED_SEND_DATA*)userContextCallback; if (expectedData != NULL) { if (Lock(expectedData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock"); } else { if (expectedData != NULL) { expectedData->dataWasSent = true; } (void)Unlock(expectedData->lock); //printf("was sent: %s\n", expectedData->expectedString); } } } static int IoTHubCallback(void* context, const char* data, size_t size) { int result = 0; // 0 means "keep processing" EXPECTED_SEND_DATA* expectedData = (EXPECTED_SEND_DATA*)context; //printf("Received: %*.*s\n", (int)size, (int)size, data); if (expectedData != NULL) { if (Lock(expectedData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock"); } else { if (size != strlen(expectedData->expectedString)) { result = 0; } else { if (memcmp(expectedData->expectedString, data, size) == 0) { expectedData->wasFound = true; result = 1; } else { result = 0; } } (void)Unlock(expectedData->lock); } } return result; } static EXPECTED_RECEIVE_DATA* RecvMacroTestData_Create(void) { EXPECTED_RECEIVE_DATA* result; result = (EXPECTED_RECEIVE_DATA*)malloc(sizeof(EXPECTED_RECEIVE_DATA)); if (result != NULL) { if ((result->lock = Lock_Init()) == NULL) { free(result); result = NULL; } else { char* tempString; result->wasFound = false; tempString = (char*)malloc(TIME_DATA_LENGTH); if (tempString == NULL) { (void)Lock_Deinit(result->lock); free(result); result = NULL; } else { time_t t = time(NULL); (void)sprintf_s(tempString, TIME_DATA_LENGTH, "%.24s", ctime(&t) ); result->compareData = tempString; result->compareDataSize = strlen(result->compareData); size_t nLen = result->compareDataSize+strlen(TEST_MACRO_RECV_DATA_FMT)+4; tempString = (char*)malloc(nLen); if (tempString == NULL) { (void)Lock_Deinit(result->lock); free((void*)result->compareData); free(result); result = NULL; } else { (void)sprintf_s(tempString, nLen, TEST_MACRO_RECV_DATA_FMT, result->compareData, g_uniqueTestId); result->toBeSendSize = strlen(tempString); result->toBeSend = tempString; } } } } return result; } static void RecvTestData_Destroy(EXPECTED_RECEIVE_DATA* data) { if (data != NULL) { (void)Lock_Deinit(data->lock); if (data->compareData != NULL) { free((void*)(data->compareData)); } if (data->toBeSend != NULL) { free((void*)data->toBeSend); } free(data); } } static EXPECTED_SEND_DATA* SendMacroTestData_Create(const char* pszTime) { EXPECTED_SEND_DATA* result = (EXPECTED_SEND_DATA*)malloc(sizeof(EXPECTED_SEND_DATA)); if (result != NULL) { if ((result->lock = Lock_Init()) == NULL) { free(result); result = NULL; } else { char temp[1000]; char* tempString; sprintf(temp, TEST_MACRO_CMP_DATA_FMT, g_uniqueTestId, pszTime); if ((tempString = (char*)malloc(strlen(temp) + 1)) == NULL) { Lock_Deinit(result->lock); free(result); result = NULL; } else { strcpy(tempString, temp); result->expectedString = tempString; result->wasFound = false; result->dataWasSent = false; } } } return result; } static void SendTestData_Destroy(EXPECTED_SEND_DATA* data) { if (data != NULL) { (void)Lock_Deinit(data->lock); if (data->expectedString != NULL) { free((void*)data->expectedString); } free(data); } } TEST_SUITE_INITIALIZE(TestClassInitialize) { g_testByTest = TEST_MUTEX_CREATE(); ASSERT_IS_NOT_NULL(g_testByTest); ASSERT_ARE_EQUAL(int, 0, platform_init() ); ASSERT_ARE_EQUAL(int, 0, serializer_init(NULL)); g_iothubAcctInfo = IoTHubAccount_Init(false); ASSERT_IS_NOT_NULL(g_iothubAcctInfo); g_uniqueTestId = 0; } TEST_SUITE_CLEANUP(TestClassCleanup) { IoTHubAccount_deinit(g_iothubAcctInfo); platform_deinit(); serializer_deinit(); TEST_MUTEX_DESTROY(g_testByTest); } TEST_FUNCTION_INITIALIZE(TestMethodInitialize) { if (TEST_MUTEX_ACQUIRE(g_testByTest)) { ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); } g_uniqueTestId++; was_my_IOTHUB_CLIENT_REPORTED_STATE_CALLBACK_called = 0; lock = Lock_Init(); ASSERT_IS_NOT_NULL(lock); } TEST_FUNCTION_CLEANUP(TestMethodCleanup) { Lock_Deinit(lock); TEST_MUTEX_RELEASE(g_testByTest); } static void AMQP_MacroRecv(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { // arrange IOTHUB_CLIENT_HANDLE iotHubClientHandle; bool continue_run; IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iotHubServiceClientAuthHandle; IOTHUB_MESSAGING_CLIENT_HANDLE iotHubMessagingHandle; IOTHUB_MESSAGING_RESULT iotHubMessagingResult; IOTHUB_MESSAGE_HANDLE messageHandle; // act //step 1: data is retrieved by device using AMQP time_t beginOperation, nowTime; // step 2: data is created g_recvMacroData = RecvMacroTestData_Create(); ASSERT_IS_NOT_NULL(g_recvMacroData); // Create message messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)g_recvMacroData->toBeSend, g_recvMacroData->toBeSendSize); // Create Service Client iotHubServiceClientAuthHandle = IoTHubServiceClientAuth_CreateFromConnectionString(IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo)); ASSERT_IS_NOT_NULL(iotHubServiceClientAuthHandle, "Could not initialize IoTHubServiceClient to send C2D messages to the device"); iotHubMessagingHandle = IoTHubMessaging_Create(iotHubServiceClientAuthHandle); ASSERT_IS_NOT_NULL(iotHubMessagingHandle, "Could not initialize IoTHubMessaging to send C2D messages to the device"); iotHubMessagingResult = IoTHubMessaging_Open(iotHubMessagingHandle, openCompleteCallback, "Context string for open"); ASSERT_ARE_EQUAL(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_OK, iotHubMessagingResult); iotHubMessagingResult = IoTHubMessaging_SendAsync(iotHubMessagingHandle, deviceToUse->deviceId, messageHandle, sendCompleteCallback, NULL); ASSERT_ARE_EQUAL(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_OK, iotHubMessagingResult, "IoTHubMessaging_SendAsync failed, could not send C2D message to the device"); iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, AMQP_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { IOTHUB_CLIENT_RESULT result; result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } deviceModel* devModel = CREATE_MODEL_INSTANCE(MacroE2EModelAction, deviceModel); ASSERT_IS_NOT_NULL(devModel); IOTHUB_CLIENT_RESULT setMessageResult = IoTHubClient_SetMessageCallback(iotHubClientHandle, IoTHubMessage, devModel); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, setMessageResult); beginOperation = time(NULL); continue_run = true; while ( ((nowTime = time(NULL)), (difftime(nowTime, beginOperation) < MAX_CLOUD_TRAVEL_TIME)) && continue_run) { if (Lock(g_recvMacroData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock macro not found"); } else { if (g_recvMacroData->wasFound) { continue_run = false; } (void)Unlock(g_recvMacroData->lock); } ThreadAPI_Sleep(100); } // assert ASSERT_IS_TRUE(g_recvMacroData->wasFound); ///cleanup DESTROY_MODEL_INSTANCE(devModel); IoTHubClient_Destroy(iotHubClientHandle); RecvTestData_Destroy(g_recvMacroData); IoTHubMessage_Destroy(messageHandle); IoTHubMessaging_Close(iotHubMessagingHandle); IoTHubMessaging_Destroy(iotHubMessagingHandle); IoTHubServiceClientAuth_Destroy(iotHubServiceClientAuthHandle); } TEST_FUNCTION(IoTClient_AMQP_MacroRecv_e2e_sas) { AMQP_MacroRecv(IoTHubAccount_GetSASDevice(g_iothubAcctInfo)); } #ifndef __APPLE__ TEST_FUNCTION(IoTClient_AMQP_MacroRecv_e2e_x509) { AMQP_MacroRecv(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif static void AMQP_MacroSend(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { // arrange IOTHUB_CLIENT_HANDLE iotHubClientHandle; deviceModel* devModel; time_t beginOperation, nowTime; bool continue_run; // step 1: prepare data time_t t = time(NULL); char sztimeText[128]; (void)sprintf_s(sztimeText, sizeof(sztimeText), "%.24s", ctime(&t)); EXPECTED_SEND_DATA* expectedData = SendMacroTestData_Create(sztimeText); ASSERT_IS_NOT_NULL(expectedData); /// act // step 2: send data with AMQP { iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, AMQP_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { IOTHUB_CLIENT_RESULT result; result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } devModel = CREATE_MODEL_INSTANCE(MacroE2EModelAction, deviceModel); ASSERT_IS_NOT_NULL(devModel); devModel->property1 = sztimeText; devModel->UniqueId = (int)g_uniqueTestId; unsigned char* destination; size_t destinationSize; CODEFIRST_RESULT nResult = SERIALIZE(&destination, &destinationSize, *devModel); ASSERT_ARE_EQUAL(CODEFIRST_RESULT, CODEFIRST_OK, nResult); IOTHUB_MESSAGE_HANDLE iothubMessageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); ASSERT_IS_NOT_NULL(iothubMessageHandle); free(destination); IOTHUB_CLIENT_RESULT iothubClientResult = IoTHubClient_SendEventAsync(iotHubClientHandle, iothubMessageHandle, iotHubMacroCallBack, expectedData); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, iothubClientResult); IoTHubMessage_Destroy(iothubMessageHandle); // Wait til the data gets sent to the callback beginOperation = time(NULL); continue_run = true; while ( ((nowTime = time(NULL)), (difftime(nowTime, beginOperation) < MAX_CLOUD_TRAVEL_TIME)) && //time box continue_run) { if (Lock(expectedData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock data was sent"); } else { if (expectedData->dataWasSent) { continue_run = false; } (void)Unlock(expectedData->lock); } ThreadAPI_Sleep(100); } } ASSERT_IS_TRUE(expectedData->dataWasSent); ///assert //step3: get the data from the other side { IOTHUB_TEST_HANDLE devhubTestHandle = IoTHubTest_Initialize(IoTHubAccount_GetEventHubConnectionString(g_iothubAcctInfo), IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo), deviceToUse->deviceId, IoTHubAccount_GetEventhubListenName(g_iothubAcctInfo), IoTHubAccount_GetEventhubAccessKey(g_iothubAcctInfo), IoTHubAccount_GetSharedAccessSignature(g_iothubAcctInfo), IoTHubAccount_GetEventhubConsumerGroup(g_iothubAcctInfo)); ASSERT_IS_NOT_NULL(devhubTestHandle); IOTHUB_TEST_CLIENT_RESULT result = IoTHubTest_ListenForEventForMaxDrainTime(devhubTestHandle, IoTHubCallback, IoTHubAccount_GetIoTHubPartitionCount(g_iothubAcctInfo), expectedData); ASSERT_ARE_EQUAL(IOTHUB_TEST_CLIENT_RESULT, IOTHUB_TEST_CLIENT_OK, result); IoTHubTest_Deinit(devhubTestHandle); } // Sent Send is Async we need to make sure all the Data has been sent continue_run = true; beginOperation = time(NULL); while ( ((nowTime = time(NULL)), (difftime(nowTime, beginOperation) < MAX_CLOUD_TRAVEL_TIME)) && //time box continue_run) { if (Lock(expectedData->lock) != LOCK_OK) { ASSERT_FAIL("unable to lock found data"); } else { if (expectedData->wasFound) { continue_run = false; } (void)Unlock(expectedData->lock); } ThreadAPI_Sleep(100); } ASSERT_IS_TRUE(expectedData->wasFound); // was found is written by the callback... ///cleanup DESTROY_MODEL_INSTANCE(devModel); IoTHubClient_Destroy(iotHubClientHandle); SendTestData_Destroy(expectedData); //cleanup } TEST_FUNCTION(IoTClient_AMQP_MacroSend_e2e_sas) { AMQP_MacroSend(IoTHubAccount_GetSASDevice(g_iothubAcctInfo)); } #ifndef __APPLE__ TEST_FUNCTION(IoTClient_AMQP_MacroSend_e2e_x509) { AMQP_MacroSend(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif static void Http_MacroRecv(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; deviceModel* devModel; IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iotHubServiceClientAuthHandle; IOTHUB_MESSAGING_CLIENT_HANDLE iotHubMessagingHandle; IOTHUB_MESSAGING_RESULT iotHubMessagingResult; IOTHUB_MESSAGE_HANDLE messageHandle; //step 1: data is created g_recvMacroData = RecvMacroTestData_Create(); ASSERT_IS_NOT_NULL(g_recvMacroData); // Create Service Client iotHubServiceClientAuthHandle = IoTHubServiceClientAuth_CreateFromConnectionString(IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo)); ASSERT_IS_NOT_NULL(iotHubServiceClientAuthHandle, "Could not initialize IoTHubServiceClient to send C2D messages to the device"); iotHubMessagingHandle = IoTHubMessaging_Create(iotHubServiceClientAuthHandle); ASSERT_IS_NOT_NULL(iotHubMessagingHandle, "Could not initialize IoTHubMessaging to send C2D messages to the device"); iotHubMessagingResult = IoTHubMessaging_Open(iotHubMessagingHandle, openCompleteCallback, "Context string for open"); ASSERT_ARE_EQUAL(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_OK, iotHubMessagingResult); // Create message messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)g_recvMacroData->toBeSend, g_recvMacroData->toBeSendSize); // act iotHubMessagingResult = IoTHubMessaging_SendAsync(iotHubMessagingHandle, deviceToUse->deviceId, messageHandle, sendCompleteCallback, NULL); ASSERT_ARE_EQUAL(IOTHUB_MESSAGING_RESULT, IOTHUB_MESSAGING_OK, iotHubMessagingResult, "IoTHubMessaging_SendAsync failed, could not send C2D message to the device"); //step 3: data is retrieved by HTTP { time_t beginOperation, nowTime; iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(deviceToUse->connectionString, HTTP_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { IOTHUB_CLIENT_RESULT result; result = IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } unsigned int minimumPollingTime = 0; /*because it should not wait*/ if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) { printf("failure to set option \"MinimumPollingTime\"\r\n"); } devModel = CREATE_MODEL_INSTANCE(MacroE2EModelAction, deviceModel); ASSERT_IS_NOT_NULL(devModel); IOTHUB_CLIENT_RESULT setMessageResult = IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, devModel); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, setMessageResult); beginOperation = time(NULL); while ( ( (nowTime = time(NULL)), (difftime(nowTime, beginOperation) < MAX_CLOUD_TRAVEL_TIME) //time box ) && (!g_recvMacroData->wasFound) //condition box ) { //just go on; IoTHubClient_LL_DoWork(iotHubClientHandle); ThreadAPI_Sleep(100); } } // assert ASSERT_IS_TRUE(g_recvMacroData->wasFound); ///cleanup DESTROY_MODEL_INSTANCE(devModel); IoTHubClient_LL_Destroy(iotHubClientHandle); RecvTestData_Destroy(g_recvMacroData); IoTHubMessage_Destroy(messageHandle); IoTHubMessaging_Close(iotHubMessagingHandle); IoTHubMessaging_Destroy(iotHubMessagingHandle); IoTHubServiceClientAuth_Destroy(iotHubServiceClientAuthHandle); } #ifndef __APPLE__ TEST_FUNCTION(IoTClient_Http_MacroRecv_e2e_x509) { Http_MacroRecv(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif static void Http_MacroSend(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { // arrange IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; deviceModel* devModel; time_t beginOperation, nowTime; // step 1: prepare data time_t t = time(NULL); char sztimeText[128]; (void)sprintf_s(sztimeText, sizeof(sztimeText), "%.24s", ctime(&t)); EXPECTED_SEND_DATA* expectedData = SendMacroTestData_Create(sztimeText); ASSERT_IS_NOT_NULL(expectedData); ///act // step 2: send data with HTTP { iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(deviceToUse->connectionString, HTTP_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { IOTHUB_CLIENT_RESULT result; result = IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } devModel = CREATE_MODEL_INSTANCE(MacroE2EModelAction, deviceModel); ASSERT_IS_NOT_NULL(devModel); devModel->property1 = sztimeText; devModel->UniqueId = (int)g_uniqueTestId; unsigned char* destination; size_t destinationSize; CODEFIRST_RESULT nResult = SERIALIZE(&destination, &destinationSize, *devModel); ASSERT_ARE_EQUAL(CODEFIRST_RESULT, CODEFIRST_OK, nResult); IOTHUB_MESSAGE_HANDLE iothubMessageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); ASSERT_IS_NOT_NULL(iothubMessageHandle); free(destination); IOTHUB_CLIENT_RESULT iothubClientResult = IoTHubClient_LL_SendEventAsync(iotHubClientHandle, iothubMessageHandle, iotHubMacroCallBack, expectedData); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, iothubClientResult); IoTHubMessage_Destroy(iothubMessageHandle); // Make sure all the Data has been sent beginOperation = time(NULL); while ( ( (nowTime = time(NULL)), (difftime(nowTime, beginOperation) < MAX_CLOUD_TRAVEL_TIME) //time box ) && (!expectedData->dataWasSent) ) //condition box { //just go on; IoTHubClient_LL_DoWork(iotHubClientHandle); ThreadAPI_Sleep(100); } } ASSERT_IS_TRUE(expectedData->dataWasSent); ///assert //step3: get the data from the other side { IOTHUB_TEST_HANDLE devhubTestHandle = IoTHubTest_Initialize(IoTHubAccount_GetEventHubConnectionString(g_iothubAcctInfo), IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo), deviceToUse->deviceId, IoTHubAccount_GetEventhubListenName(g_iothubAcctInfo), IoTHubAccount_GetEventhubAccessKey(g_iothubAcctInfo), IoTHubAccount_GetSharedAccessSignature(g_iothubAcctInfo), IoTHubAccount_GetEventhubConsumerGroup(g_iothubAcctInfo)); ASSERT_IS_NOT_NULL(devhubTestHandle); IOTHUB_TEST_CLIENT_RESULT result = IoTHubTest_ListenForEventForMaxDrainTime(devhubTestHandle, IoTHubCallback, IoTHubAccount_GetIoTHubPartitionCount(g_iothubAcctInfo), expectedData); ASSERT_ARE_EQUAL(IOTHUB_TEST_CLIENT_RESULT, IOTHUB_TEST_CLIENT_OK, result); IoTHubTest_Deinit(devhubTestHandle); } ASSERT_IS_TRUE(expectedData->wasFound); // was found is written by the callback... ///cleanup DESTROY_MODEL_INSTANCE(devModel); IoTHubClient_LL_Destroy(iotHubClientHandle); SendTestData_Destroy(expectedData); //cleanup*/ } TEST_FUNCTION(IoTClient_Http_MacroSend_e2e_sas) { Http_MacroSend(IoTHubAccount_GetSASDevice(g_iothubAcctInfo)); } #ifndef __APPLE__ TEST_FUNCTION(IoTClient_Http_MacroSend_e2e_x509) { Http_MacroSend(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif static int DeviceMethodCallback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) { /*userContextCallback is of type deviceModel*/ /*this is step 3: receive the method and push that payload into serializer (from below)*/ char* payloadZeroTerminated = (char*)malloc(size + 1); ASSERT_IS_NOT_NULL(payloadZeroTerminated); (void)memcpy(payloadZeroTerminated, payload, size); payloadZeroTerminated[size] = '\0'; METHODRETURN_HANDLE result = EXECUTE_METHOD(userContextCallback, method_name, payloadZeroTerminated); free(payloadZeroTerminated); ASSERT_IS_NOT_NULL(result); /*step 4: get the serializer answer and push it in the networking stack*/ const METHODRETURN_DATA* data = MethodReturn_GetReturn(result); int statusCode = data->statusCode; ASSERT_IS_NOT_NULL(data->jsonValue); *resp_size = strlen(data->jsonValue); *response = (unsigned char*)malloc(*resp_size); ASSERT_IS_NOT_NULL(*response); (void)memcpy(*response, data->jsonValue, *resp_size); MethodReturn_Destroy(result); return statusCode; } /*the following test shall perform the following 1: create a model instance that has a method 2: create/start the device + start listening on method 3: receive the method and push that payload into serializer 4: get the serializer answer and push it in the networking stack 5: verify on the service side that the answer matches the expectations 6: destroy all things*/ /* by convention, it shall call a function that receives 2 ints and produces the sum of the ints in the answer. by convention, the request JSON looks like { "a": 3, "b": 33 } by convention, the answer shall have "36" (or whatever the function computes) as only payload, status will be set to 10. */ static void MQTT_can_receive_a_method(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { /*step 1: create a model instance that has a method*/ IOTHUB_CLIENT_RESULT result; deviceModel* device = CREATE_MODEL_INSTANCE(MacroE2EModelAction, deviceModel); ASSERT_IS_NOT_NULL(device); /*step 2: create/start the device + start listening on method*/ IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, MQTT_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle, "Could not create IoTHubClient"); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } result = IoTHubClient_SetDeviceMethodCallback(iotHubClientHandle, DeviceMethodCallback, device); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device method callback"); // Wait for the method subscription to go through ThreadAPI_Sleep(3 * 1000); IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iotHubServiceClientHandle = IoTHubServiceClientAuth_CreateFromConnectionString(IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo)); ASSERT_IS_NOT_NULL(iotHubServiceClientHandle, "Could not create service client handle"); IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle = IoTHubDeviceMethod_Create(iotHubServiceClientHandle); ASSERT_IS_NOT_NULL(serviceClientDeviceMethodHandle, "Could not create device method handle"); /*step 5: verify on the service side that the answer matches the expectations*/ int responseStatus; unsigned char* responsePayload; size_t responsePayloadSize; IOTHUB_DEVICE_METHOD_RESULT invokeResult = IoTHubDeviceMethod_Invoke(serviceClientDeviceMethodHandle, deviceToUse->deviceId, "theSumOfThings", "{\"a\":3, \"b\":33}", 120, &responseStatus, &responsePayload, &responsePayloadSize); ASSERT_ARE_EQUAL(IOTHUB_DEVICE_METHOD_RESULT, IOTHUB_DEVICE_METHOD_OK, invokeResult, "IoTHubDeviceMethod_Invoke failed"); ASSERT_ARE_EQUAL(int, 10, responseStatus, "response status is incorrect"); /*make sure the response is null terminated*/ char* responseAsString = (char*)malloc(responsePayloadSize + 1); ASSERT_IS_NOT_NULL(responseAsString); (void)memcpy(responseAsString, responsePayload, responsePayloadSize); responseAsString[responsePayloadSize] = '\0'; /*parse the response*/ JSON_Value *jsonValue = json_parse_string(responseAsString); free(responseAsString); ASSERT_IS_NOT_NULL(jsonValue); double d = json_value_get_number(jsonValue); json_value_free(jsonValue); ASSERT_ARE_EQUAL(int, 36, (int)d); /*cleanup*/ /*step 6: destroy all things*/ free(responsePayload); IoTHubDeviceMethod_Destroy(serviceClientDeviceMethodHandle); IoTHubServiceClientAuth_Destroy(iotHubServiceClientHandle); IoTHubClient_Destroy(iotHubClientHandle); DESTROY_MODEL_INSTANCE(device); } TEST_FUNCTION(IoTClient_MQTT_can_receive_a_method_sas) { MQTT_can_receive_a_method(IoTHubAccount_GetSASDevice(g_iothubAcctInfo)); } #ifndef __APPLE__ TEST_FUNCTION(IoTClient_MQTT_can_receive_a_method_x509) { MQTT_can_receive_a_method(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif /*the following tests IotHubDeviceTwin_SendReportedState*/ /*these are steps the test will make*/ /*step 1: import the schema; create an IoTHubClient handle step 2: create an instance of the model step 3: set the model instance's value to something; send it! step 4: wait for the callback to be called (it needs to indicate success) step 5: use the C service SDK to pick up the reported properties from the service(they need to indicate the same value) step 6: succeed */ static void DeviceTwin_SendReportedState_succeeds(IOTHUB_PROVISIONED_DEVICE* deviceToUse) { /*step 1: import the schema; create an IoTHubClient handle*/ SERIALIZER_REGISTER_NAMESPACE(MacroE2EModelAction); IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, MQTT_Protocol); ASSERT_IS_NOT_NULL(iotHubClientHandle); bool traceOn = true; IoTHubClient_SetOption(iotHubClientHandle, "logtrace", &traceOn); if (deviceToUse->howToCreate == IOTHUB_ACCOUNT_AUTH_X509) { IOTHUB_CLIENT_RESULT result; result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_CERT, deviceToUse->certificate); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 certificate"); result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_X509_PRIVATE_KEY, deviceToUse->primaryAuthentication); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set the device x509 privateKey"); } /*step 2: create an instance of the model*/ deviceModel* model = IoTHubDeviceTwin_CreatedeviceModel(iotHubClientHandle); /* step 3: set the model instance's value to something; send it!*/ model->reported_int = 199; IOTHUB_CLIENT_RESULT res = IoTHubDeviceTwin_SendReportedStatedeviceModel(model, my_IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, NULL); ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, res); /*step 4: wait for the callback to be called (it needs to indicate success)*/ /*time bound it to 1 minute*/ time_t begin = time(NULL); while (difftime(time(NULL), begin)deviceId); JSON_Value* twinJson = json_parse_string(theTwin); JSON_Object* twinJsonObject = json_value_get_object(twinJson); double reported_by_the_service = json_object_dotget_number(twinJsonObject, "properties.reported.reported_int"); ASSERT_ARE_EQUAL(double, reported_by_the_service, 199); /*same like model->reported_int = 199;*/ ///cleanup json_value_free(twinJson); free(theTwin); IoTHubDeviceTwin_Destroy(serviceClientDeviceTwinHandle); IoTHubServiceClientAuth_Destroy(iotHubServiceClientHandle); IoTHubDeviceTwin_DestroydeviceModel(model); IoTHubClient_Destroy(iotHubClientHandle); } TEST_FUNCTION(IotHubDeviceTwin_SendReportedState_succeeds_sas) { DeviceTwin_SendReportedState_succeeds(IoTHubAccount_GetSASDevice(g_iothubAcctInfo)); } #ifndef __APPLE__ TEST_FUNCTION(IotHubDeviceTwin_SendReportedState_succeeds_x509) { DeviceTwin_SendReportedState_succeeds(IoTHubAccount_GetX509Device(g_iothubAcctInfo)); } #endif END_TEST_SUITE(serializer_e2e)