Skip to content

Commit

Permalink
Added allowExperiments
Browse files Browse the repository at this point in the history
  • Loading branch information
AmelBawa-msft committed Dec 20, 2024
1 parent 0de0174 commit e1bafb4
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 29 deletions.
36 changes: 32 additions & 4 deletions schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"accent",
"rainbow",
"retro",
"sixel",
"sixel",
"disabled"
]
},
Expand Down Expand Up @@ -204,8 +204,8 @@
"properties": {
"defaultModuleRoot": {
"description": "The default root directory where PowerShell modules are installed to when applying a configuration.",
"type": "string",
"maxLength": 32767
"type": "string",
"maxLength": 32767
}
}
},
Expand Down Expand Up @@ -301,10 +301,26 @@
},
"fonts": {
"description": "Enable support for managing fonts",
"type": "boolean",
"type": "boolean",
"default": false
}
}
},
"Experiments": {
"description": "Experiments",
"type": "object",
"properties": {
"CDN": {
"description": "Enables the use of an alternative CDN for package downloads",
"type": "boolean",
"default": false
}
}
},
"AllowExperiments": {
"description": "Controls whether experiments are allowed or not",
"type": "boolean",
"default": true
}
},
"allOf": [
Expand Down Expand Up @@ -355,6 +371,18 @@
"experimentalFeatures": { "$ref": "#/definitions/Experimental" }
},
"additionalItems": true
},
{
"properties": {
"experiments": { "$ref": "#/definitions/Experiments" }
},
"additionalItems": true
},
{
"properties": {
"allowExperiments": { "$ref": "#/definitions/AllowExperiments" }
},
"additionalItems": true
}
],
"additionalProperties": true
Expand Down
64 changes: 44 additions & 20 deletions src/AppInstallerCLITests/Experiment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ using namespace TestCommon;
using namespace AppInstaller::Settings;
using namespace AppInstaller::Experiment;

#define SET_POLICY_STATE(_policy_, _state_) \
GroupPolicyTestOverride policies; \
policies.SetState(_policy_, _state_);

#define SET_USER_SETTINGS(_value_) \
TestUserSettings settings; \
settings.Set<Setting::Experiments>({ \
{"TestExperiment", _value_} \
});

#define ASSERT_EXPERIMENT(_isEnabled_, _toggleSource_) \
auto testExperimentState = Experiment::GetState(ExperimentKey::TestExperiment); \
REQUIRE(_isEnabled_ == testExperimentState.IsEnabled()); \
Expand All @@ -29,32 +19,38 @@ TEST_CASE("Experiment_GroupPolicyControl", "[experiment]")
{
SECTION("Not configured")
{
SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::NotConfigured);
GroupPolicyTestOverride policies;
policies.SetState(TogglePolicy::Policy::Experiments, PolicyState::NotConfigured);
ASSERT_EXPERIMENT(true, ExperimentToggleSource::Default);
}

SECTION("Enabled")
{
SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::Enabled);
GroupPolicyTestOverride policies;
policies.SetState(TogglePolicy::Policy::Experiments, PolicyState::Enabled);
ASSERT_EXPERIMENT(true, ExperimentToggleSource::Default);
}

SECTION("Disabled")
{
SET_POLICY_STATE(TogglePolicy::Policy::Experiments, PolicyState::Disabled);
GroupPolicyTestOverride policies;
policies.SetState(TogglePolicy::Policy::Experiments, PolicyState::Disabled);
ASSERT_EXPERIMENT(false, ExperimentToggleSource::Policy);
}
}

TEST_CASE("Experiment_GroupPolicyDisabled_ReturnFalse", "[experiment]")
{
TestUserSettings settings; \
settings.Set<Setting::Experiments>({{"TestExperiment", true}});

// 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);
GroupPolicyTestOverride policies;
policies.SetState(TogglePolicy::Policy::Experiments, PolicyState::Disabled);
ASSERT_EXPERIMENT(false, ExperimentToggleSource::Policy);
}

TEST_CASE("Experiment_UserSettingsControl", "[experiment]")
TEST_CASE("Experiment_UserSettingsIndividualControl", "[experiment]")
{
SECTION("Experiments not configured in user settings")
{
Expand All @@ -64,13 +60,41 @@ TEST_CASE("Experiment_UserSettingsControl", "[experiment]")

SECTION("Experiments enabled in user settings")
{
SET_USER_SETTINGS(true);
ASSERT_EXPERIMENT(true, ExperimentToggleSource::UserSetting);
TestUserSettings settings; \
settings.Set<Setting::Experiments>({{"TestExperiment", true}});
ASSERT_EXPERIMENT(true, ExperimentToggleSource::UserSettingIndividualControl);
}

SECTION("Experiments disabled in user settings")
{
SET_USER_SETTINGS(false);
ASSERT_EXPERIMENT(false, ExperimentToggleSource::UserSetting);
TestUserSettings settings;
settings.Set<Setting::Experiments>({{"TestExperiment", false}});
ASSERT_EXPERIMENT(false, ExperimentToggleSource::UserSettingIndividualControl);
}
}

TEST_CASE("Experiment_UserSettingsGlobalControl", "[experiment]")
{
SECTION("'Allow experiments' not configured in user settings")
{
TestUserSettings settings;
settings.Set<Setting::Experiments>({{"TestExperiment", true}});
ASSERT_EXPERIMENT(true, ExperimentToggleSource::UserSettingIndividualControl);
}

SECTION("'Allow experiments' enabled in user settings")
{
TestUserSettings settings;
settings.Set<Setting::AllowExperiments>(true);
settings.Set<Setting::Experiments>({{"TestExperiment", true}});
ASSERT_EXPERIMENT(true, ExperimentToggleSource::UserSettingIndividualControl);
}

SECTION("'Allow experiments' disabled in user settings")
{
TestUserSettings settings;
settings.Set<Setting::AllowExperiments>(false);
settings.Set<Setting::Experiments>({{"TestExperiment", true}});
ASSERT_EXPERIMENT(false, ExperimentToggleSource::UserSettingGlobalControl);
}
}
18 changes: 14 additions & 4 deletions src/AppInstallerCommonCore/Experiment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,21 @@ namespace AppInstaller::Settings
}

auto experiment = Experiment::GetExperiment(key);
auto userSettingsExperiments = userSettings.Get<Setting::Experiments>();
auto experimentJsonName = experiment.JsonName();

if (!userSettings.Get<Setting::AllowExperiments>())
{
AICLI_LOG(Core, Info, << "Experiment " << Experiment::GetExperiment(key).Name() <<
" is disabled due to experiments not allowed from user settings");
return { false, ExperimentToggleSource::UserSettingGlobalControl };
}

auto userSettingsExperiments = userSettings.Get<Setting::Experiments>();
if (userSettingsExperiments.find(experimentJsonName) != userSettingsExperiments.end())
{
auto isEnabled = userSettingsExperiments[experimentJsonName];
AICLI_LOG(Core, Info, << "Experiment " << experiment.Name() << " is set to " << (isEnabled ? "true" : "false") << " in user settings");
return { isEnabled, ExperimentToggleSource::UserSetting };
return { isEnabled, ExperimentToggleSource::UserSettingIndividualControl };
}

auto isEnabled = AppInstaller::Experiment::IsEnabled(experiment.GetKey());
Expand All @@ -50,8 +58,10 @@ namespace AppInstaller::Settings
return "Default";
case ExperimentToggleSource::Policy:
return "Policy";
case ExperimentToggleSource::UserSetting:
return "UserSetting";
case ExperimentToggleSource::UserSettingIndividualControl:
return "UserSettingIndividualControl";
case ExperimentToggleSource::UserSettingGlobalControl:
return "UserSettingGlobalControl";
default:
return "Unknown";
}
Expand Down
11 changes: 10 additions & 1 deletion src/AppInstallerCommonCore/Public/winget/Experiment.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,22 @@ namespace AppInstaller::Settings
{
Default = 0,
Policy,
UserSetting,
UserSettingIndividualControl,
UserSettingGlobalControl,
};

struct ExperimentState
{
ExperimentState() = default;
ExperimentState(bool isEnabled, ExperimentToggleSource toggleSource) : m_isEnabled(isEnabled), m_toggleSource(toggleSource) {}

/// <summary>
/// Gets a value indicating whether the experiment is enabled.
/// </summary>
/// <returns>True if the experiment is enabled; otherwise, false.</returns>
/// <remarks>This API expects an experiment to be disabled by default and will
/// always return false if the user opts out of the experiment from the
/// user settings or group policy.</remarks>
bool IsEnabled() const { return m_isEnabled; }
ExperimentToggleSource ToggleSource() const { return m_toggleSource; }
std::string ToJson() const;
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCommonCore/Public/winget/UserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace AppInstaller::Settings
InteractivityDisable,
// Experiments
Experiments,
AllowExperiments,
#ifndef AICLI_DISABLE_TEST_HOOKS
// Debug
EnableSelfInitiatedMinidump,
Expand Down Expand Up @@ -210,6 +211,7 @@ namespace AppInstaller::Settings
// Experiments
using Experiments_t = std::map<std::string, bool>;
SETTINGMAPPING_SPECIALIZATION(Setting::Experiments, Experiments_t, Experiments_t, {}, ".experiments"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::AllowExperiments, bool, bool, true, ".allowExperiments"sv);

// Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types.
template <size_t... I>
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCommonCore/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ namespace AppInstaller::Settings
WINGET_VALIDATE_PASS_THROUGH(NetworkWingetAlternateSourceURL)
WINGET_VALIDATE_PASS_THROUGH(MaxResumes)
WINGET_VALIDATE_PASS_THROUGH(Experiments)
WINGET_VALIDATE_PASS_THROUGH(AllowExperiments)

#ifndef AICLI_DISABLE_TEST_HOOKS
WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump)
Expand Down

0 comments on commit e1bafb4

Please sign in to comment.