diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 7500b2c1bb..bbbd089e74 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -279,6 +279,7 @@ + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 5d28140d17..4463aea5f5 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -371,6 +371,9 @@ Source Files\Common + + Source Files\Repository + diff --git a/src/AppInstallerCLITests/Experiment.cpp b/src/AppInstallerCLITests/Experiment.cpp new file mode 100644 index 0000000000..b8b5b6e25c --- /dev/null +++ b/src/AppInstallerCLITests/Experiment.cpp @@ -0,0 +1,76 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include "TestSettings.h" +#include +#include + +using namespace TestCommon; +using namespace AppInstaller::Settings; + +#define SET_POLICY_STATE(_policy_, _state_) \ + GroupPolicyTestOverride policies; \ + policies.SetState(_policy_, _state_); + +#define SET_USER_SETTINGS(_enabled_, _disabled_) \ + TestUserSettings settings; \ + settings.Set({ \ + {"TestExperimentEnabledByDefault", _enabled_}, \ + {"TestExperimentDisabledByDefault", _disabled_} \ + }); + +#define ASSERT_EXPERIMENTS(_enabled_, _disabled_) \ + REQUIRE(_enabled_ == Experiment::IsEnabled(Experiment::Key::TestExperimentEnabledByDefault)); \ + REQUIRE(_disabled_ == Experiment::IsEnabled(Experiment::Key::TestExperimentDisabledByDefault)); + +TEST_CASE("Experiment_GroupPolicyControl", "[experiment]") +{ + SECTION("Not configured") + { + SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::NotConfigured); + ASSERT_EXPERIMENTS(true, false); + } + + SECTION("Enabled") + { + SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::Enabled); + ASSERT_EXPERIMENTS(true, false); + } + + SECTION("Disabled") + { + SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::Disabled); + ASSERT_EXPERIMENTS(false, false); + } +} + +TEST_CASE("Experiment_GroupPolicyDisabled_ReturnFalse", "[experiment]") +{ + // If the policy is disabled, then also the user settings should be ignored. + SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::Disabled); + SET_USER_SETTINGS(true, true); + ASSERT_EXPERIMENTS(false, false); +} + +TEST_CASE("Experiment_UserSettingsControl", "[experiment]") +{ + SECTION("Experiments not configured in user settings") + { + // Default values are used + ASSERT_EXPERIMENTS(true, false); + } + + SECTION("Experiments enabled in user settings") + { + SET_USER_SETTINGS(true, true); + ASSERT_EXPERIMENTS(true, true); + } + + SECTION("Experiments disabled in user settings") + { + SET_USER_SETTINGS(false, false); + ASSERT_EXPERIMENTS(false, false); + } +} diff --git a/src/AppInstallerCLITests/GroupPolicy.cpp b/src/AppInstallerCLITests/GroupPolicy.cpp index 907c55969e..1ad1ead717 100644 --- a/src/AppInstallerCLITests/GroupPolicy.cpp +++ b/src/AppInstallerCLITests/GroupPolicy.cpp @@ -402,6 +402,7 @@ TEST_CASE("GroupPolicy_AllEnabled", "[groupPolicy]") SetRegistryValue(policiesKey.get(), EnableWindowsPackageManagerCommandLineInterfaces, 1); SetRegistryValue(policiesKey.get(), ConfigurationPolicyValueName, 1); SetRegistryValue(policiesKey.get(), ProxyCommandLineOptionsPolicyValueName, 1); + SetRegistryValue(policiesKey.get(), EnableExperimentsPolicyValueName , 1); GroupPolicy groupPolicy{ policiesKey.get() }; for (const auto& policy : TogglePolicy::GetAllPolicies()) diff --git a/src/AppInstallerCLITests/TestSettings.h b/src/AppInstallerCLITests/TestSettings.h index 7da1a728ef..f100a9f0b2 100644 --- a/src/AppInstallerCLITests/TestSettings.h +++ b/src/AppInstallerCLITests/TestSettings.h @@ -23,6 +23,7 @@ namespace TestCommon const std::wstring EnableWindowsPackageManagerCommandLineInterfaces = L"EnableWindowsPackageManagerCommandLineInterfaces"; const std::wstring ConfigurationPolicyValueName = L"EnableWindowsPackageManagerConfiguration"; const std::wstring ProxyCommandLineOptionsPolicyValueName = L"EnableWindowsPackageManagerProxyCommandLineOptions"; + const std::wstring EnableExperimentsPolicyValueName = L"EnableExperiments"; const std::wstring SourceUpdateIntervalPolicyValueName = L"SourceAutoUpdateInterval"; const std::wstring SourceUpdateIntervalPolicyOldValueName = L"SourceAutoUpdateIntervalInMinutes"; @@ -90,4 +91,4 @@ namespace TestCommon }; #define REQUIRE_POLICY_EXCEPTION(_expr_, _policy_) REQUIRE_THROWS_MATCHES(_expr_, AppInstaller::Settings::GroupPolicyException, TestCommon::GroupPolicyExceptionMatcher(_policy_)) -} \ No newline at end of file +} diff --git a/src/AppInstallerCommonCore/Experiment.cpp b/src/AppInstallerCommonCore/Experiment.cpp index b934df5216..0ecb71a707 100644 --- a/src/AppInstallerCommonCore/Experiment.cpp +++ b/src/AppInstallerCommonCore/Experiment.cpp @@ -52,6 +52,11 @@ namespace AppInstaller::Settings bool Experiment::IsEnabled(Key key) { std::lock_guard lock(m_mutex); + +#ifndef AICLI_DISABLE_TEST_HOOKS + m_isEnabledCache.clear(); +#endif + if (m_isEnabledCache.find(key) == m_isEnabledCache.end()) { m_isEnabledCache[key] = IsEnabledInternal(key, User()); @@ -66,7 +71,12 @@ namespace AppInstaller::Settings { case Key::CDN: return Experiment{ "CDN experiment", "CDN", "https://aka.ms/winget-settings", "CDN"}; - +#ifndef AICLI_DISABLE_TEST_HOOKS + case Key::TestExperimentDisabledByDefault: + return Experiment{ "Test experiment disabled by default", "TestExperimentDisabledByDefault", "https://aka.ms/winget-settings", "TestExperimentDisabledByDefault" }; + case Key::TestExperimentEnabledByDefault: + return Experiment{ "Test experiment enabled by default", "TestExperimentEnabledByDefault", "https://aka.ms/winget-settings", "TestExperimentEnabledByDefault" }; +#endif default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/winget/Experiment.h b/src/AppInstallerCommonCore/Public/winget/Experiment.h index ea6ac149ea..42c30d8398 100644 --- a/src/AppInstallerCommonCore/Public/winget/Experiment.h +++ b/src/AppInstallerCommonCore/Public/winget/Experiment.h @@ -14,6 +14,11 @@ namespace AppInstaller::Settings None = 0x0, CDN = 0x1, Max, + +#ifndef AICLI_DISABLE_TEST_HOOKS + TestExperimentDisabledByDefault = 0xFFFFFFFE, + TestExperimentEnabledByDefault = 0xFFFFFFFF, +#endif }; using Key_t = std::underlying_type_t; diff --git a/src/Internal/Experiment/Experiment.cpp b/src/Internal/Experiment/Experiment.cpp index b2a3d166df..dc4316c85b 100644 --- a/src/Internal/Experiment/Experiment.cpp +++ b/src/Internal/Experiment/Experiment.cpp @@ -5,8 +5,19 @@ namespace AppInstaller::Experiment { - bool IsEnabled(const std::string&) + bool IsEnabled(const std::string& key) { - return true; +#ifndef AICLI_DISABLE_TEST_HOOKS + if (key == "TestExperimentEnabledByDefault") + { + return true; + } + + if (key == "TestExperimentDisabledByDefault") + { + return false; + } +#endif + return false; } }