From a4c09125630d8dd70e6918cf62617b4195834a9f Mon Sep 17 00:00:00 2001 From: Shishir Bhat Date: Fri, 11 Feb 2022 16:08:49 -0800 Subject: [PATCH 1/5] SDK: Fix download::make_nothrow API * With the constructor being private, clients are unable to create the download object to pass as the out param. * Fix: Use std::unique_ptr as the out param instead. --- sdk-cpp/include/do_download.h | 4 ++-- sdk-cpp/src/do_download.cpp | 30 ++++++++++++++++-------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/sdk-cpp/include/do_download.h b/sdk-cpp/include/do_download.h index 76d2d167..2660e014 100644 --- a/sdk-cpp/include/do_download.h +++ b/sdk-cpp/include/do_download.h @@ -27,7 +27,7 @@ class download ~download(); #if (DO_ENABLE_EXCEPTIONS) - static download make(const std::string& uri, const std::string& downloadFilePath); + static std::unique_ptr make(const std::string& uri, const std::string& downloadFilePath); void start(); void pause(); @@ -50,7 +50,7 @@ class download download_property_value get_property(download_property key); #endif - static std::error_code make_nothrow(const std::string& uri, const std::string& downloadFilePath, download& out) noexcept; + static std::error_code make_nothrow(const std::string& uri, const std::string& downloadFilePath, std::unique_ptr& out) noexcept; std::error_code start_nothrow() noexcept; std::error_code pause_nothrow() noexcept; diff --git a/sdk-cpp/src/do_download.cpp b/sdk-cpp/src/do_download.cpp index 93efa914..77805a92 100644 --- a/sdk-cpp/src/do_download.cpp +++ b/sdk-cpp/src/do_download.cpp @@ -27,10 +27,10 @@ download::download() download::~download() = default; #if defined(DO_ENABLE_EXCEPTIONS) -download download::make(const std::string& uri, const std::string& downloadFilePath) +std::unique_ptr download::make(const std::string& uri, const std::string& downloadFilePath) { - download out; - throw_if_fail(out._download->Init(uri, downloadFilePath)); + std::unique_ptr out; + throw_if_fail(make_nothrow(uri, downloadFilePath, out)); return out; } @@ -75,14 +75,14 @@ void download::start_and_wait_until_completion(std::chrono::seconds timeOut) void download::download_url_to_path(const std::string& uri, const std::string& downloadFilePath, std::chrono::seconds timeOut) { - download oneShotDownload = download::make(uri, downloadFilePath); - oneShotDownload.start_and_wait_until_completion(timeOut); + auto oneShotDownload = download::make(uri, downloadFilePath); + oneShotDownload->start_and_wait_until_completion(timeOut); } void download::download_url_to_path(const std::string& uri, const std::string& downloadFilePath, const std::atomic_bool& isCancelled, std::chrono::seconds timeOut) { - download oneShotDownload = download::make(uri, downloadFilePath); - oneShotDownload.start_and_wait_until_completion(isCancelled, timeOut); + auto oneShotDownload = download::make(uri, downloadFilePath); + oneShotDownload->start_and_wait_until_completion(isCancelled, timeOut); } void download::start_and_wait_until_completion(const std::atomic_bool& isCancelled, std::chrono::seconds timeOut) @@ -104,13 +104,15 @@ void download::set_property(download_property prop, const download_property_valu #endif //DO_ENABLE_EXCEPTIONS -std::error_code download::make_nothrow(const std::string& uri, const std::string& downloadFilePath, download& out) noexcept +std::error_code download::make_nothrow(const std::string& uri, const std::string& downloadFilePath, std::unique_ptr& out) noexcept { - download tmp; - tmp._download = std::make_shared(); - std::error_code code = tmp._download->Init(uri, downloadFilePath); + out.reset(); + // using 'new' to access non-public constructor + std::unique_ptr tmp(new download()); + tmp->_download = std::make_shared(); + std::error_code code = tmp->_download->Init(uri, downloadFilePath); DO_RETURN_IF_FAILED(code); - out = tmp; + out = std::move(tmp); return DO_OK; } @@ -210,9 +212,9 @@ std::error_code download::download_url_to_path_nothrow(const std::string& uri, c std::error_code download::download_url_to_path_nothrow(const std::string& uri, const std::string& downloadFilePath, const std::atomic_bool& isCancelled, std::chrono::seconds timeOut) noexcept { - download oneShotDownload; + std::unique_ptr oneShotDownload; DO_RETURN_IF_FAILED(download::make_nothrow(uri, downloadFilePath, oneShotDownload)); - return oneShotDownload.start_and_wait_until_completion_nothrow(isCancelled, timeOut); + return oneShotDownload->start_and_wait_until_completion_nothrow(isCancelled, timeOut); } std::error_code download::set_property_nothrow(download_property prop, const download_property_value& val) noexcept From ca34b06673b583de41d76a73763f7321a465cf9a Mon Sep 17 00:00:00 2001 From: Shishir Bhat Date: Fri, 11 Feb 2022 15:24:41 -0800 Subject: [PATCH 2/5] SDK: Add tests for nothrow interface --- sdk-cpp/CMakeLists.txt | 8 +- .../tests/com/download_properties_tests.cpp | 110 +++----- sdk-cpp/tests/com/download_tests.cpp | 10 +- sdk-cpp/tests/download_tests_common.cpp | 132 +++++----- sdk-cpp/tests/rest/mcc_download_tests.cpp | 10 +- sdk-cpp/tests_nothrow/CMakeLists.txt | 48 ++++ sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp | 56 ++++ .../download_properties_tests.cpp | 247 ++++++++++++++++++ sdk-cpp/tests_nothrow/download_tests.cpp | 70 +++++ sdk-cpp/tests_nothrow/test_data.cpp | 34 +++ sdk-cpp/tests_nothrow/test_data.h | 34 +++ sdk-cpp/tests_nothrow/test_helpers.cpp | 29 ++ sdk-cpp/tests_nothrow/test_helpers.h | 66 +++++ sdk-cpp/tests_nothrow/tests_common.h | 9 + 14 files changed, 719 insertions(+), 144 deletions(-) create mode 100644 sdk-cpp/tests_nothrow/CMakeLists.txt create mode 100644 sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp create mode 100644 sdk-cpp/tests_nothrow/download_properties_tests.cpp create mode 100644 sdk-cpp/tests_nothrow/download_tests.cpp create mode 100644 sdk-cpp/tests_nothrow/test_data.cpp create mode 100644 sdk-cpp/tests_nothrow/test_data.h create mode 100644 sdk-cpp/tests_nothrow/test_helpers.cpp create mode 100644 sdk-cpp/tests_nothrow/test_helpers.h create mode 100644 sdk-cpp/tests_nothrow/tests_common.h diff --git a/sdk-cpp/CMakeLists.txt b/sdk-cpp/CMakeLists.txt index 063daf37..2bd1440e 100644 --- a/sdk-cpp/CMakeLists.txt +++ b/sdk-cpp/CMakeLists.txt @@ -18,9 +18,11 @@ if (DO_PLATFORM_LINUX) fixup_compile_options_for_arm() endif() -# Currently tests run off of the throwing interface, enable them only when building for the throwing interface -if (DO_BUILD_TESTS AND DO_ENABLE_EXCEPTIONS) - add_subdirectory(tests) +if (DO_BUILD_TESTS) + if (DO_ENABLE_EXCEPTIONS) + add_subdirectory(tests) + endif () + add_subdirectory(tests_nothrow) endif() set(DO_SDK_LIB_NAME "deliveryoptimization") diff --git a/sdk-cpp/tests/com/download_properties_tests.cpp b/sdk-cpp/tests/com/download_properties_tests.cpp index d5f321dd..2124ce74 100644 --- a/sdk-cpp/tests/com/download_properties_tests.cpp +++ b/sdk-cpp/tests/com/download_properties_tests.cpp @@ -62,78 +62,54 @@ class DownloadPropertyTestsDOSVC : public ::testing::Test //At the moment, these tests are essentially just verifying that these properties can be set and download succeeds TEST_F(DownloadPropertyTestsDOSVC, SmallDownloadSetCallerNameTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); std::string strCallerName("dosdkcpp_tests"); msdo::download_property_value callerName = msdo::download_property_value::make(strCallerName); - simpleDownload.set_property(msdo::download_property::caller_name, callerName); + simpleDownload->set_property(msdo::download_property::caller_name, callerName); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); } TEST_F(DownloadPropertyTestsDOSVC, SmallDownloadWithPhfDigestandCvTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); std::vector expectedErrors = { 0, static_cast(msdo::errc::do_e_unknown_property_id) }; - msdo::download_property_value integrityCheckMandatory = msdo::download_property_value::make(true); + msdo::download_property_value integrityCheckMandatory = msdo::download_property_value::make(false); VerifyCallWithExpectedErrors([&]() { - simpleDownload.set_property(msdo::download_property::integrity_check_mandatory, integrityCheckMandatory); + simpleDownload->set_property(msdo::download_property::integrity_check_mandatory, integrityCheckMandatory); }, expectedErrors); msdo::download_property_value integrityCheckInfo = msdo::download_property_value::make(g_smallFilePhfInfoJson); VerifyCallWithExpectedErrors([&]() { - simpleDownload.set_property(msdo::download_property::integrity_check_info, integrityCheckInfo); + simpleDownload->set_property(msdo::download_property::integrity_check_info, integrityCheckInfo); }, expectedErrors); std::string strCorrelationVector("g+Vo71JZwkmJdYfF.0"); msdo::download_property_value correlationVector = msdo::download_property_value::make(strCorrelationVector); VerifyCallWithExpectedErrors([&]() { - simpleDownload.set_property(msdo::download_property::correlation_vector, correlationVector); + simpleDownload->set_property(msdo::download_property::correlation_vector, correlationVector); }, expectedErrors); - simpleDownload.start_and_wait_until_completion(); - - ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); -} - -TEST_F(DownloadPropertyTestsDOSVC, SmallDownloadWithPhfDigestandCvTestNoThrow) -{ - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); - - std::vector expectedErrors = { 0, static_cast(msdo::errc::do_e_unknown_property_id) }; - - msdo::download_property_value integrityCheckMandatory = msdo::download_property_value::make(true); - int32_t code = simpleDownload.set_property_nothrow(msdo::download_property::integrity_check_mandatory, integrityCheckMandatory).value(); - VerifyError(code, expectedErrors); - - msdo::download_property_value integrityCheckInfo = msdo::download_property_value::make(g_smallFilePhfInfoJson); - code = simpleDownload.set_property_nothrow(msdo::download_property::integrity_check_info, integrityCheckInfo).value(); - VerifyError(code, expectedErrors); - - std::string strCorrelationVector("g+Vo71JZwkmJdYfF.0"); - msdo::download_property_value correlationVector = msdo::download_property_value::make(strCorrelationVector); - code = simpleDownload.set_property_nothrow(msdo::download_property::correlation_vector, correlationVector).value(); - VerifyError(code, expectedErrors); - - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); } TEST_F(DownloadPropertyTestsDOSVC, InvalidPhfDigestTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); try { msdo::download_property_value integrityCheckInfo = msdo::download_property_value::make("blah"); - simpleDownload.set_property(msdo::download_property::integrity_check_info, integrityCheckInfo); + simpleDownload->set_property(msdo::download_property::integrity_check_info, integrityCheckInfo); } catch (const msdo::exception& e) { @@ -147,48 +123,48 @@ TEST_F(DownloadPropertyTestsDOSVC, InvalidPhfDigestTest) // For some reason, custom headers are getting rejected and returning E_INVALIDARG now, disabling test for now TEST_F(DownloadPropertyTestsDOSVC, DISABLED_SmallDownloadWithCustomHeaders) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); std::string strHttpCustomHeaders("XCustom1=someData\nXCustom2=moreData"); msdo::download_property_value httpCustomHeaders = msdo::download_property_value::make(strHttpCustomHeaders); - simpleDownload.set_property(msdo::download_property::http_custom_headers, httpCustomHeaders); + simpleDownload->set_property(msdo::download_property::http_custom_headers, httpCustomHeaders); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); } TEST_F(DownloadPropertyTestsDOSVC, CallbackTestUseDownload) { - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); bool hitError = false; msdo::download_property_value callback = msdo::download_property_value::make([&hitError](msdo::download& download, msdo::download_status& status) - { - char msgBuf[1024]; - snprintf(msgBuf, sizeof(msgBuf), "Received status callback: %llu/%llu, 0x%x, 0x%x, %u", - status.bytes_transferred(), status.bytes_total(), status.error_code().value(), status.extended_error_code().value(), - static_cast(status.state())); - std::cout << msgBuf << std::endl; - - msdo::download_status status2 = download.get_status(); - if (hitError) { - download.pause(); - } - }); + char msgBuf[1024]; + snprintf(msgBuf, sizeof(msgBuf), "Received status callback: %llu/%llu, 0x%x, 0x%x, %u", + status.bytes_transferred(), status.bytes_total(), status.error_code().value(), status.extended_error_code().value(), + static_cast(status.state())); + std::cout << msgBuf << std::endl; + + msdo::download_status status2 = download.get_status(); + if (hitError) + { + download.pause(); + } + }); - simpleDownload.set_property(msdo::download_property::callback_interface, callback); - simpleDownload.start(); + simpleDownload->set_property(msdo::download_property::callback_interface, callback); + simpleDownload->start(); std::this_thread::sleep_for(5s); hitError = true; - TestHelpers::WaitForState(simpleDownload, msdo::download_state::paused); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); } TEST_F(DownloadPropertyTestsDOSVC, SetCallbackTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); int i= 0; msdo::download_property_value callback = msdo::download_property_value::make([&i](msdo::download& download, msdo::download_status& status) @@ -200,30 +176,30 @@ TEST_F(DownloadPropertyTestsDOSVC, SetCallbackTest) std::cout << msgBuf << std::endl; i += 1; }); - simpleDownload.set_property(msdo::download_property::callback_interface, callback); + simpleDownload->set_property(msdo::download_property::callback_interface, callback); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); ASSERT_GE(i, 0); } TEST_F(DownloadPropertyTestsDOSVC, OverrideCallbackTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); int i = 0; msdo::download_property_value callback = msdo::download_property_value::make([&i](msdo::download&, msdo::download_status&) { i += 1; }); - simpleDownload.set_property(msdo::download_property::callback_interface, callback); + simpleDownload->set_property(msdo::download_property::callback_interface, callback); msdo::download_property_value::status_callback_t cb2 = [](msdo::download&, msdo::download_status&) {}; callback = msdo::download_property_value::make(cb2); - simpleDownload.set_property(msdo::download_property::callback_interface, callback); + simpleDownload->set_property(msdo::download_property::callback_interface, callback); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); ASSERT_EQ(i, 0); } @@ -232,23 +208,23 @@ TEST_F(DownloadPropertyTestsDOSVC, ForegroundBackgroundRace) { double backgroundDuration = TimeOperation([&]() { - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); msdo::download_property_value foregroundPriority = msdo::download_property_value::make(false); - simpleDownload.set_property(msdo::download_property::use_foreground_priority, foregroundPriority); + simpleDownload->set_property(msdo::download_property::use_foreground_priority, foregroundPriority); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); }); printf("Time for background download: %f ms\n", backgroundDuration); double foregroundDuration = TimeOperation([&]() { - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName2); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName2); msdo::download_property_value foregroundPriority = msdo::download_property_value::make(true); - simpleDownload.set_property(msdo::download_property::use_foreground_priority, foregroundPriority); + simpleDownload->set_property(msdo::download_property::use_foreground_priority, foregroundPriority); - simpleDownload.start_and_wait_until_completion(); + simpleDownload->start_and_wait_until_completion(); }); printf("Time for foreground download: %f ms\n", foregroundDuration); diff --git a/sdk-cpp/tests/com/download_tests.cpp b/sdk-cpp/tests/com/download_tests.cpp index 54e15819..23e82794 100644 --- a/sdk-cpp/tests/com/download_tests.cpp +++ b/sdk-cpp/tests/com/download_tests.cpp @@ -42,15 +42,15 @@ class DownloadTestsDOSVC : public ::testing::Test TEST_F(DownloadTestsDOSVC, Download1PausedDownload2SameDestTest) { ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); std::this_thread::sleep_for(1s); - simpleDownload.pause(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::paused); + simpleDownload->pause(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_smallFileSizeBytes); diff --git a/sdk-cpp/tests/download_tests_common.cpp b/sdk-cpp/tests/download_tests_common.cpp index 14904016..f1885b65 100644 --- a/sdk-cpp/tests/download_tests_common.cpp +++ b/sdk-cpp/tests/download_tests_common.cpp @@ -28,6 +28,10 @@ using namespace std::chrono_literals; // NOLINT(build/namespaces) #define HTTP_E_STATUS_NOT_FOUND ((int)0x80190194L) #endif +#ifndef E_ACCESSDENIED +#define E_ACCESSDENIED ((int)0x80070005) +#endif + void WaitForDownloadCompletion(msdo::download& simpleDownload) { msdo::download_status status = simpleDownload.get_status(); @@ -57,19 +61,19 @@ class DownloadTests : public ::testing::Test TEST_F(DownloadTests, SimpleDownloadTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); - status = simpleDownload.get_status(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); + status = simpleDownload->get_status(); ASSERT_EQ(status.bytes_total(), status.bytes_transferred()); ASSERT_EQ(status.bytes_total(), g_smallFileSizeBytes); - simpleDownload.finalize(); + simpleDownload->finalize(); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_smallFileSizeBytes); } @@ -147,7 +151,7 @@ TEST_F(DownloadTests, SimpleDownloadTest_WithMalformedPath) catch (const msdo::exception& e) { #if defined(DO_INTERFACE_COM) - //ASSERT_EQ(e.error_code().value(), msdo::DO_E_INVALID_NAME); + ASSERT_EQ(e.error_code().value(), E_ACCESSDENIED); #elif defined(DO_INTERFACE_REST) ASSERT_EQ(e.error_code().value(), DO_ERROR_FROM_SYSTEM_ERROR(ENOENT)); #endif @@ -167,7 +171,7 @@ TEST_F(DownloadTests, SimpleDownloadTest_With404UrlAndMalformedPath) catch (const msdo::exception& e) { #if defined(DO_INTERFACE_COM) - ASSERT_EQ(e.error_code().value(), HTTP_E_STATUS_NOT_FOUND); + ASSERT_EQ(e.error_code().value(), E_ACCESSDENIED); #elif defined(DO_INTERFACE_REST) ASSERT_EQ(e.error_code().value(), DO_ERROR_FROM_SYSTEM_ERROR(ENOENT)); #endif @@ -187,38 +191,38 @@ TEST_F(DownloadTests, SimpleDownloadTest_With404UrlAndMalformedPath) TEST_F(DownloadTests, Download1PausedDownload2SameDestTest) { ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); std::this_thread::sleep_for(std::chrono::seconds(1)); - simpleDownload.pause(); - status = simpleDownload.get_status(); + simpleDownload->pause(); + status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::paused); ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); - msdo::download simpleDownload2 = msdo::download::make(g_smallFileUrl, g_tmpFileName); + auto simpleDownload2 = msdo::download::make(g_smallFileUrl, g_tmpFileName); try { - simpleDownload2.start(); + simpleDownload2->start(); ASSERT_TRUE(false); } catch (const msdo::exception& e) { ASSERT_EQ(e.error_code().value(), DO_ERROR_FROM_SYSTEM_ERROR(EEXIST)); } - simpleDownload2.abort(); + simpleDownload2->abort(); ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); // not deleted, the earlier download is still active - simpleDownload.abort(); + simpleDownload->abort(); ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); // download2 should now succeed simpleDownload2 = msdo::download::make(g_smallFileUrl, g_tmpFileName); - simpleDownload2.start(); - WaitForDownloadCompletion(simpleDownload2); + simpleDownload2->start(); + WaitForDownloadCompletion(*simpleDownload2); ASSERT_EQ(boost::filesystem::file_size(g_tmpFileName), g_smallFileSizeBytes); } #endif @@ -226,37 +230,37 @@ TEST_F(DownloadTests, Download1PausedDownload2SameDestTest) TEST_F(DownloadTests, Download1PausedDownload2SameFileDownload1Resume) { ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); - simpleDownload.pause(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::paused); + simpleDownload->start(); + simpleDownload->pause(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); msdo::download::download_url_to_path(g_largeFileUrl, g_tmpFileName2); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName2)), g_largeFileSizeBytes); - simpleDownload.resume(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::transferred, g_largeFileWaitTime); + simpleDownload->resume(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::transferred, g_largeFileWaitTime); - simpleDownload.finalize(); + simpleDownload->finalize(); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_largeFileSizeBytes); } TEST_F(DownloadTests, Download1NeverStartedDownload2CancelledSameFileTest) { ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - msdo::download simpleDownload2 = msdo::download::make(g_largeFileUrl, g_tmpFileName); + auto simpleDownload2 = msdo::download::make(g_largeFileUrl, g_tmpFileName); try { - simpleDownload2.abort(); + simpleDownload2->abort(); } catch (const msdo::exception& e) { @@ -267,24 +271,24 @@ TEST_F(DownloadTests, Download1NeverStartedDownload2CancelledSameFileTest) TEST_F(DownloadTests, ResumeOnAlreadyDownloadedFileTest) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); - status = simpleDownload.get_status(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); + status = simpleDownload->get_status(); ASSERT_EQ(status.bytes_total(), status.bytes_transferred()); ASSERT_EQ(status.bytes_total(), g_smallFileSizeBytes); - simpleDownload.finalize(); + simpleDownload->finalize(); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_smallFileSizeBytes); try { - simpleDownload.resume(); + simpleDownload->resume(); } catch (const msdo::exception& e) { @@ -298,25 +302,25 @@ TEST_F(DownloadTests, ResumeOnAlreadyDownloadedFileTest) TEST_F(DownloadTests, CancelDownloadOnCompletedState) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); std::this_thread::sleep_for(5s); - status = simpleDownload.get_status(); + status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::transferred); ASSERT_EQ(status.bytes_total(), status.bytes_transferred()); ASSERT_EQ(status.bytes_total(), g_smallFileSizeBytes); - simpleDownload.finalize(); + simpleDownload->finalize(); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_smallFileSizeBytes); try { - simpleDownload.abort(); + simpleDownload->abort(); } catch (const msdo::exception& e) { @@ -330,15 +334,15 @@ TEST_F(DownloadTests, CancelDownloadOnCompletedState) TEST_F(DownloadTests, CancelDownloadInTransferredState) { - msdo::download simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_smallFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); - status = simpleDownload.get_status(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::transferred, g_smallFileWaitTime); + status = simpleDownload->get_status(); ASSERT_EQ(status.bytes_total(), status.bytes_transferred()); ASSERT_EQ(status.bytes_total(), g_smallFileSizeBytes); @@ -348,7 +352,7 @@ TEST_F(DownloadTests, CancelDownloadInTransferredState) #endif try { - simpleDownload.abort(); + simpleDownload->abort(); } catch (const msdo::exception& e) { @@ -362,12 +366,12 @@ TEST_F(DownloadTests, CancelDownloadInTransferredState) static void _PauseResumeTest(bool delayAfterStart = false) { - msdo::download simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); - msdo::download_status status = simpleDownload.get_status(); + auto simpleDownload = msdo::download::make(g_largeFileUrl, g_tmpFileName); + msdo::download_status status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::created); ASSERT_EQ(status.bytes_transferred(), 0u); - simpleDownload.start(); + simpleDownload->start(); if (delayAfterStart) { #if defined(DO_INTERFACE_REST) @@ -376,16 +380,16 @@ static void _PauseResumeTest(bool delayAfterStart = false) std::this_thread::sleep_for(5s); #endif } - simpleDownload.pause(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::paused); + simpleDownload->pause(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); - simpleDownload.resume(); - TestHelpers::WaitForState(simpleDownload, msdo::download_state::transferred, g_largeFileWaitTime); + simpleDownload->resume(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::transferred, g_largeFileWaitTime); - status = simpleDownload.get_status(); + status = simpleDownload->get_status(); ASSERT_EQ(status.state(), msdo::download_state::transferred); ASSERT_EQ(status.bytes_total(), status.bytes_transferred()); - simpleDownload.finalize(); + simpleDownload->finalize(); ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_largeFileSizeBytes); } @@ -426,7 +430,7 @@ TEST_F(DownloadTests, MultipleConcurrentDownloadTest) { msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName); } - catch (const msdo::exception& e) + catch (const msdo::exception&) { ASSERT_TRUE(false); } @@ -437,7 +441,7 @@ TEST_F(DownloadTests, MultipleConcurrentDownloadTest) { msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName2); } - catch (const msdo::exception& e) + catch (const msdo::exception&) { ASSERT_TRUE(false); } @@ -448,7 +452,7 @@ TEST_F(DownloadTests, MultipleConcurrentDownloadTest) { msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName3); } - catch (const msdo::exception& e) + catch (const msdo::exception&) { ASSERT_TRUE(false); } @@ -474,7 +478,7 @@ TEST_F(DownloadTests, MultipleConcurrentDownloadTest_WithCancels) { msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName); } - catch (const msdo::exception& e) + catch (const msdo::exception&) { ASSERT_TRUE(false); } @@ -497,7 +501,7 @@ TEST_F(DownloadTests, MultipleConcurrentDownloadTest_WithCancels) { msdo::download::download_url_to_path(g_smallFileUrl, g_tmpFileName3); } - catch (const msdo::exception& e) + catch (const msdo::exception&) { ASSERT_TRUE(false); } diff --git a/sdk-cpp/tests/rest/mcc_download_tests.cpp b/sdk-cpp/tests/rest/mcc_download_tests.cpp index dfdf9bed..176ef7bd 100644 --- a/sdk-cpp/tests/rest/mcc_download_tests.cpp +++ b/sdk-cpp/tests/rest/mcc_download_tests.cpp @@ -73,24 +73,24 @@ TEST_F(MCCDownloadTests, DownloadWithInvalidHostAndUrl) const auto invalidUrl = "http://" + std::to_string(gen()) + ".com"; std::cout << "Using invalid URL: " << invalidUrl << std::endl; - msdo::download download = msdo::download::make(invalidUrl, g_tmpFileName); - download.start(); + auto download = msdo::download::make(invalidUrl, g_tmpFileName); + download->start(); // Wait enough time to exercise the agent code that attempts both MCC and CDN in a loop. // Verify there is no progress while waiting. const auto timeout = std::chrono::seconds(90); const auto endTime = std::chrono::steady_clock::now() + timeout; - uint64_t bytesTransferred = download.get_status().bytes_transferred(); + uint64_t bytesTransferred = download->get_status().bytes_transferred(); std::cout << "Verifying there is no download progress for " << timeout.count() << " seconds" << std::endl; do { std::this_thread::sleep_for(std::chrono::seconds(2)); - const auto newStatus = download.get_status(); + const auto newStatus = download->get_status(); ASSERT_EQ(newStatus.state(), msdo::download_state::transferring); ASSERT_EQ(newStatus.bytes_transferred(), bytesTransferred); } while (std::chrono::steady_clock::now() < endTime); - download.abort(); + download->abort(); } diff --git a/sdk-cpp/tests_nothrow/CMakeLists.txt b/sdk-cpp/tests_nothrow/CMakeLists.txt new file mode 100644 index 00000000..0e9d03ed --- /dev/null +++ b/sdk-cpp/tests_nothrow/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +## Tests for DO SDK nothrow interface +## Currently building for Windows only + +find_package(Boost COMPONENTS filesystem program_options REQUIRED) +find_package(GTest REQUIRED) + +set(sdk_tests_linked_libs_common + Microsoft::deliveryoptimization + ${Boost_LIBRARIES} + GTest::GTest +) + +set(dosdkcpp_private_includes_common + "." + "../include" + "../src/internal" +) + +set(test_source_common + "*.cpp" +) + +set(dosdkcpp_private_includes + "../src/internal/com" + "../src/internal/com/util" +) +file(GLOB test_source + ${test_source_common} + "com/*.cpp" +) + +add_executable(deliveryoptimization-sdk-nothrow-tests ${test_source}) +add_platform_interface_definitions(deliveryoptimization-sdk-nothrow-tests) +# DO_ENABLE_EXCEPTIONS not defined; enables nothrow interface + +target_include_directories(deliveryoptimization-sdk-nothrow-tests + PRIVATE + ${dosdkcpp_private_includes} + ${dosdkcpp_private_includes_common} +) + +target_link_libraries(deliveryoptimization-sdk-nothrow-tests + ${sdk_tests_linked_libs} + ${sdk_tests_linked_libs_common} +) \ No newline at end of file diff --git a/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp b/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp new file mode 100644 index 00000000..01dc909a --- /dev/null +++ b/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "tests_common.h" + +#include + +#include + +#include "test_data.h" + +#if defined(DO_INTERFACE_COM) +#include +#endif + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + + namespace po = boost::program_options; + po::options_description desc("Options"); + desc.add_options() + ("help,h", "Show help messages") + ("mcc-host,m", po::value(), "MCC hostname to use (optional override)") + ("manual-start", po::bool_switch()->default_value(false), "Wait for command line input to start tests (useful for debugging)"); + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + + auto it = vm.find("mcc-host"); + if (it != vm.end()) + { + g_mccHostName = it->second.as(); + std::cout << "Got overriden MCC host: " << g_mccHostName << '\n'; + } + +#if defined(DO_INTERFACE_COM) + ASSERT_TRUE(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); // SDK leaves com init up to caller, so initialize in test exe here +#endif + + auto manualStart = vm["manual-start"].as(); + if (manualStart) + { + do + { + std::cout << '\n' << "Press a key to continue..."; + } while (std::cin.get() != '\n'); + } + + int hr = RUN_ALL_TESTS(); + +#if defined(DO_INTERFACE_COM) + CoUninitialize(); +#endif + + return hr; +} diff --git a/sdk-cpp/tests_nothrow/download_properties_tests.cpp b/sdk-cpp/tests_nothrow/download_properties_tests.cpp new file mode 100644 index 00000000..57319ec8 --- /dev/null +++ b/sdk-cpp/tests_nothrow/download_properties_tests.cpp @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "tests_common.h" + +#include +#include +#include + +#include + +#include "do_download.h" +#include "do_errors.h" +#include "test_data.h" +#include "test_helpers.h" + +namespace msdo = microsoft::deliveryoptimization; +using namespace std::chrono_literals; + +static double TimeOperation(const std::function& op) +{ + auto start = std::chrono::steady_clock::now(); + op(); + auto end = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(end - start).count(); +} + +class DownloadPropertyTestsDOSVC_NoThrow : public ::testing::Test +{ +public: + void SetUp() override + { + TestHelpers::CleanTestDir(); + ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); + } + void TearDown() override + { + TestHelpers::CleanTestDir(); + } + + // Our build/test machines run Windows Server 2019, which use an older COM interface and does not support setting IntegrityCheckInfo through DODownloadProperty com interface + // Accept multiple error codes to handle running tests both locally and on the build machine + static void VerifyError(int32_t code, const std::vector& expectedErrors) + { + ASSERT_TRUE(std::find(expectedErrors.begin(), expectedErrors.end(), code) != expectedErrors.end()); + } + + static void VerifyCallWithExpectedErrors(const std::function& op, const std::vector& expectedErrors) + { + auto ec = op(); + VerifyError(ec.value(), expectedErrors); + } +}; + +static std::unique_ptr g_MakeDownload(const std::string& url, const std::string& destPath) +{ + std::unique_ptr downloadObj; + auto ec = msdo::download::make_nothrow(url, destPath, downloadObj); + if (ec) throw std::exception(); + + // msdo::download_property_value propUri; + // auto ec = msdo::download_property_value::make_nothrow(url, propUri); + // if (ec) throw std::exception(); + // ec = obj.set_property_nothrow(msdo::download_property::uri, propUri); + // if (ec) throw std::exception(); + + // msdo::download_property_value propFilePath; + // ec = msdo::download_property_value::make_nothrow(destPath, propFilePath); + // ec = obj.set_property_nothrow(msdo::download_property::uri, propFilePath); + // if (ec) throw std::exception(); + + return downloadObj; +} + +template +static msdo::download_property_value g_MakePropertyValue(T value) +{ + msdo::download_property_value propValue; + auto ec = msdo::download_property_value::make_nothrow(value, propValue); + if (ec) + { + throw std::exception(); + } + return propValue; +} + +//TODO: Not sure how much value these tests are, functional tests utilize parsing log lines to verify these properties were set, could be useful here +//At the moment, these tests are essentially just verifying that these properties can be set and download succeeds +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, SmallDownloadSetCallerNameTest) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + std::string strCallerName("dosdkcpp_tests"); + msdo::download_property_value callerName = g_MakePropertyValue(strCallerName); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::caller_name, callerName).value(), 0); + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, SmallDownloadWithPhfDigestandCvTest) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + std::vector expectedErrors = { 0, static_cast(msdo::errc::do_e_unknown_property_id) }; + + msdo::download_property_value integrityCheckMandatory = g_MakePropertyValue(false); + int32_t code = simpleDownload->set_property_nothrow(msdo::download_property::integrity_check_mandatory, integrityCheckMandatory).value(); + VerifyError(code, expectedErrors); + + msdo::download_property_value integrityCheckInfo = g_MakePropertyValue(g_smallFilePhfInfoJson); + code = simpleDownload->set_property_nothrow(msdo::download_property::integrity_check_info, integrityCheckInfo).value(); + VerifyError(code, expectedErrors); + + std::string strCorrelationVector("g+Vo71JZwkmJdYfF.0"); + msdo::download_property_value correlationVector = g_MakePropertyValue(strCorrelationVector); + code = simpleDownload->set_property_nothrow(msdo::download_property::correlation_vector, correlationVector).value(); + VerifyError(code, expectedErrors); + + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + + ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, InvalidPhfDigestTest) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + msdo::download_property_value integrityCheckInfo = g_MakePropertyValue("blah"); + std::vector expectedErrors = { static_cast(msdo::errc::invalid_arg), + static_cast(msdo::errc::do_e_unknown_property_id) }; + VerifyCallWithExpectedErrors([&]() + { + return simpleDownload->set_property_nothrow(msdo::download_property::integrity_check_info, integrityCheckInfo); + }, { static_cast(msdo::errc::invalid_arg), static_cast(msdo::errc::do_e_unknown_property_id) }); +} + +// For some reason, custom headers are getting rejected and returning E_INVALIDARG now, disabling test for now +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, DISABLED_SmallDownloadWithCustomHeaders) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + std::string strHttpCustomHeaders("XCustom1=someData\nXCustom2=moreData"); + msdo::download_property_value httpCustomHeaders = g_MakePropertyValue(strHttpCustomHeaders); + simpleDownload->set_property_nothrow(msdo::download_property::http_custom_headers, httpCustomHeaders); + + simpleDownload->start_and_wait_until_completion_nothrow(); + + ASSERT_TRUE(boost::filesystem::exists(g_tmpFileName)); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, CallbackTestUseDownload) +{ + auto simpleDownload = g_MakeDownload(g_largeFileUrl, g_tmpFileName); + bool fPauseDownload = false; + + msdo::download_property_value callback = g_MakePropertyValue([&fPauseDownload](msdo::download& download, msdo::download_status& status) + { + char msgBuf[1024]; + snprintf(msgBuf, sizeof(msgBuf), "Received status callback: %llu/%llu, 0x%x, 0x%x, %u", + status.bytes_transferred(), status.bytes_total(), status.error_code().value(), status.extended_error_code().value(), + static_cast(status.state())); + std::cout << msgBuf << std::endl; + + msdo::download_status status2; + ASSERT_EQ(download.get_status_nothrow(status).value(), 0); + if (fPauseDownload) + { + ASSERT_EQ(download.pause_nothrow().value(), 0); + } + }); + + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::callback_interface, callback).value(), 0); + ASSERT_EQ(simpleDownload->start_nothrow().value(), 0); + std::this_thread::sleep_for(5s); + fPauseDownload = true; + + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, SetCallbackTest) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + int i= 0; + msdo::download_property_value callback = g_MakePropertyValue([&i](msdo::download& download, msdo::download_status& status) + { + char msgBuf[1024]; + snprintf(msgBuf, sizeof(msgBuf), "Received status callback: %llu/%llu, 0x%x, 0x%x, %u", + status.bytes_transferred(), status.bytes_total(), status.error_code().value(), status.extended_error_code().value(), + static_cast(status.state())); + std::cout << msgBuf << std::endl; + i += 1; + }); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::callback_interface, callback).value(), 0); + + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + + ASSERT_GE(i, 0); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, OverrideCallbackTest) +{ + auto simpleDownload = g_MakeDownload(g_smallFileUrl, g_tmpFileName); + + int i = 0; + msdo::download_property_value callback = g_MakePropertyValue([&i](msdo::download&, msdo::download_status&) + { + i += 1; + }); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::callback_interface, callback).value(), 0); + + msdo::download_property_value::status_callback_t cb2 = [](msdo::download&, msdo::download_status&) {}; + + callback = g_MakePropertyValue(cb2); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::callback_interface, callback).value(), 0); + + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + + ASSERT_EQ(i, 0); +} + +TEST_F(DownloadPropertyTestsDOSVC_NoThrow, ForegroundBackgroundRace) +{ + double backgroundDuration = TimeOperation([&]() + { + auto simpleDownload = g_MakeDownload(g_largeFileUrl, g_tmpFileName); + + msdo::download_property_value foregroundPriority = g_MakePropertyValue(false); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::use_foreground_priority, foregroundPriority).value(), 0); + + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + }); + + printf("Time for background download: %f ms\n", backgroundDuration); + double foregroundDuration = TimeOperation([&]() + { + auto simpleDownload = g_MakeDownload(g_largeFileUrl, g_tmpFileName2); + + msdo::download_property_value foregroundPriority = g_MakePropertyValue(true); + ASSERT_EQ(simpleDownload->set_property_nothrow(msdo::download_property::use_foreground_priority, foregroundPriority).value(), 0); + + ASSERT_EQ(simpleDownload->start_and_wait_until_completion_nothrow().value(), 0); + }); + printf("Time for foreground download: %f ms\n", foregroundDuration); + + ASSERT_LT(foregroundDuration, backgroundDuration); +} diff --git a/sdk-cpp/tests_nothrow/download_tests.cpp b/sdk-cpp/tests_nothrow/download_tests.cpp new file mode 100644 index 00000000..a5e350ba --- /dev/null +++ b/sdk-cpp/tests_nothrow/download_tests.cpp @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "tests_common.h" + +#include +#include +#include + +#include + +#include "do_download.h" +#include "do_errors.h" +#include "test_data.h" +#include "test_helpers.h" + +namespace msdo = microsoft::deliveryoptimization; +using namespace std::chrono_literals; + +static double TimeOperation(const std::function& op) +{ + auto start = std::chrono::steady_clock::now(); + op(); + auto end = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(end - start).count(); +} + +class DownloadTestsDOSVC : public ::testing::Test +{ +public: + void SetUp() override + { + TestHelpers::CleanTestDir(); + } + void TearDown() override + { + TestHelpers::CleanTestDir(); + } +}; + +// In the new COM interface we allow for downloading to the same destination - it overwrites and cancels the first download +TEST_F(DownloadTestsDOSVC, Download1PausedDownload2SameDestTest) +{ + ASSERT_FALSE(boost::filesystem::exists(g_tmpFileName)); + + std::unique_ptr simpleDownload; + auto ec = msdo::download::make_nothrow(g_largeFileUrl, g_tmpFileName, simpleDownload); + ASSERT_TRUE(!ec); + + // msdo::download_property_value propUri; + // ASSERT_TRUE(!msdo::download_property_value::make_nothrow(g_largeFileUrl, propUri)); + // ASSERT_TRUE(!simpleDownload->set_property_nothrow(msdo::download_property::uri, propUri)); + + // msdo::download_property_value propFilePath; + // ASSERT_TRUE(!msdo::download_property_value::make_nothrow(g_tmpFileName, propFilePath)); + // ASSERT_TRUE(!simpleDownload->set_property_nothrow(msdo::download_property::uri, propFilePath)); + + msdo::download_status status; + simpleDownload->get_status_nothrow(status); + ASSERT_EQ(status.state(), msdo::download_state::created); + ASSERT_EQ(status.bytes_transferred(), 0u); + + simpleDownload->start_nothrow(); + std::this_thread::sleep_for(1s); + simpleDownload->pause_nothrow(); + TestHelpers::WaitForState(*simpleDownload, msdo::download_state::paused); + + msdo::download::download_url_to_path_nothrow(g_smallFileUrl, g_tmpFileName); + ASSERT_EQ(boost::filesystem::file_size(boost::filesystem::path(g_tmpFileName)), g_smallFileSizeBytes); +} \ No newline at end of file diff --git a/sdk-cpp/tests_nothrow/test_data.cpp b/sdk-cpp/tests_nothrow/test_data.cpp new file mode 100644 index 00000000..2126c924 --- /dev/null +++ b/sdk-cpp/tests_nothrow/test_data.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "test_data.h" +#include "boost/filesystem.hpp" + +using namespace std::chrono_literals; // NOLINT(build/namespaces) + +const uint64_t g_smallFileSizeBytes = 1837u; +const uint64_t g_largeFileSizeBytes = 536870440u; +const uint64_t g_prodFileSizeBytes = 25006511u; + +const std::string g_largeFileUrl = "http://main.oremdl.microsoft.com.nsatc.net/dotc/ReplacementDCATFile.txt"; +const std::string g_malformedFilePath = "?f309adfasdf///dfasdfj39fjasldfjasdf/// ///.1"; +const std::string g_tmpFileName = (boost::filesystem::temp_directory_path() / boost::filesystem::path("docsdk_testfile.txt")).string(); +const std::string g_tmpFileName2 = (boost::filesystem::temp_directory_path() / boost::filesystem::path("docsdk_testfile2.txt")).string(); +const std::string g_tmpFileName3 = (boost::filesystem::temp_directory_path() / boost::filesystem::path("docsdk_testfile3.txt")).string(); +const std::string g_smallFileUrl = "http://main.oremdl.microsoft.com.nsatc.net/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/43A54FC03C6A979E9AAEAE2493757D1429A5C8A8D093FB7B8103E8CC8DF7B6B6"; +const std::string g_404Url = "http://main.oremdl.microsoft.com.nsatc.net/dotc/49c591d405d307e25e72a19f7e79b53d69f19954/nonexistent"; +const std::string g_prodFileUrl = "http://dl.delivery.mp.microsoft.com/filestreamingservice/files/52fa8751-747d-479d-8f22-e32730cc0eb1"; + +const std::chrono::seconds g_smallFileWaitTime = 10s; + +const std::chrono::seconds g_largeFileWaitTime = 5min; + +#if defined(DO_INTERFACE_REST) +const std::string g_docsProcName = "deliveryoptimization-agent"; +const std::string g_docsSvcName = "deliveryoptimization-agent.service"; +#endif + +const std::string g_smallFilePhfInfoJson = "{\"PiecesHashFileUrl\":\"https://eus2intdo101.blob.core.windows.net/partnercatalogcontainer/pc00051365650774478716C3536617271346B6B33563946436D6C794B6A516B2F74376751506F7A4933337472593D0000B9CCB33-51FB-4062-AFAC-08C8A088CB35.json\",\"HashOfHashes\":\"Q6VPwDxql56arq4kk3V9FCmlyKjQk/t7gQPozI33trY=\"}"; + +// This MCC instance only works within our test lab azure VMs. Can be overriden via cmdline. +std::string g_mccHostName = "10.1.0.70"; diff --git a/sdk-cpp/tests_nothrow/test_data.h b/sdk-cpp/tests_nothrow/test_data.h new file mode 100644 index 00000000..d9741440 --- /dev/null +++ b/sdk-cpp/tests_nothrow/test_data.h @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef _DELIVERY_OPTIMIZATION_TEST_DATA_H +#define _DELIVERY_OPTIMIZATION_TEST_DATA_H + +#include +#include + +extern const uint64_t g_smallFileSizeBytes; +extern const uint64_t g_largeFileSizeBytes; +extern const uint64_t g_prodFileSizeBytes; + +#if defined(DO_INTERFACE_REST) +extern const std::string g_docsProcName; +extern const std::string g_docsSvcName; +#endif + +extern const std::string g_smallFilePhfInfoJson; + +extern const std::string g_largeFileUrl; +extern const std::string g_malformedFilePath; +extern const std::string g_tmpFileName; +extern const std::string g_tmpFileName2; +extern const std::string g_tmpFileName3; +extern const std::string g_smallFileUrl; +extern const std::string g_404Url; +extern const std::string g_prodFileUrl; // Use this for MCC downloads. Other hostnames aren't enabled in MCC. + +extern const std::chrono::seconds g_smallFileWaitTime; +extern const std::chrono::seconds g_largeFileWaitTime; + +extern std::string g_mccHostName; +#endif diff --git a/sdk-cpp/tests_nothrow/test_helpers.cpp b/sdk-cpp/tests_nothrow/test_helpers.cpp new file mode 100644 index 00000000..e537e40b --- /dev/null +++ b/sdk-cpp/tests_nothrow/test_helpers.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "test_helpers.h" + +#include + +#include "test_data.h" + +void TestHelpers::CleanupWorkingDir() +{ + return; +} + +void TestHelpers::CleanTestDir() +{ + if (boost::filesystem::exists(g_tmpFileName)) + { + boost::filesystem::remove(g_tmpFileName); + } + if (boost::filesystem::exists(g_tmpFileName2)) + { + boost::filesystem::remove(g_tmpFileName2); + } + if (boost::filesystem::exists(g_tmpFileName3)) + { + boost::filesystem::remove(g_tmpFileName3); + } +} \ No newline at end of file diff --git a/sdk-cpp/tests_nothrow/test_helpers.h b/sdk-cpp/tests_nothrow/test_helpers.h new file mode 100644 index 00000000..fe5d0149 --- /dev/null +++ b/sdk-cpp/tests_nothrow/test_helpers.h @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef _DELIVERY_OPTIMIZATION_TEST_HELPERS_H +#define _DELIVERY_OPTIMIZATION_TEST_HELPERS_H + +#include +#include + +#include "tests_common.h" + +#include "do_download.h" +#include "do_download_status.h" + +namespace msdo = microsoft::deliveryoptimization; +using namespace std::chrono_literals; // NOLINT(build/namespaces) + +class TestHelpers +{ +public: + static void CleanTestDir(); + static void CleanupWorkingDir(); + + // On Windows, operations are async - there may be some delay setting a state internally + static void WaitForState(msdo::download& download, msdo::download_state expectedState, std::chrono::seconds waitTimeSecs = 10s) + { + msdo::download_status status; + ASSERT_FALSE(download.get_status_nothrow(status)); + const auto endtime = std::chrono::steady_clock::now() + waitTimeSecs; + while ((status.state() != expectedState) && (std::chrono::steady_clock::now() < endtime)) + { + std::this_thread::sleep_for(1s); + ASSERT_FALSE(download.get_status_nothrow(status)); + std::cout << "Transferred " << status.bytes_transferred() << " / " << status.bytes_total() << "\n"; + } + + ASSERT_EQ(status.state(), expectedState) << "Download must have reached expected state before timeout"; + } + +#if defined(DO_INTERFACE_REST) + static bool IsActiveProcess(std::string name); + static int ShutdownProcess(std::string name); + static void RestartService(const std::string& name); + static void StartService(const std::string& name); + static void StopService(const std::string& name); + static void CreateRestPortFiles(int numFiles); + static void DeleteRestPortFiles(); + static unsigned int CountRestPortFiles(); + static void DisableNetwork(); + static void EnableNetwork(); + + static std::string GetLocalIPv4Address(); +#endif // Rest + +private: +#if defined(DO_INTERFACE_REST) + static int _GetPidFromProcName(std::string name); + static int _KillProcess(int pid, int signal); +#endif // Rest + + // Disallow creating an instance of this object + TestHelpers() {} + +}; + +#endif diff --git a/sdk-cpp/tests_nothrow/tests_common.h b/sdk-cpp/tests_nothrow/tests_common.h new file mode 100644 index 00000000..958b446b --- /dev/null +++ b/sdk-cpp/tests_nothrow/tests_common.h @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef _DELIVERY_OPTIMIZATION_TESTS_COMMON_H +#define _DELIVERY_OPTIMIZATION_TESTS_COMMON_H + +#include +#include +#endif From 93c1db495b07002146d9969b41e946ba4f6e54c6 Mon Sep 17 00:00:00 2001 From: Shishir Bhat Date: Fri, 11 Feb 2022 16:54:40 -0800 Subject: [PATCH 3/5] SDK: Add noexcept to OnStatusChange * Without this, Clang complains about "exception specification of overriding function is more lax than base version" --- sdk-cpp/src/internal/com/download_impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk-cpp/src/internal/com/download_impl.cpp b/sdk-cpp/src/internal/com/download_impl.cpp index 7faa9fc9..6c4f1d88 100644 --- a/sdk-cpp/src/internal/com/download_impl.cpp +++ b/sdk-cpp/src/internal/com/download_impl.cpp @@ -201,7 +201,7 @@ class DOStatusCallback : } #if defined(DO_ENABLE_EXCEPTIONS) - IFACEMETHODIMP OnStatusChange(IDODownload* download, const DO_DOWNLOAD_STATUS* comStatus) + IFACEMETHODIMP OnStatusChange(IDODownload* download, const DO_DOWNLOAD_STATUS* comStatus) noexcept { try { @@ -225,7 +225,7 @@ class DOStatusCallback : #else // If an application builds the sdk from source and has toggled DO_ENABLE_EXCEPTIONS, it would be hypocritical for their callback to throw // Need to provide this definition because try/catch keywords in the implementation above will fail builds with exceptions disabled - IFACEMETHODIMP OnStatusChange(IDODownload* download, const DO_DOWNLOAD_STATUS* comStatus) + IFACEMETHODIMP OnStatusChange(IDODownload* download, const DO_DOWNLOAD_STATUS* comStatus) noexcept { msdo::download_status status = ConvertFromComStatus(*comStatus); _callback(*_download, status); From b05c5f441f928eab7b1d77c045226839f981aaa7 Mon Sep 17 00:00:00 2001 From: Shishir Bhat Date: Mon, 14 Feb 2022 09:14:53 -0800 Subject: [PATCH 4/5] SDK: Fix test assert --- sdk-cpp/tests/dosdkcpp_testrunner.cpp | 12 +++++++++--- sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/sdk-cpp/tests/dosdkcpp_testrunner.cpp b/sdk-cpp/tests/dosdkcpp_testrunner.cpp index 01dc909a..59228a1d 100644 --- a/sdk-cpp/tests/dosdkcpp_testrunner.cpp +++ b/sdk-cpp/tests/dosdkcpp_testrunner.cpp @@ -34,7 +34,13 @@ int main(int argc, char** argv) } #if defined(DO_INTERFACE_COM) - ASSERT_TRUE(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); // SDK leaves com init up to caller, so initialize in test exe here + // SDK leaves com init up to caller, so initialize in test exe here + const auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr)) + { + std::cout << "CoInitializeEx failed with " << hr << std::endl; + return hr; + } #endif auto manualStart = vm["manual-start"].as(); @@ -46,11 +52,11 @@ int main(int argc, char** argv) } while (std::cin.get() != '\n'); } - int hr = RUN_ALL_TESTS(); + int testsResult = RUN_ALL_TESTS(); #if defined(DO_INTERFACE_COM) CoUninitialize(); #endif - return hr; + return testsResult; } diff --git a/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp b/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp index 01dc909a..59228a1d 100644 --- a/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp +++ b/sdk-cpp/tests_nothrow/dosdkcpp_testrunner.cpp @@ -34,7 +34,13 @@ int main(int argc, char** argv) } #if defined(DO_INTERFACE_COM) - ASSERT_TRUE(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); // SDK leaves com init up to caller, so initialize in test exe here + // SDK leaves com init up to caller, so initialize in test exe here + const auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr)) + { + std::cout << "CoInitializeEx failed with " << hr << std::endl; + return hr; + } #endif auto manualStart = vm["manual-start"].as(); @@ -46,11 +52,11 @@ int main(int argc, char** argv) } while (std::cin.get() != '\n'); } - int hr = RUN_ALL_TESTS(); + int testsResult = RUN_ALL_TESTS(); #if defined(DO_INTERFACE_COM) CoUninitialize(); #endif - return hr; + return testsResult; } From 55e99c4c64572af174357420b75f7d2ccb30adc0 Mon Sep 17 00:00:00 2001 From: Shishir Bhat Date: Mon, 14 Feb 2022 09:20:06 -0800 Subject: [PATCH 5/5] SDK: Fix malformed path tests --- sdk-cpp/tests/download_tests_common.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sdk-cpp/tests/download_tests_common.cpp b/sdk-cpp/tests/download_tests_common.cpp index f1885b65..2b3d8ae5 100644 --- a/sdk-cpp/tests/download_tests_common.cpp +++ b/sdk-cpp/tests/download_tests_common.cpp @@ -4,6 +4,7 @@ #include "tests_common.h" #include +#include #include #include #include @@ -151,7 +152,11 @@ TEST_F(DownloadTests, SimpleDownloadTest_WithMalformedPath) catch (const msdo::exception& e) { #if defined(DO_INTERFACE_COM) - ASSERT_EQ(e.error_code().value(), E_ACCESSDENIED); + constexpr auto c_invalidDeviceName = (int)0x8007007b; + std::array expectedErrors{ E_ACCESSDENIED, HTTP_E_STATUS_NOT_FOUND, c_invalidDeviceName }; + // DO returns different errors on dev machine and pipeline agents (Win10/Win11?) + ASSERT_TRUE(std::find(expectedErrors.begin(), expectedErrors.end(), e.error_code().value()) != expectedErrors.end()) + << e.error_code().value(); #elif defined(DO_INTERFACE_REST) ASSERT_EQ(e.error_code().value(), DO_ERROR_FROM_SYSTEM_ERROR(ENOENT)); #endif @@ -171,7 +176,11 @@ TEST_F(DownloadTests, SimpleDownloadTest_With404UrlAndMalformedPath) catch (const msdo::exception& e) { #if defined(DO_INTERFACE_COM) - ASSERT_EQ(e.error_code().value(), E_ACCESSDENIED); + constexpr auto c_invalidDeviceName = (int)0x8007007b; + std::array expectedErrors{ E_ACCESSDENIED, HTTP_E_STATUS_NOT_FOUND, c_invalidDeviceName }; + // DO returns different errors on dev machine and pipeline agents (Win10/Win11?) + ASSERT_TRUE(std::find(expectedErrors.begin(), expectedErrors.end(), e.error_code().value()) != expectedErrors.end()) + << e.error_code().value(); #elif defined(DO_INTERFACE_REST) ASSERT_EQ(e.error_code().value(), DO_ERROR_FROM_SYSTEM_ERROR(ENOENT)); #endif