mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-02 17:28:50 +02:00
Bug 1744953 - Add support for rollouts in the Nimbus platform API r=barret
Differential Revision: https://phabricator.services.mozilla.com/D135495
This commit is contained in:
parent
636619b737
commit
f76baf9a72
3 changed files with 152 additions and 26 deletions
|
|
@ -16,44 +16,75 @@ namespace mozilla {
|
|||
|
||||
static nsTHashSet<nsCString> sExposureFeatureSet;
|
||||
|
||||
void NimbusFeatures::GetPrefName(const nsACString& aFeatureId,
|
||||
void NimbusFeatures::GetPrefName(const nsACString& branchPrefix,
|
||||
const nsACString& aFeatureId,
|
||||
const nsACString& aVariable,
|
||||
nsACString& aPref) {
|
||||
// This branch is used to store experiment data
|
||||
constexpr auto kSyncDataPrefBranch = "nimbus.syncdatastore."_ns;
|
||||
aPref.Truncate();
|
||||
aPref.Append(kSyncDataPrefBranch);
|
||||
aPref.Append(aFeatureId);
|
||||
nsAutoCString featureAndVariable;
|
||||
featureAndVariable.Append(aFeatureId);
|
||||
if (!aVariable.IsEmpty()) {
|
||||
aPref.Append(".");
|
||||
aPref.Append(aVariable);
|
||||
featureAndVariable.Append(".");
|
||||
featureAndVariable.Append(aVariable);
|
||||
}
|
||||
aPref.Truncate();
|
||||
aPref.Append(branchPrefix);
|
||||
aPref.Append(featureAndVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable value configured via experiment or rollout.
|
||||
* If a fallback pref is defined in the FeatureManifest and it
|
||||
* has a user value set this takes precedence over remote configurations.
|
||||
*/
|
||||
bool NimbusFeatures::GetBool(const nsACString& aFeatureId,
|
||||
const nsACString& aVariable, bool aDefault) {
|
||||
nsAutoCString pref;
|
||||
GetPrefName(aFeatureId, aVariable, pref);
|
||||
if (Preferences::HasUserValue(pref.get())) {
|
||||
return Preferences::GetBool(pref.get(), aDefault);
|
||||
auto prefName = GetNimbusFallbackPrefName(aFeatureId, aVariable);
|
||||
if (prefName.isSome() && Preferences::HasUserValue(prefName->get())) {
|
||||
return Preferences::GetBool(prefName->get(), aDefault);
|
||||
}
|
||||
|
||||
nsAutoCString experimentPref;
|
||||
GetPrefName(kSyncDataPrefBranch, aFeatureId, aVariable, experimentPref);
|
||||
if (Preferences::HasUserValue(experimentPref.get())) {
|
||||
return Preferences::GetBool(experimentPref.get(), aDefault);
|
||||
}
|
||||
|
||||
nsAutoCString rolloutPref;
|
||||
GetPrefName(kSyncRolloutsPrefBranch, aFeatureId, aVariable, rolloutPref);
|
||||
if (Preferences::HasUserValue(rolloutPref.get())) {
|
||||
return Preferences::GetBool(rolloutPref.get(), aDefault);
|
||||
}
|
||||
|
||||
auto prefName = GetNimbusFallbackPrefName(aFeatureId, aVariable);
|
||||
if (prefName.isSome()) {
|
||||
return Preferences::GetBool(prefName->get(), aDefault);
|
||||
}
|
||||
return aDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable value configured via experiment or rollout.
|
||||
* If a fallback pref is defined in the FeatureManifest and it
|
||||
* has a user value set this takes precedence over remote configurations.
|
||||
*/
|
||||
int NimbusFeatures::GetInt(const nsACString& aFeatureId,
|
||||
const nsACString& aVariable, int aDefault) {
|
||||
nsAutoCString pref;
|
||||
GetPrefName(aFeatureId, aVariable, pref);
|
||||
if (Preferences::HasUserValue(pref.get())) {
|
||||
return Preferences::GetInt(pref.get(), aDefault);
|
||||
auto prefName = GetNimbusFallbackPrefName(aFeatureId, aVariable);
|
||||
if (prefName.isSome() && Preferences::HasUserValue(prefName->get())) {
|
||||
return Preferences::GetInt(prefName->get(), aDefault);
|
||||
}
|
||||
|
||||
nsAutoCString experimentPref;
|
||||
GetPrefName(kSyncDataPrefBranch, aFeatureId, aVariable, experimentPref);
|
||||
if (Preferences::HasUserValue(experimentPref.get())) {
|
||||
return Preferences::GetInt(experimentPref.get(), aDefault);
|
||||
}
|
||||
|
||||
nsAutoCString rolloutPref;
|
||||
GetPrefName(kSyncRolloutsPrefBranch, aFeatureId, aVariable, rolloutPref);
|
||||
if (Preferences::HasUserValue(rolloutPref.get())) {
|
||||
return Preferences::GetInt(rolloutPref.get(), aDefault);
|
||||
}
|
||||
|
||||
auto prefName = GetNimbusFallbackPrefName(aFeatureId, aVariable);
|
||||
if (prefName.isSome()) {
|
||||
return Preferences::GetInt(prefName->get(), aDefault);
|
||||
}
|
||||
|
|
@ -64,18 +95,34 @@ nsresult NimbusFeatures::OnUpdate(const nsACString& aFeatureId,
|
|||
const nsACString& aVariable,
|
||||
PrefChangedFunc aUserCallback,
|
||||
void* aUserData) {
|
||||
nsAutoCString pref;
|
||||
GetPrefName(aFeatureId, aVariable, pref);
|
||||
return Preferences::RegisterCallback(aUserCallback, pref, aUserData);
|
||||
nsAutoCString experimentPref;
|
||||
nsAutoCString rolloutPref;
|
||||
GetPrefName(kSyncDataPrefBranch, aFeatureId, aVariable, experimentPref);
|
||||
GetPrefName(kSyncRolloutsPrefBranch, aFeatureId, aVariable, rolloutPref);
|
||||
nsresult rv =
|
||||
Preferences::RegisterCallback(aUserCallback, experimentPref, aUserData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = Preferences::RegisterCallback(aUserCallback, rolloutPref, aUserData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult NimbusFeatures::OffUpdate(const nsACString& aFeatureId,
|
||||
const nsACString& aVariable,
|
||||
PrefChangedFunc aUserCallback,
|
||||
void* aUserData) {
|
||||
nsAutoCString pref;
|
||||
GetPrefName(aFeatureId, aVariable, pref);
|
||||
return Preferences::UnregisterCallback(aUserCallback, pref, aUserData);
|
||||
nsAutoCString experimentPref;
|
||||
nsAutoCString rolloutPref;
|
||||
GetPrefName(kSyncDataPrefBranch, aFeatureId, aVariable, experimentPref);
|
||||
GetPrefName(kSyncRolloutsPrefBranch, aFeatureId, aVariable, rolloutPref);
|
||||
nsresult rv =
|
||||
Preferences::UnregisterCallback(aUserCallback, experimentPref, aUserData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = Preferences::UnregisterCallback(aUserCallback, rolloutPref, aUserData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,7 +146,7 @@ nsresult NimbusFeatures::GetExperimentSlug(const nsACString& aFeatureId,
|
|||
aExperimentSlug.Truncate();
|
||||
aBranchSlug.Truncate();
|
||||
|
||||
GetPrefName(aFeatureId, ""_ns, prefName);
|
||||
GetPrefName(kSyncDataPrefBranch, aFeatureId, EmptyCString(), prefName);
|
||||
MOZ_TRY(Preferences::GetString(prefName.get(), prefValue));
|
||||
if (prefValue.IsEmpty()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ namespace mozilla {
|
|||
|
||||
class NimbusFeatures {
|
||||
private:
|
||||
static void GetPrefName(const nsACString& aFeatureId,
|
||||
// This branch is used to store experiment data
|
||||
static constexpr auto kSyncDataPrefBranch = "nimbus.syncdatastore."_ns;
|
||||
// This branch is used to store rollouts data
|
||||
static constexpr auto kSyncRolloutsPrefBranch =
|
||||
"nimbus.syncdefaultsstore."_ns;
|
||||
static void GetPrefName(const nsACString& branchPrefix,
|
||||
const nsACString& aFeatureId,
|
||||
const nsACString& aVariable, nsACString& aPref);
|
||||
|
||||
static nsresult GetExperimentSlug(const nsACString& aFeatureId,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,62 @@ TEST(NimbusFeaturesGet, Errors)
|
|||
ASSERT_TRUE(NimbusFeatures::GetBool("foo"_ns, "enabled"_ns, false));
|
||||
}
|
||||
|
||||
TEST(NimbusFeaturesGetRollout, Errors)
|
||||
{
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdefaultsstore.rollout.value", 7,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("rollout"_ns, "value"_ns, 0), 7);
|
||||
ASSERT_EQ(Preferences::SetBool("nimbus.syncdefaultsstore.rollout.enabled",
|
||||
true, PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_TRUE(NimbusFeatures::GetBool("rollout"_ns, "enabled"_ns, false));
|
||||
}
|
||||
|
||||
TEST(NimbusFeaturesExperimentPriorityOverRollouts, Errors)
|
||||
{
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdatastore.feature.value", 12,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdefaultsstore.feature.value", 22,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("feature"_ns, "value"_ns, 0), 12);
|
||||
ASSERT_EQ(Preferences::SetBool("nimbus.syncdatastore.feature.enabled", true,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(Preferences::SetBool("nimbus.syncdefaultsstore.feature.enabled",
|
||||
false, PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_TRUE(NimbusFeatures::GetBool("feature"_ns, "enabled"_ns, false));
|
||||
}
|
||||
|
||||
// Make sure user prefs take predence over experiments and rollouts
|
||||
TEST(NimbusFeaturesDataSourcePrecedence, Errors)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
Preferences::SetInt("nimbus.testing.testInt", 29, PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("testFeature"_ns, "testInt"_ns, 0), 29);
|
||||
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdatastore.testFeature.testInt", 12,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdefaultsstore.testFeature.testInt",
|
||||
13, PrefValueKind::User),
|
||||
NS_OK);
|
||||
// Still return user pref
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("testFeature"_ns, "testInt"_ns, 0), 29);
|
||||
// After user prefs it should default to experiment value
|
||||
Preferences::ClearUser("nimbus.testing.testInt");
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("testFeature"_ns, "testInt"_ns, 0), 12);
|
||||
Preferences::ClearUser("nimbus.syncdatastore.testFeature.testInt");
|
||||
// After experiments it should default to rollouts
|
||||
ASSERT_EQ(NimbusFeatures::GetInt("testFeature"_ns, "testInt"_ns, 0), 13);
|
||||
// Cleanup
|
||||
Preferences::ClearUser("nimbus.syncdefaultsstore.testFeature.testInt");
|
||||
}
|
||||
|
||||
static void FooValueUpdated(const char* aPref, void* aUserData) {
|
||||
ASSERT_STREQ(aPref, "nimbus.syncdatastore.foo.value");
|
||||
ASSERT_EQ(aUserData, reinterpret_cast<void*>(13));
|
||||
|
|
@ -34,6 +90,13 @@ static void FooValueUpdated(const char* aPref, void* aUserData) {
|
|||
ASSERT_EQ(NimbusFeatures::GetInt("foo"_ns, "value"_ns, 0), 24);
|
||||
}
|
||||
|
||||
static void BarRolloutValueUpdated(const char* aPref, void* aUserData) {
|
||||
ASSERT_STREQ(aPref, "nimbus.syncdefaultsstore.bar.value");
|
||||
|
||||
ASSERT_FALSE(gPrefUpdate);
|
||||
gPrefUpdate = true;
|
||||
}
|
||||
|
||||
TEST(NimbusFeaturesGetFallback, Errors)
|
||||
{
|
||||
// No experiment is set and we expect to return fallback pref values
|
||||
|
|
@ -60,6 +123,10 @@ TEST(NimbusFeaturesUpdate, Errors)
|
|||
ASSERT_EQ(NimbusFeatures::OnUpdate("foo"_ns, "value"_ns, FooValueUpdated,
|
||||
reinterpret_cast<void*>(13)),
|
||||
NS_OK);
|
||||
ASSERT_EQ(
|
||||
NimbusFeatures::OnUpdate("bar"_ns, "value"_ns, BarRolloutValueUpdated,
|
||||
reinterpret_cast<void*>(13)),
|
||||
NS_OK);
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdatastore.foo.value", 24,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
|
|
@ -74,6 +141,12 @@ TEST(NimbusFeaturesUpdate, Errors)
|
|||
ASSERT_FALSE(NimbusFeatures::GetBool("foo"_ns, "enabled"_ns, true));
|
||||
gPrefUpdate = false;
|
||||
|
||||
ASSERT_EQ(Preferences::SetInt("nimbus.syncdefaultsstore.bar.value", 25,
|
||||
PrefValueKind::User),
|
||||
NS_OK);
|
||||
ASSERT_TRUE(gPrefUpdate);
|
||||
gPrefUpdate = false;
|
||||
|
||||
// Verify OffUpdate requires a matching user data pointer to unregister.
|
||||
ASSERT_EQ(NimbusFeatures::OffUpdate("foo"_ns, "value"_ns, FooValueUpdated,
|
||||
reinterpret_cast<void*>(14)),
|
||||
|
|
|
|||
Loading…
Reference in a new issue