From 19b7457970d899511f6c43cb618a2b0a8c04bba6 Mon Sep 17 00:00:00 2001 From: Eric Wolz Date: Fri, 21 Oct 2022 18:39:37 -0700 Subject: [PATCH] Allow empty file blob uploads (#2393) * Allow empty file blob uploads * PR feedback * pr feed back --- iothub_client/src/blob.c | 10 +- iothub_client/tests/blob_ut/blob_ut.c | 65 +++++++++- .../iothubclient_uploadtoblob_e2e.c | 112 ++++++++++-------- 3 files changed, 128 insertions(+), 59 deletions(-) diff --git a/iothub_client/src/blob.c b/iothub_client/src/blob.c index 6b012109ae..34fed2356c 100644 --- a/iothub_client/src/blob.c +++ b/iothub_client/src/blob.c @@ -13,6 +13,9 @@ #include "azure_c_shared_utility/azure_base64.h" #include "azure_c_shared_utility/shared_util_options.h" +#define HTTP_STATUS_CODE_OK 200 +#define IS_HTTP_STATUS_CODE_SUCCESS(x) ((x) >= 100 && (x) < 300) + static const char blockListXmlBegin[] = "\r\n"; static const char blockListXmlEnd[] = ""; static const char blockListUriMarker[] = "&comp=blocklist"; @@ -105,7 +108,7 @@ BLOB_RESULT Blob_UploadBlock( LogError("unable to HTTPAPIEX_ExecuteRequest"); result = BLOB_HTTP_ERROR; } - else if (*httpStatus >= 300) + else if (!IS_HTTP_STATUS_CODE_SUCCESS(*httpStatus)) { LogError("HTTP status from storage does not indicate success (%d)", (int)*httpStatus); result = BLOB_OK; @@ -152,6 +155,7 @@ static BLOB_RESULT InvokeUserCallbackAndSendBlobs(HTTPAPIEX_HANDLE httpApiExHand { uploadOneMoreBlock = 0; result = BLOB_OK; + *httpStatus = HTTP_STATUS_CODE_OK; } else { @@ -195,7 +199,7 @@ static BLOB_RESULT InvokeUserCallbackAndSendBlobs(HTTPAPIEX_HANDLE httpApiExHand LogError("unable to Blob_UploadBlock. Returned value=%d", result); isError = 1; } - else if (*httpStatus >= 300) + else if (!IS_HTTP_STATUS_CODE_SUCCESS(*httpStatus)) { LogError("unable to Blob_UploadBlock. Returned httpStatus=%u", (unsigned int)*httpStatus); isError = 1; @@ -348,7 +352,7 @@ BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIEN { LogError("Failed in invoking callback/sending blob step"); } - else if (*httpStatus < 300) + else if (IS_HTTP_STATUS_CODE_SUCCESS(*httpStatus)) { // Per SRS_BLOB_02_026, it possible for us to have a result=BLOB_OK AND a non-success HTTP status code. // In order to maintain back-compat with existing code, we will return the BLOB_OK to the caller but NOT invoke this final step. diff --git a/iothub_client/tests/blob_ut/blob_ut.c b/iothub_client/tests/blob_ut/blob_ut.c index b01a6201a2..be03d0e19e 100644 --- a/iothub_client/tests/blob_ut/blob_ut.c +++ b/iothub_client/tests/blob_ut/blob_ut.c @@ -47,6 +47,10 @@ static void my_gballoc_free(void* s) #undef malloc #endif +#define TEST_HTTPAPIEX_HANDLE (HTTPAPIEX_HANDLE)0x4343; +#define HTTP_OK 200 +#define HTTP_NOT_FOUND 404 + TEST_DEFINE_ENUM_TYPE(HTTPAPI_REQUEST_TYPE, HTTPAPI_REQUEST_TYPE_VALUES); IMPLEMENT_UMOCK_C_ENUM_TYPE(HTTPAPI_REQUEST_TYPE, HTTPAPI_REQUEST_TYPE_VALUES); @@ -129,9 +133,8 @@ static void on_umock_c_error(UMOCK_C_ERROR_CODE error_code) } static BUFFER_HANDLE testValidBufferHandle; /*assigned in TEST_SUITE_INITIALIZE*/ -static unsigned int httpResponse; /*used as out parameter in every call to Blob_....*/ -static const unsigned int TwoHundred = 200; -static const unsigned int FourHundredFour = 404; +static const unsigned int TwoHundred = HTTP_OK; +static const unsigned int FourHundredFour = HTTP_NOT_FOUND; // Allocate test content during initial test setup only. This buffer is very large, // which means significant performance degradation on Valgrind tests if we were to @@ -337,6 +340,7 @@ TEST_FUNCTION_CLEANUP(Cleanup) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_with_NULL_SasUri_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; ///act BLOB_RESULT result = Blob_UploadMultipleBlocksFromSasUri(NULL, FileUpload_GetData_Callback, &context, &httpResponse, testValidBufferHandle, NULL, NULL, NULL); @@ -351,6 +355,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_with_NULL_SasUri_fails) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_with_NULL_getDataCallBack_and_non_NULL_context_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; ///act BLOB_RESULT result = Blob_UploadMultipleBlocksFromSasUri(TEST_VALID_SASURI_1, NULL, &context, &httpResponse, testValidBufferHandle, NULL, NULL, NULL); @@ -365,6 +370,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_with_NULL_getDataCallBack_and_ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_succeeds_when_HTTP_status_code_is_404) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; size_t size = 1; context.size = size; @@ -406,7 +412,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_succeeds_when_HTTP_status_code STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)) /*this is getting the relative path as const char* */ .IgnoreArgument_handle(); - int responseCode = 404; /*not found*/ + int responseCode = HTTP_NOT_FOUND; /*not found*/ STRICT_EXPECTED_CALL(HTTPAPIEX_ExecuteRequest(IGNORED_PTR_ARG, HTTPAPI_REQUEST_PUT, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, &httpResponse, NULL, testValidBufferHandle)) .IgnoreArgument_handle() .IgnoreArgument_relativePath() @@ -428,6 +434,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_succeeds_when_HTTP_status_code ///assert ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_NOT_FOUND, httpResponse); ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls()); ///cleanup @@ -436,6 +443,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_succeeds_when_HTTP_status_code TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_HTTPAPIEX_ExecuteRequest_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; size_t size = 1; context.size = size; @@ -477,7 +485,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_HTTPAPIEX_ExecuteRe STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)) /*this is getting the relative path as const char* */ .IgnoreArgument_handle(); - int responseCode = 200; /*ok*/ + int responseCode = HTTP_OK; /*ok*/ STRICT_EXPECTED_CALL(HTTPAPIEX_ExecuteRequest(IGNORED_PTR_ARG, HTTPAPI_REQUEST_PUT, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, &httpResponse, NULL, testValidBufferHandle)) .IgnoreArgument_handle() .IgnoreArgument_relativePath() @@ -509,6 +517,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_HTTPAPIEX_ExecuteRe TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_BUFFER_create_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; context.size = 1; context.source = &c; @@ -540,6 +549,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_BUFFER_create_fails TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_HTTPAPIEX_Create_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; context.size = 1; context.source = &c; @@ -567,6 +577,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_HTTPAPIEX_Create_fa TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_malloc_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; context.size = 1; context.source = &c; @@ -591,6 +602,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_fails_when_malloc_fails) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_SasUri_is_wrong_fails_1) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; context.size = 1; context.source = &c; @@ -611,6 +623,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_SasUri_is_wrong_fails_1) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_SasUri_is_wrong_fails_2) { ///arrange + unsigned int httpResponse = HTTP_OK; unsigned char c = '3'; context.size = 1; context.source = &c; @@ -630,6 +643,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_SasUri_is_wrong_fails_2) static void Blob_UploadMultipleBlocksFromSasUri_various_sizes_happy_path_Impl(HTTP_PROXY_OPTIONS *proxyOptions, const char * networkInterface, const char* certificate, size_t* blockSizesToTest, size_t numberBlockSizesToTest) { + unsigned int httpResponse = HTTP_OK; for (size_t iSize = 0; iSize < numberBlockSizesToTest; iSize++) { umock_c_reset_all_calls(); @@ -751,6 +765,7 @@ static void Blob_UploadMultipleBlocksFromSasUri_various_sizes_happy_path_Impl(HT ///assert ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls()); ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); } @@ -792,6 +807,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_various_sizes_happy_path) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_unhappy_paths) { + unsigned int httpResponse = HTTP_OK; (void)umock_c_negative_tests_init(); umock_c_reset_all_calls(); @@ -916,6 +932,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_unhappy_paths) ///assert ASSERT_ARE_NOT_EQUAL(BLOB_RESULT, BLOB_OK, result, temp_str); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); } } @@ -924,8 +941,9 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_unhappy_paths) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_with_certificate_and_network_interface_unhappy_paths) { - (void)umock_c_negative_tests_init(); + unsigned int httpResponse = HTTP_OK; + (void)umock_c_negative_tests_init(); umock_c_reset_all_calls(); ///arrange @@ -1052,6 +1070,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_with_certificate_and_netw ///assert ASSERT_ARE_NOT_EQUAL(BLOB_RESULT, BLOB_OK, result, temp_str); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); } } @@ -1061,6 +1080,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_64MB_with_certificate_and_netw TEST_FUNCTION(Blob_UploadFromSasUri_when_http_code_is_404_it_immediately_succeeds) { ///arrange + unsigned int httpResponse = HTTP_OK; umock_c_reset_all_calls(); memset(testUploadToBlobContent, '3', testUploadToBlobContentMaxSize); @@ -1130,11 +1150,13 @@ TEST_FUNCTION(Blob_UploadFromSasUri_when_http_code_is_404_it_immediately_succeed ///assert ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls()); ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_NOT_FOUND, httpResponse); } TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_too_big_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = BLOCK_SIZE + 1; @@ -1147,6 +1169,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_too_big_fails) ///assert ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_INVALID_ARG, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); ///cleanup gballoc_free(fakeContext.fakeData); @@ -1155,6 +1178,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_too_big_fails) TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_is_4MB_succeeds) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = BLOCK_SIZE; @@ -1167,6 +1191,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_is_4MB_succeeds ///assert ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); ///cleanup gballoc_free(fakeContext.fakeData); @@ -1175,6 +1200,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockSize_is_4MB_succeeds TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_maximum_succeeds) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = 1; @@ -1187,6 +1213,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_maximum_suc ///assert ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); ///cleanup gballoc_free(fakeContext.fakeData); @@ -1195,6 +1222,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_maximum_suc TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_one_over_maximum_fails) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = 1; @@ -1207,6 +1235,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_one_over_ma ///assert ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_INVALID_ARG, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); ///cleanup gballoc_free(fakeContext.fakeData); @@ -1215,6 +1244,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_when_blockCount_is_one_over_ma TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_returns_BLOB_ABORTED_when_callback_aborts_immediately) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = 1; @@ -1235,6 +1265,7 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_returns_BLOB_ABORTED_when_call TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_returns_BLOB_ABORTED_when_callback_aborts_after_5_blocks) { ///arrange + unsigned int httpResponse = HTTP_OK; BLOB_UPLOAD_CONTEXT_FAKE fakeContext; fakeContext.blockSent = 0; fakeContext.blockSize = 1; @@ -1252,4 +1283,26 @@ TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_returns_BLOB_ABORTED_when_call gballoc_free(fakeContext.fakeData); } +TEST_FUNCTION(Blob_UploadMultipleBlocksFromSasUri_with_empty_payload) +{ + ///arrange + unsigned int httpResponse = HTTP_OK; + BLOB_UPLOAD_CONTEXT_FAKE fakeContext; + fakeContext.blockSent = 0; + fakeContext.blockSize = 1; + fakeContext.blocksCount = 0; + fakeContext.fakeData = NULL; + fakeContext.abortOnBlockNumber = 5; + + ///act + BLOB_RESULT result = Blob_UploadMultipleBlocksFromSasUri("https://h.h/something?a=b", FileUpload_GetFakeData_Callback, &fakeContext, &httpResponse, testValidBufferHandle, NULL, NULL, NULL); + + ///assert + ASSERT_ARE_EQUAL(BLOB_RESULT, BLOB_OK, result); + ASSERT_ARE_EQUAL(int, HTTP_OK, httpResponse); + + ///cleanup + gballoc_free(fakeContext.fakeData); +} + END_TEST_SUITE(blob_ut); diff --git a/iothub_client/tests/iothubclient_uploadtoblob_e2e/iothubclient_uploadtoblob_e2e.c b/iothub_client/tests/iothubclient_uploadtoblob_e2e/iothubclient_uploadtoblob_e2e.c index 69eb9011bb..eadbad7769 100644 --- a/iothub_client/tests/iothubclient_uploadtoblob_e2e/iothubclient_uploadtoblob_e2e.c +++ b/iothub_client/tests/iothubclient_uploadtoblob_e2e/iothubclient_uploadtoblob_e2e.c @@ -251,32 +251,34 @@ static void e2e_uploadtoblob_test(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol, IOT deviceToUse = IoTHubAccount_GetSASDevice(g_iothubAcctInfo); } ASSERT_IS_NOT_NULL(deviceToUse); - - IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, protocol); - ASSERT_IS_NOT_NULL(iotHubClientHandle, "Could not invoke IoTHubClient_CreateFromConnectionString"); - - if (accountAuthMethod == IOTHUB_ACCOUNT_AUTH_X509) + if (deviceToUse != NULL) { - 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"); - } + IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, protocol); + ASSERT_IS_NOT_NULL(iotHubClientHandle, "Could not invoke IoTHubClient_CreateFromConnectionString"); + + if (accountAuthMethod == 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"); + } #if defined(__APPLE__) || defined(AZIOT_LINUX) - bool curl_verbose = true; - result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_CURL_VERBOSE, &curl_verbose); - ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set curl_verbose opt"); + bool curl_verbose = true; + result = IoTHubClient_SetOption(iotHubClientHandle, OPTION_CURL_VERBOSE, &curl_verbose); + ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not set curl_verbose opt"); #endif - - g_uploadToBlobStatus = UPLOADTOBLOB_CALLBACK_PENDING; - result = IoTHubClient_UploadToBlobAsync(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE, UPLOADTOBLOB_E2E_TEST_DATA, strlen((const char*)UPLOADTOBLOB_E2E_TEST_DATA), uploadToBlobCallback, &g_uploadToBlobStatus); - ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not IoTHubClient_UploadToBlobAsync"); - poll_for_upload_completion(&g_uploadToBlobStatus); - check_upload_result(g_uploadToBlobStatus); + g_uploadToBlobStatus = UPLOADTOBLOB_CALLBACK_PENDING; + result = IoTHubClient_UploadToBlobAsync(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE, UPLOADTOBLOB_E2E_TEST_DATA, strlen((const char*)UPLOADTOBLOB_E2E_TEST_DATA), uploadToBlobCallback, &g_uploadToBlobStatus); + ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not IoTHubClient_UploadToBlobAsync"); - IoTHubClient_Destroy(iotHubClientHandle); + poll_for_upload_completion(&g_uploadToBlobStatus); + check_upload_result(g_uploadToBlobStatus); + + IoTHubClient_Destroy(iotHubClientHandle); + } } // Callback invoked by the SDK to retrieve data. @@ -353,32 +355,34 @@ static void uploadToBlobGetData(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigne (void)uploadToBlobGetDataEx(result, data, size, context); } -static void e2e_uploadtoblob_multiblock_test(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol, bool useExMethod, bool causeAbort) +static void e2e_uploadtoblob_multiblock_test(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol, bool useExMethod, bool causeAbort, bool noData) { IOTHUB_CLIENT_RESULT result; IOTHUB_PROVISIONED_DEVICE* deviceToUse = IoTHubAccount_GetSASDevice(g_iothubAcctInfo); ASSERT_IS_NOT_NULL(deviceToUse); - - IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, protocol); - ASSERT_IS_NOT_NULL(iotHubClientHandle, "Could not invoke IoTHubClient_CreateFromConnectionString"); - - uploadToBlobCauseAbort = causeAbort; - g_uploadToBlobStatus = UPLOADTOBLOB_CALLBACK_PENDING; - uploadBlobNumber = 0; - if (useExMethod) + if (deviceToUse != NULL) { - result = IoTHubClient_UploadMultipleBlocksToBlobAsyncEx(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE_MULTIBLOCK, uploadToBlobGetDataEx, &g_uploadToBlobStatus); - } - else - { - result = IoTHubClient_UploadMultipleBlocksToBlobAsync(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE_MULTIBLOCK, uploadToBlobGetData, &g_uploadToBlobStatus); - } - ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not IoTHubClient_UploadToBlobAsync"); + IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(deviceToUse->connectionString, protocol); + ASSERT_IS_NOT_NULL(iotHubClientHandle, "Could not invoke IoTHubClient_CreateFromConnectionString"); + + uploadToBlobCauseAbort = causeAbort; + g_uploadToBlobStatus = UPLOADTOBLOB_CALLBACK_PENDING; + uploadBlobNumber = noData ? 1 : 0; + if (useExMethod) + { + result = IoTHubClient_UploadMultipleBlocksToBlobAsyncEx(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE_MULTIBLOCK, uploadToBlobGetDataEx, &g_uploadToBlobStatus); + } + else + { + result = IoTHubClient_UploadMultipleBlocksToBlobAsync(iotHubClientHandle, TEST_UPLOADTOBLOB_DESTINATION_FILE_MULTIBLOCK, uploadToBlobGetData, &g_uploadToBlobStatus); + } + ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result, "Could not IoTHubClient_UploadToBlobAsync"); - poll_for_upload_completion(&g_uploadToBlobStatus); - check_upload_result(g_uploadToBlobStatus); + poll_for_upload_completion(&g_uploadToBlobStatus); + check_upload_result(g_uploadToBlobStatus); - IoTHubClient_Destroy(iotHubClientHandle); + IoTHubClient_Destroy(iotHubClientHandle); + } } // uploadToBlobTestEarlyClose is the callback passed to SDK that will retrieve uploadToBlob data. For this test, however, we simply @@ -394,18 +398,21 @@ static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT uploadToBlobBlockForALongTime(I ASSERT_ARE_EQUAL(int, (int)LOCK_OK, (int)Lock(updateBlobTestLock)); bool* slowThreadContext = context; - ASSERT_IS_FALSE(*slowThreadContext); - (void)Unlock(updateBlobTestLock); - - LogInfo("Slow worker thread created, context=%p. It will sleep %d milliseconds before return", context, TEST_SLEEP_SLOW_WORKER_THREAD); - ThreadAPI_Sleep(TEST_SLEEP_SLOW_WORKER_THREAD); - LogInfo("Slow worker thread sleep finished, context=%p. Returning to caller.", context); + ASSERT_IS_NOT_NULL(slowThreadContext); + if (slowThreadContext != NULL) + { + ASSERT_IS_FALSE(*slowThreadContext); + (void)Unlock(updateBlobTestLock); - // Set the context to indicate to test that we have actually executed this thread. - ASSERT_ARE_EQUAL(int, (int)LOCK_OK, (int)Lock(updateBlobTestLock)); - *slowThreadContext = true; - (void)Unlock(updateBlobTestLock); + LogInfo("Slow worker thread created, context=%p. It will sleep %d milliseconds before return", context, TEST_SLEEP_SLOW_WORKER_THREAD); + ThreadAPI_Sleep(TEST_SLEEP_SLOW_WORKER_THREAD); + LogInfo("Slow worker thread sleep finished, context=%p. Returning to caller.", context); + // Set the context to indicate to test that we have actually executed this thread. + ASSERT_ARE_EQUAL(int, (int)LOCK_OK, (int)Lock(updateBlobTestLock)); + *slowThreadContext = true; + (void)Unlock(updateBlobTestLock); + } // It doesn't matter for the test what code we return, as the worker thread can continue to execute indefinitely as the Destroy() call will block. // Since we're testing the SDK close here and not the Hub, just terminate the request. return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT; @@ -492,7 +499,12 @@ TEST_FUNCTION_INITIALIZE(TestMethodInitialize) #ifdef TEST_MQTT TEST_FUNCTION(IoTHub_MQTT_UploadMultipleBlocksToBlobEx) { - e2e_uploadtoblob_multiblock_test(MQTT_Protocol, true, false); + e2e_uploadtoblob_multiblock_test(MQTT_Protocol, true, false, false); +} + +TEST_FUNCTION(IoTHub_MQTT_UploadMultipleBlocksNoData) +{ + e2e_uploadtoblob_multiblock_test(MQTT_Protocol, true, false, true); } #ifndef __APPLE__