From 9f41af4701985970c6a5ed5e863007ed09374c75 Mon Sep 17 00:00:00 2001 From: Han Zhu Date: Mon, 26 Jan 2015 15:40:55 +0800 Subject: [PATCH] Azure Storage Client Library for C++ v0.5.0 --- .gitignore | 7 + BreakingChanges.txt | 13 + Changelog.txt | 39 +- Microsoft.WindowsAzure.Storage.autopkg | 2 +- Microsoft.WindowsAzure.Storage/CMakeLists.txt | 2 +- ...icrosoft.WindowsAzure.Storage.v120.vcxproj | 9 +- ....WindowsAzure.Storage.v120.vcxproj.filters | 3 + .../Microsoft.WindowsAzure.Storage.vcxproj | 9 +- ...osoft.WindowsAzure.Storage.vcxproj.filters | 3 + .../includes/was/blob.h | 67 +- .../includes/was/core.h | 52 +- .../includes/was/error_code_strings.h | 634 ++++++++++++++++++ .../includes/was/queue.h | 14 +- .../includes/wascore/blobstreams.h | 8 +- .../includes/wascore/constants.h | 19 +- .../includes/wascore/executor.h | 46 +- .../includes/wascore/protocol.h | 2 +- .../includes/wascore/protocol_xml.h | 7 + .../includes/wascore/resources.h | 1 + .../includes/wascore/util.h | 2 +- .../includes/wascore/xmlhelpers.h | 7 +- .../src/CMakeLists.txt | 2 +- .../src/blob_request_factory.cpp | 8 +- .../src/cloud_blob.cpp | 1 + .../src/cloud_blob_container.cpp | 1 + .../src/cloud_blob_istreambuf.cpp | 3 +- .../src/cloud_blob_ostreambuf.cpp | 3 +- .../src/cloud_block_blob.cpp | 2 +- .../src/cloud_page_blob.cpp | 20 +- .../src/cloud_queue.cpp | 24 +- .../src/cloud_queue_client.cpp | 10 +- .../src/cloud_table.cpp | 20 +- .../src/cloud_table_client.cpp | 10 +- .../src/protocol_json.cpp | 42 +- .../src/protocol_xml.cpp | 21 +- .../src/response_parsers.cpp | 2 +- Microsoft.WindowsAzure.Storage/src/util.cpp | 16 +- .../src/xmlhelpers.cpp | 19 - .../tests/CMakeLists.txt | 10 +- ...indowsAzure.Storage.UnitTests.v120.vcxproj | 7 +- ...ure.Storage.UnitTests.v120.vcxproj.filters | 21 +- ...oft.WindowsAzure.Storage.UnitTests.vcxproj | 7 +- ...owsAzure.Storage.UnitTests.vcxproj.filters | 23 +- .../tests/blob_lease_test.cpp | 45 +- .../tests/blob_streams_test.cpp | 279 +++++++- .../tests/blob_test_base.h | 6 +- .../tests/cloud_blob_client_test.cpp | 177 ++--- .../tests/cloud_blob_container_test.cpp | 2 +- .../tests/cloud_blob_directory_test.cpp | 2 +- .../tests/cloud_blob_test.cpp | 10 +- .../tests/cloud_block_blob_test.cpp | 53 +- .../tests/cloud_page_blob_test.cpp | 55 +- .../tests/cloud_queue_client_test.cpp | 14 +- .../tests/cloud_queue_test.cpp | 44 +- .../tests/cloud_storage_account_test.cpp | 51 +- .../tests/cloud_table_client_test.cpp | 14 +- .../tests/cloud_table_test.cpp | 118 ++-- .../tests/executor_test.cpp | 34 +- .../tests/queue_test_base.cpp | 45 ++ .../tests/queue_test_base.h | 44 ++ .../tests/retry_policy_test.cpp | 8 +- .../tests/storage_exception_test.cpp | 68 ++ .../tests/table_test_base.cpp | 62 ++ .../tests/table_test_base.h | 44 ++ .../tests/test_base.cpp | 105 ++- .../tests/test_base.h | 17 + Microsoft.WindowsAzure.Storage/version.rc | Bin 4548 -> 4548 bytes README.md | 2 +- 68 files changed, 2049 insertions(+), 468 deletions(-) create mode 100644 Microsoft.WindowsAzure.Storage/includes/was/error_code_strings.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/queue_test_base.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/queue_test_base.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/storage_exception_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/table_test_base.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/table_test_base.h diff --git a/.gitignore b/.gitignore index 6792278e..bce5a607 100644 --- a/.gitignore +++ b/.gitignore @@ -163,6 +163,9 @@ App_Data/*.ldf # Microsoft Fakes FakesAssemblies/ +# Nuget packages +*.nupkg + # ========================= # Windows detritus # ========================= @@ -176,3 +179,7 @@ Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ + +# build folders under Linux +Microsoft.WindowsAzure.Storage/build.*/ + diff --git a/BreakingChanges.txt b/BreakingChanges.txt index 7ff5b2c3..2a739fa9 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,6 +1,19 @@ Azure Storage Client Library for C++ History of Breaking Changes +Breaking Changes in v0.5: +- Added parameter sequence_number to the following functions: + - cloud_page_blob::create_async + - cloud_page_blob::open_write_async + - cloud_page_blob::upload_from_stream_async + - cloud_page_blob::upload_from_file_async + - cloud_page_blob::create + - cloud_page_blob::open_write + - cloud_page_blob::upload_from_stream + - cloud_page_blob::upload_from_file +- The client checks blob lease times and throws std::invalid_argument for invalid lease times. Previously, this would have been checked on the service + and azure::storage::storage_exception would have been thrown. + Breaking Changes in v0.4: - Upgraded Casablanca dependency to 2.3.0 - Changed the exception type in some cases if you upload a blob from a seekable stream but the stream is too short for the desired blob length. diff --git a/Changelog.txt b/Changelog.txt index 466082e0..dae8c4cb 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,6 +1,31 @@ Azure Storage Client Library for C++ History of Changes +Changes in v0.5: +- Default REST API version is 2014-02-14 +- Added parameter sequence_number to the following functions: + - cloud_page_blob::create_async + - cloud_page_blob::open_write_async + - cloud_page_blob::upload_from_stream_async + - cloud_page_blob::upload_from_file_async + - cloud_page_blob::create + - cloud_page_blob::open_write + - cloud_page_blob::upload_from_stream + - cloud_page_blob::upload_from_file +- The client checks blob lease times and throws std::invalid_argument for invalid lease times. Previously, this would have been checked on the service and azure::storage::storage_exception would have been thrown. +- Improvements on error handling: + - azure::storage::storage_exception now has inner_exception populated with the actual underlying exception thrown + - azure::storage::storage_exception now has more information about errors on storage service side + - was/error_code_strings.h is added including messages for all possible error codes from storage service +- Improvements on Unit Tests: + - All test cases now print out test name and client reqeust ID + - Added new cases for containers listing and continuation token + - Unit Test configuraiton file is copied to output directory via CMake now, so no need to copy it manually under Linux +- Several fixes for bugs: + - Cannot generate SAS tokens for tables with upper-case letters in table name + - cloud_blob_istreambuf and cloud_blob_ostreambuf is set to a wrong position after seek() method is called. + - Content-MD5 header is incorrectly set when sending Put Page requests with page-write set to clear + Changes in v0.4: - Now supports building on Linux - Added http_buffer_size request option to control the internal buffer size used in the HTTP layer when downloading data. The default size is 64KB. @@ -10,9 +35,9 @@ Changes in v0.4: - Fixed incorrect initialization of some members in the cloud_blob_container_properties and retry_info classes. - Added a default of 24 days for a maximum execution time for an operation - Changed the exception type if you upload a blob from a seekable stream but the stream is too short for the desired blob length. - - Used to throw azure::storage::storage_exception in some cases, now throws std::invalid_argument always. - - Now checks for this case explicitly before the upload is started. - - Non-seekable streams still throw std::invalid_argument as before. + - Used to throw azure::storage::storage_exception in some cases, now throws std::invalid_argument always. + - Now checks for this case explicitly before the upload is started. + - Non-seekable streams still throw std::invalid_argument as before. - Fixed several bugs, including incorrect uses of std::move and compile-time differences between Visual Studio and g++. - Added client request ID into log lines. - Upgraded Casablanca dependency to 2.3.0 @@ -40,10 +65,10 @@ Changes in v0.3: - Removed the constness from the return values of filter_string and select_columns functions in table_query class. - Changed many functions to accept pass-by-value arguments to support move semantics. - Changed many functions to accept pass-by-reference arguments or return references as a performance improvement. -- Made some constructors explicit to avoid implicit conversions. This affects these classes: - canonicalizer, shared_key_blob_queue_canonicalizer, shared_key_lite_blob_queue_canonicalizer, shared_key_table_canonicalizer, shared_key_lite_table_canonicalizer, - sas_authentication_handler, shared_key_authentication_handler, continuation_token, retry_info, retry_policy, cloud_queue_message, cloud_queue_client, - basic_common_retry_policy, cloud_client +- Made some constructors explicit to avoid implicit conversions. This affects these classes: + canonicalizer, shared_key_blob_queue_canonicalizer, shared_key_lite_blob_queue_canonicalizer, shared_key_table_canonicalizer, + shared_key_lite_table_canonicalizer, sas_authentication_handler, shared_key_authentication_handler, continuation_token, + retry_info, retry_policy, cloud_queue_message, cloud_queue_client, basic_common_retry_policy, cloud_client - Major performance improvement when executing many requests in parallel. - Minor bug fixes and performance improvements. diff --git a/Microsoft.WindowsAzure.Storage.autopkg b/Microsoft.WindowsAzure.Storage.autopkg index aba167d9..4c37b81f 100644 --- a/Microsoft.WindowsAzure.Storage.autopkg +++ b/Microsoft.WindowsAzure.Storage.autopkg @@ -1,7 +1,7 @@ nuget { nuspec { id = wastorage; - version: 0.4.0-preview; + version: 0.5.0-preview; title: Microsoft Azure Storage Client Library for C++; authors: {Microsoft Corporation}; owners: {Microsoft Corporation}; diff --git a/Microsoft.WindowsAzure.Storage/CMakeLists.txt b/Microsoft.WindowsAzure.Storage/CMakeLists.txt index a00109a9..3f7e2d39 100644 --- a/Microsoft.WindowsAzure.Storage/CMakeLists.txt +++ b/Microsoft.WindowsAzure.Storage/CMakeLists.txt @@ -99,4 +99,4 @@ set(AZURESTORAGE_LIBRARIES ${AZURESTORAGE_LIBRARY} ${CASABLANCA_LIBRARIES} ${Boo include_directories(${AZURESTORAGE_INCLUDE_DIRS}) add_subdirectory(src) -#add_subdirectory(tests) +#add_subdirectory(tests) diff --git a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj index cccc279b..c7fcfda6 100644 --- a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj +++ b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj @@ -111,7 +111,7 @@ Windows true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -129,7 +129,7 @@ Windows true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -152,7 +152,7 @@ true true true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -175,7 +175,7 @@ true true true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -184,6 +184,7 @@ + diff --git a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj.filters b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj.filters index aae6c14d..5c61d0db 100644 --- a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj.filters +++ b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.v120.vcxproj.filters @@ -99,6 +99,9 @@ Header Files + + Header Files + diff --git a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj index 1bb32482..abc197dc 100644 --- a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj +++ b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj @@ -111,7 +111,7 @@ Windows true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -129,7 +129,7 @@ Windows true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -152,7 +152,7 @@ true true true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -175,7 +175,7 @@ true true true - rpcrt4.lib;xmllite.lib;bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + rpcrt4.lib;xmllite.lib;bcrypt.lib;%(AdditionalDependencies) @@ -184,6 +184,7 @@ + diff --git a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj.filters b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj.filters index aae6c14d..bb24bbe8 100644 --- a/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj.filters +++ b/Microsoft.WindowsAzure.Storage/Microsoft.WindowsAzure.Storage.vcxproj.filters @@ -78,6 +78,9 @@ Header Files + + Header Files + Header Files diff --git a/Microsoft.WindowsAzure.Storage/includes/was/blob.h b/Microsoft.WindowsAzure.Storage/includes/was/blob.h index 8beaf537..40a679fe 100644 --- a/Microsoft.WindowsAzure.Storage/includes/was/blob.h +++ b/Microsoft.WindowsAzure.Storage/includes/was/blob.h @@ -800,7 +800,10 @@ namespace azure { namespace storage { lease_break_period(const std::chrono::seconds& seconds) : m_seconds(seconds) { - utility::assert_in_bounds(U("seconds"), seconds, std::chrono::seconds::zero()); + if (seconds != std::chrono::seconds::max()) + { + utility::assert_in_bounds(U("seconds"), seconds, protocol::minimum_lease_break_period, protocol::maximum_lease_break_period); + } } /// @@ -870,6 +873,10 @@ namespace azure { namespace storage { lease_time(const std::chrono::seconds& seconds) : m_seconds(seconds) { + if (seconds.count() != -1) + { + utility::assert_in_bounds(U("seconds"), seconds, protocol::minimum_fixed_lease_duration, protocol::maximum_fixed_lease_duration); + } } /// @@ -1553,18 +1560,18 @@ namespace azure { namespace storage { } /// - /// Gets the block size for writing to a block blob. + /// Gets the minimum number of bytes to buffer when writing to a blob stream. /// - /// The size of a block, in bytes, ranging from between 16 KB and 4 MB inclusive. + /// The minimum number of bytes to buffer, ranging from between 16 KB and 4 MB inclusive. size_t stream_write_size_in_bytes() const { return m_stream_write_size; } /// - /// Sets the block size for writing to a block blob. + /// Sets the minimum number of bytes to buffer when writing to a blob stream. /// - /// The size of a block, in bytes, ranging from between 16 KB and 4 MB inclusive. + /// The minimum number of bytes to buffer, ranging from between 16 KB and 4 MB inclusive. void set_stream_write_size_in_bytes(size_t value) { utility::assert_in_bounds(U("value"), value, 16 * 1024, 4 * 1024 * 1024); @@ -4900,13 +4907,14 @@ namespace azure { namespace storage { /// Opens a stream for writing to a new page blob. /// /// The size of the write operation, in bytes. The size must be a multiple of 512. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A stream to be used for writing to the blob. - concurrency::streams::ostream open_write(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context) + concurrency::streams::ostream open_write(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - return open_write_async(size, condition, options, context).get(); + return open_write_async(size, sequence_number, condition, options, context).get(); } /// @@ -4934,18 +4942,19 @@ namespace azure { namespace storage { /// A object of type that represents the current operation. pplx::task open_write_async(utility::size64_t size) { - return open_write_async(size, access_condition(), blob_request_options(), operation_context()); + return open_write_async(size, 0, access_condition(), blob_request_options(), operation_context()); } /// /// Intitiates an asynchronous operation to open a stream for writing to a new page blob. /// /// The size of the write operation, in bytes. The size must be a multiple of 512. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A object of type that represents the current operation. - WASTORAGE_API pplx::task open_write_async(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context); + WASTORAGE_API pplx::task open_write_async(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context); /// /// Clears pages from a page blob. @@ -5147,12 +5156,13 @@ namespace azure { namespace storage { /// Uploads a stream to a page blob. /// /// The stream providing the blob content. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. - void upload_from_stream(concurrency::streams::istream source, const access_condition& condition, const blob_request_options& options, operation_context context) + void upload_from_stream(concurrency::streams::istream source, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - upload_from_stream_async(source, condition, options, context).wait(); + upload_from_stream_async(source, sequence_number, condition, options, context).wait(); } /// @@ -5170,12 +5180,13 @@ namespace azure { namespace storage { /// /// The stream providing the blob content. /// The number of bytes to write from the source stream at its current position. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. - void upload_from_stream(concurrency::streams::istream source, utility::size64_t length, const access_condition& condition, const blob_request_options& options, operation_context context) + void upload_from_stream(concurrency::streams::istream source, utility::size64_t length, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - upload_from_stream_async(source, length, condition, options, context).wait(); + upload_from_stream_async(source, length, sequence_number, condition, options, context).wait(); } /// @@ -5192,13 +5203,14 @@ namespace azure { namespace storage { /// Intitiates an asynchronous operation to upload a stream to a page blob. /// /// The stream providing the blob content. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A object that represents the current operation. - pplx::task upload_from_stream_async(concurrency::streams::istream source, const access_condition& condition, const blob_request_options& options, operation_context context) + pplx::task upload_from_stream_async(concurrency::streams::istream source, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - return upload_from_stream_async(source, std::numeric_limits::max(), condition, options, context); + return upload_from_stream_async(source, std::numeric_limits::max(), sequence_number, condition, options, context); } /// @@ -5209,7 +5221,7 @@ namespace azure { namespace storage { /// A object that represents the current operation. pplx::task upload_from_stream_async(concurrency::streams::istream source, utility::size64_t length) { - return upload_from_stream_async(source, length, access_condition(), blob_request_options(), operation_context()); + return upload_from_stream_async(source, length, 0, access_condition(), blob_request_options(), operation_context()); } /// @@ -5217,11 +5229,12 @@ namespace azure { namespace storage { /// /// The stream providing the blob content. /// The number of bytes to write from the source stream at its current position. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A object that represents the current operation. - WASTORAGE_API pplx::task upload_from_stream_async(concurrency::streams::istream source, utility::size64_t length, const access_condition& condition, const blob_request_options& options, operation_context context); + WASTORAGE_API pplx::task upload_from_stream_async(concurrency::streams::istream source, utility::size64_t length, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context); /// /// Uploads a file to a page blob. @@ -5236,12 +5249,13 @@ namespace azure { namespace storage { /// Uploads a file to a page blob. /// /// The file providing the blob content. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. - void upload_from_file(const utility::string_t &path, const access_condition& condition, const blob_request_options& options, operation_context context) + void upload_from_file(const utility::string_t &path, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - upload_from_file_async(path, condition, options, context).wait(); + upload_from_file_async(path, sequence_number, condition, options, context).wait(); } /// @@ -5251,18 +5265,19 @@ namespace azure { namespace storage { /// A object that represents the current operation. pplx::task upload_from_file_async(const utility::string_t &path) { - return upload_from_file_async(path, access_condition(), blob_request_options(), operation_context()); + return upload_from_file_async(path, 0, access_condition(), blob_request_options(), operation_context()); } /// /// Intitiates an asynchronous operation to upload a file to a page blob. /// /// The file providing the blob content. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A object that represents the current operation. - WASTORAGE_API pplx::task upload_from_file_async(const utility::string_t &path, const access_condition& condition, const blob_request_options& options, operation_context context); + WASTORAGE_API pplx::task upload_from_file_async(const utility::string_t &path, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context); /// /// Creates a page blob. @@ -5277,12 +5292,13 @@ namespace azure { namespace storage { /// Creates a page blob. /// /// The maximum size of the page blob, in bytes. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. - void create(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context) + void create(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { - create_async(size, condition, options, context).wait(); + create_async(size, sequence_number, condition, options, context).wait(); } /// @@ -5292,18 +5308,19 @@ namespace azure { namespace storage { /// A object that represents the current operation. pplx::task create_async(utility::size64_t size) { - return create_async(size, access_condition(), blob_request_options(), operation_context()); + return create_async(size, 0, access_condition(), blob_request_options(), operation_context()); } /// /// Intitiates an asynchronous operation to create a page blob. /// /// The maximum size of the page blob, in bytes. + /// A user-controlled number to track request sequence, whose value must be between 0 and 2^63 - 1. /// An object that represents the access condition for the operation. /// A object that specifies additional options for the request. /// An object that represents the context for the current operation. /// A object that represents the current operation. - WASTORAGE_API pplx::task create_async(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context); + WASTORAGE_API pplx::task create_async(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context); /// /// Resizes the page blob to the specified size. diff --git a/Microsoft.WindowsAzure.Storage/includes/was/core.h b/Microsoft.WindowsAzure.Storage/includes/was/core.h index c557bda8..b313ad77 100644 --- a/Microsoft.WindowsAzure.Storage/includes/was/core.h +++ b/Microsoft.WindowsAzure.Storage/includes/was/core.h @@ -211,6 +211,18 @@ namespace azure { namespace storage { { m_sas_token = m_sas_token.substr(1); } + + auto splitted_query = web::uri::split_query(m_sas_token); + if (!splitted_query.empty()) + { + splitted_query[protocol::uri_query_sas_api_version] = protocol::header_value_storage_version; + web::uri_builder builder; + for (const auto& kv : splitted_query) + { + builder.append_query(kv.first, kv.second, false); + } + m_sas_token_with_api_version = builder.query(); + } } /// @@ -224,7 +236,7 @@ namespace azure { namespace storage { if (is_sas() && !resource_uri.is_empty()) { - return web::http::uri_builder(resource_uri).append_query(m_sas_token).to_uri(); + return web::http::uri_builder(resource_uri).append_query(m_sas_token_with_api_version).to_uri(); } return resource_uri; @@ -287,6 +299,7 @@ namespace azure { namespace storage { private: utility::string_t m_sas_token; + utility::string_t m_sas_token_with_api_version; utility::string_t m_account_name; std::vector m_account_key; }; @@ -627,6 +640,17 @@ namespace azure { namespace storage { { } + /// + /// Initializes a new instance of the class. + /// + /// The error message. + /// The inner exception. + /// Indicates whether the request is retryable. + storage_exception(const std::string& message, std::exception_ptr inner_exception, bool retryable = true) + : std::runtime_error(message), m_retryable(retryable), m_inner_exception(inner_exception) + { + } + /// /// Initializes a new instance of the class. /// @@ -638,6 +662,18 @@ namespace azure { namespace storage { { } + /// + /// Initializes a new instance of the class. + /// + /// The error message. + /// The request result. + /// The inner exception. + /// Indicates whether the request is retryable. + storage_exception(const std::string& message, request_result result, std::exception_ptr inner_exception, bool retryable = true) + : std::runtime_error(message), m_result(std::move(result)), m_retryable(retryable), m_inner_exception(inner_exception) + { + } + /// /// Gets the request result. /// @@ -656,10 +692,24 @@ namespace azure { namespace storage { return m_retryable; } + /// + /// Gets the inner exception object that is the cause for the current . + /// + /// + /// An exception_ptr object. It points to the inner exception that is the cause for the current + /// . A null exception_ptr is returned if there is no inner exception + /// object. + /// + std::exception_ptr inner_exception() const + { + return m_inner_exception; + } + private: request_result m_result; bool m_retryable; + std::exception_ptr m_inner_exception; }; /// diff --git a/Microsoft.WindowsAzure.Storage/includes/was/error_code_strings.h b/Microsoft.WindowsAzure.Storage/includes/was/error_code_strings.h new file mode 100644 index 00000000..df5562eb --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/includes/was/error_code_strings.h @@ -0,0 +1,634 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#include "wascore/basic_types.h" + +namespace azure { namespace storage { namespace protocol { + +#pragma region storage_error_code_strings + + // This section provides error code strings that are common to all storage services. + + /// + /// The specified HTTP verb is not supported. + /// + const utility::string_t error_code_unsupported_http_verb(U("UnsupportedHttpVerb")); + + /// + /// The Content-Length header is required for this request. + /// + const utility::string_t error_code_missing_content_length_header(U("MissingContentLengthHeader")); + + /// + /// A required header was missing. + /// + const utility::string_t error_code_missing_required_header(U("MissingRequiredHeader")); + + /// + /// A required XML node was missing. + /// + const utility::string_t error_code_missing_required_xml_node(U("MissingRequiredXmlNode")); + + /// + /// One or more header values are not supported. + /// + const utility::string_t error_code_unsupported_header(U("UnsupportedHeader")); + + /// + /// One or more XML nodes are not supported. + /// + const utility::string_t error_code_unsupported_xml_node(U("UnsupportedXmlNode")); + + /// + /// One or more header values are invalid. + /// + const utility::string_t error_code_invalid_header_value(U("InvalidHeaderValue")); + + /// + /// One or more XML node values are invalid. + /// + const utility::string_t error_code_invalid_xml_node_value(U("InvalidXmlNodeValue")); + + /// + /// A required query parameter is missing. + /// + const utility::string_t error_code_missing_required_query_parameter(U("MissingRequiredQueryParameter")); + + /// + /// One or more query parameters is not supported. + /// + const utility::string_t error_code_unsupported_query_parameter(U("UnsupportedQueryParameter")); + + /// + /// One or more query parameters are invalid. + /// + const utility::string_t error_code_invalid_query_parameter_value(U("InvalidQueryParameterValue")); + + /// + /// One or more query parameters are out of range. + /// + const utility::string_t error_code_out_of_range_query_parameter_value(U("OutOfRangeQueryParameterValue")); + + /// + /// The URI is invalid. + /// + const utility::string_t error_code_invalid_uri(U("InvalidUri")); + + /// + /// The HTTP verb is invalid. + /// + const utility::string_t error_code_invalid_http_verb(U("InvalidHttpVerb")); + + /// + /// The metadata key is empty. + /// + const utility::string_t error_code_empty_metadata_key(U("EmptyMetadataKey")); + + /// + /// The request body is too large. + /// + const utility::string_t error_code_request_body_too_large(U("RequestBodyTooLarge")); + + /// + /// The specified XML document is invalid. + /// + const utility::string_t error_code_invalid_xml_document(U("InvalidXmlDocument")); + + /// + /// An internal error occurred. + /// + const utility::string_t error_code_internal_error(U("InternalError")); + + /// + /// Authentication failed. + /// + const utility::string_t error_code_authentication_failed(U("AuthenticationFailed")); + + /// + /// The specified MD5 hash does not match the server value. + /// + const utility::string_t error_code_md5_mismatch(U("Md5Mismatch")); + + /// + /// The specified MD5 hash is invalid. + /// + const utility::string_t error_code_invalid_md5(U("InvalidMd5")); + + /// + /// The input is out of range. + /// + const utility::string_t error_code_out_of_range_input(U("OutOfRangeInput")); + + /// + /// The input is invalid. + /// + const utility::string_t error_code_invalid_input(U("InvalidInput")); + + /// + /// The operation timed out. + /// + const utility::string_t error_code_operation_timed_out(U("OperationTimedOut")); + + /// + /// The specified resource was not found. + /// + const utility::string_t error_code_resource_not_found(U("ResourceNotFound")); + + /// + /// The specified metadata is invalid. + /// + const utility::string_t error_code_invalid_metadata(U("InvalidMetadata")); + + /// + /// The specified metadata is too large. + /// + const utility::string_t error_code_metadata_too_large(U("MetadataTooLarge")); + + /// + /// The specified condition was not met. + /// + const utility::string_t error_code_condition_not_met(U("ConditionNotMet")); + + /// + /// The specified range is invalid. + /// + const utility::string_t error_code_invalid_range(U("InvalidRange")); + + /// + /// The server is busy. + /// + const utility::string_t error_code_server_busy(U("ServerBusy")); + + /// + /// The url in the request could not be parsed. + /// + const utility::string_t error_code_request_url_failed_to_parse(U("RequestUrlFailedToParse")); + + /// + /// The authentication information was not provided in the correct format. Verify the value of Authorization header. + /// + const utility::string_t error_code_invalid_authentication_info(U("InvalidAuthenticationInfo")); + + /// + /// The specified resource name contains invalid characters. + /// + const utility::string_t error_code_invalid_resource_name(U("InvalidResourceName")); + + /// + /// Condition headers are not supported. + /// + const utility::string_t error_code_condition_headers_not_supported(U("ConditionHeadersNotSupported")); + + /// + /// Multiple condition headers are not supported. + /// + const utility::string_t error_code_multiple_condition_headers_not_supported(U("MultipleConditionHeadersNotSupported")); + + /// + /// Read-access geo-redundant replication is not enabled for the account, write operations to the secondary location are not allowed, + /// or the account being accessed does not have sufficient permissions to execute this operation. + /// + const utility::string_t error_code_insufficient_account_permissions(U("InsufficientAccountPermissions")); + + /// + /// The specified account is disabled. + /// + const utility::string_t error_code_account_is_disabled(U("AccountIsDisabled")); + + /// + /// The specified account already exists. + /// + const utility::string_t error_code_account_already_exists(U("AccountAlreadyExists")); + + /// + /// The specified account is in the process of being created. + /// + const utility::string_t error_code_account_being_created(U("AccountBeingCreated")); + + /// + /// The specified resource already exists. + /// + const utility::string_t error_code_resource_already_exists(U("ResourceAlreadyExists")); + + /// + /// The specified resource type does not match the type of the existing resource. + /// + const utility::string_t error_code_resource_type_mismatch(U("ResourceTypeMismatch")); + +#pragma endregion + +#pragma region blob_error_code_strings + + // This section provides error code strings that are specific to the Blob service. + + /// + /// Error code that may be returned when the specified block or blob is invalid. + /// + const utility::string_t error_code_invalid_blob_or_block(U("InvalidBlobOrBlock")); + + /// + /// Error code that may be returned when a block ID is invalid. + /// + const utility::string_t error_code_invalid_block_id(U("InvalidBlockId")); + + /// + /// Error code that may be returned when a block list is invalid. + /// + const utility::string_t error_code_invalid_block_list(U("InvalidBlockList")); + + /// + /// The specified container was not found. + /// + const utility::string_t error_code_container_not_found(U("ContainerNotFound")); + + /// + /// Error code that may be returned when a blob with the specified address cannot be found. + /// + const utility::string_t error_code_blob_not_found(U("BlobNotFound")); + + /// + /// The specified container already exists. + /// + const utility::string_t error_code_container_already_exists(U("ContainerAlreadyExists")); + + /// + /// The specified container is disabled. + /// + const utility::string_t error_code_container_disabled(U("ContainerDisabled")); + + /// + /// The specified container is being deleted. + /// + const utility::string_t error_code_container_being_deleted(U("ContainerBeingDeleted")); + + /// + /// Error code that may be returned when a client attempts to create a blob that already exists. + /// + const utility::string_t error_code_blob_already_exists(U("BlobAlreadyExists")); + + /// + /// Error code that may be returned when there is currently no lease on the blob. + /// + const utility::string_t error_code_lease_not_present_with_blob_operation(U("LeaseNotPresentWithBlobOperation")); + + /// + /// Error code that may be returned when there is currently no lease on the container. + /// + const utility::string_t error_code_lease_not_present_with_container_operation(U("LeaseNotPresentWithContainerOperation")); + + /// + /// Error code that may be returned when a lease ID was specified, but the lease has expired. + /// + const utility::string_t error_code_lease_lost(U("LeaseLost")); + + /// + /// Error code that may be returned when the lease ID specified did not match the lease ID for the blob. + /// + const utility::string_t error_code_lease_id_mismatch_with_blob_operation(U("LeaseIdMismatchWithBlobOperation")); + + /// + /// Error code that may be returned when the lease ID specified did not match the lease ID for the container. + /// + const utility::string_t error_code_lease_id_mismatch_with_container_operation(U("LeaseIdMismatchWithContainerOperation")); + + /// + /// Error code that may be returned when there is currently a lease on the resource and no lease ID was specified in the request. + /// + const utility::string_t error_code_lease_id_missing(U("LeaseIdMissing")); + + /// + /// Error code that may be returned when there is currently no lease on the resource. + /// + const utility::string_t error_code_lease_not_present_with_lease_operation(U("LeaseNotPresentWithLeaseOperation")); + + /// + /// Error code that may be returned when the lease ID specified did not match the lease ID. + /// + const utility::string_t error_code_lease_id_mismatch_with_lease_operation(U("LeaseIdMismatchWithLeaseOperation")); + + /// + /// Error code that may be returned when there is already a lease present. + /// + const utility::string_t error_code_lease_already_present(U("LeaseAlreadyPresent")); + + /// + /// Error code that may be returned when the lease has already been broken and cannot be broken again. + /// + const utility::string_t error_code_lease_already_broken(U("LeaseAlreadyBroken")); + + /// + /// Error code that may be returned when the lease ID matched, but the lease has been broken explicitly and cannot be renewed. + /// + const utility::string_t error_code_lease_is_broken_and_cannot_be_renewed(U("LeaseIsBrokenAndCannotBeRenewed")); + + /// + /// Error code that may be returned when the lease ID matched, but the lease is breaking and cannot be acquired. + /// + const utility::string_t error_code_lease_is_breaking_and_cannot_be_acquired(U("LeaseIsBreakingAndCannotBeAcquired")); + + /// + /// Error code that may be returned when the lease ID matched, but the lease is breaking and cannot be changed. + /// + const utility::string_t error_code_lease_is_breaking_and_cannot_be_changed(U("LeaseIsBreakingAndCannotBeChanged")); + + /// + /// Error code that may be returned when the destination of a copy operation has a lease of fixed duration. + /// + const utility::string_t error_code_infinite_lease_duration_required(U("InfiniteLeaseDurationRequired")); + + /// + /// Error code that may be returned when the operation is not permitted because the blob has snapshots. + /// + const utility::string_t error_code_snapshots_present(U("SnapshotsPresent")); + + /// + /// Error code that may be returned when the blob type is invalid for this operation. + /// + const utility::string_t error_code_invalid_blob_type(U("InvalidBlobType")); + + /// + /// Error code that may be returned when the operation on page blobs uses a version prior to 2009-09-19. + /// + const utility::string_t error_code_invalid_version_for_page_blob_operation(U("InvalidVersionForPageBlobOperation")); + + /// + /// Error code that may be returned when the page range specified is invalid. + /// + const utility::string_t error_code_invalid_page_range(U("InvalidPageRange")); + + /// + /// Error code that may be returned when the sequence number condition specified was not met. + /// + const utility::string_t error_code_sequence_number_condition_not_met(U("SequenceNumberConditionNotMet")); + + /// + /// Error code that may be returned when the sequence number increment cannot be performed because it would result in overflow of the sequence number. + /// + const utility::string_t error_code_sequence_number_increment_too_large(U("SequenceNumberIncrementTooLarge")); + + /// + /// Error code that may be returned when the source condition specified using HTTP conditional header(s) is not met. + /// + const utility::string_t error_code_source_condition_not_met(U("SourceConditionNotMet")); + + /// + /// Error code that may be returned when the target condition specified using HTTP conditional header(s) is not met. + /// + const utility::string_t error_code_target_condition_not_met(U("TargetConditionNotMet")); + + /// + /// Error code that may be returned when the copy source account and destination account are not the same. + /// + const utility::string_t error_code_copy_across_accounts_not_supported(U("CopyAcrossAccountsNotSupported")); + + /// + /// Error code that may be returned when the source of a copy cannot be accessed. + /// + const utility::string_t error_code_cannot_verify_copy_source(U("CannotVerifyCopySource")); + + /// + /// Error code that may be returned when an attempt to modify the destination of a pending copy is made. + /// + const utility::string_t error_code_pending_copy_operation(U("PendingCopyOperation")); + + /// + /// Error code that may be returned when an Abort Copy operation is called when there is no pending copy. + /// + const utility::string_t error_code_no_pending_copy_operation(U("NoPendingCopyOperation")); + + /// + /// Error code that may be returned when the copy ID specified in an Abort Copy operation does not match the current pending copy ID. + /// + const utility::string_t error_code_copy_id_mismatch(U("CopyIdMismatch")); + +#pragma endregion + +#pragma region table_error_code_strings + + // This section provides error code strings that are specific to the Table service. + + /// + /// The request uses X-HTTP-Method with an HTTP verb other than POST. + /// + const utility::string_t error_code_x_method_not_using_post(U("XMethodNotUsingPost")); + + /// + /// The specified X-HTTP-Method is invalid. + /// + const utility::string_t error_code_x_method_incorrect_value(U("XMethodIncorrectValue")); + + /// + /// More than one X-HTTP-Method is specified. + /// + const utility::string_t error_code_x_method_incorrect_count(U("XMethodIncorrectCount")); + + /// + /// The specified table has no properties. + /// + const utility::string_t error_code_table_has_no_properties(U("TableHasNoProperties")); + + /// + /// A property is specified more than once. + /// + const utility::string_t error_code_duplicate_properties_specified(U("DuplicatePropertiesSpecified")); + + /// + /// The specified table has no such property. + /// + const utility::string_t error_code_table_has_no_such_property(U("TableHasNoSuchProperty")); + + /// + /// A duplicate key property was specified. + /// + const utility::string_t error_code_duplicate_key_property_specified(U("DuplicateKeyPropertySpecified")); + + /// + /// The specified table already exists. + /// + const utility::string_t error_code_table_already_exists(U("TableAlreadyExists")); + + /// + /// The specified table was not found. + /// + const utility::string_t error_code_table_not_found(U("TableNotFound")); + + /// + /// The specified entity was not found. + /// + const utility::string_t error_code_entity_not_found(U("EntityNotFound")); + + /// + /// The specified entity already exists. + /// + const utility::string_t error_code_entity_already_exists(U("EntityAlreadyExists")); + + /// + /// The partition key was not specified. + /// + const utility::string_t error_code_partition_key_not_specified(U("PartitionKeyNotSpecified")); + + /// + /// One or more specified operators are invalid. + /// + const utility::string_t error_code_operator_invalid(U("OperatorInvalid")); + + /// + /// The specified update condition was not satisfied. + /// + const utility::string_t error_code_update_condition_not_satisfied(U("UpdateConditionNotSatisfied")); + + /// + /// All properties must have values. + /// + const utility::string_t error_code_properties_need_value(U("PropertiesNeedValue")); + + /// + /// The partition key property cannot be updated. + /// + const utility::string_t error_code_partition_key_property_cannot_be_updated(U("PartitionKeyPropertyCannotBeUpdated")); + + /// + /// The entity contains more properties than allowed. + /// + const utility::string_t error_code_too_many_properties(U("TooManyProperties")); + + /// + /// The entity is larger than the maximum size permitted. + /// + const utility::string_t error_code_entity_too_large(U("EntityTooLarge")); + + /// + /// The property value is larger than the maximum size permitted. + /// + const utility::string_t error_code_property_value_too_large(U("PropertyValueTooLarge")); + + /// + /// One or more value types are invalid. + /// + const utility::string_t error_code_invalid_value_type(U("InvalidValueType")); + + /// + /// The specified table is being deleted. + /// + const utility::string_t error_code_table_being_deleted(U("TableBeingDeleted")); + + /// + /// The Table service server is out of memory. + /// + const utility::string_t error_code_table_server_out_of_memory(U("TableServerOutOfMemory")); + + /// + /// The type of the primary key property is invalid. + /// + const utility::string_t error_code_primary_key_property_is_invalid_type(U("PrimaryKeyPropertyIsInvalidType")); + + /// + /// The property name exceeds the maximum allowed length. + /// + const utility::string_t error_code_property_name_too_long(U("PropertyNameTooLong")); + + /// + /// The property name is invalid. + /// + const utility::string_t error_code_property_name_invalid(U("PropertyNameInvalid")); + + /// + /// Batch operations are not supported for this operation type. + /// + const utility::string_t error_code_batch_operation_not_supported(U("BatchOperationNotSupported")); + + /// + /// JSON format is not supported. + /// + const utility::string_t error_code_json_format_not_supported(U("JsonFormatNotSupported")); + + /// + /// The specified method is not allowed. + /// + const utility::string_t error_code_method_not_allowed(U("MethodNotAllowed")); + + /// + /// The specified operation is not yet implemented. + /// + const utility::string_t error_code_not_implemented(U("NotImplemented")); + + /// + /// The required host information is not present in the request. You must send a non-empty Host header or include the absolute URI in the request line. + /// + const utility::string_t error_code_host_information_not_present(U("HostInformationNotPresent")); + +#pragma endregion + +#pragma region queue_error_code_strings + + // This section provides error code strings that are specific to the Queue service. + + /// + /// Error code that may be returned when the specified queue was not found. + /// + const utility::string_t error_code_queue_not_found(U("QueueNotFound")); + + /// + /// Error code that may be returned when the specified queue is disabled. + /// + const utility::string_t error_code_queue_disabled(U("QueueDisabled")); + + /// + /// Error code that may be returned when the specified queue already exists. + /// + const utility::string_t error_code_queue_already_exists(U("QueueAlreadyExists")); + + /// + /// Error code that may be returned when the specified queue is not empty. + /// + const utility::string_t error_code_queue_not_empty(U("QueueNotEmpty")); + + /// + /// Error code that may be returned when the specified queue is being deleted. + /// + const utility::string_t error_code_queue_being_deleted(U("QueueBeingDeleted")); + + /// + /// Error code that may be returned when the specified pop receipt does not match. + /// + const utility::string_t error_code_pop_receipt_mismatch(U("PopReceiptMismatch")); + + /// + /// Error code that may be returned when one or more request parameters are invalid. + /// + const utility::string_t error_code_invalid_parameter(U("InvalidParameter")); + + /// + /// Error code that may be returned when the specified message was not found. + /// + const utility::string_t error_code_message_not_found(U("MessageNotFound")); + + /// + /// Error code that may be returned when the specified message is too large. + /// + const utility::string_t error_code_message_too_large(U("MessageTooLarge")); + + /// + /// Error code that may be returned when the specified marker is invalid. + /// + const utility::string_t error_code_invalid_marker(U("InvalidMarker")); + +#pragma endregion + +}}} // namespace azure::storage::protocol diff --git a/Microsoft.WindowsAzure.Storage/includes/was/queue.h b/Microsoft.WindowsAzure.Storage/includes/was/queue.h index 2c3be2eb..2c89db30 100644 --- a/Microsoft.WindowsAzure.Storage/includes/was/queue.h +++ b/Microsoft.WindowsAzure.Storage/includes/was/queue.h @@ -726,7 +726,7 @@ namespace azure { namespace storage { /// Initializes a new instance of the class. /// cloud_queue() - : m_approximate_message_count(-1) + : m_metadata(std::make_shared()), m_approximate_message_count(std::make_shared(-1)) { } @@ -1447,7 +1447,7 @@ namespace azure { namespace storage { /// Call the download_attributes function to retrieve this data from the queue service. int approximate_message_count() const { - return m_approximate_message_count; + return *m_approximate_message_count; } /// @@ -1457,7 +1457,7 @@ namespace azure { namespace storage { /// Call the download_attributes function to retrieve this data from the queue service. cloud_metadata& metadata() { - return m_metadata; + return *m_metadata; } /// @@ -1467,7 +1467,7 @@ namespace azure { namespace storage { /// Call the download_attributes function to retrieve this data from the queue service. const cloud_metadata& metadata() const { - return m_metadata; + return *m_metadata; } /// @@ -1477,7 +1477,7 @@ namespace azure { namespace storage { /// Call the upload_attributes function to save this data to the queue service. void set_metadata(cloud_metadata value) { - m_metadata = std::move(value); + *m_metadata = std::move(value); } private: @@ -1495,8 +1495,8 @@ namespace azure { namespace storage { cloud_queue_client m_client; utility::string_t m_name; storage_uri m_uri; - int m_approximate_message_count; - cloud_metadata m_metadata; + std::shared_ptr m_approximate_message_count; + std::shared_ptr m_metadata; friend class cloud_queue_client; }; diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/blobstreams.h b/Microsoft.WindowsAzure.Storage/includes/wascore/blobstreams.h index 148500ad..01b9c022 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/blobstreams.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/blobstreams.h @@ -117,7 +117,7 @@ namespace azure { namespace storage { namespace core { break; } - return seekpos((pos_type)offset, direction); + return seekpos(new_pos, direction); } return (pos_type)traits::eof(); @@ -250,7 +250,7 @@ namespace azure { namespace storage { namespace core { break; } - return seekpos((pos_type)offset, direction); + return seekpos(new_pos, direction); } return (pos_type)traits::eof(); @@ -418,9 +418,9 @@ namespace azure { namespace storage { namespace core { { if (can_seek() && (direction == std::ios_base::out)) { - if ((pos < (pos_type)0) || (pos >= (pos_type)size())) + if ((pos < (pos_type)0) || (pos > (pos_type)size())) { - throw std::invalid_argument("pos"); + return (pos_type)traits::eof(); } sync().wait(); diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/constants.h b/Microsoft.WindowsAzure.Storage/includes/wascore/constants.h index 76ba5baa..03afbd75 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/constants.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/constants.h @@ -43,6 +43,12 @@ namespace azure { namespace storage { namespace protocol { // For the following value, "0" means "don't send a timeout to the service" const std::chrono::seconds default_server_timeout(0); + // lease break period and duration constants + const std::chrono::seconds minimum_lease_break_period(0); + const std::chrono::seconds maximum_lease_break_period(60); + const std::chrono::seconds minimum_fixed_lease_duration(15); + const std::chrono::seconds maximum_fixed_lease_duration(60); + // uri query parameters const utility::string_t uri_query_timeout(U("timeout")); const utility::string_t uri_query_resource_type(U("restype")); @@ -75,6 +81,7 @@ namespace azure { namespace storage { namespace protocol { const utility::string_t uri_query_sas_content_encoding(U("rsce")); const utility::string_t uri_query_sas_content_language(U("rscl")); const utility::string_t uri_query_sas_content_disposition(U("rscd")); + const utility::string_t uri_query_sas_api_version(U("api-version")); // table query parameters const utility::string_t table_query_next_partition_key(U("NextPartitionKey")); @@ -171,7 +178,7 @@ namespace azure { namespace storage { namespace protocol { const utility::string_t ms_header_time_next_visible(U("x-ms-time-next-visible")); // header values - const utility::string_t header_value_storage_version(U("2013-08-15")); + const utility::string_t header_value_storage_version(U("2014-02-14")); const utility::string_t header_value_true(U("true")); const utility::string_t header_value_false(U("false")); const utility::string_t header_value_locked(U("locked")); @@ -257,6 +264,7 @@ namespace azure { namespace storage { namespace protocol { const utility::string_t xml_block(U("Block")); const utility::string_t xml_name(U("Name")); const utility::string_t xml_size(U("Size")); + const utility::string_t xml_error_root(U("Error")); const utility::string_t xml_code(U("Code")); const utility::string_t xml_code_table(U("code")); const utility::string_t xml_message(U("Message")); @@ -298,16 +306,11 @@ namespace azure { namespace storage { namespace protocol { const utility::string_t xml_service_stats_geo_replication_last_sync_time(U("LastSyncTime")); const utility::string_t xml_url(U("Url")); - // error codes - const utility::string_t error_code_container_already_exists(U("ContainerAlreadyExists")); - const utility::string_t error_code_container_not_found(U("ContainerNotFound")); - const utility::string_t error_code_blob_not_found(U("BlobNotFound")); - // user agent #if defined(WIN32) - const utility::string_t header_value_user_agent(U("Azure-Storage/0.4.0 (Native; Windows)")); + const utility::string_t header_value_user_agent(U("Azure-Storage/0.5.0 (Native; Windows)")); #else - const utility::string_t header_value_user_agent(U("Azure-Storage/0.4.0 (Native)")); + const utility::string_t header_value_user_agent(U("Azure-Storage/0.5.0 (Native)")); #endif }}} // namespace azure::storage::protocol diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/executor.h b/Microsoft.WindowsAzure.Storage/includes/wascore/executor.h index 26418da1..8d6f7a1b 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/executor.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/executor.h @@ -37,13 +37,18 @@ namespace azure { namespace storage { namespace core { { } - static pplx::task create(concurrency::streams::istream stream, bool calculate_md5 = false, utility::size64_t length = std::numeric_limits::max()) + static pplx::task create(concurrency::streams::istream stream, bool calculate_md5 = false, utility::size64_t length = std::numeric_limits::max(), utility::size64_t max_length = std::numeric_limits::max()) { if (length == std::numeric_limits::max()) { length = get_remaining_stream_length(stream); } + if (length != std::numeric_limits::max() && length > max_length) + { + throw std::invalid_argument(protocol::error_stream_length); + } + if (!calculate_md5 && stream.can_seek()) { return pplx::task_from_result(istream_descriptor(stream, length, utility::string_t())); @@ -62,7 +67,7 @@ namespace azure { namespace storage { namespace core { temp_stream = temp_buffer.create_ostream(); } - return stream_copy_async(stream, temp_stream, length).then([temp_buffer, provider] (pplx::task buffer_task) mutable -> istream_descriptor + return stream_copy_async(stream, temp_stream, length, max_length).then([temp_buffer, provider] (pplx::task buffer_task) mutable -> istream_descriptor { provider.close(); return istream_descriptor(concurrency::streams::container_stream>::open_istream(temp_buffer.collection()), buffer_task.get(), provider.hash()); @@ -312,14 +317,6 @@ namespace azure { namespace storage { namespace core { instance->m_command->m_request_body.rewind(); instance->m_request.set_body(instance->m_command->m_request_body.stream(), instance->m_command->m_request_body.length(), utility::string_t()); } - else - { - // TODO: Remove the following when we can take a dependency on Casablanca 2.3. This line fo code is a workaround for a bug in 2.2. - if ((((utility::string_t)instance->m_request.method()) == U("HEAD")) || (((utility::string_t)instance->m_request.method()) == U("DELETE")) || (((utility::string_t)instance->m_request.method()) == U("GET"))) - { - instance->m_request.headers().add(protocol::xml_content_length, 0); - } - } // If the command wants to copy the response body to a stream, set it // on the http_request object @@ -357,14 +354,6 @@ namespace azure { namespace storage { namespace core { web::http::client::http_client_config config; config.set_timeout(instance->remaining_time()); - // TODO: Remove the following when we can take a dependency on Casablanca 2.3. This line fo code is a workaround for a bug in 2.2. -#ifndef WIN32 - if (config.timeout() < std::chrono::seconds(1)) - { - config.set_timeout(std::chrono::seconds(1)); - } -#endif - size_t http_buffer_size = instance->m_request_options.http_buffer_size(); if (http_buffer_size > 0) { @@ -517,6 +506,10 @@ namespace azure { namespace storage { namespace core { } catch (const std::exception& e) { + // + // exception thrown by previous steps are handled here below + // + if (logger::instance().should_log(instance->m_context, client_log_level::log_level_warning)) { logger::instance().log(instance->m_context, client_log_level::log_level_warning, U("Exception thrown while processing response: ") + utility::conversions::to_string_t(e.what())); @@ -529,7 +522,7 @@ namespace azure { namespace storage { namespace core { logger::instance().log(instance->m_context, client_log_level::log_level_error, U("Exception was not retryable: ") + utility::conversions::to_string_t(e.what())); } - throw storage_exception(e.what(), instance->m_request_result, false); + throw storage_exception(e.what(), instance->m_request_result, capture_inner_exception(e), false); } // An exception occured and thus the request might be retried. Ask the retry policy. @@ -542,7 +535,7 @@ namespace azure { namespace storage { namespace core { logger::instance().log(instance->m_context, client_log_level::log_level_error, U("Retry policy did not allow for a retry, so throwing exception: ") + utility::conversions::to_string_t(e.what())); } - throw storage_exception(e.what(), instance->m_request_result, false); + throw storage_exception(e.what(), instance->m_request_result, capture_inner_exception(e), false); } instance->m_current_location = retry.target_location(); @@ -563,7 +556,7 @@ namespace azure { namespace storage { namespace core { logger::instance().log(instance->m_context, client_log_level::log_level_error, U("Cannot recover request for retry, so throwing exception: ") + utility::conversions::to_string_t(e.what())); } - throw storage_exception(e.what(), instance->m_request_result, false); + throw storage_exception(e.what(), instance->m_request_result, capture_inner_exception(e), false); } if (logger::instance().should_log(instance->m_context, client_log_level::log_level_informational)) @@ -711,6 +704,17 @@ namespace azure { namespace storage { namespace core { } } + static std::exception_ptr capture_inner_exception(const std::exception& exception) + { + if (nullptr == dynamic_cast(&exception)) + { + // exception other than storage_exception is captured as inner exception. + return std::current_exception(); + } + + return nullptr; + } + std::shared_ptr> m_command; request_options m_request_options; operation_context m_context; diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/protocol.h b/Microsoft.WindowsAzure.Storage/includes/wascore/protocol.h index 2c80ee3e..6a59bc22 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/protocol.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/protocol.h @@ -52,7 +52,7 @@ namespace azure { namespace storage { namespace protocol { web::http::http_request get_page_ranges(utility::size64_t offset, utility::size64_t length, const utility::string_t& snapshot_time, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); web::http::http_request put_page(page_range range, page_write write, const utility::string_t& content_md5, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); web::http::http_request put_block_blob(const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); - web::http::http_request put_page_blob(utility::size64_t size, const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); + web::http::http_request put_page_blob(utility::size64_t size, int64_t sequence_number, const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); web::http::http_request get_blob(utility::size64_t offset, utility::size64_t length, bool get_range_content_md5, const utility::string_t& snapshot_time, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); web::http::http_request get_blob_properties(const utility::string_t& snapshot_time, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); web::http::http_request set_blob_properties(const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context); diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/protocol_xml.h b/Microsoft.WindowsAzure.Storage/includes/wascore/protocol_xml.h index c6e24d58..82c1fd33 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/protocol_xml.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/protocol_xml.h @@ -45,12 +45,19 @@ namespace azure { namespace storage { namespace protocol { return std::move(m_error_message); } + std::unordered_map move_details() + { + parse(); + return std::move(m_details); + } + protected: virtual void handle_element(const utility::string_t& element_name); utility::string_t m_error_code; utility::string_t m_error_message; + std::unordered_map m_details; }; class cloud_blob_container_list_item diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/resources.h b/Microsoft.WindowsAzure.Storage/includes/wascore/resources.h index 7c9b1598..d5a0b980 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/resources.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/resources.h @@ -34,6 +34,7 @@ namespace azure { namespace storage { namespace protocol { const std::string error_cannot_modify_snapshot("Cannot perform this operation on a blob representing a snapshot."); const std::string error_page_blob_size_unknown("The size of the page blob could not be determined, because stream is not seekable and a length argument is not provided."); const std::string error_stream_short("The requested number of bytes exceeds the length of the stream remaining from the specified position."); + const std::string error_stream_length("The length of the stream exceeds the permitted length."); const std::string error_unsupported_text_blob("Only plain text with utf-8 encoding is supported."); const std::string error_multiple_snapshots("Cannot provide snapshot time as part of the address and as constructor parameter. Either pass in the address or use a different constructor."); const std::string error_multiple_credentials("Cannot provide credentials as part of the address and as constructor parameter. Either pass in the address or use a different constructor."); diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/util.h b/Microsoft.WindowsAzure.Storage/includes/wascore/util.h index 23752c3a..64f0e0a8 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/util.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/util.h @@ -54,7 +54,7 @@ namespace azure { namespace storage { namespace core { utility::string_t make_query_parameter(const utility::string_t& parameter_name, const utility::string_t& parameter_value, bool do_encoding = true); utility::size64_t get_remaining_stream_length(concurrency::streams::istream stream); - pplx::task stream_copy_async(concurrency::streams::istream istream, concurrency::streams::ostream ostream, utility::size64_t length); + pplx::task stream_copy_async(concurrency::streams::istream istream, concurrency::streams::ostream ostream, utility::size64_t length, utility::size64_t max_length = std::numeric_limits::max()); pplx::task complete_after(std::chrono::milliseconds timeout); std::vector string_split(const utility::string_t& string, const utility::string_t& separator); bool is_empty_or_whitespace(const utility::string_t& value); diff --git a/Microsoft.WindowsAzure.Storage/includes/wascore/xmlhelpers.h b/Microsoft.WindowsAzure.Storage/includes/wascore/xmlhelpers.h index aad0fe7a..babc30ce 100644 --- a/Microsoft.WindowsAzure.Storage/includes/wascore/xmlhelpers.h +++ b/Microsoft.WindowsAzure.Storage/includes/wascore/xmlhelpers.h @@ -140,7 +140,7 @@ class xml_reader bool move_to_first_attribute(); /// - /// Moves to the first attribute in the node + /// Moves to the next attribute in the node /// bool move_to_next_attribute(); @@ -159,11 +159,6 @@ class xml_reader /// void initialize(concurrency::streams::istream stream); - /// - /// Remove Byte Order Mark from the stream - /// - void remove_bom(concurrency::streams::istream stream); - /// /// Can be called by the derived classes in the handle_* routines, to cause the parse routine to exit early, /// in order to capture records as they are parsed. Parsing is resumed by invoking the parse method again. diff --git a/Microsoft.WindowsAzure.Storage/src/CMakeLists.txt b/Microsoft.WindowsAzure.Storage/src/CMakeLists.txt index 00feb409..bbe8024f 100644 --- a/Microsoft.WindowsAzure.Storage/src/CMakeLists.txt +++ b/Microsoft.WindowsAzure.Storage/src/CMakeLists.txt @@ -123,7 +123,7 @@ target_link_libraries(${AZURESTORAGE_LIBRARY} # Portions specific to cpprest binary versioning. set (AZURESTORAGE_VERSION_MAJOR 0) -set (AZURESTORAGE_VERSION_MINOR 4) +set (AZURESTORAGE_VERSION_MINOR 5) set (AZURESTORAGE_VERSION_REVISION 0) if(WIN32) set_target_properties(${AZURESTORAGE_LIBRARY} PROPERTIES diff --git a/Microsoft.WindowsAzure.Storage/src/blob_request_factory.cpp b/Microsoft.WindowsAzure.Storage/src/blob_request_factory.cpp index 7756054a..3c4fa576 100644 --- a/Microsoft.WindowsAzure.Storage/src/blob_request_factory.cpp +++ b/Microsoft.WindowsAzure.Storage/src/blob_request_factory.cpp @@ -311,12 +311,12 @@ namespace azure { namespace storage { namespace protocol { web::http::http_headers& headers = request.headers(); headers.add(ms_header_range, range.to_string()); - headers.add(web::http::header_names::content_md5, content_md5); switch (write) { case page_write::update: headers.add(ms_header_page_write, header_value_page_write_update); + add_optional_header(headers, web::http::header_names::content_md5, content_md5); break; case page_write::clear: @@ -339,13 +339,13 @@ namespace azure { namespace storage { namespace protocol { return request; } - web::http::http_request put_page_blob(utility::size64_t size, const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context) + web::http::http_request put_page_blob(utility::size64_t size, int64_t sequence_number, const cloud_blob_properties& properties, const cloud_metadata& metadata, const access_condition& condition, web::http::uri_builder uri_builder, const std::chrono::seconds& timeout, operation_context context) { web::http::http_request request(base_request(web::http::methods::PUT, uri_builder, timeout, context)); web::http::http_headers& headers = request.headers(); headers.add(ms_header_blob_type, header_value_blob_type_page); headers.add(ms_header_blob_content_length, size); - headers.add(ms_header_blob_sequence_number, properties.page_blob_sequence_number()); + headers.add(ms_header_blob_sequence_number, sequence_number); add_properties(request, properties); add_metadata(request, metadata); add_access_condition(request, condition); @@ -544,7 +544,7 @@ namespace azure { namespace storage { namespace protocol { if (!condition.lease_id().empty()) { - throw storage_exception(protocol::error_lease_id_on_source); + throw storage_exception(protocol::error_lease_id_on_source, false); } } diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_blob.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_blob.cpp index f6f05638..27883c45 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_blob.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_blob.cpp @@ -17,6 +17,7 @@ #include "stdafx.h" #include "was/blob.h" +#include "was/error_code_strings.h" #include "wascore/protocol.h" #include "wascore/resources.h" #include "wascore/blobstreams.h" diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_blob_container.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_blob_container.cpp index a956eeda..2fc4b09f 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_blob_container.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_blob_container.cpp @@ -17,6 +17,7 @@ #include "stdafx.h" #include "was/blob.h" +#include "was/error_code_strings.h" #include "wascore/protocol.h" #include "wascore/protocol_xml.h" #include "wascore/util.h" diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_blob_istreambuf.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_blob_istreambuf.cpp index 3de90478..b219e8e0 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_blob_istreambuf.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_blob_istreambuf.cpp @@ -32,8 +32,9 @@ namespace azure { namespace storage { namespace core { } pos_type end(size()); - if ((pos >= 0) && (pos < end)) + if ((pos >= 0) && (pos <= end)) { + // Do not allow read beyond the end. m_current_blob_offset = pos; m_next_blob_offset = m_current_blob_offset; m_buffer = concurrency::streams::container_buffer>(std::ios_base::in); diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_blob_ostreambuf.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_blob_ostreambuf.cpp index a6c22544..af764672 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_blob_ostreambuf.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_blob_ostreambuf.cpp @@ -76,7 +76,8 @@ namespace azure { namespace storage { namespace core { pplx::task basic_cloud_blob_ostreambuf::_putc(concurrency::streams::ostream::traits::char_type ch) { pplx::task upload_task = pplx::task_from_result(); - + + m_current_streambuf_offset += 1; auto result = m_buffer.putc(ch).get(); if (m_buffer_size == m_buffer.in_avail()) { diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_block_blob.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_block_blob.cpp index 22b5d5d3..3616ded4 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_block_blob.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_block_blob.cpp @@ -33,7 +33,7 @@ namespace azure { namespace storage { auto command = std::make_shared>(uri()); command->set_authentication_handler(service_client().authentication_handler()); command->set_preprocess_response(std::bind(protocol::preprocess_response_void, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - return core::istream_descriptor::create(block_data, needs_md5).then([command, context, block_id, content_md5, modified_options, condition] (core::istream_descriptor request_body) -> pplx::task + return core::istream_descriptor::create(block_data, needs_md5, std::numeric_limits::max(), protocol::max_block_size).then([command, context, block_id, content_md5, modified_options, condition](core::istream_descriptor request_body) -> pplx::task { const utility::string_t& md5 = content_md5.empty() ? request_body.content_md5() : content_md5; command->set_build_request(std::bind(protocol::put_block, block_id, md5, condition, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_page_blob.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_page_blob.cpp index 90eb4cfe..fa52ad7e 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_page_blob.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_page_blob.cpp @@ -66,7 +66,7 @@ namespace azure { namespace storage { properties->update_etag_and_last_modified(parsed_properties); properties->update_page_blob_sequence_number(parsed_properties); }); - return core::istream_descriptor::create(page_data, needs_md5).then([command, context, start_offset, content_md5, modified_options, condition] (core::istream_descriptor request_body) -> pplx::task + return core::istream_descriptor::create(page_data, needs_md5, std::numeric_limits::max(), protocol::max_block_size).then([command, context, start_offset, content_md5, modified_options, condition](core::istream_descriptor request_body) -> pplx::task { const utility::string_t& md5 = content_md5.empty() ? request_body.content_md5() : content_md5; auto end_offset = start_offset + request_body.length() - 1; @@ -95,20 +95,20 @@ namespace azure { namespace storage { }); } - pplx::task cloud_page_blob::open_write_async(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context) + pplx::task cloud_page_blob::open_write_async(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { assert_no_snapshot(); blob_request_options modified_options(options); modified_options.apply_defaults(service_client().default_request_options(), type(), false); auto instance = std::make_shared(*this); - return instance->create_async(size, condition, modified_options, context).then([instance, size, condition, modified_options, context] () -> concurrency::streams::ostream + return instance->create_async(size, sequence_number, condition, modified_options, context).then([instance, size, condition, modified_options, context]() -> concurrency::streams::ostream { return core::cloud_page_blob_ostreambuf(instance, size, condition, modified_options, context).create_ostream(); }); } - pplx::task cloud_page_blob::upload_from_stream_async(concurrency::streams::istream source, utility::size64_t length, const access_condition& condition, const blob_request_options& options, operation_context context) + pplx::task cloud_page_blob::upload_from_stream_async(concurrency::streams::istream source, utility::size64_t length, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { assert_no_snapshot(); blob_request_options modified_options(options); @@ -123,7 +123,7 @@ namespace azure { namespace storage { } } - return open_write_async(length, condition, modified_options, context).then([source, length] (concurrency::streams::ostream blob_stream) -> pplx::task + return open_write_async(length, sequence_number, condition, modified_options, context).then([source, length](concurrency::streams::ostream blob_stream) -> pplx::task { return core::stream_copy_async(source, blob_stream, length).then([blob_stream] (utility::size64_t) -> pplx::task { @@ -132,12 +132,12 @@ namespace azure { namespace storage { }); } - pplx::task cloud_page_blob::upload_from_file_async(const utility::string_t &path, const access_condition& condition, const blob_request_options& options, operation_context context) + pplx::task cloud_page_blob::upload_from_file_async(const utility::string_t& path, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { auto instance = std::make_shared(*this); - return concurrency::streams::file_stream::open_istream(path).then([instance, condition, options, context] (concurrency::streams::istream stream) -> pplx::task + return concurrency::streams::file_stream::open_istream(path).then([instance, sequence_number, condition, options, context](concurrency::streams::istream stream) -> pplx::task { - return instance->upload_from_stream_async(stream, condition, options, context).then([stream] (pplx::task upload_task) -> pplx::task + return instance->upload_from_stream_async(stream, sequence_number, condition, options, context).then([stream](pplx::task upload_task) -> pplx::task { return stream.close().then([upload_task] () { @@ -147,7 +147,7 @@ namespace azure { namespace storage { }); } - pplx::task cloud_page_blob::create_async(utility::size64_t size, const access_condition& condition, const blob_request_options& options, operation_context context) + pplx::task cloud_page_blob::create_async(utility::size64_t size, int64_t sequence_number, const access_condition& condition, const blob_request_options& options, operation_context context) { assert_no_snapshot(); blob_request_options modified_options(options); @@ -156,7 +156,7 @@ namespace azure { namespace storage { auto properties = m_properties; auto command = std::make_shared>(uri()); - command->set_build_request(std::bind(protocol::put_page_blob, size, *properties, metadata(), condition, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + command->set_build_request(std::bind(protocol::put_page_blob, size, sequence_number, *properties, metadata(), condition, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); command->set_authentication_handler(service_client().authentication_handler()); command->set_preprocess_response([properties, size] (const web::http::http_response& response, const request_result& result, operation_context context) { diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_queue.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_queue.cpp index 5303b835..fa20d5c1 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_queue.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_queue.cpp @@ -25,17 +25,17 @@ namespace azure { namespace storage { cloud_queue::cloud_queue(const storage_uri& uri) - : m_client(create_service_client(uri, storage_credentials())), m_name(read_queue_name(uri)), m_uri(create_uri(uri)), m_approximate_message_count(-1) + : m_client(create_service_client(uri, storage_credentials())), m_name(read_queue_name(uri)), m_uri(create_uri(uri)), m_metadata(std::make_shared()), m_approximate_message_count(std::make_shared(-1)) { } cloud_queue::cloud_queue(const storage_uri& uri, storage_credentials credentials) - : m_client(create_service_client(uri, std::move(credentials))), m_name(read_queue_name(uri)), m_uri(create_uri(uri)), m_approximate_message_count(-1) + : m_client(create_service_client(uri, std::move(credentials))), m_name(read_queue_name(uri)), m_uri(create_uri(uri)), m_metadata(std::make_shared()), m_approximate_message_count(std::make_shared(-1)) { } cloud_queue::cloud_queue(cloud_queue_client client, utility::string_t name) - : m_client(std::move(client)), m_name(std::move(name)), m_uri(core::append_path_to_uri(m_client.base_uri(), m_name)), m_approximate_message_count(-1) + : m_client(std::move(client)), m_name(std::move(name)), m_uri(core::append_path_to_uri(m_client.base_uri(), m_name)), m_metadata(std::make_shared()), m_approximate_message_count(std::make_shared(-1)) { } @@ -49,14 +49,15 @@ namespace azure { namespace storage { pplx::task cloud_queue::create_if_not_exists_async(const queue_request_options& options, operation_context context) { - return exists_async_impl(options, context, /* allow_secondary */ false).then([this, options, context] (bool exists) -> pplx::task + auto instance = std::make_shared(*this); + return exists_async_impl(options, context, /* allow_secondary */ false).then([instance, options, context] (bool exists) -> pplx::task { if (exists) { return pplx::task_from_result(false); } - return create_async_impl(options, context, /* allow_conflict */ true); + return instance->create_async_impl(options, context, /* allow_conflict */ true); }); } @@ -69,14 +70,15 @@ namespace azure { namespace storage { pplx::task cloud_queue::delete_queue_if_exists_async(const queue_request_options& options, operation_context context) { - return exists_async_impl(options, context, /* allow_secondary */ false).then([this, options, context] (bool exists) -> pplx::task + auto instance = std::make_shared(*this); + return exists_async_impl(options, context, /* allow_secondary */ false).then([instance, options, context] (bool exists) -> pplx::task { if (!exists) { return pplx::task_from_result(false); } - return delete_async_impl(options, context, /* allow_not_found */ true); + return instance->delete_async_impl(options, context, /* allow_not_found */ true); }); } @@ -320,15 +322,17 @@ namespace azure { namespace storage { queue_request_options modified_options = get_modified_options(options); storage_uri uri = protocol::generate_queue_uri(service_client(), *this); + auto metadata = m_metadata; + auto approximate_message_count = m_approximate_message_count; std::shared_ptr> command = std::make_shared>(uri); command->set_build_request(std::bind(protocol::download_queue_metadata, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); command->set_authentication_handler(service_client().authentication_handler()); command->set_location_mode(core::command_location_mode::primary_or_secondary); - command->set_preprocess_response([this](const web::http::http_response& response, const request_result& result, operation_context context) + command->set_preprocess_response([metadata, approximate_message_count](const web::http::http_response& response, const request_result& result, operation_context context) { protocol::preprocess_response_void(response, result, context); - m_metadata = protocol::parse_metadata(response); - m_approximate_message_count = protocol::parse_approximate_messages_count(response); + *metadata = protocol::parse_metadata(response); + *approximate_message_count = protocol::parse_approximate_messages_count(response); }); return core::executor::execute_async(command, modified_options, context); } diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_queue_client.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_queue_client.cpp index fbc245e3..077986f8 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_queue_client.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_queue_client.cpp @@ -37,9 +37,10 @@ namespace azure { namespace storage { std::shared_ptr> results = std::make_shared>(); std::shared_ptr token = std::make_shared(); - return pplx::details::do_while([this, results, prefix, get_metadata, token, options, context] () mutable -> pplx::task + auto instance = std::make_shared(*this); + return pplx::details::do_while([instance, results, prefix, get_metadata, token, options, context]() mutable -> pplx::task { - return list_queues_segmented_async(prefix, get_metadata, -1, *token, options, context).then([results, token] (queue_result_segment result_segment) mutable -> bool + return instance->list_queues_segmented_async(prefix, get_metadata, -1, *token, options, context).then([results, token](queue_result_segment result_segment) mutable -> bool { std::vector partial_results = result_segment.results(); results->insert(results->end(), partial_results.begin(), partial_results.end()); @@ -57,12 +58,13 @@ namespace azure { namespace storage { queue_request_options modified_options = get_modified_options(options); storage_uri uri = protocol::generate_queue_uri(*this, prefix, get_metadata, max_results, token); + auto instance = std::make_shared(*this); std::shared_ptr> command = std::make_shared>(uri); command->set_build_request(std::bind(protocol::list_queues, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); command->set_authentication_handler(authentication_handler()); command->set_location_mode(core::command_location_mode::primary_or_secondary, token.target_location()); command->set_preprocess_response(std::bind(protocol::preprocess_response, queue_result_segment(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - command->set_postprocess_response([this, get_metadata] (const web::http::http_response& response, const request_result& result, const core::ostream_descriptor&, operation_context context) -> pplx::task + command->set_postprocess_response([instance, get_metadata] (const web::http::http_response& response, const request_result& result, const core::ostream_descriptor&, operation_context context) -> pplx::task { UNREFERENCED_PARAMETER(context); protocol::list_queues_reader reader(response.body()); @@ -73,7 +75,7 @@ namespace azure { namespace storage { for (std::vector::iterator it = queue_items.begin(); it != queue_items.end(); ++it) { - cloud_queue queue = get_queue_reference(it->move_name()); + cloud_queue queue = instance->get_queue_reference(it->move_name()); if (get_metadata) { queue.set_metadata(it->move_metadata()); diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_table.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_table.cpp index c97692b5..cc4929b7 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_table.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_table.cpp @@ -61,14 +61,15 @@ namespace azure { namespace storage { pplx::task cloud_table::create_if_not_exists_async(const table_request_options& options, operation_context context) { - return exists_async_impl(options, context, /* allow_secondary */ false).then([this, options, context] (bool exists) -> pplx::task + auto instance = std::make_shared(*this); + return exists_async_impl(options, context, /* allow_secondary */ false).then([instance, options, context] (bool exists) -> pplx::task { if (exists) { return pplx::task_from_result(false); } - return create_async_impl(options, context, /* allow_conflict */ true); + return instance->create_async_impl(options, context, /* allow_conflict */ true); }); } @@ -81,14 +82,15 @@ namespace azure { namespace storage { pplx::task cloud_table::delete_table_if_exists_async(const table_request_options& options, operation_context context) { - return exists_async_impl(options, context, /* allow_secondary */ false).then([this, options, context] (bool exists) -> pplx::task + auto instance = std::make_shared(*this); + return exists_async_impl(options, context, /* allow_secondary */ false).then([instance, options, context] (bool exists) -> pplx::task { if (!exists) { return pplx::task_from_result(false); } - return delete_async_impl(options, context, /* allow_not_found */ true); + return instance->delete_async_impl(options, context, /* allow_not_found */ true); }); } @@ -215,9 +217,10 @@ namespace azure { namespace storage { std::shared_ptr> results = std::make_shared>(); std::shared_ptr token = std::make_shared(); - return pplx::details::do_while([this, results, query, token, options, context] () mutable -> pplx::task + auto instance = std::make_shared(*this); + return pplx::details::do_while([instance, results, query, token, options, context] () mutable -> pplx::task { - return execute_query_segmented_async(query, *token, options, context).then([results, token] (table_query_segment query_segment) mutable -> bool + return instance->execute_query_segmented_async(query, *token, options, context).then([results, token] (table_query_segment query_segment) mutable -> bool { std::vector partial_results = query_segment.results(); results->insert(results->end(), partial_results.begin(), partial_results.end()); @@ -264,8 +267,11 @@ namespace azure { namespace storage { throw std::logic_error(protocol::error_sas_missing_credentials); } + utility::string_t table_name = name(); + std::transform(table_name.begin(), table_name.end(), table_name.begin(), core::utility_char_tolower); + utility::ostringstream_t resource_str; - resource_str << U('/') << service_client().credentials().account_name() << U('/') << name(); + resource_str << U('/') << service_client().credentials().account_name() << U('/') << table_name; return protocol::get_table_sas_token(stored_policy_identifier, policy, name(), start_partition_key, start_row_key, end_partition_key, end_row_key, resource_str.str(), service_client().credentials()); } diff --git a/Microsoft.WindowsAzure.Storage/src/cloud_table_client.cpp b/Microsoft.WindowsAzure.Storage/src/cloud_table_client.cpp index e680ca67..67a1b61d 100644 --- a/Microsoft.WindowsAzure.Storage/src/cloud_table_client.cpp +++ b/Microsoft.WindowsAzure.Storage/src/cloud_table_client.cpp @@ -33,9 +33,10 @@ namespace azure { namespace storage { std::shared_ptr> results = std::make_shared>(); std::shared_ptr token = std::make_shared(); - return pplx::details::do_while([this, results, prefix, token, options, context] () mutable -> pplx::task + auto instance = std::make_shared(*this); + return pplx::details::do_while([instance, results, prefix, token, options, context] () mutable -> pplx::task { - return list_tables_segmented_async(prefix, -1, *token, options, context).then([results, token] (table_result_segment result_segment) mutable -> bool + return instance->list_tables_segmented_async(prefix, -1, *token, options, context).then([results, token] (table_result_segment result_segment) mutable -> bool { std::vector partial_results = result_segment.results(); results->insert(results->end(), partial_results.begin(), partial_results.end()); @@ -72,7 +73,8 @@ namespace azure { namespace storage { query.set_filter_string(filter_string); } - return table.execute_query_segmented_async(query, token, options, context).then([this] (table_query_segment query_segment) -> table_result_segment + auto instance = std::make_shared(*this); + return table.execute_query_segmented_async(query, token, options, context).then([instance] (table_query_segment query_segment) -> table_result_segment { std::vector query_results = query_segment.results(); @@ -85,7 +87,7 @@ namespace azure { namespace storage { table_entity entity = *itr; utility::string_t table_name = entity.properties()[U("TableName")].string_value(); - cloud_table current_table = get_table_reference(std::move(table_name)); + cloud_table current_table = instance->get_table_reference(std::move(table_name)); table_results.push_back(std::move(current_table)); } diff --git a/Microsoft.WindowsAzure.Storage/src/protocol_json.cpp b/Microsoft.WindowsAzure.Storage/src/protocol_json.cpp index 3051d3c2..4a214eb0 100644 --- a/Microsoft.WindowsAzure.Storage/src/protocol_json.cpp +++ b/Microsoft.WindowsAzure.Storage/src/protocol_json.cpp @@ -193,22 +193,38 @@ namespace azure { namespace storage { namespace protocol { if (error_it != result_obj.cend() && error_it->second.is_object()) { const web::json::object& error_obj = error_it->second.as_object(); - - web::json::object::const_iterator code_it = error_obj.find(U("code")); - if (code_it != error_obj.cend() && code_it->second.is_string()) - { - error_code = code_it->second.as_string(); - } - - web::json::object::const_iterator message_it = error_obj.find(U("message")); - if (message_it != error_obj.cend() && message_it->second.is_object()) + for (auto prop_it = error_obj.cbegin(); prop_it != error_obj.cend(); ++prop_it) { - const web::json::object& message_obj = message_it->second.as_object(); + auto prop_name = prop_it->first; + if (prop_name == U("code") && prop_it->second.is_string()) + { + error_code = prop_it->second.as_string(); + } + else if (prop_name == U("message") && prop_it->second.is_object()) + { + const web::json::object& message_obj = prop_it->second.as_object(); - web::json::object::const_iterator message_text_it = message_obj.find(U("value")); - if (message_text_it != message_obj.cend() && message_text_it->second.is_string()) + web::json::object::const_iterator message_text_it = message_obj.find(U("value")); + if (message_text_it != message_obj.cend() && message_text_it->second.is_string()) + { + error_message = message_text_it->second.as_string(); + } + } + else if (prop_name == U("innererror")) + { + const web::json::object& inner_error_obj = prop_it->second.as_object(); + for (auto details_it = inner_error_obj.cbegin(); details_it != inner_error_obj.cend(); ++details_it) + { + if (details_it->second.is_string()) + { + details.insert(std::make_pair(details_it->first, details_it->second.as_string())); + } + } + } + else if (prop_name.find_first_of(U('.')) != utility::string_t::npos) { - error_message = message_text_it->second.as_string(); + // annotation property + // TODO: parse annotation property and add it to details } } } diff --git a/Microsoft.WindowsAzure.Storage/src/protocol_xml.cpp b/Microsoft.WindowsAzure.Storage/src/protocol_xml.cpp index ac39081f..aded3790 100644 --- a/Microsoft.WindowsAzure.Storage/src/protocol_xml.cpp +++ b/Microsoft.WindowsAzure.Storage/src/protocol_xml.cpp @@ -23,19 +23,18 @@ namespace azure { namespace storage { namespace protocol { void storage_error_reader::handle_element(const utility::string_t& element_name) { - if (get_parent_element_name() != xml_innererror_table) + if (element_name == xml_code && get_parent_element_name() == xml_error_root) { - if (element_name == xml_code || element_name == xml_code_table) - { - m_error_code = get_current_element_text(); - } - else if (element_name == xml_message || element_name == xml_message_table) - { - m_error_message = get_current_element_text(); - } + m_error_code = get_current_element_text(); + } + else if (element_name == xml_message && get_parent_element_name() == xml_error_root) + { + m_error_message = get_current_element_text(); + } + else + { + m_details.insert(std::make_pair(element_name, get_current_element_text())); } - - // TODO parse details } void list_containers_reader::handle_begin_element(const utility::string_t& element_name) diff --git a/Microsoft.WindowsAzure.Storage/src/response_parsers.cpp b/Microsoft.WindowsAzure.Storage/src/response_parsers.cpp index f8eb2b36..62162ff1 100644 --- a/Microsoft.WindowsAzure.Storage/src/response_parsers.cpp +++ b/Microsoft.WindowsAzure.Storage/src/response_parsers.cpp @@ -237,10 +237,10 @@ namespace azure { namespace storage { namespace protocol { std::unordered_map details; concurrency::streams::istream body_stream = response.body(); - protocol::storage_error_reader reader(body_stream); error_code = reader.move_error_code(); error_message = reader.move_error_message(); + details = reader.move_details(); return storage_extended_error(std::move(error_code), std::move(error_message), std::move(details)); } diff --git a/Microsoft.WindowsAzure.Storage/src/util.cpp b/Microsoft.WindowsAzure.Storage/src/util.cpp index a22e8282..e26ea490 100644 --- a/Microsoft.WindowsAzure.Storage/src/util.cpp +++ b/Microsoft.WindowsAzure.Storage/src/util.cpp @@ -77,10 +77,15 @@ namespace azure { namespace storage { namespace core { return std::numeric_limits::max(); } - pplx::task stream_copy_async(concurrency::streams::istream istream, concurrency::streams::ostream ostream, utility::size64_t length) + pplx::task stream_copy_async(concurrency::streams::istream istream, concurrency::streams::ostream ostream, utility::size64_t length, utility::size64_t max_length) { size_t buffer_size(protocol::default_buffer_size); utility::size64_t istream_length = length == std::numeric_limits::max() ? get_remaining_stream_length(istream) : length; + if ((istream_length != std::numeric_limits::max()) && (istream_length > max_length)) + { + throw std::invalid_argument(protocol::error_stream_length); + } + if ((istream_length != std::numeric_limits::max()) && (istream_length < buffer_size)) { buffer_size = static_cast(istream_length); @@ -89,7 +94,7 @@ namespace azure { namespace storage { namespace core { auto obuffer = ostream.streambuf(); auto length_ptr = (length != std::numeric_limits::max()) ? std::make_shared(length) : nullptr; auto total_ptr = std::make_shared(0); - return pplx::details::do_while([istream, obuffer, buffer_size, length_ptr, total_ptr] () -> pplx::task + return pplx::details::do_while([istream, obuffer, buffer_size, length_ptr, total_ptr, max_length] () -> pplx::task { size_t read_length = buffer_size; if ((length_ptr != nullptr) && (*length_ptr < read_length)) @@ -97,7 +102,7 @@ namespace azure { namespace storage { namespace core { read_length = static_cast(*length_ptr); } - return istream.read(obuffer, read_length).then([length_ptr, total_ptr] (size_t count) -> bool + return istream.read(obuffer, read_length).then([length_ptr, total_ptr, max_length] (size_t count) -> bool { *total_ptr += count; if (length_ptr != nullptr) @@ -105,6 +110,11 @@ namespace azure { namespace storage { namespace core { *length_ptr -= count; } + if (*total_ptr > max_length) + { + throw std::invalid_argument(protocol::error_stream_length); + } + return (count > 0) && (length_ptr == nullptr || *length_ptr > 0); }); }).then([total_ptr, length] (bool) -> utility::size64_t diff --git a/Microsoft.WindowsAzure.Storage/src/xmlhelpers.cpp b/Microsoft.WindowsAzure.Storage/src/xmlhelpers.cpp index 9e43d9f9..96559c68 100644 --- a/Microsoft.WindowsAzure.Storage/src/xmlhelpers.cpp +++ b/Microsoft.WindowsAzure.Storage/src/xmlhelpers.cpp @@ -59,8 +59,6 @@ namespace azure { namespace storage { namespace core { namespace xml { void xml_reader::initialize(streams::istream stream) { - remove_bom(stream); - #ifdef WIN32 HRESULT hr; CComPtr pInputStream; @@ -107,23 +105,6 @@ namespace azure { namespace storage { namespace core { namespace xml { #endif } - /// - /// Remove Byte Order Mark from the stream - /// - void xml_reader::remove_bom(streams::istream stream) - { - // Synchronous. - if (stream.peek().get() == 0xEF - && stream.peek().get() == 0xBB - && stream.peek().get() == 0xBF) - { - stream.read().get(); - stream.read().get(); - stream.read().get(); - return; - } - } - bool xml_reader::parse() { if (m_streamDone) return false; diff --git a/Microsoft.WindowsAzure.Storage/tests/CMakeLists.txt b/Microsoft.WindowsAzure.Storage/tests/CMakeLists.txt index bbe779af..7af28e15 100644 --- a/Microsoft.WindowsAzure.Storage/tests/CMakeLists.txt +++ b/Microsoft.WindowsAzure.Storage/tests/CMakeLists.txt @@ -19,12 +19,14 @@ if(UNIX) cloud_table_test.cpp executor_test.cpp main.cpp + queue_test_base.cpp read_from_secondary_test.cpp retry_policy_test.cpp service_properties_test.cpp stdafx.cpp + storage_exception_test.cpp + table_test_base.cpp test_base.cpp - test_helper.cpp ) if(APPLE) # set(SOURCES ${SOURCES} apple.cpp) @@ -51,12 +53,14 @@ elseif(WIN32) cloud_table_test.cpp executor_test.cpp main.cpp + queue_test_base.cpp read_from_secondary_test.cpp retry_policy_test.cpp service_properties_test.cpp stdafx.cpp + storage_exception_test.cpp + table_test_base.cpp test_base.cpp - test_helper.cpp ) add_definitions( -D_ASYNCRT_EXPORT @@ -95,3 +99,5 @@ else() SOVERSION ${AZURESTORAGE_VERSION_MAJOR}.${AZURESTORAGE_VERSION_MINOR}) endif() +# Copy test configuration to output directory +file(COPY test_configurations.json DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj index eaed55b2..b08f01f1 100644 --- a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj @@ -95,10 +95,11 @@ + + - @@ -111,6 +112,7 @@ + @@ -125,8 +127,9 @@ Create Create + + - diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters index bb50afff..8136dc85 100644 --- a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters @@ -24,9 +24,6 @@ Header Files - - Header Files - Header Files @@ -36,6 +33,12 @@ Header Files + + Header Files + + + Header Files + @@ -95,9 +98,6 @@ Source Files - - Source Files - Source Files @@ -107,6 +107,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj index 370c56fb..ecaaa5e3 100644 --- a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj @@ -95,10 +95,11 @@ + + - @@ -111,6 +112,7 @@ + @@ -125,8 +127,9 @@ Create Create + + - diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters index bb50afff..c291e982 100644 --- a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters @@ -24,9 +24,6 @@ Header Files - - Header Files - Header Files @@ -36,6 +33,12 @@ Header Files + + Header Files + + + Header Files + @@ -95,9 +98,6 @@ Source Files - - Source Files - Source Files @@ -107,9 +107,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + - \ No newline at end of file + diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp b/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp index 125a810d..0c6cb578 100644 --- a/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp @@ -147,8 +147,8 @@ SUITE(Blob) { m_blob.upload_text(U("test"), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); - CHECK_THROW(m_blob.acquire_lease(azure::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - CHECK_THROW(m_blob.acquire_lease(azure::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.acquire_lease(azure::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), std::invalid_argument); + CHECK_THROW(m_blob.acquire_lease(azure::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), std::invalid_argument); } TEST_FIXTURE(block_blob_test_base, blob_lease_renew) @@ -294,8 +294,8 @@ SUITE(Blob) { m_container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context); - CHECK_THROW(m_container.acquire_lease(azure::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - CHECK_THROW(m_container.acquire_lease(azure::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_container.acquire_lease(azure::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), std::invalid_argument); + CHECK_THROW(m_container.acquire_lease(azure::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context), std::invalid_argument); } TEST_FIXTURE(container_test_base, container_lease_renew) @@ -388,4 +388,41 @@ SUITE(Blob) std::this_thread::sleep_for(std::chrono::seconds(40)); check_lease_access(m_container, azure::storage::lease_state::broken, lease_id, false, true); } + + TEST_FIXTURE(blob_test_base, lease_time_constructor) + { + azure::storage::lease_time lease_time1; + CHECK_EQUAL(-1, lease_time1.seconds().count()); + + int valid_times[] = { -1, 15, 20, 60 }; + for (int valid_time : valid_times) + { + CHECK_EQUAL(valid_time, azure::storage::lease_time(std::chrono::seconds(valid_time)).seconds().count()); + } + + int invalid_times[] = { -2, 0, 1, 14, 61 }; + for (int invalid_time : invalid_times) + { + CHECK_THROW(azure::storage::lease_time(std::chrono::seconds(invalid_time)), std::invalid_argument); + } + } + + TEST_FIXTURE(blob_test_base, lease_break_period_constructor) + { + CHECK_EQUAL(std::numeric_limits::max(), azure::storage::lease_break_period().seconds().count()); + CHECK_EQUAL(std::numeric_limits::max(), azure::storage::lease_break_period(std::chrono::seconds(std::numeric_limits::max())).seconds().count()); + CHECK_THROW(azure::storage::lease_break_period(std::chrono::seconds(std::numeric_limits::min())), std::invalid_argument); + + int valid_periods[] = { 0, 1, 60}; + for (int valid_period : valid_periods) + { + CHECK_EQUAL(valid_period, azure::storage::lease_break_period(std::chrono::seconds(valid_period)).seconds().count()); + } + + int invalid_periods[] = { -1, -2, 61 }; + for (int invalid_period : invalid_periods) + { + CHECK_THROW(azure::storage::lease_break_period(std::chrono::seconds(invalid_period)), std::invalid_argument); + } + } } diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp b/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp index 74639c29..6a95c8c4 100644 --- a/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp @@ -30,6 +30,152 @@ size_t seek_read_and_compare(concurrency::streams::istream stream, std::vector& content_to_compare) +{ + size_t content_len = (int)content_to_compare.size(); + size_t pos1 = 0; + size_t pos2 = content_len / 4; + size_t pos3 = content_len / 2; + size_t pos4 = content_len - content_len / 4; + + std::vector buffer; + buffer.resize(content_len); + + CHECK_EQUAL(pos2, stream.seek((int)pos2, std::ios_base::beg)); + for (size_t i = pos2; i < pos3; ++i) + { + buffer[i] = (uint8_t)stream.read().get(); + } + + CHECK_EQUAL(pos1, stream.seek((int)(pos1 - pos3), std::ios_base::cur)); + for (size_t i = pos1; i < pos2; ++i) + { + buffer[i] = (uint8_t)stream.read().get(); + } + + CHECK_EQUAL(pos3, stream.seek((int)(pos3 - content_len), std::ios_base::end)); + for (size_t i = pos3; i < pos4; ++i) + { + buffer[i] = (uint8_t)stream.read().get(); + } + + CHECK_EQUAL(pos4, stream.seek((int)(pos4 - content_len), std::ios_base::end)); + for (size_t i = pos4; i < content_len; ++i) + { + buffer[i] = (uint8_t)stream.read().get(); + } + + CHECK_ARRAY_EQUAL(content_to_compare.data(), buffer.data(), (int)content_to_compare.size()); +} + +void seek_and_read_getn(concurrency::streams::istream stream, const std::vector& content_to_compare) +{ + size_t content_len = content_to_compare.size(); + size_t pos1 = 0; + size_t pos2 = content_len / 4; + size_t pos3 = content_len / 2; + size_t pos4 = content_len - content_len / 4; + + std::vector buffer; + buffer.resize(content_len); + concurrency::streams::container_buffer> sbuf(buffer, std::ios_base::out); + + sbuf.seekpos(pos3, std::ios_base::out); + CHECK_EQUAL(pos3, stream.seek((int)pos3, std::ios_base::beg)); + CHECK_EQUAL(pos4 - pos3, stream.read(sbuf, pos4 - pos3).get()); + + sbuf.seekpos(pos1, std::ios_base::out); + CHECK_EQUAL(pos1, stream.seek((int)(pos1 - pos4), std::ios_base::cur)); + CHECK_EQUAL(pos2 - pos1, stream.read(sbuf, pos2 - pos1).get()); + + sbuf.seekpos(pos2, std::ios_base::out); + CHECK_EQUAL(pos2, stream.seek((int)(pos2 - content_len), std::ios_base::end)); + CHECK_EQUAL(pos3 - pos2, stream.read(sbuf, pos3 - pos2).get()); + + sbuf.seekpos(pos4, std::ios_base::out); + CHECK_EQUAL(pos4, stream.seek((int)(pos4 - pos3), std::ios_base::cur)); + CHECK_EQUAL(content_len - pos4, stream.read(sbuf, content_len - pos4).get()); + + CHECK_ARRAY_EQUAL(content_to_compare.data(), sbuf.collection().data(), (int)content_len); + + CHECK_EQUAL((concurrency::streams::istream::pos_type)concurrency::streams::istream::traits::eof(), stream.seek(-1, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::istream::pos_type)0, stream.seek(0, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::istream::pos_type)content_len, stream.seek(0, std::ios_base::end)); + CHECK_EQUAL((concurrency::streams::istream::pos_type)concurrency::streams::istream::traits::eof(), stream.seek(1, std::ios_base::end)); + CHECK_EQUAL((concurrency::streams::istream::pos_type)content_len, stream.seek(content_len, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::istream::pos_type)concurrency::streams::istream::traits::eof(), stream.seek(content_len + 1, std::ios_base::beg)); +} + +void seek_and_write_putc(concurrency::streams::ostream stream, const std::vector& content_buffer) +{ + size_t content_len = content_buffer.size(); + size_t pos1 = 0; + size_t pos2 = content_len / 4; + size_t pos3 = content_len / 2; + size_t pos4 = content_len - content_len / 4; + + CHECK_EQUAL(pos2, stream.seek((int)pos2, std::ios_base::beg)); + for (size_t i = pos2; i < pos3; ++i) + { + stream.write(content_buffer[i]); + } + + CHECK_EQUAL(pos4, stream.seek((int)(pos4 - pos3), std::ios_base::cur)); + for (size_t i = pos4; i < content_len - pos4; ++i) + { + stream.write(content_buffer[i]); + } + + CHECK_EQUAL(pos3, stream.seek((int)(pos3 - content_len), std::ios_base::end)); + for (size_t i = pos3; i < pos4; ++i) + { + stream.write(content_buffer[i]); + } + + CHECK_EQUAL(pos1, stream.seek((int)(pos1 - content_len), std::ios_base::end)); + for (size_t i = pos1; i < pos2; ++i) + { + stream.write(content_buffer[i]); + } + + stream.close().wait(); +} + +void seek_and_write_putn(concurrency::streams::ostream stream, const std::vector& content_buffer) +{ + size_t content_len = content_buffer.size(); + size_t pos1 = 0; + size_t pos2 = content_len / 4; + size_t pos3 = content_len / 2; + size_t pos4 = content_len - content_len / 4; + + concurrency::streams::container_buffer> sbuf(content_buffer); + sbuf.seekpos(pos2, std::ios_base::in); + CHECK_EQUAL(pos2, stream.seek((int)pos2, std::ios_base::beg)); + CHECK_EQUAL(pos3 - pos2, stream.write(sbuf, pos3 - pos2).get()); + + sbuf.seekpos(pos4, std::ios_base::in); + CHECK_EQUAL(pos4, stream.seek((int)(pos4 - pos3), std::ios_base::cur)); + CHECK_EQUAL(content_len - pos4, stream.write(sbuf, content_len - pos4).get()); + + sbuf.seekpos(pos3, std::ios_base::in); + CHECK_EQUAL(pos3, stream.seek((int)(pos3 - content_len), std::ios_base::end)); + CHECK_EQUAL(pos4 - pos3, stream.write(sbuf, pos4 - pos3).get()); + + sbuf.seekpos(pos1, std::ios_base::in); + CHECK_EQUAL(pos1, stream.seek((int)(pos1 - content_len), std::ios_base::end)); + CHECK_EQUAL(pos2 - pos1, stream.write(sbuf, pos2 - pos1).get()); + + CHECK_EQUAL((concurrency::streams::ostream::pos_type)0, stream.seek(0, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::ostream::pos_type)concurrency::streams::ostream::traits::eof(), stream.seek(-1, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::ostream::pos_type)content_len, stream.seek(0, std::ios_base::end)); + CHECK_EQUAL((concurrency::streams::ostream::pos_type)concurrency::streams::ostream::traits::eof(), stream.seek(1, std::ios_base::end)); + CHECK_EQUAL((concurrency::streams::ostream::pos_type)content_len, stream.seek(content_len, std::ios_base::beg)); + CHECK_EQUAL((concurrency::streams::ostream::pos_type)concurrency::streams::ostream::traits::eof(), stream.seek(content_len + 1, std::ios_base::beg)); + + stream.close().wait(); +} + SUITE(Blob) { TEST_FIXTURE(block_blob_test_base, blob_read_stream_download) @@ -143,6 +289,36 @@ SUITE(Blob) CHECK_EQUAL(attempts, m_context.request_results().size()); } + TEST_FIXTURE(block_blob_test_base, existing_block_blob_stream_seek_read_getc) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 16 * 1024; + std::vector input_buffer; + input_buffer.resize(buffer_size); + fill_buffer_and_get_md5(input_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(input_buffer), azure::storage::access_condition(), options, m_context); + + auto blob_stream = m_blob.open_read(azure::storage::access_condition(), options, m_context); + seek_and_read_getc(blob_stream, input_buffer); + blob_stream.close().wait(); + } + + TEST_FIXTURE(block_blob_test_base, existing_block_blob_stream_seek_read_getn) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 64 * 1024; + std::vector input_buffer; + input_buffer.resize(buffer_size); + fill_buffer_and_get_md5(input_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(input_buffer), azure::storage::access_condition(), options, m_context); + + auto blob_stream = m_blob.open_read(azure::storage::access_condition(), options, m_context); + seek_and_read_getn(blob_stream, input_buffer); + blob_stream.close().wait(); + } + TEST_FIXTURE(block_blob_test_base, block_blob_write_stream_seek) { auto stream = m_blob.open_write(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); @@ -155,7 +331,7 @@ SUITE(Blob) azure::storage::blob_request_options options; options.set_store_blob_content_md5(true); - auto stream = m_blob.open_write(16 * 1024, azure::storage::access_condition(), options, m_context); + auto stream = m_blob.open_write(16 * 1024, 0, azure::storage::access_condition(), options, m_context); CHECK(!stream.can_seek()); CHECK_EQUAL(concurrency::streams::ostream::traits::eof(), stream.seek(0)); } @@ -169,7 +345,7 @@ SUITE(Blob) azure::storage::blob_request_options options; options.set_store_blob_content_md5(false); - auto stream = m_blob.open_write(final_blob_contents.size(), azure::storage::access_condition(), options, m_context); + auto stream = m_blob.open_write(final_blob_contents.size(), 0, azure::storage::access_condition(), options, m_context); CHECK(stream.can_seek()); // Because container create and blob create are also in the request results, @@ -224,7 +400,7 @@ SUITE(Blob) std::vector buffer; buffer.resize(16 * 1024); fill_buffer_and_get_md5(buffer); - m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), azure::storage::access_condition(), options, m_context); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), 0, azure::storage::access_condition(), options, m_context); CHECK_THROW(m_blob.open_write(azure::storage::access_condition(), options, m_context), std::logic_error); @@ -299,43 +475,106 @@ SUITE(Blob) CHECK_THROW(stream.close().wait(), azure::storage::storage_exception); } + TEST_FIXTURE(page_blob_test_base, existing_page_blob_stream_seek_read_getc) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 16 * 1024; + std::vector input_buffer; + input_buffer.resize(buffer_size); + fill_buffer_and_get_md5(input_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(input_buffer), 0, azure::storage::access_condition(), options, m_context); + CHECK_EQUAL(buffer_size, m_blob.properties().size()); + + auto blob_stream = m_blob.open_read(azure::storage::access_condition(), options, m_context); + seek_and_read_getc(blob_stream, input_buffer); + blob_stream.close().wait(); + } + + TEST_FIXTURE(page_blob_test_base, existing_page_blob_stream_seek_read_getn) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 64 * 1024; + std::vector input_buffer; + input_buffer.resize(buffer_size); + fill_buffer_and_get_md5(input_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(input_buffer), 0, azure::storage::access_condition(), options, m_context); + CHECK_EQUAL(buffer_size, m_blob.properties().size()); + + auto blob_stream = m_blob.open_read(azure::storage::access_condition(), options, m_context); + seek_and_read_getn(blob_stream, input_buffer); + blob_stream.close().wait(); + } + + TEST_FIXTURE(page_blob_test_base, existing_page_blob_stream_seek_write_putc) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 16 * 1024; + std::vector original_buffer; + original_buffer.resize(buffer_size); + fill_buffer_and_get_md5(original_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(original_buffer), 0, azure::storage::access_condition(), options, m_context); + CHECK_EQUAL(buffer_size, m_blob.properties().size()); + + auto blob_stream = m_blob.open_write(azure::storage::access_condition(), options, m_context); + seek_and_write_putc(blob_stream, original_buffer); + blob_stream.close().wait(); + } + + TEST_FIXTURE(page_blob_test_base, existing_page_blob_stream_seek_write_putn) + { + azure::storage::blob_request_options options; + + const size_t buffer_size = 64 * 1024; + std::vector original_buffer; + original_buffer.resize(buffer_size); + fill_buffer_and_get_md5(original_buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(original_buffer), 0, azure::storage::access_condition(), options, m_context); + CHECK_EQUAL(buffer_size, m_blob.properties().size()); + + auto blob_stream = m_blob.open_write(azure::storage::access_condition(), options, m_context); + seek_and_write_putn(blob_stream, original_buffer); + blob_stream.close().wait(); + } TEST_FIXTURE(page_blob_test_base, page_blob_write_stream_access_condition) { - m_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); auto missing_blob = m_container.get_page_blob_reference(U("missing_blob1")); - CHECK_THROW(missing_blob.open_write(0, azure::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(missing_blob.open_write(0, 0, azure::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); missing_blob = m_container.get_page_blob_reference(U("missing_blob2")); - missing_blob.open_write(0, azure::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); + missing_blob.open_write(0, 0, azure::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); missing_blob = m_container.get_page_blob_reference(U("missing_blob3")); - missing_blob.open_write(0, azure::storage::access_condition::generate_if_none_match_condition(U("*")), azure::storage::blob_request_options(), m_context).close().wait(); + missing_blob.open_write(0, 0, azure::storage::access_condition::generate_if_none_match_condition(U("*")), azure::storage::blob_request_options(), m_context).close().wait(); missing_blob = m_container.get_page_blob_reference(U("missing_blob4")); - missing_blob.open_write(0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); + missing_blob.open_write(0, 0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); missing_blob = m_container.get_page_blob_reference(U("missing_blob5")); - missing_blob.open_write(0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); + missing_blob.open_write(0, 0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); - m_blob.open_write(0, azure::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); + m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); - CHECK_THROW(m_blob.open_write(0, azure::storage::access_condition::generate_if_match_condition(missing_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_match_condition(missing_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - m_blob.open_write(0, azure::storage::access_condition::generate_if_none_match_condition(missing_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); + m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_none_match_condition(missing_blob.properties().etag()), azure::storage::blob_request_options(), m_context).close().wait(); - CHECK_THROW(m_blob.open_write(0, azure::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - CHECK_THROW(m_blob.open_write(0, azure::storage::access_condition::generate_if_none_match_condition(U("*")), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_none_match_condition(U("*")), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - m_blob.open_write(0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); + m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); - CHECK_THROW(m_blob.open_write(0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); - m_blob.open_write(0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); + m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context).close().wait(); - CHECK_THROW(m_blob.open_write(0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.open_write(0, 0, azure::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); } TEST_FIXTURE(block_blob_test_base, block_blob_write_stream_maximum_execution_time) @@ -360,7 +599,7 @@ SUITE(Blob) azure::storage::blob_request_options options; options.set_maximum_execution_time(duration); - auto stream = m_blob.open_write(512, azure::storage::access_condition(), options, m_context); + auto stream = m_blob.open_write(512, 0, azure::storage::access_condition(), options, m_context); std::this_thread::sleep_for(duration); @@ -378,7 +617,7 @@ SUITE(Blob) azure::storage::blob_request_options options; options.set_maximum_execution_time(duration); - m_blob.create(512, azure::storage::access_condition(), options, m_context); + m_blob.create(512, 0, azure::storage::access_condition(), options, m_context); auto stream = m_blob.open_write(azure::storage::access_condition(), options, m_context); std::this_thread::sleep_for(duration); diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h index 9fedeb75..9edbcd0d 100644 --- a/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h +++ b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h @@ -129,7 +129,11 @@ class blob_service_test_base_with_objects_to_delete : public blob_service_test_b } protected: - + void create_containers(const utility::string_t& prefix, std::size_t num); + void create_blobs(const azure::storage::cloud_blob_container& container, const utility::string_t& prefix, std::size_t num); + void check_container_list(const std::vector& list, const utility::string_t& prefix, bool check_found); + void check_blob_list(const std::vector& list); + std::vector m_blobs_to_delete; std::vector m_containers_to_delete; }; diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp index bbcf47f6..ed1b8abc 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp @@ -21,6 +21,87 @@ #pragma region Fixture +void blob_service_test_base_with_objects_to_delete::create_containers(const utility::string_t& prefix, std::size_t num) +{ + for (std::size_t i = 0; i < num; ++i) + { + auto index = utility::conversions::print_string(i); + auto container = m_client.get_container_reference(prefix + index); + m_containers_to_delete.push_back(container); + container.metadata()[U("index")] = index; + container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context); + } +} + +void blob_service_test_base_with_objects_to_delete::create_blobs(const azure::storage::cloud_blob_container& container, const utility::string_t& prefix, std::size_t num) +{ + for (std::size_t i = 0; i < num; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = container.get_block_blob_reference(prefix + index); + m_blobs_to_delete.push_back(blob); + blob.upload_text(U("test"), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + } +} + +void blob_service_test_base_with_objects_to_delete::check_container_list(const std::vector& list, const utility::string_t& prefix, bool check_found) +{ + auto container_list_sorted = std::is_sorted(list.cbegin(), list.cend(), [](const azure::storage::cloud_blob_container& a, const azure::storage::cloud_blob_container& b) + { + return a.name() < b.name(); + }); + CHECK(container_list_sorted); + + std::vector containers(m_containers_to_delete); + + for (auto list_iter = list.begin(); list_iter != list.end(); ++list_iter) + { + bool found = false; + for (auto iter = containers.begin(); iter != containers.end(); ++iter) + { + if (iter->name() == list_iter->name()) + { + auto index_str = list_iter->metadata().find(U("index")); + CHECK(index_str != list_iter->metadata().end()); + CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second); + containers.erase(iter); + found = true; + break; + } + } + if (check_found) + { + CHECK(found); + } + } + CHECK(containers.empty()); +} + +void blob_service_test_base_with_objects_to_delete::check_blob_list(const std::vector& list) +{ + auto blob_list_sorted = std::is_sorted(list.cbegin(), list.cend(), [](const azure::storage::cloud_blob& a, const azure::storage::cloud_blob& b) + { + return a.name() < b.name(); + }); + CHECK(blob_list_sorted); + + std::vector blobs(m_blobs_to_delete); + + for (auto list_iter = list.begin(); list_iter != list.end(); ++list_iter) + { + for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) + { + if (iter->name() == list_iter->name()) + { + blobs.erase(iter); + break; + } + } + } + + CHECK(blobs.empty()); +} + std::vector blob_service_test_base::list_all_containers(const utility::string_t& prefix, azure::storage::container_listing_details::values includes, int max_results, const azure::storage::blob_request_options& options) { std::vector containers; @@ -89,72 +170,33 @@ SUITE(Blob) { auto prefix = get_random_container_name(); - for (int i = 0; i < 1; i++) - { - auto index = utility::conversions::print_string(i); - auto container = m_client.get_container_reference(prefix + index); - m_containers_to_delete.push_back(container); - container.metadata()[U("index")] = index; - container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context); - } - - std::vector containers(m_containers_to_delete); + create_containers(prefix, 1); auto listing = list_all_containers(prefix, azure::storage::container_listing_details::all, 1, azure::storage::blob_request_options()); - for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) - { - bool found = false; - for (auto iter = containers.begin(); iter != containers.end(); ++iter) - { - if (iter->name() == listing_iter->name()) - { - auto index_str = listing_iter->metadata().find(U("index")); - CHECK(index_str != listing_iter->metadata().end()); - CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second); - containers.erase(iter); - found = true; - break; - } - } - - CHECK(found); - } - - CHECK(containers.empty()); + + check_container_list(listing, prefix, true); } TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_containers) { auto prefix = get_random_container_name(); - for (int i = 0; i < 1; i++) - { - auto index = utility::conversions::print_string(i); - auto container = m_client.get_container_reference(prefix + index); - m_containers_to_delete.push_back(container); - container.metadata()[U("index")] = index; - container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context); - } + create_containers(prefix, 1); - std::vector containers(m_containers_to_delete); + auto listing = list_all_containers(utility::string_t(), azure::storage::container_listing_details::all, 5001, azure::storage::blob_request_options()); + + check_container_list(listing, prefix, false); + } - auto listing = list_all_containers(utility::string_t(), azure::storage::container_listing_details::all, 5000, azure::storage::blob_request_options()); - for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) - { - for (auto iter = containers.begin(); iter != containers.end(); ++iter) - { - if (iter->name() == listing_iter->name()) - { - auto index_str = listing_iter->metadata().find(U("index")); - CHECK(index_str != listing_iter->metadata().end()); - CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second); - containers.erase(iter); - break; - } - } - } + TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_containers_with_continuation_token) + { + auto prefix = get_random_container_name(); + + create_containers(prefix, 10); - CHECK(containers.empty()); + auto listing = list_all_containers(prefix, azure::storage::container_listing_details::all, 3, azure::storage::blob_request_options()); + + check_container_list(listing, prefix, true); } TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_blobs_from_client_root) @@ -164,30 +206,11 @@ SUITE(Blob) auto prefix = get_random_container_name(); - for (int i = 0; i < 1; i++) - { - auto index = utility::conversions::print_string(i); - auto blob = root_container.get_block_blob_reference(prefix + index); - m_blobs_to_delete.push_back(blob); - blob.upload_text(U("test"), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); - } - - std::vector blobs(m_blobs_to_delete); + create_blobs(root_container, prefix, 1); auto listing = list_all_blobs_from_client(prefix, azure::storage::blob_listing_details::none, 1, azure::storage::blob_request_options()); - for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) - { - for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) - { - if (iter->name() == listing_iter->name()) - { - blobs.erase(iter); - break; - } - } - } - - CHECK(blobs.empty()); + + check_blob_list(listing); } TEST_FIXTURE(blob_test_base, list_blobs_from_client) diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp index 22cb0f32..0ff8507c 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp @@ -203,7 +203,7 @@ SUITE(Blob) auto blob = m_container.get_page_blob_reference(U("pageblob") + index); blob.metadata()[U("index")] = index; - blob.create(i * 512, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + blob.create(i * 512, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); blobs[blob.name()] = blob; } diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp index c15d2c55..cffe70c7 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp @@ -58,7 +58,7 @@ void create_blob_tree(const azure::storage::cloud_blob_container& container, con if (iter->type() == azure::storage::blob_type::page_blob) { azure::storage::cloud_page_blob page_blob(blob); - page_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), context); + page_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), context); } else { diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp index 950d3287..ceb9214e 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp @@ -83,11 +83,11 @@ azure::storage::operation_context blob_test_base::upload_and_download(azure::sto azure::storage::cloud_page_blob page_blob(blob); if (blob_size == 0) { - page_blob.upload_from_stream(stream, azure::storage::access_condition(), options, context); + page_blob.upload_from_stream(stream, 0, azure::storage::access_condition(), options, context); } else { - page_blob.upload_from_stream(stream, blob_size, azure::storage::access_condition(), options, context); + page_blob.upload_from_stream(stream, blob_size, 0, azure::storage::access_condition(), options, context); } } @@ -220,7 +220,7 @@ SUITE(Blob) auto blob = m_container.get_page_blob_reference(U("pageblob")); CHECK(!blob.exists(azure::storage::blob_request_options(), m_context)); - blob.create(1024, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + blob.create(1024, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); auto same_blob = m_container.get_page_blob_reference(U("pageblob")); @@ -236,7 +236,7 @@ SUITE(Blob) auto blob = m_container.get_page_blob_reference(U("pageblob")); CHECK_THROW(blob.download_attributes(azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); - blob.create(1024, azure::storage::access_condition(), options, m_context); + blob.create(1024, 0, azure::storage::access_condition(), options, m_context); CHECK_EQUAL(1024, blob.properties().size()); CHECK(!blob.properties().etag().empty()); CHECK((utility::datetime::utc_now() - blob.properties().last_modified()) < (int64_t)utility::datetime::from_minutes(5)); @@ -289,7 +289,7 @@ SUITE(Blob) { auto page_blob = m_container.get_page_blob_reference(U("pageblob")); CHECK(azure::storage::blob_type::page_blob == page_blob.type()); - page_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + page_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); auto block_blob = m_container.get_block_blob_reference(U("blockblob")); CHECK(azure::storage::blob_type::block_blob == block_blob.type()); diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp index f53232b0..71d0fbd1 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp @@ -19,6 +19,9 @@ #include "blob_test_base.h" #include "check_macros.h" +#include "cpprest/producerconsumerstream.h" +#include "wascore/constants.h" + #pragma region Fixture utility::string_t block_blob_test_base::get_block_id(uint16_t block_index) @@ -129,16 +132,56 @@ SUITE(Blob) CHECK_UTF8_EQUAL(md5, md5_header); } + options.set_use_transactional_md5(false); + { + // upload a block of max_block_size + std::vector big_buffer; + big_buffer.resize(azure::storage::protocol::max_block_size); + auto md5 = fill_buffer_and_get_md5(big_buffer); + auto stream = concurrency::streams::bytestream::open_istream(big_buffer); + auto block_id = get_block_id(9); + uncommitted_blocks.push_back(azure::storage::block_list_item(block_id)); + m_blob.upload_block(block_id, stream, md5, azure::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + check_block_list_equal(committed_blocks, uncommitted_blocks); std::copy(uncommitted_blocks.begin(), uncommitted_blocks.end(), std::back_inserter(committed_blocks)); m_blob.upload_block_list(committed_blocks, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); uncommitted_blocks.clear(); - options.set_use_transactional_md5(true); - fill_buffer_and_get_md5(buffer); - auto stream = concurrency::streams::bytestream::open_istream(buffer); - CHECK_THROW(m_blob.upload_block(get_block_id(0), stream, dummy_md5, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); - CHECK_UTF8_EQUAL(dummy_md5, md5_header); + { + options.set_use_transactional_md5(true); + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + CHECK_THROW(m_blob.upload_block(get_block_id(0), stream, dummy_md5, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); + CHECK_UTF8_EQUAL(dummy_md5, md5_header); + } + + options.set_use_transactional_md5(false); + + // trying upload blocks bigger than max_block_size + { + buffer.resize(azure::storage::protocol::max_block_size + 1); + fill_buffer_and_get_md5(buffer); + + // seekable stream + auto stream = concurrency::streams::bytestream::open_istream(buffer); + CHECK_THROW(m_blob.upload_block(get_block_id(0), stream, utility::string_t(), azure::storage::access_condition(), options, m_context), std::invalid_argument); + } + + { + buffer.resize(azure::storage::protocol::max_block_size * 2); + fill_buffer_and_get_md5(buffer); + + concurrency::streams::producer_consumer_buffer pcbuffer; + pcbuffer.putn(buffer.data(), azure::storage::protocol::max_block_size * 2); + pcbuffer.close(std::ios_base::out); + + // non-seekable stream + auto stream = pcbuffer.create_istream(); + CHECK_THROW(m_blob.upload_block(get_block_id(0), stream, utility::string_t(), azure::storage::access_condition(), options, m_context), std::invalid_argument); + } check_block_list_equal(committed_blocks, uncommitted_blocks); diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp index 710e45ec..812ef6dc 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp @@ -40,7 +40,7 @@ SUITE(Blob) { auto same_blob = m_container.get_page_blob_reference(m_blob.name()); CHECK(!same_blob.exists(azure::storage::blob_request_options(), m_context)); - m_blob.create(1024, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(1024, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK_EQUAL(1024, m_blob.properties().size()); CHECK_EQUAL(0, same_blob.properties().size()); CHECK(same_blob.exists(azure::storage::blob_request_options(), m_context)); @@ -48,7 +48,7 @@ SUITE(Blob) m_blob.delete_blob(azure::storage::delete_snapshots_option::none, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK(!same_blob.exists(azure::storage::blob_request_options(), m_context)); CHECK_EQUAL(1024, same_blob.properties().size()); - m_blob.create(2048, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(2048, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK_EQUAL(2048, m_blob.properties().size()); CHECK_EQUAL(1024, same_blob.properties().size()); CHECK(same_blob.exists(azure::storage::blob_request_options(), m_context)); @@ -58,7 +58,7 @@ SUITE(Blob) TEST_FIXTURE(page_blob_test_base, page_blob_resize) { m_blob.properties().set_content_language(U("tr,en")); - m_blob.create(1024, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(1024, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK_EQUAL(1024, m_blob.properties().size()); m_blob.properties().set_content_language(U("en")); @@ -73,7 +73,7 @@ SUITE(Blob) TEST_FIXTURE(page_blob_test_base, page_blob_sequence_number) { m_blob.properties().set_content_language(U("tr,en")); - m_blob.create(512, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(512, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK_EQUAL(512, m_blob.properties().size()); CHECK_EQUAL(0, m_blob.properties().page_blob_sequence_number()); @@ -106,6 +106,14 @@ SUITE(Blob) CHECK_THROW(m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_condition(7), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception); } + TEST_FIXTURE(page_blob_test_base, page_blob_create_with_sequence_number) + { + int64_t seq = get_random_int64() & 0x7FFFFFFFFFFFFFFFllu; + m_blob.create(512, seq, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.set_sequence_number(azure::storage::sequence_number::increment(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + CHECK_EQUAL(seq + 1, m_blob.properties().page_blob_sequence_number()); + } + TEST_FIXTURE(page_blob_test_base, page_upload) { std::vector buffer; @@ -122,7 +130,7 @@ SUITE(Blob) } }); - m_blob.create(1 * 1024 * 1024, azure::storage::access_condition(), options, m_context); + m_blob.create(12 * 1024 * 1024, 0, azure::storage::access_condition(), options, m_context); options.set_use_transactional_md5(false); for (int i = 0; i < 3; ++i) @@ -161,6 +169,19 @@ SUITE(Blob) CHECK_UTF8_EQUAL(md5, md5_header); } + options.set_use_transactional_md5(false); + { + // upload a page range of max_block_size + std::vector big_buffer; + big_buffer.resize(azure::storage::protocol::max_block_size); + auto md5 = fill_buffer_and_get_md5(big_buffer); + auto stream = concurrency::streams::bytestream::open_istream(big_buffer); + azure::storage::page_range range(4 * 1024 * 1024, 4 * 1024 * 1024 + azure::storage::protocol::max_block_size - 1); + pages.push_back(range); + m_blob.upload_pages(stream, range.start_offset(), md5, azure::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + check_page_ranges_equal(pages); options.set_use_transactional_md5(true); @@ -169,6 +190,16 @@ SUITE(Blob) CHECK_THROW(m_blob.upload_pages(stream, 0, dummy_md5, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); CHECK_UTF8_EQUAL(dummy_md5, md5_header); + // trying upload page ranges bigger than max_block_size + { + buffer.resize(azure::storage::protocol::max_block_size + 1); + fill_buffer_and_get_md5(buffer); + + azure::storage::page_range range(8 * 1024 * 1024, 8 * 1024 * 1024 + azure::storage::protocol::max_block_size -1); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + CHECK_THROW(m_blob.upload_pages(stream, range.start_offset(), utility::string_t(), azure::storage::access_condition(), options, m_context), std::invalid_argument); + } + check_page_ranges_equal(pages); m_context.set_sending_request(std::function()); @@ -182,7 +213,7 @@ SUITE(Blob) fill_buffer_and_get_md5(buffer); auto stream = concurrency::streams::bytestream::open_istream(buffer); - m_blob.upload_from_stream(stream, azure::storage::access_condition(), options, m_context); + m_blob.upload_from_stream(stream, 0, azure::storage::access_condition(), options, m_context); std::vector pages; pages.push_back(azure::storage::page_range(0, 512 - 1)); @@ -339,10 +370,10 @@ SUITE(Blob) }); temp_file invalid_file(1000); - CHECK_THROW(m_blob.upload_from_file(invalid_file.path(), azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.upload_from_file(invalid_file.path(), 0, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); temp_file file(1024); - m_blob.upload_from_file(file.path(), azure::storage::access_condition(), options, m_context); + m_blob.upload_from_file(file.path(), 0, azure::storage::access_condition(), options, m_context); CHECK_UTF8_EQUAL(file.content_md5(), md5_header); m_context.set_sending_request(std::function()); @@ -371,7 +402,7 @@ SUITE(Blob) TEST_FIXTURE(page_blob_test_base, page_blob_constructor) { - m_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK(!m_blob.properties().etag().empty()); azure::storage::cloud_page_blob blob1(m_blob.uri()); @@ -397,7 +428,7 @@ SUITE(Blob) { m_blob.metadata()[U("key1")] = U("value1"); m_blob.metadata()[U("key2")] = U("value2"); - m_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); auto same_blob = m_container.get_page_blob_reference(m_blob.name()); CHECK(same_blob.metadata().empty()); @@ -411,7 +442,7 @@ SUITE(Blob) { m_blob.metadata()[U("key1")] = U("value1"); m_blob.metadata()[U("key2")] = U("value2"); - m_blob.create(0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); + m_blob.create(0, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); auto snapshot1 = m_blob.create_snapshot(azure::storage::cloud_metadata(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context); CHECK_EQUAL(2, snapshot1.metadata().size()); @@ -457,7 +488,7 @@ SUITE(Blob) std::this_thread::sleep_for(duration); }); - CHECK_THROW(m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(std::move(buffer)), azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); + CHECK_THROW(m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(std::move(buffer)), 0, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception); CHECK_EQUAL(2, m_context.request_results().size()); m_context.set_response_received(std::function()); diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp index d30392e1..fd62a38b 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp @@ -16,12 +16,12 @@ // ----------------------------------------------------------------------------------------- #include "stdafx.h" -#include "test_helper.h" +#include "queue_test_base.h" #include "was/queue.h" SUITE(QueueClient) { - TEST(QueueClient_Empty) + TEST_FIXTURE(queue_service_test_base, QueueClient_Empty) { azure::storage::cloud_queue_client client; @@ -30,7 +30,7 @@ SUITE(QueueClient) CHECK(client.credentials().is_anonymous()); } - TEST(QueueClient_BaseUri) + TEST_FIXTURE(queue_service_test_base, QueueClient_BaseUri) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); @@ -41,7 +41,7 @@ SUITE(QueueClient) CHECK(client.credentials().is_anonymous()); } - TEST(QueueClient_BaseUriAndCredentials) + TEST_FIXTURE(queue_service_test_base, QueueClient_BaseUriAndCredentials) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -54,7 +54,7 @@ SUITE(QueueClient) CHECK(client.credentials().is_shared_key()); } - TEST(QueueClient_BaseUriAndCredentialsAndDefaultRequestOptions) + TEST_FIXTURE(queue_service_test_base, QueueClient_BaseUriAndCredentialsAndDefaultRequestOptions) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -73,7 +73,7 @@ SUITE(QueueClient) CHECK(client.default_request_options().location_mode() == azure::storage::location_mode::secondary_only); } - TEST(ListQueues_Normal) + TEST_FIXTURE(queue_service_test_base, ListQueues_Normal) { const int QUEUE_COUNT = 5; @@ -207,7 +207,7 @@ SUITE(QueueClient) } } - TEST(ListQueues_Segmented) + TEST_FIXTURE(queue_service_test_base, ListQueues_Segmented) { const int QUEUE_COUNT = 5; diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp index bef981a3..40879f33 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp @@ -16,12 +16,12 @@ // ----------------------------------------------------------------------------------------- #include "stdafx.h" -#include "test_helper.h" +#include "queue_test_base.h" #include "was/queue.h" SUITE(Queue) { - TEST(Queue_Empty) + TEST_FIXTURE(queue_service_test_base, Queue_Empty) { azure::storage::cloud_queue queue; @@ -34,7 +34,7 @@ SUITE(Queue) CHECK_EQUAL(-1, queue.approximate_message_count()); } - TEST(Queue_Uri) + TEST_FIXTURE(queue_service_test_base, Queue_Uri) { azure::storage::storage_uri uri(web::http::uri(U("https://myaccount.queue.core.windows.net/myqueue")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net/myqueue"))); @@ -67,7 +67,7 @@ SUITE(Queue) CHECK_EQUAL(-1, queue2.approximate_message_count()); } - TEST(Queue_UriAndCredentials) + TEST_FIXTURE(queue_service_test_base, Queue_UriAndCredentials) { azure::storage::storage_uri uri(web::http::uri(U("https://myaccount.queue.core.windows.net/myqueue")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net/myqueue"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -109,7 +109,7 @@ SUITE(Queue) CHECK_THROW(azure::storage::cloud_queue(sas_uri, credentials), std::invalid_argument); } - TEST(Message_Empty) + TEST_FIXTURE(queue_service_test_base, Message_Empty) { azure::storage::cloud_queue_message message; @@ -123,7 +123,7 @@ SUITE(Queue) CHECK_EQUAL(0, message.dequeue_count()); } - TEST(Message_String) + TEST_FIXTURE(queue_service_test_base, Message_String) { utility::string_t content = get_random_string(); @@ -151,7 +151,7 @@ SUITE(Queue) CHECK_EQUAL(0, message.dequeue_count()); } - TEST(Message_Binary) + TEST_FIXTURE(queue_service_test_base, Message_Binary) { std::vector content = get_random_binary_data(); @@ -179,7 +179,7 @@ SUITE(Queue) CHECK_EQUAL(0, message.dequeue_count()); } - TEST(Message_IdAndPopReceipt) + TEST_FIXTURE(queue_service_test_base, Message_IdAndPopReceipt) { utility::string_t id = get_random_string(); utility::string_t pop_receipt = get_random_string(); @@ -196,14 +196,14 @@ SUITE(Queue) CHECK_EQUAL(0, message.dequeue_count()); } - TEST(QueueRequestOptions_Normal) + TEST_FIXTURE(queue_service_test_base, QueueRequestOptions_Normal) { azure::storage::queue_request_options options; CHECK(options.location_mode() == azure::storage::location_mode::primary_only); } - TEST(Queue_CreateAndDelete) + TEST_FIXTURE(queue_service_test_base, Queue_CreateAndDelete) { utility::string_t queue_name1 = get_queue_name(); utility::string_t queue_name2 = get_queue_name(); @@ -309,7 +309,6 @@ SUITE(Queue) CHECK_EQUAL(web::http::status_codes::Conflict, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("QueueAlreadyExists")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -327,7 +326,6 @@ SUITE(Queue) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("QueueAlreadyExists")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } { @@ -394,7 +392,6 @@ SUITE(Queue) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("QueueNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -412,11 +409,10 @@ SUITE(Queue) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("QueueNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } } - TEST(Queue_CreateIfNotExistsAndDeleteIfExists) + TEST_FIXTURE(queue_service_test_base, Queue_CreateIfNotExistsAndDeleteIfExists) { utility::string_t queue_name = get_queue_name(); azure::storage::cloud_queue_client client = get_queue_client(); @@ -604,7 +600,7 @@ SUITE(Queue) } } - TEST(Queue_ApproximateMessageCount) + TEST_FIXTURE(queue_service_test_base, Queue_ApproximateMessageCount) { const int MESSAGE_COUNT = 3; @@ -642,7 +638,7 @@ SUITE(Queue) queue.delete_queue(); } - TEST(Queue_Metadata) + TEST_FIXTURE(queue_service_test_base, Queue_Metadata) { azure::storage::cloud_queue_client client = get_queue_client(); utility::string_t queue_name = get_queue_name(); @@ -730,7 +726,7 @@ SUITE(Queue) queue1.delete_queue(); } - TEST(Queue_NotFound) + TEST_FIXTURE(queue_service_test_base, Queue_NotFound) { utility::string_t queue_name = get_queue_name(); azure::storage::cloud_queue_client client = get_queue_client(); @@ -755,7 +751,6 @@ SUITE(Queue) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("QueueNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -773,10 +768,9 @@ SUITE(Queue) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("QueueNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } - TEST(Queue_Messages) + TEST_FIXTURE(queue_service_test_base, Queue_Messages) { azure::storage::cloud_queue queue = get_queue(); @@ -1362,7 +1356,7 @@ SUITE(Queue) queue.delete_queue(); } - TEST(Queue_InvalidMessages) + TEST_FIXTURE(queue_service_test_base, Queue_InvalidMessages) { azure::storage::cloud_queue queue = get_queue(); @@ -1538,7 +1532,7 @@ SUITE(Queue) } } - TEST(Queue_Permissions) + TEST_FIXTURE(queue_service_test_base, Queue_Permissions) { azure::storage::cloud_queue queue = get_queue(); @@ -1694,7 +1688,7 @@ SUITE(Queue) } } - TEST(Queue_SharedAccessSignature) + TEST_FIXTURE(queue_service_test_base, Queue_SharedAccessSignature) { azure::storage::cloud_queue queue1 = get_queue(); @@ -1724,7 +1718,7 @@ SUITE(Queue) CHECK_THROW(queue2.update_message(message2, std::chrono::seconds(0), /* update_content */ false), azure::storage::storage_exception); } - TEST(Queue_RepeatedAddAndGetAndUpdateMessage) + TEST_FIXTURE(queue_service_test_base, Queue_RepeatedAddAndGetAndUpdateMessage) { azure::storage::cloud_queue queue = get_queue(); diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp index b7035032..dad3cc2d 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp @@ -17,14 +17,17 @@ #include "stdafx.h" +#include "test_base.h" #include "check_macros.h" #include "was/storage_account.h" #include "was/blob.h" #include "was/queue.h" #include "was/table.h" +#include "wascore/constants.h" const utility::string_t test_uri(U("http://test/abc")); const utility::string_t token(U("sp=abcde&sig=1")); +const utility::string_t token_with_api_version(azure::storage::protocol::uri_query_sas_api_version + U("=") + azure::storage::protocol::header_value_storage_version + U("&sig=1&sp=abcde")); const utility::string_t test_account_name(U("test")); const utility::string_t test_account_key(U("Fby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); const utility::string_t test_endpoint_suffix(U("fake.endpoint.suffix")); @@ -51,9 +54,16 @@ void check_account_equal(azure::storage::cloud_storage_account a, azure::storage check_credentials_equal(a.credentials(), b.credentials()); } +void check_string_roundtrip(const utility::string_t& connection_string) +{ + auto account = azure::storage::cloud_storage_account::parse(connection_string); + CHECK_UTF8_EQUAL(connection_string, account.to_string(true)); + check_account_equal(account, azure::storage::cloud_storage_account::parse(account.to_string(true))); +} + SUITE(Core) { - TEST(storage_credentials_anonymous) + TEST_FIXTURE(test_base, storage_credentials_anonymous) { azure::storage::storage_credentials creds; @@ -67,7 +77,7 @@ SUITE(Core) CHECK_UTF8_EQUAL(test_uri, creds.transform_uri(uri).to_string()); } - TEST(storage_credentials_shared_key) + TEST_FIXTURE(test_base, storage_credentials_shared_key) { azure::storage::storage_credentials creds(test_account_name, test_account_key); @@ -83,7 +93,7 @@ SUITE(Core) CHECK_UTF8_EQUAL(test_account_key, utility::conversions::to_base64(creds.account_key())); } - TEST(storage_credentials_sas) + TEST_FIXTURE(test_base, storage_credentials_sas) { { azure::storage::storage_credentials creds(token); @@ -96,7 +106,7 @@ SUITE(Core) CHECK(!creds.is_shared_key()); web::http::uri uri(test_uri); - CHECK_UTF8_EQUAL(test_uri + U("?") + token, creds.transform_uri(uri).to_string()); + CHECK_UTF8_EQUAL(test_uri + U("?") + token_with_api_version, creds.transform_uri(uri).to_string()); } { @@ -110,11 +120,11 @@ SUITE(Core) CHECK(!creds.is_shared_key()); web::http::uri uri(test_uri); - CHECK_UTF8_EQUAL(test_uri + U("?") + token, creds.transform_uri(uri).to_string()); + CHECK_UTF8_EQUAL(test_uri + U("?") + token_with_api_version, creds.transform_uri(uri).to_string()); } } - TEST(storage_credentials_empty_key) + TEST_FIXTURE(test_base, storage_credentials_empty_key) { const utility::string_t defaults_connection_string(U("DefaultEndpointsProtocol=https;AccountName=") + test_account_name + U(";AccountKey=")); @@ -135,7 +145,7 @@ SUITE(Core) check_credentials_equal(creds, account2.credentials()); } - TEST(cloud_storage_account_devstore) + TEST_FIXTURE(test_base, cloud_storage_account_devstore) { auto account = azure::storage::cloud_storage_account::development_storage_account(); CHECK_UTF8_EQUAL(U("http://127.0.0.1:10000/devstoreaccount1"), account.blob_endpoint().primary_uri().to_string()); @@ -146,7 +156,7 @@ SUITE(Core) CHECK_UTF8_EQUAL(U("http://127.0.0.1:10002/devstoreaccount1-secondary"), account.table_endpoint().secondary_uri().to_string()); } - TEST(cloud_storage_account_default_http) + TEST_FIXTURE(test_base, cloud_storage_account_default_http) { azure::storage::storage_credentials creds(test_account_name, test_account_key); azure::storage::cloud_storage_account account(creds, false); @@ -162,7 +172,7 @@ SUITE(Core) check_account_equal(account, account2); } - TEST(cloud_storage_account_default_https) + TEST_FIXTURE(test_base, cloud_storage_account_default_https) { azure::storage::storage_credentials creds(test_account_name, test_account_key); azure::storage::cloud_storage_account account(creds, true); @@ -178,7 +188,7 @@ SUITE(Core) check_account_equal(account, account2); } - TEST(cloud_storage_account_endpoint_suffix_http) + TEST_FIXTURE(test_base, cloud_storage_account_endpoint_suffix_http) { utility::ostringstream_t str; str << U("DefaultEndpointsProtocol=http;AccountName=") << test_account_name << ";AccountKey=" << test_account_key << ";EndpointSuffix=" << test_endpoint_suffix; @@ -192,7 +202,7 @@ SUITE(Core) CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.table.") + test_endpoint_suffix + U("/"), account.table_endpoint().secondary_uri().to_string()); } - TEST(cloud_storage_account_endpoint_suffix_https) + TEST_FIXTURE(test_base, cloud_storage_account_endpoint_suffix_https) { utility::ostringstream_t str; str << U("DefaultEndpointsProtocol=https;AccountName=") << test_account_name << ";AccountKey=" << test_account_key << ";EndpointSuffix=" << test_endpoint_suffix; @@ -206,14 +216,7 @@ SUITE(Core) CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.table.") + test_endpoint_suffix + U("/"), account.table_endpoint().secondary_uri().to_string()); } - void check_string_roundtrip(const utility::string_t& connection_string) - { - auto account = azure::storage::cloud_storage_account::parse(connection_string); - CHECK_UTF8_EQUAL(connection_string, account.to_string(true)); - check_account_equal(account, azure::storage::cloud_storage_account::parse(account.to_string(true))); - } - - TEST(cloud_storage_account_string_roundtrip) + TEST_FIXTURE(test_base, cloud_storage_account_string_roundtrip) { check_string_roundtrip(U("UseDevelopmentStorage=true")); check_string_roundtrip(U("DevelopmentStorageProxyUri=http://ipv4.fiddler/;UseDevelopmentStorage=true")); @@ -232,13 +235,13 @@ SUITE(Core) check_string_roundtrip(U("TableEndpoint=https://alternate.table.endpoint/;SharedAccessSignature=abc=def")); } - TEST(cloud_storage_account_string_empty_values) + TEST_FIXTURE(test_base, cloud_storage_account_string_empty_values) { auto account = azure::storage::cloud_storage_account::parse(U(";BlobEndpoint=http://blobs/;;AccountName=test;;AccountKey=abc=;")); CHECK_UTF8_EQUAL(U("BlobEndpoint=http://blobs/;AccountName=test;AccountKey=abc="), account.to_string(true)); } - TEST(cloud_storage_account_clients) + TEST_FIXTURE(test_base, cloud_storage_account_clients) { azure::storage::storage_credentials creds(test_account_name, test_account_key); azure::storage::cloud_storage_account account(creds, false); @@ -256,21 +259,21 @@ SUITE(Core) CHECK_UTF8_EQUAL(account.table_endpoint().secondary_uri().to_string(), table_client.base_uri().secondary_uri().to_string()); } - TEST(cloud_storage_account_incorrect_devstore) + TEST_FIXTURE(test_base, cloud_storage_account_incorrect_devstore) { CHECK_THROW(azure::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=false")), std::invalid_argument); CHECK_THROW(azure::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;AccountName=devstoreaccount1")), std::invalid_argument); CHECK_THROW(azure::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;BlobEndpoint=http://127.0.0.1:1000/devstoreaccount1")), std::invalid_argument); } - TEST(cloud_storage_account_blob_endpoint) + TEST_FIXTURE(test_base, cloud_storage_account_blob_endpoint) { auto account = azure::storage::cloud_storage_account::parse(U("DefaultEndpointsProtocol=http;BlobEndpoint=http://customdomain.com/;AccountName=asdf;AccountKey=abc=")); CHECK_UTF8_EQUAL(U("http://customdomain.com/"), account.blob_endpoint().primary_uri().to_string()); CHECK(account.blob_endpoint().secondary_uri().is_empty()); } - TEST(cloud_storage_account_devstore_proxy) + TEST_FIXTURE(test_base, cloud_storage_account_devstore_proxy) { auto account = azure::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://ipv4.fiddler")); CHECK_UTF8_EQUAL(U("http://ipv4.fiddler:10000/devstoreaccount1"), account.blob_endpoint().primary_uri().to_string()); diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp index 33f9195e..20d1c5b9 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp @@ -16,12 +16,12 @@ // ----------------------------------------------------------------------------------------- #include "stdafx.h" -#include "test_helper.h" +#include "table_test_base.h" #include "was/table.h" SUITE(TableClient) { - TEST(TableClient_Empty) + TEST_FIXTURE(table_service_test_base, TableClient_Empty) { azure::storage::cloud_table_client client; @@ -31,7 +31,7 @@ SUITE(TableClient) CHECK(client.default_request_options().payload_format() == azure::storage::table_payload_format::json); } - TEST(TableClient_BaseUri) + TEST_FIXTURE(table_service_test_base, TableClient_BaseUri) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); @@ -43,7 +43,7 @@ SUITE(TableClient) CHECK(client.default_request_options().payload_format() == azure::storage::table_payload_format::json); } - TEST(TableClient_BaseUriAndCredentials) + TEST_FIXTURE(table_service_test_base, TableClient_BaseUriAndCredentials) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -57,7 +57,7 @@ SUITE(TableClient) CHECK(client.default_request_options().payload_format() == azure::storage::table_payload_format::json); } - TEST(TableClient_BaseUriAndCredentialsAndDefaultRequestOptions) + TEST_FIXTURE(table_service_test_base, TableClient_BaseUriAndCredentialsAndDefaultRequestOptions) { azure::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -77,7 +77,7 @@ SUITE(TableClient) CHECK(client.default_request_options().payload_format() == azure::storage::table_payload_format::json_no_metadata); } - TEST(ListTables_Normal) + TEST_FIXTURE(table_service_test_base, ListTables_Normal) { const int TABLE_COUNT = 5; @@ -146,7 +146,7 @@ SUITE(TableClient) } } - TEST(ListTables_Segmented) + TEST_FIXTURE(table_service_test_base, ListTables_Segmented) { const int TABLE_COUNT = 5; diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp index bc4b2c68..a98054d6 100644 --- a/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp @@ -16,7 +16,7 @@ // ----------------------------------------------------------------------------------------- #include "stdafx.h" -#include "test_helper.h" +#include "table_test_base.h" #include "was/table.h" #include "was/storage_account.h" @@ -24,7 +24,7 @@ SUITE(Table) { - TEST(Table_Empty) + TEST_FIXTURE(table_service_test_base, Table_Empty) { azure::storage::cloud_table table; @@ -36,7 +36,7 @@ SUITE(Table) CHECK(table.uri().secondary_uri().is_empty()); } - TEST(Table_Uri) + TEST_FIXTURE(table_service_test_base, Table_Uri) { azure::storage::storage_uri uri(web::http::uri(U("https://myaccount.table.core.windows.net/mytable")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net/mytable"))); @@ -67,7 +67,7 @@ SUITE(Table) CHECK(table2.uri().secondary_uri() == uri.secondary_uri()); } - TEST(Table_UriAndCredentials) + TEST_FIXTURE(table_service_test_base, Table_UriAndCredentials) { azure::storage::storage_uri uri(web::http::uri(U("https://myaccount.table.core.windows.net/mytable")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net/mytable"))); azure::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); @@ -107,7 +107,7 @@ SUITE(Table) CHECK_THROW(azure::storage::cloud_table(sas_uri, credentials), std::invalid_argument); } - TEST(EntityProperty_Binary) + TEST_FIXTURE(table_service_test_base, EntityProperty_Binary) { std::vector value = get_random_binary_data(); azure::storage::entity_property property(value); @@ -171,7 +171,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_Boolean) + TEST_FIXTURE(table_service_test_base, EntityProperty_Boolean) { bool value = get_random_boolean(); azure::storage::entity_property property(value); @@ -244,7 +244,7 @@ SUITE(Table) CHECK_EQUAL(0, property.str().size()); } - TEST(EntityProperty_DateTime) + TEST_FIXTURE(table_service_test_base, EntityProperty_DateTime) { utility::datetime value = get_random_datetime(); azure::storage::entity_property property(value); @@ -281,7 +281,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_Double) + TEST_FIXTURE(table_service_test_base, EntityProperty_Double) { double value = get_random_double(); azure::storage::entity_property property(value); @@ -360,7 +360,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_Guid) + TEST_FIXTURE(table_service_test_base, EntityProperty_Guid) { utility::uuid value = get_random_guid(); azure::storage::entity_property property(value); @@ -424,7 +424,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_Int32) + TEST_FIXTURE(table_service_test_base, EntityProperty_Int32) { int32_t value = get_random_int32(); azure::storage::entity_property property(value); @@ -479,7 +479,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_Int64) + TEST_FIXTURE(table_service_test_base, EntityProperty_Int64) { int64_t value = get_random_int64(); azure::storage::entity_property property(value); @@ -516,7 +516,7 @@ SUITE(Table) CHECK(property.str().size() > 0); } - TEST(EntityProperty_String) + TEST_FIXTURE(table_service_test_base, EntityProperty_String) { { utility::string_t value = get_random_string(); @@ -591,7 +591,7 @@ SUITE(Table) } } - TEST(EntityProperty_Null) + TEST_FIXTURE(table_service_test_base, EntityProperty_Null) { azure::storage::entity_property property; @@ -665,7 +665,7 @@ SUITE(Table) CHECK_THROW(property.string_value(), std::runtime_error); } - TEST(Entity_PartitionKeyAndRowKey) + TEST_FIXTURE(table_service_test_base, Entity_PartitionKeyAndRowKey) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -699,7 +699,7 @@ SUITE(Table) CHECK(entity.properties().size() == 2U); } - TEST(Entity_PartitionKeyAndRowKeyAndETagAndProperties) + TEST_FIXTURE(table_service_test_base, Entity_PartitionKeyAndRowKeyAndETagAndProperties) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -740,7 +740,7 @@ SUITE(Table) CHECK(entity.properties().size() == 5U); } - TEST(Operation_Delete) + TEST_FIXTURE(table_service_test_base, Operation_Delete) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -755,7 +755,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::delete_operation); } - TEST(Operation_Insert) + TEST_FIXTURE(table_service_test_base, Operation_Insert) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -770,7 +770,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::insert_operation); } - TEST(Operation_InsertOrMerge) + TEST_FIXTURE(table_service_test_base, Operation_InsertOrMerge) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -785,7 +785,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::insert_or_merge_operation); } - TEST(Operation_InsertOrReplace) + TEST_FIXTURE(table_service_test_base, Operation_InsertOrReplace) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -800,7 +800,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::insert_or_replace_operation); } - TEST(Operation_Merge) + TEST_FIXTURE(table_service_test_base, Operation_Merge) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -815,7 +815,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::merge_operation); } - TEST(Operation_Replace) + TEST_FIXTURE(table_service_test_base, Operation_Replace) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -830,7 +830,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::replace_operation); } - TEST(Operation_Retrieve) + TEST_FIXTURE(table_service_test_base, Operation_Retrieve) { utility::string_t partition_key = get_random_string(); utility::string_t row_key = get_random_string(); @@ -844,7 +844,7 @@ SUITE(Table) CHECK(operation.operation_type() == azure::storage::table_operation_type::retrieve_operation); } - TEST(Query_Normal) + TEST_FIXTURE(table_service_test_base, Query_Normal) { azure::storage::table_query query; @@ -875,7 +875,7 @@ SUITE(Table) CHECK(query.select_columns()[2].compare(U("PropertyC")) == 0); } - TEST(TableRequestOptions_Normal) + TEST_FIXTURE(table_service_test_base, TableRequestOptions_Normal) { azure::storage::table_request_options options; @@ -887,7 +887,7 @@ SUITE(Table) CHECK(options.payload_format() == payload_format); } - TEST(Table_CreateAndDelete) + TEST_FIXTURE(table_service_test_base, Table_CreateAndDelete) { utility::string_t table_name = get_table_name(); azure::storage::cloud_table_client client = get_table_client(); @@ -958,7 +958,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::Conflict, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("TableAlreadyExists")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -976,7 +975,6 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("TableAlreadyExists")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } { @@ -1043,7 +1041,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -1061,11 +1058,10 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } } - TEST(Table_CreateIfNotExistsAndDeleteIfExists) + TEST_FIXTURE(table_service_test_base, Table_CreateIfNotExistsAndDeleteIfExists) { utility::string_t table_name = get_table_name(); azure::storage::cloud_table_client client = get_table_client(); @@ -1253,7 +1249,7 @@ SUITE(Table) } } - TEST(Table_NotFound) + TEST_FIXTURE(table_service_test_base, Table_NotFound) { utility::string_t table_name = get_table_name(); azure::storage::cloud_table_client client = get_table_client(); @@ -1286,7 +1282,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("TableNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -1304,10 +1299,9 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("TableNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } - TEST(EntityOperation_InsertAndDelete) + TEST_FIXTURE(table_service_test_base, EntityOperation_InsertAndDelete) { azure::storage::cloud_table table = get_table(); @@ -1444,7 +1438,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::Conflict, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("EntityAlreadyExists")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -1462,7 +1455,6 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("EntityAlreadyExists")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } { @@ -1551,7 +1543,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -1569,13 +1560,12 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } table.delete_table(); } - TEST(EntityOperation_InsertAndMerge) + TEST_FIXTURE(table_service_test_base, EntityOperation_InsertAndMerge) { azure::storage::cloud_table table = get_table(); @@ -1976,7 +1966,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -1994,13 +1983,12 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } table.delete_table(); } - TEST(EntityOperation_InsertAndReplace) + TEST_FIXTURE(table_service_test_base, EntityOperation_InsertAndReplace) { azure::storage::cloud_table table = get_table(); @@ -2389,7 +2377,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::NotFound, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -2407,13 +2394,12 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("ResourceNotFound")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } table.delete_table(); } - TEST(EntityOperation_Timeout) + TEST_FIXTURE(table_service_test_base, EntityOperation_Timeout) { azure::storage::cloud_table table = get_table(); @@ -2530,7 +2516,7 @@ SUITE(Table) table.delete_table(); } - TEST(EntityOperation_InvalidValueType) + TEST_FIXTURE(table_service_test_base, EntityOperation_InvalidValueType) { azure::storage::cloud_table table = get_table(); @@ -2560,7 +2546,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::BadRequest, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -2578,13 +2563,12 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } table.delete_table(); } - TEST(EntityOperation_DoubleSpecialValues) + TEST_FIXTURE(table_service_test_base, EntityOperation_DoubleSpecialValues) { azure::storage::cloud_table table = get_table(); @@ -2682,7 +2666,7 @@ SUITE(Table) table.delete_table(); } - TEST(Casablanca_DoubleJsonParsing) + TEST_FIXTURE(table_service_test_base, Casablanca_DoubleJsonParsing) { for (int i = 0; i < 50000; ++i) { @@ -2697,7 +2681,7 @@ SUITE(Table) web::json::value input_document = web::json::value::object(fields); utility::string_t message = input_document.serialize(); web::json::value output_document = web::json::value::parse(message); - + CHECK(output_document.is_object()); CHECK(output_document.as_object().find(U("DoubleProperty")) != output_document.as_object().cend()); CHECK_EQUAL(web::json::value::value_type::Number, output_document.as_object().find(U("DoubleProperty"))->second.type()); @@ -2706,7 +2690,7 @@ SUITE(Table) } } - TEST(EntityBatch_Normal) + TEST_FIXTURE(table_service_test_base, EntityBatch_Normal) { const int BATCH_SIZE = 3; @@ -3298,7 +3282,7 @@ SUITE(Table) table.delete_table(); } - TEST(EntityBatch_InvalidInput) + TEST_FIXTURE(table_service_test_base, EntityBatch_InvalidInput) { const int BATCH_SIZE = 3; @@ -3334,7 +3318,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::BadRequest, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -3352,7 +3335,6 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } { @@ -3374,7 +3356,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::BadRequest, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -3392,13 +3373,12 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); } table.delete_table(); } - TEST(EntityBatch_PartitionKeyMismatch) + TEST_FIXTURE(table_service_test_base, EntityBatch_PartitionKeyMismatch) { azure::storage::cloud_table table = get_table(false); @@ -3420,7 +3400,7 @@ SUITE(Table) CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); } - TEST(EntityBatch_MultipleRetrieve) + TEST_FIXTURE(table_service_test_base, EntityBatch_MultipleRetrieve) { azure::storage::cloud_table table = get_table(false); @@ -3438,7 +3418,7 @@ SUITE(Table) CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); } - TEST(EntityBatch_RetrieveMixture) + TEST_FIXTURE(table_service_test_base, EntityBatch_RetrieveMixture) { azure::storage::cloud_table table = get_table(false); @@ -3458,7 +3438,7 @@ SUITE(Table) CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); } - TEST(EntityQuery_Normal) + TEST_FIXTURE(table_service_test_base, EntityQuery_Normal) { azure::storage::cloud_table table = get_table(); @@ -3753,7 +3733,7 @@ SUITE(Table) table.delete_table(); } - TEST(EntityQuery_Segmented) + TEST_FIXTURE(table_service_test_base, EntityQuery_Segmented) { azure::storage::cloud_table table = get_table(); @@ -4050,7 +4030,7 @@ SUITE(Table) table.delete_table(); } - TEST(EntityQuery_Empty) + TEST_FIXTURE(table_service_test_base, EntityQuery_Empty) { azure::storage::cloud_table table = get_table(); @@ -4092,7 +4072,7 @@ SUITE(Table) table.delete_table(); } - TEST(EntityQuery_InvalidInput) + TEST_FIXTURE(table_service_test_base, EntityQuery_InvalidInput) { azure::storage::cloud_table table = get_table(); @@ -4116,7 +4096,6 @@ SUITE(Table) CHECK_EQUAL(web::http::status_codes::BadRequest, e.result().http_status_code()); CHECK(e.result().extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!e.result().extended_error().message().empty()); - CHECK(e.result().extended_error().details().empty()); } CHECK(!context.client_request_id().empty()); @@ -4134,12 +4113,11 @@ SUITE(Table) CHECK(context.request_results()[0].etag().empty()); CHECK(context.request_results()[0].extended_error().code().compare(U("InvalidInput")) == 0); CHECK(!context.request_results()[0].extended_error().message().empty()); - CHECK(context.request_results()[0].extended_error().details().empty()); table.delete_table(); } - TEST(EntityQuery_UriEncoding) + TEST_FIXTURE(table_service_test_base, EntityQuery_UriEncoding) { azure::storage::cloud_table table = get_table(); @@ -4240,7 +4218,7 @@ SUITE(Table) table.delete_table(); } - TEST(Table_Permissions) + TEST_FIXTURE(table_service_test_base, Table_Permissions) { azure::storage::cloud_table table = get_table(); @@ -4419,7 +4397,7 @@ SUITE(Table) } } - TEST(Table_SharedAccessSignature) + TEST_FIXTURE(table_service_test_base, Table_SharedAccessSignature) { azure::storage::cloud_table table1 = get_table(); @@ -4488,7 +4466,7 @@ SUITE(Table) } } - TEST(Table_TruncatedDateTime) + TEST_FIXTURE(table_service_test_base, Table_TruncatedDateTime) { azure::storage::cloud_table table = get_table(); diff --git a/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp b/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp index 419e3d3c..62b7da9a 100644 --- a/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp @@ -21,7 +21,7 @@ SUITE(Core) { - TEST(timeout) + TEST_FIXTURE(test_base, timeout) { azure::storage::cloud_blob_client client = test_config::instance().account().create_cloud_blob_client(); azure::storage::cloud_blob_container container = client.get_container_reference(U("this-container-does-not-exist")); @@ -62,7 +62,7 @@ SUITE(Core) } } - TEST(operation_context) + TEST_FIXTURE(test_base, operation_context) { auto client = test_config::instance().account().create_cloud_blob_client(); @@ -104,7 +104,7 @@ SUITE(Core) CHECK(result.end_time().to_interval() > result.start_time().to_interval()); } - TEST(storage_uri) + TEST_FIXTURE(test_base, storage_uri) { azure::storage::storage_uri(U("http://www.microsoft.com/test1")); azure::storage::storage_uri(U("http://www.microsoft.com/test1"), U("http://www.microsoft.com/test1")); @@ -140,4 +140,32 @@ SUITE(Core) CHECK_THROW(azure::storage::storage_uri(U("http://www.microsoft.com/test1/example1"), U("http://127.0.0.1:10000/account/test2/example2")), std::invalid_argument); CHECK_THROW(azure::storage::storage_uri(U("http://www.microsoft.com/test1?parameter=value1"), U("http://127.0.0.1:10000/account/test1?parameter=value2")), std::invalid_argument); } + + TEST(storage_exception) + { + azure::storage::cloud_blob blob(azure::storage::storage_uri(U("http://www.nonexistenthost.com/test1"))); + + bool caught_storage_exception = false; + bool caught_http_exception = false; + try + { + blob.exists(); + } + catch (const azure::storage::storage_exception& ex1) + { + caught_storage_exception = true; + + try + { + std::rethrow_exception(ex1.inner_exception()); + } + catch (web::http::http_exception&) + { + caught_http_exception = true; + } + } + + CHECK_EQUAL(true, caught_storage_exception); + CHECK_EQUAL(true, caught_http_exception); + } } diff --git a/Microsoft.WindowsAzure.Storage/tests/queue_test_base.cpp b/Microsoft.WindowsAzure.Storage/tests/queue_test_base.cpp new file mode 100644 index 00000000..3fafa11c --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/queue_test_base.cpp @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "queue_test_base.h" +#include "check_macros.h" +#include "wascore/streams.h" + +utility::string_t queue_service_test_base::queue_type_name = utility::string_t(U("queue")); + +azure::storage::cloud_queue_client queue_service_test_base::get_queue_client() +{ + return test_config::instance().account().create_cloud_queue_client(); +} + +utility::string_t queue_service_test_base::get_queue_name() +{ + return get_object_name(queue_type_name); +} + +azure::storage::cloud_queue queue_service_test_base::get_queue(bool create) +{ + azure::storage::cloud_queue_client client = get_queue_client(); + utility::string_t queue_name = get_queue_name(); + azure::storage::cloud_queue queue = client.get_queue_reference(queue_name); + if (create) + { + queue.create_if_not_exists(); + } + return queue; +} diff --git a/Microsoft.WindowsAzure.Storage/tests/queue_test_base.h b/Microsoft.WindowsAzure.Storage/tests/queue_test_base.h new file mode 100644 index 00000000..033a44c0 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/queue_test_base.h @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#include +#include "CurrentTest.h" + +#include "test_base.h" +#include "was/queue.h" + +class queue_service_test_base : public test_base +{ +public: + + queue_service_test_base() + { + } + + ~queue_service_test_base() + { + } + +protected: + static utility::string_t queue_type_name; + + static azure::storage::cloud_queue_client get_queue_client(); + static utility::string_t get_queue_name(); + static azure::storage::cloud_queue get_queue(bool create = true); +}; diff --git a/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp b/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp index 6ddfd594..f4967441 100644 --- a/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp @@ -110,7 +110,7 @@ static azure::storage::retry_info create_fake_retry_info(azure::storage::storage SUITE(Core) { - TEST(retry_info) + TEST_FIXTURE(test_base, retry_info) { { azure::storage::retry_info info; @@ -138,7 +138,7 @@ SUITE(Core) } } - TEST(no_retry_results) + TEST_FIXTURE(test_base, no_retry_results) { auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds { @@ -192,7 +192,7 @@ SUITE(Core) } } - TEST(exponential_retry_results) + TEST_FIXTURE(test_base, exponential_retry_results) { auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds { @@ -274,7 +274,7 @@ SUITE(Core) } } - TEST(linear_retry_results) + TEST_FIXTURE(test_base, linear_retry_results) { auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds { diff --git a/Microsoft.WindowsAzure.Storage/tests/storage_exception_test.cpp b/Microsoft.WindowsAzure.Storage/tests/storage_exception_test.cpp new file mode 100644 index 00000000..2cad25b3 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/storage_exception_test.cpp @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "table_test_base.h" +#include "check_macros.h" + +SUITE(Core) +{ + TEST_FIXTURE(block_blob_test_base, storage_extended_error_verify_xml_with_details) + { + const size_t buffer_size = 8 * 1024; + std::vector buffer; + buffer.resize(buffer_size); + auto md5 = fill_buffer_and_get_md5(buffer); + + auto stream = concurrency::streams::bytestream::open_istream(buffer); + m_blob.upload_block(get_block_id(0), stream, md5); + + try + { + stream.seek(1024, std::ios_base::beg); + m_blob.upload_block(get_block_id(1), stream, md5); + CHECK(false); + } + catch (azure::storage::storage_exception& ex) + { + CHECK_UTF8_EQUAL(U("Md5Mismatch"), ex.result().extended_error().code()); + CHECK(!ex.result().extended_error().message().empty()); + CHECK(ex.result().extended_error().details().size() > 0); + } + } + + TEST_FIXTURE(table_service_test_base, storage_extended_error_verify_json_with_details) + { + utility::string_t table_name = get_table_name(); + azure::storage::cloud_table_client client = get_table_client(); + azure::storage::cloud_table table = client.get_table_reference(table_name); + + azure::storage::table_request_options options; + azure::storage::operation_context context; + try + { + table.delete_table(options, context); + CHECK(false); + } + catch (const azure::storage::storage_exception& e) + { + CHECK(e.result().extended_error().code().compare(U("ResourceNotFound")) == 0); + CHECK(!e.result().extended_error().message().empty()); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/table_test_base.cpp b/Microsoft.WindowsAzure.Storage/tests/table_test_base.cpp new file mode 100644 index 00000000..476b9ebc --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/table_test_base.cpp @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "table_test_base.h" +#include "check_macros.h" +#include "wascore/streams.h" + +utility::string_t table_service_test_base::table_type_name = utility::string_t(U("table")); + +azure::storage::cloud_table_client table_service_test_base::get_table_client() +{ + return test_config::instance().account().create_cloud_table_client(); +} + +utility::string_t table_service_test_base::get_table_name() +{ + // table name naming convention: "^[A-Za-z][A-Za-z0-9]{2,62}$" + // here we construct the table name as: + // object_name_prefix + "tableA0" + random_characters + + std::vector charset; + for (utility::char_t c = 'A'; c <= 'Z'; ++c) charset.push_back(c); + for (utility::char_t c = 'a'; c <= 'z'; ++c) charset.push_back(c); + for (utility::char_t c = '0'; c <= '9'; ++c) charset.push_back(c); + + utility::string_t table_name; + table_name.reserve(39U + table_type_name.size()); + table_name.append(object_name_prefix); + table_name.append(table_type_name); + table_name.append(1, U('A')); + table_name.append(1, U('0')); + table_name.append(get_random_string(charset, 10)); + + return table_name; +} + +azure::storage::cloud_table table_service_test_base::get_table(bool create) +{ + azure::storage::cloud_table_client client = get_table_client(); + utility::string_t table_name = get_table_name(); + azure::storage::cloud_table table = client.get_table_reference(table_name); + if (create) + { + table.create_if_not_exists(); + } + return table; +} \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/table_test_base.h b/Microsoft.WindowsAzure.Storage/tests/table_test_base.h new file mode 100644 index 00000000..5f16057f --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/table_test_base.h @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#include +#include "CurrentTest.h" + +#include "test_base.h" +#include "was/table.h" + +class table_service_test_base : public test_base +{ +public: + + table_service_test_base() + { + } + + ~table_service_test_base() + { + } + +protected: + static utility::string_t table_type_name; + + static azure::storage::cloud_table_client get_table_client(); + static utility::string_t get_table_name(); + static azure::storage::cloud_table get_table(bool create = true); +}; diff --git a/Microsoft.WindowsAzure.Storage/tests/test_base.cpp b/Microsoft.WindowsAzure.Storage/tests/test_base.cpp index 19e52f67..22136a40 100644 --- a/Microsoft.WindowsAzure.Storage/tests/test_base.cpp +++ b/Microsoft.WindowsAzure.Storage/tests/test_base.cpp @@ -20,6 +20,9 @@ #include "test_base.h" #include "cpprest/json.h" +utility::string_t test_base::object_name_prefix = utility::string_t(U("nativeclientlibraryunittest")); +bool test_base::is_random_initialized = false; + test_config::test_config() { utility::ifstream_t config_file; @@ -59,5 +62,105 @@ void test_base::print_client_request_id(const azure::storage::operation_context& { std::string suite_name(UnitTest::CurrentTest::Details()->suiteName); std::string test_name(UnitTest::CurrentTest::Details()->testName); - ucout << utility::conversions::to_string_t(suite_name) << U("::") << utility::conversions::to_string_t(test_name) << U(": ") << purpose << U(" client request ID: ") << context.client_request_id() << std::endl; + ucout << utility::conversions::to_string_t(suite_name) << U(":") << utility::conversions::to_string_t(test_name) << U(": ") << purpose << U(" client request ID: ") << context.client_request_id() << std::endl; +} + +utility::string_t test_base::get_string(utility::char_t value1, utility::char_t value2) +{ + utility::ostringstream_t result; + result << value1 << value2; + return result.str(); +} + +void test_base::initialize_random() +{ + if (!is_random_initialized) + { + srand((unsigned int)time(NULL)); + is_random_initialized = true; + } +} + +bool test_base::get_random_boolean() +{ + initialize_random(); + return (rand() & 0x1) == 0; +} + +int32_t test_base::get_random_int32() +{ + initialize_random(); + return (int32_t)rand() << 16 | (int32_t)rand(); +} + +int64_t test_base::get_random_int64() +{ + initialize_random(); + return (int64_t)rand() << 48 | (int64_t)rand() << 32 | (int64_t)rand() << 16 | (int64_t)rand(); +} + +double test_base::get_random_double() +{ + initialize_random(); + return (double)rand() / RAND_MAX; +} + +utility::string_t test_base::get_random_string(const std::vector charset, size_t size) +{ + initialize_random(); + utility::string_t result; + result.reserve(size); + for (size_t i = 0; i < size; ++i) + { + result.push_back(charset[rand() % charset.size()]); + } + + return result; +} + +utility::string_t test_base::get_random_string() +{ + initialize_random(); + const int SIZE = 10; + utility::string_t result; + result.reserve(SIZE); + for (int i = 0; i < SIZE; ++i) + { + result.push_back((utility::char_t) (U('0') + rand() % 10)); + } + return result; +} + +utility::datetime test_base::get_random_datetime() +{ + initialize_random(); + return utility::datetime::utc_now() + rand(); +} + +std::vector test_base::get_random_binary_data() +{ + initialize_random(); + const int SIZE = 100; + std::vector result; + result.reserve(SIZE); + for (int i = 0; i < SIZE; ++i) + { + result.push_back((unsigned char)(rand() % 256)); + } + return result; +} + +utility::uuid test_base::get_random_guid() +{ + return utility::new_uuid(); +} + +utility::string_t test_base::get_object_name(const utility::string_t& object_type_name) +{ + utility::string_t object_name; + object_name.reserve(37U + object_type_name.size()); + object_name.append(object_name_prefix); + object_name.append(object_type_name); + object_name.append(get_random_string()); + return object_name; } diff --git a/Microsoft.WindowsAzure.Storage/tests/test_base.h b/Microsoft.WindowsAzure.Storage/tests/test_base.h index c6299193..d2ea7ca8 100644 --- a/Microsoft.WindowsAzure.Storage/tests/test_base.h +++ b/Microsoft.WindowsAzure.Storage/tests/test_base.h @@ -60,4 +60,21 @@ class test_base static void print_client_request_id(const azure::storage::operation_context& context, const utility::string_t& purpose); azure::storage::operation_context m_context; + + static utility::string_t object_name_prefix; + static bool is_random_initialized; + + static utility::string_t get_string(utility::char_t value1, utility::char_t value2); + static void initialize_random(); + static bool get_random_boolean(); + static int32_t get_random_int32(); + static int64_t get_random_int64(); + static double get_random_double(); + static utility::string_t get_random_string(const std::vector charset, size_t size); + static utility::string_t get_random_string(); + static utility::datetime get_random_datetime(); + static std::vector get_random_binary_data(); + static utility::uuid get_random_guid(); + static azure::storage::storage_credentials get_credentials(); + static utility::string_t get_object_name(const utility::string_t& object_type_name); }; diff --git a/Microsoft.WindowsAzure.Storage/version.rc b/Microsoft.WindowsAzure.Storage/version.rc index 7124050961b076e2c03f3817ecbc3681d02fba6b..dddcdd7e2fc2cb2ec58fa88f2c52ddafcba474d6 100644 GIT binary patch delta 48 zcmX@2d_;Le9|xo9&AH_#AK?{Re1w+;0A)rE Ao&W#< delta 48 zcmX@2d_;Le9|xn!&AH_#AK?{Re1w+;0A#=o AnE(I) diff --git a/README.md b/README.md index 36c8beb3..e8f6348b 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The Azure Storage Client Library for C++ depends on the C++ REST SDK (codename " How-to topics focused around accomplishing specific tasks are available in the [samples folder](https://github.com/Azure/azure-storage-cpp/tree/master/Microsoft.WindowsAzure.Storage). ## Getting Started on Linux -As mentioned above, the Azure Storage Client Library for C++ depends on Casablanca. Follow [these instructions](https://casablanca.codeplex.com/wikipage?title=Setup%20and%20Build%20on%20Linux&referringTitle=Documentation) to compile it. Version 0.4.0 of the library depends on Casablanca version 2.3.0. +As mentioned above, the Azure Storage Client Library for C++ depends on Casablanca. Follow [these instructions](https://casablanca.codeplex.com/wikipage?title=Setup%20and%20Build%20on%20Linux&referringTitle=Documentation) to compile it. Current version of the library depends on Casablanca version 2.3.0. Once this is complete, then: