diff --git a/src/extensions/extension_manager/tests/CMakeLists.txt b/src/extensions/extension_manager/tests/CMakeLists.txt index 7fe6392a0..34cce0698 100644 --- a/src/extensions/extension_manager/tests/CMakeLists.txt +++ b/src/extensions/extension_manager/tests/CMakeLists.txt @@ -40,7 +40,12 @@ endif () target_link_libraries (${target_name} PRIVATE libaducpal) -target_compile_definitions (${target_name} PRIVATE ADUC_TEST_DATA_FOLDER="${ADUC_TEST_DATA_FOLDER}") +target_compile_definitions ( + ${target_name} + PRIVATE + ADUC_TEST_DATA_FOLDER="${ADUC_TEST_DATA_FOLDER}" + ADUC_CONTENT_DOWNLOADER_EXTENSION_PATH="${ADUC_EXTENSIONS_FOLDER}/${ADUC_EXTENSIONS_SUBDIR_CONTENT_DOWNLOADER}/${ADUC_EXTENSION_REG_FILENAME}" + ADUC_COMPONENT_ENUMERATOR_EXTENSION_PATH="${ADUC_EXTENSIONS_FOLDER}/${ADUC_EXTENSIONS_SUBDIR_COMPONENT_ENUMERATOR}/${ADUC_EXTENSION_REG_FILENAME}") include (CTest) include (Catch) diff --git a/src/extensions/extension_manager/tests/src/extension_manager_helper_ut.cpp b/src/extensions/extension_manager/tests/src/extension_manager_helper_ut.cpp index 59e5f30aa..7fb7319eb 100644 --- a/src/extensions/extension_manager/tests/src/extension_manager_helper_ut.cpp +++ b/src/extensions/extension_manager/tests/src/extension_manager_helper_ut.cpp @@ -6,6 +6,7 @@ * Licensed under the MIT License. */ +#include #include #include #include @@ -13,7 +14,59 @@ #include +#include // setenv, getenv #include // memset +#include + +namespace +{ + +// RAII guard that forces ADUC_ConfigInfo_GetInstance() to return nullptr by +// temporarily pointing the config folder env var at a non-existent directory +// and draining any existing singleton refcount. +struct ScopedInvalidateConfig +{ + std::string savedEnv; + bool hadEnv = false; + + ScopedInvalidateConfig() + { + const char* env = getenv("ADUC_CONF_FOLDER"); + if (env != nullptr) + { + hadEnv = true; + savedEnv = env; + } + // Drain any existing singleton so re-init is attempted next time + // (ReleaseInstance is a no-op once refCount reaches 0). + const ADUC_ConfigInfo* cfg = ADUC_ConfigInfo_GetInstance(); + if (cfg != nullptr) + { + // Release the ref we just took + ADUC_ConfigInfo_ReleaseInstance(cfg); + // Release again to drop refcount to 0 and uninit the singleton + ADUC_ConfigInfo_ReleaseInstance(cfg); + } + setenv("ADUC_CONF_FOLDER", "/nonexistent_path_for_test", 1); + } + + ~ScopedInvalidateConfig() + { + if (hadEnv) + { + setenv("ADUC_CONF_FOLDER", savedEnv.c_str(), 1); + } + else + { + unsetenv("ADUC_CONF_FOLDER"); + } + } + + ScopedInvalidateConfig(const ScopedInvalidateConfig&) = delete; + ScopedInvalidateConfig& operator=(const ScopedInvalidateConfig&) = delete; +}; + +} // namespace // ===================================================================== // ProcessDownloadHandlerExtensibility - Bad argument tests @@ -130,15 +183,14 @@ TEST_CASE("GetDownloadTimeoutInMinutes returns default when downloadOptions is n TEST_CASE("GetDownloadTimeoutInMinutes returns downloadOptions value when config has zero timeout") { + ScopedInvalidateConfig noConfig; ExtensionManager_Download_Options options{}; options.timeoutInMinutes = 42; unsigned int timeout = GetDownloadTimeoutInMinutes(&options); - // When config singleton is not initialized, it falls through to the - // downloadOptions path but config is nullptr so we get default. - // The config singleton returns nullptr if not initialized, so we - // get the default value. + // Config singleton is not initialized (env points to invalid path), + // so the function returns the default. CHECK(timeout == CONTENT_DOWNLOADER_MAX_TIMEOUT_IN_MINUTES_DEFAULT); } @@ -166,6 +218,7 @@ TEST_CASE("Default_ExtensionManager_Download_Options has default timeout") TEST_CASE("GetDownloadTimeoutInMinutes returns default when downloadOptions has zero timeout") { + ScopedInvalidateConfig noConfig; ExtensionManager_Download_Options options{}; options.timeoutInMinutes = 0; @@ -177,6 +230,7 @@ TEST_CASE("GetDownloadTimeoutInMinutes returns default when downloadOptions has TEST_CASE("GetDownloadTimeoutInMinutes returns default for large timeout value in options") { + ScopedInvalidateConfig noConfig; ExtensionManager_Download_Options options{}; options.timeoutInMinutes = 99999; @@ -188,6 +242,7 @@ TEST_CASE("GetDownloadTimeoutInMinutes returns default for large timeout value i TEST_CASE("Default_ExtensionManager_Download_Options can be modified and used") { + ScopedInvalidateConfig noConfig; ExtensionManager_Download_Options opts = Default_ExtensionManager_Download_Options; opts.timeoutInMinutes = 120; diff --git a/src/extensions/extension_manager/tests/src/extension_manager_ut.cpp b/src/extensions/extension_manager/tests/src/extension_manager_ut.cpp index 0e26e415e..f1cbb27e1 100644 --- a/src/extensions/extension_manager/tests/src/extension_manager_ut.cpp +++ b/src/extensions/extension_manager/tests/src/extension_manager_ut.cpp @@ -20,6 +20,7 @@ #include #include #include +#include // std::rename #include #include #include @@ -59,6 +60,33 @@ struct ExtMgrCleanup } }; +// RAII guard that temporarily hides a file by renaming it so that +// tests do not accidentally pick up real extension registrations +// installed on the host (e.g. /var/lib/adu/extensions/…/extension.json). +struct ScopedHideFile +{ + std::string original; + std::string hidden; + bool renamed = false; + + explicit ScopedHideFile(const std::string& path) + : original(path), hidden(path + ".hidden_by_test") + { + renamed = (std::rename(original.c_str(), hidden.c_str()) == 0); + } + + ~ScopedHideFile() + { + if (renamed) + { + std::rename(hidden.c_str(), original.c_str()); + } + } + + ScopedHideFile(const ScopedHideFile&) = delete; + ScopedHideFile& operator=(const ScopedHideFile&) = delete; +}; + } // namespace // ===================================================================== @@ -417,6 +445,7 @@ TEST_CASE("LoadContentDownloaderLibrary returns cached downloader") TEST_CASE("LoadContentDownloaderLibrary fails when extension load fails and no cached") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_CONTENT_DOWNLOADER_EXTENSION_PATH); ExtensionManager::SetContentDownloaderLibrary(nullptr); void* lib = nullptr; @@ -431,6 +460,7 @@ TEST_CASE("LoadContentDownloaderLibrary fails when extension load fails and no c TEST_CASE("IsComponentsEnumeratorRegistered returns false when no enumerator loaded") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_COMPONENT_ENUMERATOR_EXTENSION_PATH); bool registered = ExtensionManager::IsComponentsEnumeratorRegistered(); CHECK_FALSE(registered); } @@ -442,6 +472,7 @@ TEST_CASE("IsComponentsEnumeratorRegistered returns false when no enumerator loa TEST_CASE("LoadComponentEnumeratorLibrary fails when extension not found") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_COMPONENT_ENUMERATOR_EXTENSION_PATH); void* lib = nullptr; ADUC_Result result = ExtensionManager::LoadComponentEnumeratorLibrary(&lib); CHECK(result.ResultCode == 0); @@ -454,6 +485,7 @@ TEST_CASE("LoadComponentEnumeratorLibrary fails when extension not found") TEST_CASE("InitializeContentDownloader fails when downloader lib not loaded") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_CONTENT_DOWNLOADER_EXTENSION_PATH); ExtensionManager::SetContentDownloaderLibrary(nullptr); ADUC_Result result = ExtensionManager::InitializeContentDownloader("test", ADUC_LOG_DEBUG); @@ -481,6 +513,7 @@ TEST_CASE("InitializeContentDownloader fails when contract version is unsupporte TEST_CASE("GetAllComponents fails when component enumerator not loadable") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_COMPONENT_ENUMERATOR_EXTENSION_PATH); std::string output; ADUC_Result result = ExtensionManager::GetAllComponents(output); CHECK(result.ResultCode == 0); @@ -490,6 +523,7 @@ TEST_CASE("GetAllComponents fails when component enumerator not loadable") TEST_CASE("SelectComponents fails when component enumerator not loadable") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_COMPONENT_ENUMERATOR_EXTENSION_PATH); std::string output; ADUC_Result result = ExtensionManager::SelectComponents("{}", output); CHECK(result.ResultCode == 0); @@ -508,6 +542,7 @@ TEST_CASE("ExtensionManager_Uninit wraps Uninit") TEST_CASE("ExtensionManager_InitializeContentDownloader wrapper returns failure when no downloader") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_CONTENT_DOWNLOADER_EXTENSION_PATH); ExtensionManager::SetContentDownloaderLibrary(nullptr); ADUC_Result result = ExtensionManager_InitializeContentDownloader("test", ADUC_LOG_DEBUG); @@ -761,6 +796,7 @@ TEST_CASE("Download returns hash-type-not-supported when entity has no hashes") TEST_CASE("ExtensionManager_Download C wrapper fails when no downloader library loaded") { ExtMgrCleanup cleanup; + ScopedHideFile hideExtJson(ADUC_CONTENT_DOWNLOADER_EXTENSION_PATH); ExtensionManager::SetContentDownloaderLibrary(nullptr); const std::string testWorkfolder = std::string{ ADUC_TEST_DATA_FOLDER } + "/extension_manager";