blob: af7fbc7953a8b8cdaa0c108607df07e52cf7b22e [file] [log] [blame]
// 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);
}