| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/user_education/user_education_configuration_provider.h" |
| |
| #include <initializer_list> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/feature_list.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "components/feature_engagement/public/configuration.h" |
| #include "components/feature_engagement/public/feature_list.h" |
| #include "components/feature_engagement/public/group_list.h" |
| #include "components/feature_engagement/public/tracker.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/user_education/common/feature_promo/feature_promo_handle.h" |
| #include "components/user_education/common/feature_promo/feature_promo_registry.h" |
| #include "components/user_education/common/feature_promo/feature_promo_specification.h" |
| #include "components/user_education/common/user_education_features.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/interaction/element_identifier.h" |
| |
| namespace { |
| |
| constexpr char kToastTrigger[] = "ToastIphFeature_trigger"; |
| constexpr char kToastUsed[] = "ToastIphFeature_used"; |
| constexpr char kSnoozeTrigger[] = "SnoozeIphFeature_trigger"; |
| constexpr char kSnoozeUsed[] = "SnoozeIphFeature_used"; |
| constexpr uint32_t kAdditionalConditionDelayDays = 6; |
| constexpr uint32_t kAdditionalConditionUsedLimit = 2; |
| constexpr char kAdditionalConditionName[] = "AdditionalCondition"; |
| constexpr char kAdditionalCondition2Name[] = "AdditionalCondition2"; |
| constexpr uint32_t kAdditionalConditionCount = 3; |
| constexpr uint32_t kAdditionalConditionDays = 14; |
| constexpr char kKeyedNoticeTrigger[] = "KeyedNoticeIphFeature_trigger"; |
| constexpr char kKeyedNoticeUsed[] = "KeyedNoticeIphFeature_used"; |
| constexpr char kLegalNoticeTrigger[] = "LegalNoticeIphFeature_trigger"; |
| constexpr char kLegalNoticeUsed[] = "LegalNoticeIphFeature_used"; |
| constexpr char kActionableAlertTrigger[] = "ActionableAlertFeature_trigger"; |
| constexpr char kActionableAlertUsed[] = "ActionableAlertFeature_used"; |
| BASE_FEATURE(kToastIphFeature, |
| "IPH_ToastIphFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| BASE_FEATURE(kSnoozeIphFeature, |
| "IPH_SnoozeIphFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| BASE_FEATURE(kIphWithConditionFeature, |
| "IPH_kIphWithConditionFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| BASE_FEATURE(kKeyedNoticeIphFeature, |
| "IPH_KeyedNoticeIphFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| BASE_FEATURE(kLegalNoticeIphFeature, |
| "IPH_LegalNoticeIphFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| BASE_FEATURE(kActionableAlertFeature, |
| "IPH_ActionableAlertFeature", |
| base::FEATURE_ENABLED_BY_DEFAULT); |
| const std::initializer_list<const base::Feature*> kKnownFeatures{ |
| &kToastIphFeature, &kSnoozeIphFeature, |
| &kIphWithConditionFeature, &kKeyedNoticeIphFeature, |
| &kActionableAlertFeature, &kLegalNoticeIphFeature}; |
| const std::initializer_list<const base::Feature*> kKnownGroups{}; |
| |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementId); |
| |
| // Provider must be created after possibly setting v2 feature flag. |
| // |
| // Call this only when the provider is actually needed for the test, and after |
| // SetEnableV2 (if necessary). |
| template <typename... Args> |
| std::unique_ptr<UserEducationConfigurationProvider> CreateProvider( |
| Args... additional_specifications) { |
| user_education::FeaturePromoRegistry registry; |
| |
| registry.RegisterFeature( |
| user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kToastIphFeature, kTestElementId, IDS_OK, IDS_CANCEL, |
| user_education::FeaturePromoSpecification::AcceleratorInfo( |
| IDC_HOME))); |
| |
| registry.RegisterFeature( |
| user_education::FeaturePromoSpecification::CreateForSnoozePromo( |
| kSnoozeIphFeature, kTestElementId, IDS_CLOSE)); |
| |
| auto spec = user_education::FeaturePromoSpecification::CreateForCustomAction( |
| kKeyedNoticeIphFeature, kTestElementId, IDS_CANCEL, IDS_OK, |
| base::DoNothing()); |
| spec.set_promo_subtype_for_testing( |
| user_education::FeaturePromoSpecification::PromoSubtype::kKeyedNotice); |
| registry.RegisterFeature(std::move(spec)); |
| |
| spec = user_education::FeaturePromoSpecification::CreateForCustomAction( |
| kLegalNoticeIphFeature, kTestElementId, IDS_CLEAR, IDS_CLOSE, |
| base::DoNothing()); |
| spec.set_promo_subtype_for_testing( |
| user_education::FeaturePromoSpecification::PromoSubtype::kLegalNotice); |
| registry.RegisterFeature(std::move(spec)); |
| |
| spec = user_education::FeaturePromoSpecification::CreateForCustomAction( |
| kActionableAlertFeature, kTestElementId, IDS_CLEAR, IDS_CLOSE, |
| base::DoNothing()); |
| spec.set_promo_subtype_for_testing(user_education::FeaturePromoSpecification:: |
| PromoSubtype::kActionableAlert); |
| registry.RegisterFeature(std::move(spec)); |
| |
| (registry.RegisterFeature(std::move(additional_specifications)), ...); |
| |
| return std::make_unique<UserEducationConfigurationProvider>( |
| std::move(registry)); |
| } |
| |
| } // namespace |
| |
| using AdditionalConditions = |
| user_education::FeaturePromoSpecification::AdditionalConditions; |
| |
| class UserEducationConfigurationProviderTest : public testing::Test { |
| public: |
| UserEducationConfigurationProviderTest() { |
| kSessionRateImpactNone.type = |
| feature_engagement::SessionRateImpact::Type::NONE; |
| } |
| ~UserEducationConfigurationProviderTest() override = default; |
| |
| auto GetDefaultTrigger(const char* name) { |
| return feature_engagement::EventConfig( |
| name, kAny, feature_engagement::kMaxStoragePeriod, |
| feature_engagement::kMaxStoragePeriod); |
| } |
| |
| auto GetAnyTrigger(const char* name) { |
| return feature_engagement::EventConfig( |
| name, kAny, feature_engagement::kMaxStoragePeriod, |
| feature_engagement::kMaxStoragePeriod); |
| } |
| |
| auto GetDefaultUsed(const char* name) { |
| return feature_engagement::EventConfig( |
| name, kEqualsZero, feature_engagement::kMaxStoragePeriod, |
| feature_engagement::kMaxStoragePeriod); |
| } |
| |
| protected: |
| const feature_engagement::Comparator kAny; |
| const feature_engagement::Blocking kBlockingAll; |
| const feature_engagement::BlockedBy kBlockedByAll; |
| const feature_engagement::Comparator kEqualsZero{feature_engagement::EQUAL, |
| 0}; |
| const feature_engagement::Comparator kLessThan5{feature_engagement::LESS_THAN, |
| 5}; |
| const feature_engagement::Comparator kAtLeast7{ |
| feature_engagement::GREATER_THAN_OR_EQUAL, 7}; |
| feature_engagement::SessionRateImpact kSessionRateImpactNone; |
| const feature_engagement::SessionRateImpact kSessionRateImpactAll; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| TEST_F(UserEducationConfigurationProviderTest, ProvidesToastConfiguration) { |
| feature_engagement::FeatureConfig config; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kToastIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_TRUE(config.valid); |
| |
| EXPECT_EQ(GetDefaultUsed(kToastUsed), config.used); |
| |
| EXPECT_EQ(GetDefaultTrigger(kToastTrigger), config.trigger); |
| |
| EXPECT_TRUE(config.event_configs.empty()); |
| |
| EXPECT_EQ(kAny, config.session_rate); |
| |
| EXPECT_EQ(kSessionRateImpactNone, config.session_rate_impact); |
| |
| EXPECT_EQ(feature_engagement::BlockedBy(), config.blocked_by); |
| |
| EXPECT_EQ(feature_engagement::Blocking(), config.blocking); |
| |
| EXPECT_EQ(kAny, config.availability); |
| |
| EXPECT_FALSE(config.tracking_only); |
| |
| EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params); |
| |
| EXPECT_TRUE(config.groups.empty()); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, ProvidesSnoozeConfiguration) { |
| feature_engagement::FeatureConfig config; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_TRUE(config.valid); |
| |
| EXPECT_EQ(GetDefaultUsed(kSnoozeUsed), config.used); |
| |
| EXPECT_EQ(GetDefaultTrigger(kSnoozeTrigger), config.trigger); |
| |
| EXPECT_TRUE(config.event_configs.empty()); |
| |
| EXPECT_EQ(kAny, config.session_rate); |
| |
| EXPECT_EQ(kSessionRateImpactAll, config.session_rate_impact); |
| |
| EXPECT_EQ(feature_engagement::BlockedBy(), config.blocked_by); |
| |
| EXPECT_EQ(feature_engagement::Blocking(), config.blocking); |
| |
| EXPECT_EQ(kAny, config.availability); |
| |
| EXPECT_FALSE(config.tracking_only); |
| |
| EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params); |
| |
| EXPECT_TRUE(config.groups.empty()); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| ProvidesKeyedNoticeConfiguration) { |
| feature_engagement::FeatureConfig config; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kKeyedNoticeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_TRUE(config.valid); |
| |
| EXPECT_EQ(GetDefaultUsed(kKeyedNoticeUsed), config.used); |
| |
| EXPECT_EQ(GetAnyTrigger(kKeyedNoticeTrigger), config.trigger); |
| |
| EXPECT_TRUE(config.event_configs.empty()); |
| |
| EXPECT_EQ(kAny, config.session_rate); |
| |
| EXPECT_EQ(kSessionRateImpactAll, config.session_rate_impact); |
| |
| EXPECT_EQ(feature_engagement::BlockedBy(), config.blocked_by); |
| |
| EXPECT_EQ(feature_engagement::Blocking(), config.blocking); |
| |
| EXPECT_EQ(kAny, config.availability); |
| |
| EXPECT_FALSE(config.tracking_only); |
| |
| EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params); |
| |
| EXPECT_TRUE(config.groups.empty()); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| ProvidesLegalNoticeConfiguration) { |
| feature_engagement::FeatureConfig config; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kLegalNoticeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_TRUE(config.valid); |
| |
| EXPECT_EQ(GetDefaultUsed(kLegalNoticeUsed), config.used); |
| |
| EXPECT_EQ(GetAnyTrigger(kLegalNoticeTrigger), config.trigger); |
| |
| EXPECT_TRUE(config.event_configs.empty()); |
| |
| EXPECT_EQ(kAny, config.session_rate); |
| |
| EXPECT_EQ(kSessionRateImpactAll, config.session_rate_impact); |
| |
| EXPECT_EQ(feature_engagement::BlockedBy(), config.blocked_by); |
| |
| EXPECT_EQ(feature_engagement::Blocking(), config.blocking); |
| |
| EXPECT_EQ(kAny, config.availability); |
| |
| EXPECT_FALSE(config.tracking_only); |
| |
| EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params); |
| |
| EXPECT_TRUE(config.groups.empty()); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| ProvidesActionableAlertConfiguration) { |
| feature_engagement::FeatureConfig config; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kActionableAlertFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_TRUE(config.valid); |
| |
| EXPECT_EQ(GetDefaultUsed(kActionableAlertUsed), config.used); |
| |
| EXPECT_EQ(GetAnyTrigger(kActionableAlertTrigger), config.trigger); |
| |
| EXPECT_TRUE(config.event_configs.empty()); |
| |
| EXPECT_EQ(kAny, config.session_rate); |
| |
| EXPECT_EQ(kSessionRateImpactAll, config.session_rate_impact); |
| |
| EXPECT_EQ(feature_engagement::BlockedBy(), config.blocked_by); |
| |
| EXPECT_EQ(feature_engagement::Blocking(), config.blocking); |
| |
| EXPECT_EQ(kAny, config.availability); |
| |
| EXPECT_FALSE(config.tracking_only); |
| |
| EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params); |
| |
| EXPECT_TRUE(config.groups.empty()); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, HandlesEventConfigs) { |
| feature_engagement::EventConfig event("other_event", kEqualsZero, 100, 100); |
| |
| feature_engagement::FeatureConfig config; |
| config.event_configs.insert(event); |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(kAtLeast7, config.availability); |
| |
| EXPECT_THAT(config.event_configs, testing::ElementsAre(event)); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, DoesntOverwriteNames) { |
| feature_engagement::FeatureConfig config; |
| config.trigger.name = "foo"; |
| config.used.name = "bar"; |
| |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(GetDefaultTrigger("foo"), config.trigger); |
| EXPECT_EQ(GetDefaultUsed("bar"), config.used); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, OverwritesValid_SpecifiesUsed) { |
| feature_engagement::FeatureConfig config; |
| const feature_engagement::EventConfig trigger( |
| "foo", feature_engagement::Comparator(feature_engagement::LESS_THAN, 10), |
| 100, 99); |
| const feature_engagement::EventConfig used( |
| "bar", feature_engagement::Comparator(feature_engagement::LESS_THAN, 8), |
| 97, 98); |
| config.trigger = trigger; |
| config.used = used; |
| config.valid = true; |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(GetDefaultTrigger("foo"), config.trigger); |
| EXPECT_EQ(used, config.used); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, OverwritesValid_NoUsedEvent) { |
| feature_engagement::FeatureConfig config; |
| const feature_engagement::EventConfig trigger( |
| "foo", feature_engagement::Comparator(feature_engagement::LESS_THAN, 10), |
| 100, 99); |
| config.trigger = trigger; |
| config.valid = true; |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(GetDefaultTrigger("foo"), config.trigger); |
| EXPECT_EQ(GetDefaultUsed(kSnoozeUsed), config.used); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_OriginalAvailabilityPreserved) { |
| feature_engagement::FeatureConfig config; |
| const feature_engagement::Comparator availability = { |
| feature_engagement::GREATER_THAN, 4}; |
| config.availability = availability; |
| config.valid = true; |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(availability, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_DefaultAvailability) { |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(kAny, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AvailabilityFromSpec) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.set_initial_delay_days(kAdditionalConditionDelayDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| const feature_engagement::Comparator availability = { |
| feature_engagement::GREATER_THAN_OR_EQUAL, kAdditionalConditionDelayDays}; |
| EXPECT_EQ(availability, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_UsedLimitCustomAvailability) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.set_used_limit(kAdditionalConditionUsedLimit); |
| additional_conditions.set_initial_delay_days(kAdditionalConditionDelayDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| const feature_engagement::Comparator expected_used{ |
| feature_engagement::LESS_THAN_OR_EQUAL, kAdditionalConditionUsedLimit}; |
| const feature_engagement::Comparator expected_availability{ |
| feature_engagement::GREATER_THAN_OR_EQUAL, kAdditionalConditionDelayDays}; |
| EXPECT_EQ(expected_used, config.used.comparator); |
| EXPECT_EQ(expected_availability, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionUpperBoundDefaultAvailability) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtMost, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(1U, config.event_configs.size()); |
| auto& event_config = *config.event_configs.begin(); |
| EXPECT_EQ(kAdditionalConditionName, event_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, event_config.comparator.value); |
| EXPECT_EQ(feature_engagement::LESS_THAN_OR_EQUAL, |
| event_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, event_config.window); |
| EXPECT_LE(kAdditionalConditionDays, event_config.storage); |
| EXPECT_EQ(kAtLeast7, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionUpperBoundCustomAvailability) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.set_initial_delay_days(kAdditionalConditionDelayDays); |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtMost, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(1U, config.event_configs.size()); |
| auto& event_config = *config.event_configs.begin(); |
| EXPECT_EQ(kAdditionalConditionName, event_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, event_config.comparator.value); |
| EXPECT_EQ(feature_engagement::LESS_THAN_OR_EQUAL, |
| event_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, event_config.window); |
| EXPECT_LE(kAdditionalConditionDays, event_config.storage); |
| const feature_engagement::Comparator expected_availability{ |
| feature_engagement::GREATER_THAN_OR_EQUAL, kAdditionalConditionDelayDays}; |
| EXPECT_EQ(expected_availability, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionUpperBoundInAddition) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtMost, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| feature_engagement::EventConfig event_config; |
| event_config.name = kAdditionalCondition2Name; |
| event_config.comparator = {feature_engagement::LESS_THAN_OR_EQUAL, 1}; |
| config.event_configs.insert(event_config); |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(2U, config.event_configs.size()); |
| for (const auto& cur_config : config.event_configs) { |
| if (cur_config.name == event_config.name) { |
| EXPECT_EQ(event_config.comparator, cur_config.comparator); |
| } else { |
| EXPECT_EQ(kAdditionalConditionName, cur_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, cur_config.comparator.value); |
| EXPECT_EQ(feature_engagement::LESS_THAN_OR_EQUAL, |
| cur_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, cur_config.window); |
| EXPECT_LE(kAdditionalConditionDays, cur_config.storage); |
| } |
| } |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionLowerBoundDefaultAvailability) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtLeast, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(1U, config.event_configs.size()); |
| auto& event_config = *config.event_configs.begin(); |
| EXPECT_EQ(kAdditionalConditionName, event_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, event_config.comparator.value); |
| EXPECT_EQ(feature_engagement::GREATER_THAN_OR_EQUAL, |
| event_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, event_config.window); |
| EXPECT_LE(kAdditionalConditionDays, event_config.storage); |
| EXPECT_EQ(kAny, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionLowerBoundCustomAvailability) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.set_initial_delay_days(kAdditionalConditionDelayDays); |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtLeast, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(1U, config.event_configs.size()); |
| auto& event_config = *config.event_configs.begin(); |
| EXPECT_EQ(kAdditionalConditionName, event_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, event_config.comparator.value); |
| EXPECT_EQ(feature_engagement::GREATER_THAN_OR_EQUAL, |
| event_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, event_config.window); |
| EXPECT_LE(kAdditionalConditionDays, event_config.storage); |
| const feature_engagement::Comparator expected_availability{ |
| feature_engagement::GREATER_THAN_OR_EQUAL, kAdditionalConditionDelayDays}; |
| EXPECT_EQ(expected_availability, config.availability); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionLowerBoundInAddition) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtLeast, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| feature_engagement::EventConfig event_config; |
| event_config.name = kAdditionalCondition2Name; |
| event_config.comparator = {feature_engagement::LESS_THAN_OR_EQUAL, 1}; |
| config.event_configs.insert(event_config); |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(2U, config.event_configs.size()); |
| for (const auto& cur_config : config.event_configs) { |
| if (cur_config.name == event_config.name) { |
| EXPECT_EQ(event_config.comparator, cur_config.comparator); |
| } else { |
| EXPECT_EQ(kAdditionalConditionName, cur_config.name); |
| EXPECT_EQ(kAdditionalConditionCount, cur_config.comparator.value); |
| EXPECT_EQ(feature_engagement::GREATER_THAN_OR_EQUAL, |
| cur_config.comparator.type); |
| EXPECT_EQ(kAdditionalConditionDays, cur_config.window); |
| EXPECT_LE(kAdditionalConditionDays, cur_config.storage); |
| } |
| } |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, |
| AdditionalConditions_AddedConditionNotOverridden) { |
| auto spec = user_education::FeaturePromoSpecification::CreateForToastPromo( |
| kIphWithConditionFeature, kTestElementId, IDS_CLOSE, IDS_CANCEL, {}); |
| AdditionalConditions additional_conditions; |
| additional_conditions.AddAdditionalCondition( |
| kAdditionalConditionName, AdditionalConditions::Constraint::kAtLeast, |
| kAdditionalConditionCount, kAdditionalConditionDays); |
| spec.SetAdditionalConditions(std::move(additional_conditions)); |
| |
| feature_engagement::FeatureConfig config; |
| feature_engagement::EventConfig event_config; |
| event_config.name = kAdditionalConditionName; |
| event_config.comparator = {feature_engagement::LESS_THAN_OR_EQUAL, 1}; |
| event_config.window = 4; |
| event_config.storage = 8; |
| config.event_configs.insert(event_config); |
| EXPECT_TRUE(CreateProvider(std::move(spec)) |
| ->MaybeProvideFeatureConfiguration(kIphWithConditionFeature, |
| config, kKnownFeatures, |
| kKnownGroups)); |
| |
| EXPECT_EQ(1U, config.event_configs.size()); |
| const auto& new_config = *config.event_configs.begin(); |
| EXPECT_EQ(event_config.name, kAdditionalConditionName); |
| EXPECT_EQ(event_config.comparator, new_config.comparator); |
| EXPECT_EQ(event_config.window, new_config.window); |
| EXPECT_LE(event_config.storage, new_config.storage); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, SessionRate) { |
| feature_engagement::FeatureConfig config; |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kSnoozeIphFeature, config, kKnownFeatures, kKnownGroups)); |
| |
| EXPECT_EQ(feature_engagement::ANY, config.session_rate.type); |
| EXPECT_EQ(0U, config.session_rate.value); |
| EXPECT_EQ(feature_engagement::SessionRateImpact::Type::ALL, |
| config.session_rate_impact.type); |
| } |
| |
| TEST_F(UserEducationConfigurationProviderTest, RemovesDuplicateTrigger) { |
| feature_engagement::FeatureConfig config; |
| config.trigger.name = kToastTrigger; |
| config.used.name = kToastUsed; |
| config.event_configs.emplace(kToastTrigger, kLessThan5, 10, 10); |
| config.event_configs.emplace(kToastUsed, kAtLeast7, 10, 10); |
| EXPECT_TRUE(CreateProvider()->MaybeProvideFeatureConfiguration( |
| kToastIphFeature, config, kKnownFeatures, kKnownGroups)); |
| EXPECT_EQ(1U, config.event_configs.size()); |
| EXPECT_EQ(kToastUsed, config.event_configs.begin()->name); |
| } |