1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/feature_engagement/internal/feature_config_condition_validator.h"
6
7 #include <map>
8 #include <memory>
9 #include <string>
10
11 #include "base/feature_list.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/test/scoped_feature_list.h"
14 #include "components/feature_engagement/internal/availability_model.h"
15 #include "components/feature_engagement/internal/event_model.h"
16 #include "components/feature_engagement/internal/noop_display_lock_controller.h"
17 #include "components/feature_engagement/internal/proto/feature_event.pb.h"
18 #include "components/feature_engagement/internal/test/event_util.h"
19 #include "components/feature_engagement/public/configuration.h"
20 #include "components/feature_engagement/public/tracker.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace feature_engagement {
24
25 namespace {
26
27 const base::Feature kFeatureConfigTestFeatureFoo{
28 "test_foo", base::FEATURE_DISABLED_BY_DEFAULT};
29 const base::Feature kFeatureConfigTestFeatureBar{
30 "test_bar", base::FEATURE_DISABLED_BY_DEFAULT};
31 const base::Feature kFeatureConfigTestFeatureQux{
32 "test_qux", base::FEATURE_DISABLED_BY_DEFAULT};
33 const base::Feature kFeatureConfigTestFeatureXyz{
34 "test_xyz", base::FEATURE_DISABLED_BY_DEFAULT};
35
GetValidFeatureConfig()36 FeatureConfig GetValidFeatureConfig() {
37 FeatureConfig config;
38 config.valid = true;
39 return config;
40 }
41
GetAcceptingFeatureConfig()42 FeatureConfig GetAcceptingFeatureConfig() {
43 FeatureConfig config;
44 config.valid = true;
45 config.used = EventConfig("used", Comparator(ANY, 0), 0, 0);
46 config.trigger = EventConfig("trigger", Comparator(ANY, 0), 0, 0);
47 config.session_rate = Comparator(ANY, 0);
48 config.availability = Comparator(ANY, 0);
49 return config;
50 }
51
CreateSessionRateImpactTypeExplicit(std::vector<std::string> affected_features)52 SessionRateImpact CreateSessionRateImpactTypeExplicit(
53 std::vector<std::string> affected_features) {
54 SessionRateImpact impact;
55 impact.type = SessionRateImpact::Type::EXPLICIT;
56 impact.affected_features = affected_features;
57 return impact;
58 }
59
60 class TestEventModel : public EventModel {
61 public:
TestEventModel()62 TestEventModel() : ready_(true) {}
63
Initialize(const OnModelInitializationFinished & callback,uint32_t current_day)64 void Initialize(const OnModelInitializationFinished& callback,
65 uint32_t current_day) override {}
66
IsReady() const67 bool IsReady() const override { return ready_; }
68
SetIsReady(bool ready)69 void SetIsReady(bool ready) { ready_ = ready; }
70
GetEvent(const std::string & event_name) const71 const Event* GetEvent(const std::string& event_name) const override {
72 auto search = events_.find(event_name);
73 if (search == events_.end())
74 return nullptr;
75
76 return &search->second;
77 }
78
SetEvent(const Event & event)79 void SetEvent(const Event& event) { events_[event.name()] = event; }
80
IncrementEvent(const std::string & event_name,uint32_t day)81 void IncrementEvent(const std::string& event_name, uint32_t day) override {}
82
83 private:
84 std::map<std::string, Event> events_;
85 bool ready_;
86 };
87
88 class TestAvailabilityModel : public AvailabilityModel {
89 public:
TestAvailabilityModel()90 TestAvailabilityModel() : ready_(true) {}
91 ~TestAvailabilityModel() override = default;
92
Initialize(AvailabilityModel::OnInitializedCallback callback,uint32_t current_day)93 void Initialize(AvailabilityModel::OnInitializedCallback callback,
94 uint32_t current_day) override {}
95
IsReady() const96 bool IsReady() const override { return ready_; }
97
SetIsReady(bool ready)98 void SetIsReady(bool ready) { ready_ = ready; }
99
GetAvailability(const base::Feature & feature) const100 base::Optional<uint32_t> GetAvailability(
101 const base::Feature& feature) const override {
102 auto search = availabilities_.find(feature.name);
103 if (search == availabilities_.end())
104 return base::nullopt;
105
106 return search->second;
107 }
108
SetAvailability(const base::Feature * feature,base::Optional<uint32_t> availability)109 void SetAvailability(const base::Feature* feature,
110 base::Optional<uint32_t> availability) {
111 availabilities_[feature->name] = availability;
112 }
113
114 private:
115 bool ready_;
116
117 std::map<std::string, base::Optional<uint32_t>> availabilities_;
118
119 DISALLOW_COPY_AND_ASSIGN(TestAvailabilityModel);
120 };
121
122 class TestDisplayLockController : public DisplayLockController {
123 public:
124 TestDisplayLockController() = default;
125 ~TestDisplayLockController() override = default;
126
AcquireDisplayLock()127 std::unique_ptr<DisplayLockHandle> AcquireDisplayLock() override {
128 return nullptr;
129 }
130
IsDisplayLocked() const131 bool IsDisplayLocked() const override { return next_display_locked_result_; }
132
SetNextIsDisplayLockedResult(bool result)133 void SetNextIsDisplayLockedResult(bool result) {
134 next_display_locked_result_ = result;
135 }
136
137 private:
138 // The next result to return from IsDisplayLocked().
139 bool next_display_locked_result_ = false;
140
141 DISALLOW_COPY_AND_ASSIGN(TestDisplayLockController);
142 };
143
144 class FeatureConfigConditionValidatorTest : public ::testing::Test {
145 public:
146 FeatureConfigConditionValidatorTest() = default;
147
148 protected:
GetResultForDayAndEventWindow(Comparator comparator,uint32_t window,uint32_t current_day)149 ConditionValidator::Result GetResultForDayAndEventWindow(
150 Comparator comparator,
151 uint32_t window,
152 uint32_t current_day) {
153 FeatureConfig config = GetAcceptingFeatureConfig();
154 config.event_configs.insert(EventConfig("event1", comparator, window, 0));
155 return validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
156 event_model_, availability_model_,
157 display_lock_controller_, current_day);
158 }
159
GetResultForDay(const FeatureConfig & config,uint32_t current_day)160 ConditionValidator::Result GetResultForDay(const FeatureConfig& config,
161 uint32_t current_day) {
162 return validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
163 event_model_, availability_model_,
164 display_lock_controller_, current_day);
165 }
166
GetResultForDayZero(const FeatureConfig & config)167 ConditionValidator::Result GetResultForDayZero(const FeatureConfig& config) {
168 return validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
169 event_model_, availability_model_,
170 display_lock_controller_, 0);
171 }
172
GetResultForDayZeroForFeature(const base::Feature & feature,const FeatureConfig & config)173 ConditionValidator::Result GetResultForDayZeroForFeature(
174 const base::Feature& feature,
175 const FeatureConfig& config) {
176 return validator_.MeetsConditions(feature, config, event_model_,
177 availability_model_,
178 display_lock_controller_, 0);
179 }
180
181 TestEventModel event_model_;
182 TestAvailabilityModel availability_model_;
183 TestDisplayLockController display_lock_controller_;
184 FeatureConfigConditionValidator validator_;
185 uint32_t current_day_;
186
187 private:
188 DISALLOW_COPY_AND_ASSIGN(FeatureConfigConditionValidatorTest);
189 };
190
191 } // namespace
192
TEST_F(FeatureConfigConditionValidatorTest,ModelNotReadyShouldFail)193 TEST_F(FeatureConfigConditionValidatorTest, ModelNotReadyShouldFail) {
194 base::test::ScopedFeatureList scoped_feature_list;
195 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
196
197 event_model_.SetIsReady(false);
198
199 ConditionValidator::Result result =
200 GetResultForDayZero(GetValidFeatureConfig());
201 EXPECT_FALSE(result.NoErrors());
202 EXPECT_FALSE(result.event_model_ready_ok);
203 }
204
TEST_F(FeatureConfigConditionValidatorTest,ConfigInvalidShouldFail)205 TEST_F(FeatureConfigConditionValidatorTest, ConfigInvalidShouldFail) {
206 base::test::ScopedFeatureList scoped_feature_list;
207 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
208
209 ConditionValidator::Result result = GetResultForDayZero(FeatureConfig());
210 EXPECT_FALSE(result.NoErrors());
211 EXPECT_FALSE(result.config_ok);
212 }
213
TEST_F(FeatureConfigConditionValidatorTest,MultipleErrorsShouldBeSet)214 TEST_F(FeatureConfigConditionValidatorTest, MultipleErrorsShouldBeSet) {
215 base::test::ScopedFeatureList scoped_feature_list;
216 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
217
218 event_model_.SetIsReady(false);
219
220 ConditionValidator::Result result = GetResultForDayZero(FeatureConfig());
221 EXPECT_FALSE(result.NoErrors());
222 EXPECT_FALSE(result.event_model_ready_ok);
223 EXPECT_FALSE(result.config_ok);
224 }
225
TEST_F(FeatureConfigConditionValidatorTest,ReadyModelEmptyConfig)226 TEST_F(FeatureConfigConditionValidatorTest, ReadyModelEmptyConfig) {
227 base::test::ScopedFeatureList scoped_feature_list;
228 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
229
230 EXPECT_TRUE(GetResultForDayZero(GetValidFeatureConfig()).NoErrors());
231 }
232
TEST_F(FeatureConfigConditionValidatorTest,ReadyModelAcceptingConfig)233 TEST_F(FeatureConfigConditionValidatorTest, ReadyModelAcceptingConfig) {
234 base::test::ScopedFeatureList scoped_feature_list;
235 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
236
237 EXPECT_TRUE(GetResultForDayZero(GetAcceptingFeatureConfig()).NoErrors());
238 }
239
TEST_F(FeatureConfigConditionValidatorTest,CurrentlyShowing)240 TEST_F(FeatureConfigConditionValidatorTest, CurrentlyShowing) {
241 base::test::ScopedFeatureList scoped_feature_list;
242 scoped_feature_list.InitWithFeatures(
243 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar}, {});
244
245 validator_.NotifyIsShowing(
246 kFeatureConfigTestFeatureBar, FeatureConfig(),
247 {kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name});
248 ConditionValidator::Result result =
249 GetResultForDayZero(GetAcceptingFeatureConfig());
250 EXPECT_FALSE(result.NoErrors());
251 EXPECT_FALSE(result.currently_showing_ok);
252 }
253
TEST_F(FeatureConfigConditionValidatorTest,Used)254 TEST_F(FeatureConfigConditionValidatorTest, Used) {
255 base::test::ScopedFeatureList scoped_feature_list;
256 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
257
258 FeatureConfig config = GetAcceptingFeatureConfig();
259 config.used = EventConfig("used", Comparator(LESS_THAN, 0), 0, 0);
260
261 ConditionValidator::Result result = GetResultForDayZero(config);
262 EXPECT_FALSE(result.NoErrors());
263 EXPECT_FALSE(result.used_ok);
264 }
265
TEST_F(FeatureConfigConditionValidatorTest,Trigger)266 TEST_F(FeatureConfigConditionValidatorTest, Trigger) {
267 base::test::ScopedFeatureList scoped_feature_list;
268 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
269
270 FeatureConfig config = GetAcceptingFeatureConfig();
271 config.trigger = EventConfig("trigger", Comparator(LESS_THAN, 0), 0, 0);
272
273 ConditionValidator::Result result = GetResultForDayZero(config);
274 EXPECT_FALSE(result.NoErrors());
275 EXPECT_FALSE(result.trigger_ok);
276 }
277
TEST_F(FeatureConfigConditionValidatorTest,SingleOKPrecondition)278 TEST_F(FeatureConfigConditionValidatorTest, SingleOKPrecondition) {
279 base::test::ScopedFeatureList scoped_feature_list;
280 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
281
282 FeatureConfig config = GetAcceptingFeatureConfig();
283 config.event_configs.insert(EventConfig("event1", Comparator(ANY, 0), 0, 0));
284
285 EXPECT_TRUE(GetResultForDayZero(config).NoErrors());
286 }
287
TEST_F(FeatureConfigConditionValidatorTest,MultipleOKPreconditions)288 TEST_F(FeatureConfigConditionValidatorTest, MultipleOKPreconditions) {
289 base::test::ScopedFeatureList scoped_feature_list;
290 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
291
292 FeatureConfig config = GetAcceptingFeatureConfig();
293 config.event_configs.insert(EventConfig("event1", Comparator(ANY, 0), 0, 0));
294 config.event_configs.insert(EventConfig("event2", Comparator(ANY, 0), 0, 0));
295
296 EXPECT_TRUE(GetResultForDayZero(config).NoErrors());
297 }
298
TEST_F(FeatureConfigConditionValidatorTest,OneOKThenOneFailingPrecondition)299 TEST_F(FeatureConfigConditionValidatorTest, OneOKThenOneFailingPrecondition) {
300 base::test::ScopedFeatureList scoped_feature_list;
301 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
302
303 FeatureConfig config = GetAcceptingFeatureConfig();
304 config.event_configs.insert(EventConfig("event1", Comparator(ANY, 0), 0, 0));
305 config.event_configs.insert(
306 EventConfig("event2", Comparator(LESS_THAN, 0), 0, 0));
307
308 ConditionValidator::Result result = GetResultForDayZero(config);
309 EXPECT_FALSE(result.NoErrors());
310 EXPECT_FALSE(result.preconditions_ok);
311 }
312
TEST_F(FeatureConfigConditionValidatorTest,OneFailingThenOneOKPrecondition)313 TEST_F(FeatureConfigConditionValidatorTest, OneFailingThenOneOKPrecondition) {
314 base::test::ScopedFeatureList scoped_feature_list;
315 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
316
317 FeatureConfig config = GetAcceptingFeatureConfig();
318 config.event_configs.insert(EventConfig("event1", Comparator(ANY, 0), 0, 0));
319 config.event_configs.insert(
320 EventConfig("event2", Comparator(LESS_THAN, 0), 0, 0));
321
322 ConditionValidator::Result result = GetResultForDayZero(config);
323 EXPECT_FALSE(result.NoErrors());
324 EXPECT_FALSE(result.preconditions_ok);
325 }
326
TEST_F(FeatureConfigConditionValidatorTest,TwoFailingPreconditions)327 TEST_F(FeatureConfigConditionValidatorTest, TwoFailingPreconditions) {
328 base::test::ScopedFeatureList scoped_feature_list;
329 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
330
331 FeatureConfig config = GetAcceptingFeatureConfig();
332 config.event_configs.insert(
333 EventConfig("event1", Comparator(LESS_THAN, 0), 0, 0));
334 config.event_configs.insert(
335 EventConfig("event2", Comparator(LESS_THAN, 0), 0, 0));
336
337 ConditionValidator::Result result = GetResultForDayZero(config);
338 EXPECT_FALSE(result.NoErrors());
339 EXPECT_FALSE(result.preconditions_ok);
340 }
341
TEST_F(FeatureConfigConditionValidatorTest,SessionRate)342 TEST_F(FeatureConfigConditionValidatorTest, SessionRate) {
343 base::test::ScopedFeatureList scoped_feature_list;
344 scoped_feature_list.InitWithFeatures(
345 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar}, {});
346 std::vector<std::string> all_feature_names = {
347 kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name};
348
349 FeatureConfig foo_config = GetAcceptingFeatureConfig();
350 foo_config.session_rate = Comparator(LESS_THAN, 2u);
351 FeatureConfig bar_config = GetAcceptingFeatureConfig();
352
353 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
354
355 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, bar_config,
356 all_feature_names);
357 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
358 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
359
360 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, bar_config,
361 all_feature_names);
362 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
363 ConditionValidator::Result result = GetResultForDayZero(foo_config);
364 EXPECT_FALSE(result.NoErrors());
365 EXPECT_FALSE(result.session_rate_ok);
366
367 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, bar_config,
368 all_feature_names);
369 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
370 result = GetResultForDayZero(foo_config);
371 EXPECT_FALSE(result.NoErrors());
372 EXPECT_FALSE(result.session_rate_ok);
373 }
374
TEST_F(FeatureConfigConditionValidatorTest,SessionRateImpactAffectsNone)375 TEST_F(FeatureConfigConditionValidatorTest, SessionRateImpactAffectsNone) {
376 base::test::ScopedFeatureList scoped_feature_list;
377 scoped_feature_list.InitWithFeatures(
378 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar}, {});
379 std::vector<std::string> all_feature_names = {
380 kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name};
381
382 FeatureConfig foo_config = GetAcceptingFeatureConfig();
383 foo_config.session_rate = Comparator(LESS_THAN, 2u);
384 FeatureConfig affects_none_config = GetAcceptingFeatureConfig();
385 affects_none_config.session_rate_impact = SessionRateImpact();
386 affects_none_config.session_rate_impact.type = SessionRateImpact::Type::NONE;
387
388 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
389
390 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, affects_none_config,
391 all_feature_names);
392 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
393 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
394
395 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, affects_none_config,
396 all_feature_names);
397 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
398 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
399
400 validator_.NotifyIsShowing(kFeatureConfigTestFeatureBar, affects_none_config,
401 all_feature_names);
402 validator_.NotifyDismissed(kFeatureConfigTestFeatureBar);
403 EXPECT_TRUE(GetResultForDayZero(foo_config).NoErrors());
404 }
405
TEST_F(FeatureConfigConditionValidatorTest,SessionRateImpactAffectsExplicit)406 TEST_F(FeatureConfigConditionValidatorTest, SessionRateImpactAffectsExplicit) {
407 base::test::ScopedFeatureList scoped_feature_list;
408 scoped_feature_list.InitWithFeatures(
409 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar,
410 kFeatureConfigTestFeatureQux},
411 {});
412 std::vector<std::string> all_feature_names = {
413 kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name,
414 kFeatureConfigTestFeatureQux.name};
415
416 FeatureConfig foo_config = GetAcceptingFeatureConfig();
417 foo_config.session_rate = Comparator(LESS_THAN, 2u);
418 FeatureConfig bar_config = GetAcceptingFeatureConfig();
419 bar_config.session_rate = Comparator(LESS_THAN, 2u);
420
421 FeatureConfig affects_only_foo_config = GetAcceptingFeatureConfig();
422 affects_only_foo_config.session_rate_impact =
423 CreateSessionRateImpactTypeExplicit({kFeatureConfigTestFeatureFoo.name});
424
425 EXPECT_TRUE(
426 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
427 .NoErrors());
428 EXPECT_TRUE(
429 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
430 .NoErrors());
431
432 validator_.NotifyIsShowing(kFeatureConfigTestFeatureQux,
433 affects_only_foo_config, all_feature_names);
434 validator_.NotifyDismissed(kFeatureConfigTestFeatureQux);
435 EXPECT_TRUE(
436 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
437 .NoErrors());
438 EXPECT_TRUE(
439 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
440 .NoErrors());
441
442 validator_.NotifyIsShowing(kFeatureConfigTestFeatureQux,
443 affects_only_foo_config, all_feature_names);
444 validator_.NotifyDismissed(kFeatureConfigTestFeatureQux);
445 ConditionValidator::Result result =
446 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config);
447 EXPECT_FALSE(result.NoErrors());
448 EXPECT_FALSE(result.session_rate_ok);
449 EXPECT_TRUE(
450 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
451 .NoErrors());
452 }
453
TEST_F(FeatureConfigConditionValidatorTest,SessionRateImpactAffectsSelf)454 TEST_F(FeatureConfigConditionValidatorTest, SessionRateImpactAffectsSelf) {
455 base::test::ScopedFeatureList scoped_feature_list;
456 scoped_feature_list.InitWithFeatures(
457 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar,
458 kFeatureConfigTestFeatureQux},
459 {});
460 std::vector<std::string> all_feature_names = {
461 kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name};
462
463 FeatureConfig foo_config = GetAcceptingFeatureConfig();
464 foo_config.session_rate = Comparator(LESS_THAN, 2u);
465 FeatureConfig bar_config = GetAcceptingFeatureConfig();
466 bar_config.session_rate = Comparator(LESS_THAN, 2u);
467
468 FeatureConfig affects_only_foo_config = GetAcceptingFeatureConfig();
469 affects_only_foo_config.session_rate_impact =
470 CreateSessionRateImpactTypeExplicit({kFeatureConfigTestFeatureFoo.name});
471
472 EXPECT_TRUE(
473 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
474 .NoErrors());
475 EXPECT_TRUE(
476 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
477 .NoErrors());
478
479 validator_.NotifyIsShowing(kFeatureConfigTestFeatureFoo,
480 affects_only_foo_config, all_feature_names);
481 validator_.NotifyDismissed(kFeatureConfigTestFeatureFoo);
482 EXPECT_TRUE(
483 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
484 .NoErrors());
485 EXPECT_TRUE(
486 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
487 .NoErrors());
488
489 validator_.NotifyIsShowing(kFeatureConfigTestFeatureFoo,
490 affects_only_foo_config, all_feature_names);
491 validator_.NotifyDismissed(kFeatureConfigTestFeatureFoo);
492 ConditionValidator::Result result =
493 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config);
494 EXPECT_FALSE(result.NoErrors());
495 EXPECT_FALSE(result.session_rate_ok);
496 EXPECT_TRUE(
497 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
498 .NoErrors());
499 }
500
TEST_F(FeatureConfigConditionValidatorTest,SessionRateImpactAffectsExplicitMultipleFeatures)501 TEST_F(FeatureConfigConditionValidatorTest,
502 SessionRateImpactAffectsExplicitMultipleFeatures) {
503 base::test::ScopedFeatureList scoped_feature_list;
504 scoped_feature_list.InitWithFeatures(
505 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar,
506 kFeatureConfigTestFeatureQux, kFeatureConfigTestFeatureXyz},
507 {});
508 std::vector<std::string> all_feature_names = {
509 kFeatureConfigTestFeatureFoo.name, kFeatureConfigTestFeatureBar.name,
510 kFeatureConfigTestFeatureQux.name, kFeatureConfigTestFeatureXyz.name};
511
512 FeatureConfig foo_config = GetAcceptingFeatureConfig();
513 foo_config.session_rate = Comparator(LESS_THAN, 2u);
514 FeatureConfig bar_config = GetAcceptingFeatureConfig();
515 bar_config.session_rate = Comparator(LESS_THAN, 2u);
516 FeatureConfig xyz_config = GetAcceptingFeatureConfig();
517 xyz_config.session_rate = Comparator(LESS_THAN, 2u);
518
519 FeatureConfig affects_foo_and_bar_config = GetAcceptingFeatureConfig();
520 affects_foo_and_bar_config.session_rate_impact =
521 CreateSessionRateImpactTypeExplicit({kFeatureConfigTestFeatureFoo.name,
522 kFeatureConfigTestFeatureBar.name});
523
524 EXPECT_TRUE(
525 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
526 .NoErrors());
527 EXPECT_TRUE(
528 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
529 .NoErrors());
530 EXPECT_TRUE(
531 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureXyz, xyz_config)
532 .NoErrors());
533
534 validator_.NotifyIsShowing(kFeatureConfigTestFeatureQux,
535 affects_foo_and_bar_config, all_feature_names);
536 validator_.NotifyDismissed(kFeatureConfigTestFeatureQux);
537 EXPECT_TRUE(
538 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config)
539 .NoErrors());
540 EXPECT_TRUE(
541 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureBar, bar_config)
542 .NoErrors());
543 EXPECT_TRUE(
544 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureXyz, xyz_config)
545 .NoErrors());
546
547 validator_.NotifyIsShowing(kFeatureConfigTestFeatureQux,
548 affects_foo_and_bar_config, all_feature_names);
549 validator_.NotifyDismissed(kFeatureConfigTestFeatureQux);
550 ConditionValidator::Result foo_result =
551 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, foo_config);
552 EXPECT_FALSE(foo_result.NoErrors());
553 EXPECT_FALSE(foo_result.session_rate_ok);
554 ConditionValidator::Result bar_result =
555 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureFoo, bar_config);
556 EXPECT_FALSE(bar_result.NoErrors());
557 EXPECT_FALSE(bar_result.session_rate_ok);
558 EXPECT_TRUE(
559 GetResultForDayZeroForFeature(kFeatureConfigTestFeatureXyz, xyz_config)
560 .NoErrors());
561 }
562
TEST_F(FeatureConfigConditionValidatorTest,Availability)563 TEST_F(FeatureConfigConditionValidatorTest, Availability) {
564 base::test::ScopedFeatureList scoped_feature_list;
565 scoped_feature_list.InitWithFeatures(
566 {kFeatureConfigTestFeatureFoo, kFeatureConfigTestFeatureBar}, {});
567
568 FeatureConfig config = GetAcceptingFeatureConfig();
569 EXPECT_TRUE(GetResultForDayZero(config).NoErrors());
570 EXPECT_TRUE(GetResultForDay(config, 100u).NoErrors());
571
572 // When the AvailabilityModel is not ready, it should fail.
573 availability_model_.SetIsReady(false);
574 ConditionValidator::Result result = GetResultForDayZero(config);
575 EXPECT_FALSE(result.NoErrors());
576 EXPECT_FALSE(result.availability_model_ready_ok);
577 result = GetResultForDay(config, 100u);
578 EXPECT_FALSE(result.NoErrors());
579 EXPECT_FALSE(result.availability_model_ready_ok);
580
581 // Reset state back to ready.
582 availability_model_.SetIsReady(true);
583
584 // For a feature that became available on day 2 that has to have been
585 // available for at least 1 day, it should start being accepted on day 3.
586 availability_model_.SetAvailability(&kFeatureConfigTestFeatureFoo, 2u);
587 config.availability = Comparator(GREATER_THAN_OR_EQUAL, 1u);
588 result = GetResultForDay(config, 1u);
589 EXPECT_FALSE(result.NoErrors());
590 EXPECT_FALSE(result.availability_ok);
591 result = GetResultForDay(config, 2u);
592 EXPECT_FALSE(result.NoErrors());
593 EXPECT_FALSE(result.availability_ok);
594 EXPECT_TRUE(GetResultForDay(config, 3u).NoErrors());
595 EXPECT_TRUE(GetResultForDay(config, 4u).NoErrors());
596
597 // For a feature that became available on day 10 that has to have been
598 // available for at least 3 days, it should start being accepted on day 13.
599 availability_model_.SetAvailability(&kFeatureConfigTestFeatureFoo, 10u);
600 config.availability = Comparator(GREATER_THAN_OR_EQUAL, 3u);
601 result = GetResultForDay(config, 11u);
602 EXPECT_FALSE(result.NoErrors());
603 EXPECT_FALSE(result.availability_ok);
604 result = GetResultForDay(config, 12u);
605 EXPECT_FALSE(result.NoErrors());
606 EXPECT_FALSE(result.availability_ok);
607 EXPECT_TRUE(GetResultForDay(config, 13u).NoErrors());
608 EXPECT_TRUE(GetResultForDay(config, 14u).NoErrors());
609 }
610
TEST_F(FeatureConfigConditionValidatorTest,SingleEventChangingComparator)611 TEST_F(FeatureConfigConditionValidatorTest, SingleEventChangingComparator) {
612 base::test::ScopedFeatureList scoped_feature_list;
613 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
614
615 uint32_t current_day = 102u;
616 uint32_t window = 10u;
617
618 // Create event with 10 events per day for three days.
619 Event event1;
620 event1.set_name("event1");
621 test::SetEventCountForDay(&event1, 100u, 10u);
622 test::SetEventCountForDay(&event1, 101u, 10u);
623 test::SetEventCountForDay(&event1, 102u, 10u);
624 event_model_.SetEvent(event1);
625
626 EXPECT_TRUE(GetResultForDayAndEventWindow(Comparator(LESS_THAN, 50u), window,
627 current_day)
628 .NoErrors());
629 EXPECT_TRUE(
630 GetResultForDayAndEventWindow(Comparator(EQUAL, 30u), window, current_day)
631 .NoErrors());
632 EXPECT_FALSE(GetResultForDayAndEventWindow(Comparator(LESS_THAN, 30u), window,
633 current_day)
634 .NoErrors());
635 }
636
TEST_F(FeatureConfigConditionValidatorTest,SingleEventChangingWindow)637 TEST_F(FeatureConfigConditionValidatorTest, SingleEventChangingWindow) {
638 base::test::ScopedFeatureList scoped_feature_list;
639 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
640
641 Event event1;
642 event1.set_name("event1");
643 test::SetEventCountForDay(&event1, 100u, 10u);
644 test::SetEventCountForDay(&event1, 101u, 10u);
645 test::SetEventCountForDay(&event1, 102u, 10u);
646 test::SetEventCountForDay(&event1, 103u, 10u);
647 test::SetEventCountForDay(&event1, 104u, 10u);
648 event_model_.SetEvent(event1);
649
650 uint32_t current_day = 104u;
651
652 EXPECT_FALSE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 0,
653 current_day)
654 .NoErrors());
655 EXPECT_FALSE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 1u,
656 current_day)
657 .NoErrors());
658 EXPECT_FALSE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 2u,
659 current_day)
660 .NoErrors());
661 EXPECT_FALSE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 3u,
662 current_day)
663 .NoErrors());
664 EXPECT_TRUE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 4u,
665 current_day)
666 .NoErrors());
667 EXPECT_TRUE(GetResultForDayAndEventWindow(Comparator(GREATER_THAN, 30u), 5u,
668 current_day)
669 .NoErrors());
670 }
671
TEST_F(FeatureConfigConditionValidatorTest,CapEarliestAcceptedDayAtEpoch)672 TEST_F(FeatureConfigConditionValidatorTest, CapEarliestAcceptedDayAtEpoch) {
673 base::test::ScopedFeatureList scoped_feature_list;
674 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
675
676 Event event1;
677 event1.set_name("event1");
678 test::SetEventCountForDay(&event1, 0, 10u);
679 test::SetEventCountForDay(&event1, 1u, 10u);
680 test::SetEventCountForDay(&event1, 2u, 10u);
681 event_model_.SetEvent(event1);
682
683 uint32_t current_day = 100u;
684
685 EXPECT_TRUE(
686 GetResultForDayAndEventWindow(Comparator(EQUAL, 10u), 99u, current_day)
687 .NoErrors());
688 EXPECT_TRUE(
689 GetResultForDayAndEventWindow(Comparator(EQUAL, 20u), 100u, current_day)
690 .NoErrors());
691 EXPECT_TRUE(
692 GetResultForDayAndEventWindow(Comparator(EQUAL, 30u), 101u, current_day)
693 .NoErrors());
694 EXPECT_TRUE(
695 GetResultForDayAndEventWindow(Comparator(EQUAL, 30u), 1000u, current_day)
696 .NoErrors());
697 }
698
TEST_F(FeatureConfigConditionValidatorTest,TestMultipleEvents)699 TEST_F(FeatureConfigConditionValidatorTest, TestMultipleEvents) {
700 base::test::ScopedFeatureList scoped_feature_list;
701 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
702
703 Event event1;
704 event1.set_name("event1");
705 test::SetEventCountForDay(&event1, 0, 10u);
706 test::SetEventCountForDay(&event1, 1u, 10u);
707 test::SetEventCountForDay(&event1, 2u, 10u);
708 event_model_.SetEvent(event1);
709
710 Event event2;
711 event2.set_name("event2");
712 test::SetEventCountForDay(&event2, 0, 5u);
713 test::SetEventCountForDay(&event2, 1u, 5u);
714 test::SetEventCountForDay(&event2, 2u, 5u);
715 event_model_.SetEvent(event2);
716
717 uint32_t current_day = 100u;
718
719 // Verify validator counts correctly for two events last 99 days.
720 FeatureConfig config = GetAcceptingFeatureConfig();
721 config.event_configs.insert(
722 EventConfig("event1", Comparator(EQUAL, 10u), 99u, 0));
723 config.event_configs.insert(
724 EventConfig("event2", Comparator(EQUAL, 5u), 99u, 0));
725 ConditionValidator::Result result = validator_.MeetsConditions(
726 kFeatureConfigTestFeatureFoo, config, event_model_, availability_model_,
727 display_lock_controller_, current_day);
728 EXPECT_TRUE(result.NoErrors());
729
730 // Verify validator counts correctly for two events last 100 days.
731 config = GetAcceptingFeatureConfig();
732 config.event_configs.insert(
733 EventConfig("event1", Comparator(EQUAL, 20u), 100u, 0));
734 config.event_configs.insert(
735 EventConfig("event2", Comparator(EQUAL, 10u), 100u, 0));
736 result = validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
737 event_model_, availability_model_,
738 display_lock_controller_, current_day);
739 EXPECT_TRUE(result.NoErrors());
740
741 // Verify validator counts correctly for two events last 101 days.
742 config = GetAcceptingFeatureConfig();
743 config.event_configs.insert(
744 EventConfig("event1", Comparator(EQUAL, 30u), 101u, 0));
745 config.event_configs.insert(
746 EventConfig("event2", Comparator(EQUAL, 15u), 101u, 0));
747 result = validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
748 event_model_, availability_model_,
749 display_lock_controller_, current_day);
750 EXPECT_TRUE(result.NoErrors());
751
752 // Verify validator counts correctly for two events last 101 days, and returns
753 // error when first event fails.
754 config = GetAcceptingFeatureConfig();
755 config.event_configs.insert(
756 EventConfig("event1", Comparator(EQUAL, 0), 101u, 0));
757 config.event_configs.insert(
758 EventConfig("event2", Comparator(EQUAL, 15u), 101u, 0));
759 result = validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
760 event_model_, availability_model_,
761 display_lock_controller_, current_day);
762 EXPECT_FALSE(result.NoErrors());
763 EXPECT_FALSE(result.preconditions_ok);
764
765 // Verify validator counts correctly for two events last 101 days, and returns
766 // error when second event fails.
767 config = GetAcceptingFeatureConfig();
768 config.event_configs.insert(
769 EventConfig("event1", Comparator(EQUAL, 30u), 101u, 0));
770 config.event_configs.insert(
771 EventConfig("event2", Comparator(EQUAL, 0), 101u, 0));
772 result = validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
773 event_model_, availability_model_,
774 display_lock_controller_, current_day);
775 EXPECT_FALSE(result.NoErrors());
776 EXPECT_FALSE(result.preconditions_ok);
777
778 // Verify validator counts correctly for two events last 101 days, and returns
779 // error when both events fail.
780 config = GetAcceptingFeatureConfig();
781 config.event_configs.insert(
782 EventConfig("event1", Comparator(EQUAL, 0), 101u, 0));
783 config.event_configs.insert(
784 EventConfig("event2", Comparator(EQUAL, 0), 101u, 0));
785 result = validator_.MeetsConditions(kFeatureConfigTestFeatureFoo, config,
786 event_model_, availability_model_,
787 display_lock_controller_, current_day);
788 EXPECT_FALSE(result.NoErrors());
789 EXPECT_FALSE(result.preconditions_ok);
790 }
791
TEST_F(FeatureConfigConditionValidatorTest,TestStaggeredTriggering)792 TEST_F(FeatureConfigConditionValidatorTest, TestStaggeredTriggering) {
793 base::test::ScopedFeatureList scoped_feature_list;
794 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
795
796 // Trigger maximum 2 times, and only 1 time last 2 days (today + yesterday).
797 FeatureConfig config;
798 config.valid = true;
799 config.used = EventConfig("used", Comparator(ANY, 0), 0, 0);
800 config.trigger = EventConfig("trigger", Comparator(LESS_THAN, 2), 100u, 100u);
801 config.session_rate = Comparator(ANY, 0);
802 config.availability = Comparator(ANY, 0);
803 config.event_configs.insert(
804 EventConfig("trigger", Comparator(LESS_THAN, 1u), 2u, 100u));
805
806 // Should be OK to trigger initially on day 0.
807 EXPECT_TRUE(GetResultForDay(config, 0u).NoErrors());
808
809 // Set that we triggered on day 0. We should then only trigger on day 2+.
810 Event trigger_event;
811 trigger_event.set_name("trigger");
812 test::SetEventCountForDay(&trigger_event, 0u, 1u);
813 event_model_.SetEvent(trigger_event);
814 EXPECT_FALSE(GetResultForDay(config, 0u).NoErrors());
815 EXPECT_FALSE(GetResultForDay(config, 1u).NoErrors());
816 EXPECT_TRUE(GetResultForDay(config, 2u).NoErrors());
817 EXPECT_TRUE(GetResultForDay(config, 3u).NoErrors());
818
819 // Set that we triggered again on day 2. We should then not trigger again
820 // until max storage time has passed (100 days), which would expire the
821 // trigger from day 0.
822 test::SetEventCountForDay(&trigger_event, 2u, 1u);
823 event_model_.SetEvent(trigger_event);
824 EXPECT_FALSE(GetResultForDay(config, 2u).NoErrors());
825 EXPECT_FALSE(GetResultForDay(config, 3u).NoErrors());
826 EXPECT_FALSE(GetResultForDay(config, 4u).NoErrors());
827 EXPECT_FALSE(GetResultForDay(config, 5u).NoErrors());
828 EXPECT_FALSE(GetResultForDay(config, 99u).NoErrors());
829 EXPECT_TRUE(GetResultForDay(config, 100u).NoErrors());
830 }
831
TEST_F(FeatureConfigConditionValidatorTest,TestMultipleEventsWithSameName)832 TEST_F(FeatureConfigConditionValidatorTest, TestMultipleEventsWithSameName) {
833 base::test::ScopedFeatureList scoped_feature_list;
834 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
835
836 // Trigger maximum 2 times, and only 1 time last 2 days (today + yesterday).
837 FeatureConfig config = GetAcceptingFeatureConfig();
838 config.event_configs.insert(
839 EventConfig("event1", Comparator(LESS_THAN, 1u), 2u, 100u));
840 config.event_configs.insert(
841 EventConfig("event1", Comparator(LESS_THAN, 2u), 100u, 100u));
842
843 // Should be OK to trigger initially on day 0.
844 EXPECT_TRUE(GetResultForDay(config, 0u).NoErrors());
845
846 // Set that we had event1 on day 0. We should then only trigger on day 2+.
847 Event event1;
848 event1.set_name("event1");
849 test::SetEventCountForDay(&event1, 0u, 1u);
850 event_model_.SetEvent(event1);
851 EXPECT_FALSE(GetResultForDay(config, 0u).NoErrors());
852 EXPECT_FALSE(GetResultForDay(config, 1u).NoErrors());
853 EXPECT_TRUE(GetResultForDay(config, 2u).NoErrors());
854 EXPECT_TRUE(GetResultForDay(config, 3u).NoErrors());
855
856 // Set that we had event1 again on day 2. We should then not trigger again
857 // until max storage time has passed (100 days), which would expire the
858 // trigger from day 0.
859 test::SetEventCountForDay(&event1, 2u, 1u);
860 event_model_.SetEvent(event1);
861 EXPECT_FALSE(GetResultForDay(config, 2u).NoErrors());
862 EXPECT_FALSE(GetResultForDay(config, 3u).NoErrors());
863 EXPECT_FALSE(GetResultForDay(config, 4u).NoErrors());
864 EXPECT_FALSE(GetResultForDay(config, 5u).NoErrors());
865 EXPECT_FALSE(GetResultForDay(config, 99u).NoErrors());
866 EXPECT_TRUE(GetResultForDay(config, 100u).NoErrors());
867 }
868
TEST_F(FeatureConfigConditionValidatorTest,DisplayLockedStatus)869 TEST_F(FeatureConfigConditionValidatorTest, DisplayLockedStatus) {
870 base::test::ScopedFeatureList scoped_feature_list;
871 scoped_feature_list.InitWithFeatures({kFeatureConfigTestFeatureFoo}, {});
872
873 // When the display is locked, the result should be negative.
874 display_lock_controller_.SetNextIsDisplayLockedResult(true);
875
876 ConditionValidator::Result result =
877 GetResultForDayZero(GetAcceptingFeatureConfig());
878 EXPECT_FALSE(result.NoErrors());
879 EXPECT_FALSE(result.display_lock_ok);
880
881 // Setting the display to unlocked should make the result positive.
882 display_lock_controller_.SetNextIsDisplayLockedResult(false);
883
884 EXPECT_TRUE(GetResultForDayZero(GetAcceptingFeatureConfig()).NoErrors());
885 }
886
887 } // namespace feature_engagement
888