Float upload to blob errors in convenience layer (gh issue #2569) (#2593)

* Float upload to blob errors in convenience layer (gh issue #2569)

* Add unit tests

* Address CR comments
This commit is contained in:
Ewerton Scaboro da Silva 2024-03-08 22:43:34 -08:00 коммит произвёл GitHub
Родитель 09d4e9ca46
Коммит e0c0c2c3c1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
2 изменённых файлов: 178 добавлений и 16 удалений

Просмотреть файл

@ -73,6 +73,7 @@ typedef struct UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA_TAG
{
IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback;
IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx;
bool isCallbackInvokedWithError;
}UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA;
typedef struct INVOKE_METHOD_SAVED_DATA_TAG
@ -2419,6 +2420,44 @@ IOTHUB_CLIENT_RESULT IoTHubClientCore_UploadToBlobAsync(IOTHUB_CLIENT_CORE_HANDL
return result;
}
// This callback wrapper allows iothub_client_core to track if the upload to blob callback has
// been fired with a FILE_UPLOAD_ERROR already. That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlob
// will fire the user callback with error only in specific situations (i.e., uploading to Azure Storage).
// All other errors within IoTHubClientCore_LL_UploadMultipleBlocksToBlob do not cause the callback to be invoked,
// only signaling the upload error through the return code. However, the return code is not exposed to the user application
// by iothub_client_core. So iothub_client_core must invoke the user callback so the user can get a feedback that
// an upload has failed if IoTHubClientCore_LL_UploadMultipleBlocksToBlob has not done so.
static void uploadToBlobMultiblockCallbackWrapper(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)context;
if (result == FILE_UPLOAD_ERROR)
{
threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError = true;
}
threadInfo->uploadBlobMultiblockSavedData.getDataCallback(result, data, size, threadInfo->context);
}
// This callback wrapper allows iothub_client_core to track if the upload to blob callback has
// been fired with a FILE_UPLOAD_ERROR already. That is because IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx
// will fire the user callback with error only in specific situations (i.e., uploading to Azure Storage).
// All other errors within IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx do not cause the callback to be invoked,
// only signaling the upload error through the return code. However, the return code is not exposed to the user application
// by iothub_client_core. So iothub_client_core must invoke the user callback so the user can get a feedback that
// an upload has failed if IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx has not done so.
static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT uploadToBlobMultiblockCallbackWrapperEx(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)context;
if (result == FILE_UPLOAD_ERROR)
{
threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError = true;
}
return threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(result, data, size, threadInfo->context);
}
static int uploadMultipleBlock_thread(void* data)
{
HTTPWORKER_THREAD_INFO* threadInfo = (HTTPWORKER_THREAD_INFO*)data;
@ -2430,11 +2469,21 @@ static int uploadMultipleBlock_thread(void* data)
if (threadInfo->uploadBlobMultiblockSavedData.getDataCallback != NULL)
{
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlob(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallback, threadInfo->context);
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlob(llHandle, threadInfo->destinationFileName, uploadToBlobMultiblockCallbackWrapper, threadInfo);
if (result != IOTHUB_CLIENT_OK && !threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError)
{
threadInfo->uploadBlobMultiblockSavedData.getDataCallback(FILE_UPLOAD_ERROR, NULL, NULL, threadInfo->context);
}
}
else
{
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx, threadInfo->context);
result = IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(llHandle, threadInfo->destinationFileName, uploadToBlobMultiblockCallbackWrapperEx, threadInfo);
if (result != IOTHUB_CLIENT_OK && !threadInfo->uploadBlobMultiblockSavedData.isCallbackInvokedWithError)
{
(void)threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx(FILE_UPLOAD_ERROR, NULL, NULL, threadInfo->context);
}
}
(void)markThreadReadyToBeGarbageCollected(threadInfo);

Просмотреть файл

