Create RegisterForContinuousDisplay() & RegisterForSingleDisplay().
The PromosManager needs to expose two public methods enabling promos to
be registered for single or continuous display.
Although the active promos are stored in a list Pref, the order promo
entries are populated in the list is irrelevant, as active promos are
stored in an in-memory set during PromosManager evaluation.
A given promo can be registered both for single & continuous display.
The manager will handle this use-case gracefully.
Change-Id: I8e62d2995f776a8390ba4390912c92259a566d57
Bug: 1359359
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3864316
Reviewed-by: Robbie Gibson <rkgibson@google.com>
Commit-Queue: Benjamin Williams <bwwilliams@google.com>
Cr-Commit-Position: refs/heads/main@{#1042716}
diff --git a/ios/chrome/browser/promos_manager/promos_manager.h b/ios/chrome/browser/promos_manager/promos_manager.h
index c5789d8..cabedd2 100644
--- a/ios/chrome/browser/promos_manager/promos_manager.h
+++ b/ios/chrome/browser/promos_manager/promos_manager.h
@@ -28,6 +28,14 @@
// Returns the next promo for display, if any.
absl::optional<promos_manager::Promo> NextPromoForDisplay() const;
+ // Registers `promo` for continuous display, and persists registration status
+ // across app launches.
+ void RegisterPromoForContinuousDisplay(promos_manager::Promo promo);
+
+ // Registers `promo` for single (one-time) display, and persists registration
+ // status across app launches.
+ void RegisterPromoForSingleDisplay(promos_manager::Promo promo);
+
// Initialize the Promos Manager by restoring state from Prefs. Must be called
// after creation and before any other operation.
void Init();
@@ -182,6 +190,24 @@
ReturnsBlankActivePromosForBlankPrefs);
FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
ReturnsActivePromosAndSkipsMalformedData);
+ FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
+ RegistersPromoForContinuousDisplay);
+ FRIEND_TEST_ALL_PREFIXES(
+ PromosManagerTest,
+ RegistersPromoForContinuousDisplayForEmptyActivePromos);
+ FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForContinuousDisplay);
+ FRIEND_TEST_ALL_PREFIXES(
+ PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForContinuousDisplayForEmptyActivePromos);
+ FRIEND_TEST_ALL_PREFIXES(PromosManagerTest, RegistersPromoForSingleDisplay);
+ FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
+ RegistersPromoForSingleDisplayForEmptyActivePromos);
+ FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForSingleDisplay);
+ FRIEND_TEST_ALL_PREFIXES(
+ PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForSingleDisplayForEmptyActivePromos);
};
#endif // IOS_CHROME_BROWSER_PROMOS_MANAGER_PROMOS_MANAGER_H_
diff --git a/ios/chrome/browser/promos_manager/promos_manager.mm b/ios/chrome/browser/promos_manager/promos_manager.mm
index df7dcb7..9eb0b3e6 100644
--- a/ios/chrome/browser/promos_manager/promos_manager.mm
+++ b/ios/chrome/browser/promos_manager/promos_manager.mm
@@ -16,6 +16,7 @@
#import "base/time/time.h"
#import "base/values.h"
#import "components/prefs/pref_service.h"
+#import "components/prefs/scoped_user_pref_update.h"
#import "ios/chrome/browser/prefs/pref_names.h"
#import "ios/chrome/browser/promos_manager/constants.h"
#import "ios/chrome/browser/promos_manager/features.h"
@@ -34,6 +35,25 @@
return (base::Time::Now() - base::Time::UnixEpoch()).InDays();
}
+// Conditionally appends `promo` to the list pref `pref_path`. If `promo`
+// already exists in the list pref `pref_path`, does nothing. If `promo` doesn't
+// exist in the list pref `pref_path`, appends `promo` to the list.
+void ConditionallyAppendPromoToPrefList(promos_manager::Promo promo,
+ const std::string& pref_path,
+ PrefService* local_state) {
+ DCHECK(local_state);
+
+ ListPrefUpdate update(local_state, pref_path);
+ base::Value::List& active_promos = update->GetList();
+ std::string promo_name = promos_manager::NameForPromo(promo);
+
+ // Erase `promo_name` if it already exists in `active_promos`; avoid polluting
+ // `active_promos` with duplicate `promo_name` entries.
+ active_promos.EraseValue(base::Value(promo_name));
+
+ active_promos.Append(promo_name);
+}
+
} // namespace
#pragma mark - PromosManager
@@ -63,6 +83,17 @@
local_state_->GetValueList(prefs::kIosPromosManagerImpressions));
}
+void PromosManager::RegisterPromoForContinuousDisplay(
+ promos_manager::Promo promo) {
+ ConditionallyAppendPromoToPrefList(
+ promo, prefs::kIosPromosManagerActivePromos, local_state_);
+}
+
+void PromosManager::RegisterPromoForSingleDisplay(promos_manager::Promo promo) {
+ ConditionallyAppendPromoToPrefList(
+ promo, prefs::kIosPromosManagerSingleDisplayActivePromos, local_state_);
+}
+
absl::optional<promos_manager::Promo> PromosManager::NextPromoForDisplay()
const {
// Construct a superset including active (1) single-display and
diff --git a/ios/chrome/browser/promos_manager/promos_manager_unittest.mm b/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
index 4d159fc..5869ecf 100644
--- a/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
+++ b/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
@@ -636,3 +636,260 @@
EXPECT_EQ(expected, promos_manager_->ActivePromos(promos));
}
+
+// Tests PromosManager::RegisterPromoForContinuousDisplay() correctly registers
+// a promo for continuous display by writing the promo's name to the pref
+// `kIosPromosManagerActivePromos`.
+TEST_F(PromosManagerTest, RegistersPromoForContinuousDisplay) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::AppStoreRating);
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)2);
+
+ // Register new promo.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::DefaultBrowser);
+
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)3);
+ EXPECT_EQ(local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[0],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[1],
+ promos_manager::NameForPromo(promos_manager::Promo::AppStoreRating));
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[2],
+ promos_manager::NameForPromo(promos_manager::Promo::DefaultBrowser));
+}
+
+// Tests PromosManager::RegisterPromoForContinuousDisplay() correctly registers
+// a promo—for the very first time—for continuous display by writing the
+// promo's name to the pref `kIosPromosManagerActivePromos`.
+TEST_F(PromosManagerTest,
+ RegistersPromoForContinuousDisplayForEmptyActivePromos) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).empty());
+
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::DefaultBrowser);
+
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)1);
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[0],
+ promos_manager::NameForPromo(promos_manager::Promo::DefaultBrowser));
+}
+
+// Tests PromosManager::RegisterPromoForContinuousDisplay() correctly registers
+// an already-registered promo for continuous display by first erasing, and then
+// re-writing, the promo's name to the pref `kIosPromosManagerActivePromos`;
+// tests no duplicate entries are created.
+TEST_F(PromosManagerTest, RegistersAlreadyRegisteredPromoForContinuousDisplay) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::AppStoreRating);
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)2);
+
+ // Register existing promo.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)2);
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[0],
+ promos_manager::NameForPromo(promos_manager::Promo::AppStoreRating));
+ EXPECT_EQ(local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[1],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+}
+
+// Tests PromosManager::RegisterPromoForContinuousDisplay() correctly registers
+// an already-registered promo for continuous display—for the very first time—by
+// first erasing, and then re-writing, the promo's name to the pref
+// `kIosPromosManagerActivePromos`; tests no duplicate entries are created.
+TEST_F(
+ PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForContinuousDisplayForEmptyActivePromos) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ // Register existing promo.
+ promos_manager_->RegisterPromoForContinuousDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ EXPECT_EQ(
+ local_state_->GetValueList(prefs::kIosPromosManagerActivePromos).size(),
+ (size_t)1);
+ EXPECT_EQ(local_state_->GetValueList(prefs::kIosPromosManagerActivePromos)[0],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+}
+
+// Tests PromosManager::RegisterPromoForSingleDisplay() correctly registers
+// a promo for single display by writing the promo's name to the pref
+// `kIosPromosManagerSingleDisplayActivePromos`.
+TEST_F(PromosManagerTest, RegistersPromoForSingleDisplay) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::AppStoreRating);
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)2);
+
+ // Register new promo.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::DefaultBrowser);
+
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)3);
+ EXPECT_EQ(local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[0],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+ EXPECT_EQ(
+ local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[1],
+ promos_manager::NameForPromo(promos_manager::Promo::AppStoreRating));
+ EXPECT_EQ(
+ local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[2],
+ promos_manager::NameForPromo(promos_manager::Promo::DefaultBrowser));
+}
+
+// Tests PromosManager::RegisterPromoForSingleDisplay() correctly registers
+// a promo for single display—for the very first time—by writing the promo's
+// name to the pref `kIosPromosManagerSingleDisplayActivePromos`.
+TEST_F(PromosManagerTest, RegistersPromoForSingleDisplayForEmptyActivePromos) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .empty());
+
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::DefaultBrowser);
+
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)1);
+ EXPECT_EQ(
+ local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[0],
+ promos_manager::NameForPromo(promos_manager::Promo::DefaultBrowser));
+}
+
+// Tests PromosManager::RegisterPromoForSingleDisplay() correctly registers
+// an already-registered promo for single display by first erasing, and then
+// re-writing, the promo's name to the pref
+// `kIosPromosManagerSingleDisplayActivePromos`; tests no duplicate entries are
+// created.
+TEST_F(PromosManagerTest, RegistersAlreadyRegisteredPromoForSingleDisplay) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::AppStoreRating);
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)2);
+
+ // Register existing promo.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)2);
+ EXPECT_EQ(
+ local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[0],
+ promos_manager::NameForPromo(promos_manager::Promo::AppStoreRating));
+ EXPECT_EQ(local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[1],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+}
+
+// Tests PromosManager::RegisterPromoForSingleDisplay() correctly registers
+// an already-registered promo for single display—for the very first time—by
+// first erasing, and then re-writing, the promo's name to the pref
+// `kIosPromosManagerSingleDisplayActivePromos`; tests no duplicate entries are
+// created.
+TEST_F(PromosManagerTest,
+ RegistersAlreadyRegisteredPromoForSingleDisplayForEmptyActivePromos) {
+ CreatePromosManager();
+ EXPECT_TRUE(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .empty());
+
+ // Initial active promos state.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ // Register existing promo.
+ promos_manager_->RegisterPromoForSingleDisplay(
+ promos_manager::Promo::CredentialProviderExtension);
+
+ EXPECT_EQ(
+ local_state_
+ ->GetValueList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+ .size(),
+ (size_t)1);
+ EXPECT_EQ(local_state_->GetValueList(
+ prefs::kIosPromosManagerSingleDisplayActivePromos)[0],
+ promos_manager::NameForPromo(
+ promos_manager::Promo::CredentialProviderExtension));
+}