@ -118,20 +118,30 @@ static int my_DeviceMethodCallback_Impl(const char* method_name, const unsigned
return 200;
}
typedef struct TEST_GETDATA_CALLBACK_ARGS_TAG
{
IOTHUB_CLIENT_FILE_UPLOAD_RESULT last_result;
unsigned char const ** last_data;
size_t* last_size;
int invokation_counter;
} TEST_GETDATA_CALLBACK_ARGS;
static void my_FileUpload_GetData_Callback(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
(void)data;
(void)size;
(void)context;
(void)result;
TEST_GETDATA_CALLBACK_ARGS* saved_args = (TEST_GETDATA_CALLBACK_ARGS*)context;
saved_args->last_result = result;
saved_args->last_data = data;
saved_args->last_size = size;
saved_args->invokation_counter++;
}
static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT my_FileUpload_GetData_CallbackEx(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context)
{
(void)data;
(void)size;
(void)context;
(void)result;
TEST_GETDATA_CALLBACK_ARGS* saved_args = (TEST_GETDATA_CALLBACK_ARGS*)context;
saved_args->last_result = result;
saved_args->last_data = data;
saved_args->last_size = size;
saved_args->invokation_counter++;
return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK;
}
@ -167,6 +177,9 @@ IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES);
TEST_DEFINE_ENUM_TYPE(IOTHUB_CLIENT_RETRY_POLICY, IOTHUB_CLIENT_RETRY_POLICY_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_CLIENT_RETRY_POLICY, IOTHUB_CLIENT_RETRY_POLICY_VALUES);
TEST_DEFINE_ENUM_TYPE(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES);
// Overloading operators for Micromock
static TEST_MUTEX_HANDLE test_serialize_mutex;
@ -3216,7 +3229,7 @@ TEST_FUNCTION(IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_handle
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = NULL;
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
///act
@ -3234,7 +3247,7 @@ TEST_FUNCTION(IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_destin
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
///act
@ -3270,7 +3283,7 @@ static void IoTHubClientCore_UploadMultipleBlocksToBlobAsync_succeeds_Impl(bool
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
set_expected_calls_for_allocateUploadToBlob();
@ -3345,7 +3358,7 @@ static void IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_ThreadAP
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
set_expected_calls_for_allocateUploadToBlob();
@ -3395,7 +3408,7 @@ static void IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_strcpy_f
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
@ -3438,7 +3451,7 @@ static void IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_malloc_f
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
int context = 1;
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG))
@ -3474,6 +3487,106 @@ TEST_FUNCTION(IoTHubClientCore_UploadMultipleBlocksToBlobAsyncEx_fails_when_mall
{
IoTHubClientCore_UploadMultipleBlocksToBlobAsync_fails_when_malloc_fails_Impl(true);
}
TEST_FUNCTION(IoTHubClientCore_UploadMultipleBlocksToBlobAsync_callback_invoked_on_error_return_if_not_already)
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
set_expected_calls_for_allocateUploadToBlob();
STRICT_EXPECTED_CALL(ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(singlylinkedlist_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)); /*this is adding HTTPWORKER_THREAD_INFO to the list of HTTPWORKER_THREAD_INFO's to be cleaned*/
STRICT_EXPECTED_CALL(ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(get_time(IGNORED_NUM_ARG)).CallCannotFail();
STRICT_EXPECTED_CALL(IoTHubClientCore_LL_UploadMultipleBlocksToBlob(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
.SetReturn(IOTHUB_CLIENT_ERROR);
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(ThreadAPI_Exit(0));
///act
IOTHUB_CLIENT_RESULT result = IoTHubClientCore_UploadMultipleBlocksToBlobAsync(iothub_handle, "someFileName.txt", my_FileUpload_GetData_Callback, NULL, &context);
g_thread_func(g_thread_func_arg); /*this is the thread uploading function*/
///assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
ASSERT_ARE_EQUAL(int, 1, context.invokation_counter);
ASSERT_ARE_EQUAL(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, FILE_UPLOAD_ERROR, context.last_result);
ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result);
///cleanup
umock_c_reset_all_calls();
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
EXPECTED_CALL(ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
EXPECTED_CALL(singlylinkedlist_get_head_item(TEST_SLL_HANDLE))
.SetReturn(TEST_LIST_HANDLE);
setup_gargageCollection(my_malloc_items[2], true);
setup_IothubClient_Destroy_after_garbage_collection();
IoTHubClientCore_Destroy(iothub_handle);
}
TEST_FUNCTION(IoTHubClientCore_UploadMultipleBlocksToBlobAsync_callbackEx_invoked_on_error_return_if_not_already)
{
///arrange
IOTHUB_CLIENT_CORE_HANDLE iothub_handle = IoTHubClientCore_Create(TEST_CLIENT_CONFIG);
TEST_GETDATA_CALLBACK_ARGS context = { 0 };
umock_c_reset_all_calls();
set_expected_calls_for_allocateUploadToBlob();
STRICT_EXPECTED_CALL(ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(singlylinkedlist_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)); /*this is adding HTTPWORKER_THREAD_INFO to the list of HTTPWORKER_THREAD_INFO's to be cleaned*/
STRICT_EXPECTED_CALL(ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(get_time(IGNORED_NUM_ARG)).CallCannotFail();
STRICT_EXPECTED_CALL(IoTHubClientCore_LL_UploadMultipleBlocksToBlobEx(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
.SetReturn(IOTHUB_CLIENT_ERROR);
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(ThreadAPI_Exit(0));
///act
IOTHUB_CLIENT_RESULT result = IoTHubClientCore_UploadMultipleBlocksToBlobAsync(iothub_handle, "someFileName.txt", NULL, my_FileUpload_GetData_CallbackEx, &context);
g_thread_func(g_thread_func_arg); /*this is the thread uploading function*/
///assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
ASSERT_ARE_EQUAL(int, 1, context.invokation_counter);
ASSERT_ARE_EQUAL(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, FILE_UPLOAD_ERROR, context.last_result);
ASSERT_ARE_EQUAL(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK, result);
///cleanup
umock_c_reset_all_calls();
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Unlock(IGNORED_PTR_ARG));
EXPECTED_CALL(ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG));
STRICT_EXPECTED_CALL(Lock(IGNORED_PTR_ARG));
EXPECTED_CALL(singlylinkedlist_get_head_item(TEST_SLL_HANDLE))
.SetReturn(TEST_LIST_HANDLE);
setup_gargageCollection(my_malloc_items[2], true);
setup_IothubClient_Destroy_after_garbage_collection();
IoTHubClientCore_Destroy(iothub_handle);
}
#endif
/* SYNC DEVICE METHOD */