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/event_model_impl.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/feature_list.h"
12 #include "base/test/test_simple_task_runner.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "components/feature_engagement/internal/editable_configuration.h"
15 #include "components/feature_engagement/internal/in_memory_event_store.h"
16 #include "components/feature_engagement/internal/never_event_storage_validator.h"
17 #include "components/feature_engagement/internal/proto/feature_event.pb.h"
18 #include "components/feature_engagement/internal/test/event_util.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace feature_engagement {
22
23 namespace {
24
25 // A test-only implementation of InMemoryEventStore that tracks calls to
26 // WriteEvent(...).
27 class TestInMemoryEventStore : public InMemoryEventStore {
28 public:
TestInMemoryEventStore(std::unique_ptr<std::vector<Event>> events,bool load_should_succeed)29 TestInMemoryEventStore(std::unique_ptr<std::vector<Event>> events,
30 bool load_should_succeed)
31 : InMemoryEventStore(std::move(events)),
32 store_operation_count_(0),
33 load_should_succeed_(load_should_succeed) {}
34
Load(const OnLoadedCallback & callback)35 void Load(const OnLoadedCallback& callback) override {
36 HandleLoadResult(callback, load_should_succeed_);
37 }
38
WriteEvent(const Event & event)39 void WriteEvent(const Event& event) override {
40 ++store_operation_count_;
41 last_written_event_.reset(new Event(event));
42 }
43
DeleteEvent(const std::string & event_name)44 void DeleteEvent(const std::string& event_name) override {
45 ++store_operation_count_;
46 last_deleted_event_ = event_name;
47 }
48
GetLastWrittenEvent()49 const Event* GetLastWrittenEvent() { return last_written_event_.get(); }
50
GetLastDeletedEvent()51 const std::string GetLastDeletedEvent() { return last_deleted_event_; }
52
GetStoreOperationCount()53 uint32_t GetStoreOperationCount() { return store_operation_count_; }
54
55 private:
56 // Temporary store the last written event.
57 std::unique_ptr<Event> last_written_event_;
58
59 // Temporary store the last deleted event.
60 std::string last_deleted_event_;
61
62 // Tracks the number of operations performed on the store.
63 uint32_t store_operation_count_;
64
65 // Denotes whether the call to Load(...) should succeed or not. This impacts
66 // both the ready-state and the result for the OnLoadedCallback.
67 bool load_should_succeed_;
68 };
69
70 class TestEventStorageValidator : public EventStorageValidator {
71 public:
TestEventStorageValidator()72 TestEventStorageValidator() : should_store_(true) {}
73
ShouldStore(const std::string & event_name) const74 bool ShouldStore(const std::string& event_name) const override {
75 return should_store_;
76 }
77
ShouldKeep(const std::string & event_name,uint32_t event_day,uint32_t current_day) const78 bool ShouldKeep(const std::string& event_name,
79 uint32_t event_day,
80 uint32_t current_day) const override {
81 auto search = max_keep_ages_.find(event_name);
82 if (search == max_keep_ages_.end())
83 return false;
84
85 return (current_day - event_day) < search->second;
86 }
87
SetShouldStore(bool should_store)88 void SetShouldStore(bool should_store) { should_store_ = should_store; }
89
SetMaxKeepAge(const std::string & event_name,uint32_t age)90 void SetMaxKeepAge(const std::string& event_name, uint32_t age) {
91 max_keep_ages_[event_name] = age;
92 }
93
94 private:
95 bool should_store_;
96 std::map<std::string, uint32_t> max_keep_ages_;
97
98 DISALLOW_COPY_AND_ASSIGN(TestEventStorageValidator);
99 };
100
101 // Creates a TestInMemoryEventStore containing three hard coded events.
CreatePrefilledStore()102 std::unique_ptr<TestInMemoryEventStore> CreatePrefilledStore() {
103 std::unique_ptr<std::vector<Event>> events =
104 std::make_unique<std::vector<Event>>();
105
106 Event foo;
107 foo.set_name("foo");
108 test::SetEventCountForDay(&foo, 1, 1);
109 events->push_back(foo);
110
111 Event bar;
112 bar.set_name("bar");
113 test::SetEventCountForDay(&bar, 1, 3);
114 test::SetEventCountForDay(&bar, 2, 3);
115 test::SetEventCountForDay(&bar, 5, 5);
116 events->push_back(bar);
117
118 Event qux;
119 qux.set_name("qux");
120 test::SetEventCountForDay(&qux, 1, 5);
121 test::SetEventCountForDay(&qux, 2, 1);
122 test::SetEventCountForDay(&qux, 3, 2);
123 events->push_back(qux);
124
125 return std::make_unique<TestInMemoryEventStore>(std::move(events), true);
126 }
127
128 class EventModelImplTest : public ::testing::Test {
129 public:
EventModelImplTest()130 EventModelImplTest()
131 : task_runner_(new base::TestSimpleTaskRunner),
132 handle_(task_runner_),
133 got_initialize_callback_(false),
134 initialize_callback_result_(false) {}
135
SetUp()136 void SetUp() override {
137 std::unique_ptr<TestInMemoryEventStore> store = CreateStore();
138 store_ = store.get();
139
140 auto storage_validator = std::make_unique<TestEventStorageValidator>();
141 storage_validator_ = storage_validator.get();
142
143 model_.reset(
144 new EventModelImpl(std::move(store), std::move(storage_validator)));
145
146 // By default store all events for a very long time.
147 storage_validator_->SetMaxKeepAge("foo", 10000u);
148 storage_validator_->SetMaxKeepAge("bar", 10000u);
149 storage_validator_->SetMaxKeepAge("qux", 10000u);
150 }
151
CreateStore()152 virtual std::unique_ptr<TestInMemoryEventStore> CreateStore() {
153 return CreatePrefilledStore();
154 }
155
OnModelInitializationFinished(bool success)156 void OnModelInitializationFinished(bool success) {
157 got_initialize_callback_ = true;
158 initialize_callback_result_ = success;
159 }
160
161 protected:
162 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
163 base::ThreadTaskRunnerHandle handle_;
164
165 std::unique_ptr<EventModelImpl> model_;
166 TestInMemoryEventStore* store_;
167 TestEventStorageValidator* storage_validator_;
168 bool got_initialize_callback_;
169 bool initialize_callback_result_;
170 };
171
172 class LoadFailingEventModelImplTest : public EventModelImplTest {
173 public:
LoadFailingEventModelImplTest()174 LoadFailingEventModelImplTest() {}
175
CreateStore()176 std::unique_ptr<TestInMemoryEventStore> CreateStore() override {
177 return std::make_unique<TestInMemoryEventStore>(
178 std::make_unique<std::vector<Event>>(), false);
179 }
180 };
181
182 } // namespace
183
TEST_F(EventModelImplTest,InitializeShouldBeReadyImmediatelyAfterCallback)184 TEST_F(EventModelImplTest, InitializeShouldBeReadyImmediatelyAfterCallback) {
185 model_->Initialize(
186 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
187 base::Unretained(this)),
188 1000u);
189
190 // Only run pending tasks on the queue. Do not run any subsequently queued
191 // tasks that result from running the current pending tasks.
192 task_runner_->RunPendingTasks();
193
194 EXPECT_TRUE(got_initialize_callback_);
195 EXPECT_TRUE(model_->IsReady());
196 }
197
TEST_F(EventModelImplTest,InitializeShouldLoadEntries)198 TEST_F(EventModelImplTest, InitializeShouldLoadEntries) {
199 model_->Initialize(
200 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
201 base::Unretained(this)),
202 1000u);
203 task_runner_->RunUntilIdle();
204 EXPECT_TRUE(model_->IsReady());
205 EXPECT_TRUE(got_initialize_callback_);
206 EXPECT_TRUE(initialize_callback_result_);
207
208 // Verify that all the data matches what was put into the store in
209 // CreateStore().
210 const Event* foo_event = model_->GetEvent("foo");
211 EXPECT_EQ("foo", foo_event->name());
212 EXPECT_EQ(1, foo_event->events_size());
213 test::VerifyEventCount(foo_event, 1u, 1u);
214
215 const Event* bar_event = model_->GetEvent("bar");
216 EXPECT_EQ("bar", bar_event->name());
217 EXPECT_EQ(3, bar_event->events_size());
218 test::VerifyEventCount(bar_event, 1u, 3u);
219 test::VerifyEventCount(bar_event, 2u, 3u);
220 test::VerifyEventCount(bar_event, 5u, 5u);
221
222 const Event* qux_event = model_->GetEvent("qux");
223 EXPECT_EQ("qux", qux_event->name());
224 EXPECT_EQ(3, qux_event->events_size());
225 test::VerifyEventCount(qux_event, 1u, 5u);
226 test::VerifyEventCount(qux_event, 2u, 1u);
227 test::VerifyEventCount(qux_event, 3u, 2u);
228 }
229
TEST_F(EventModelImplTest,InitializeShouldOnlyLoadEntriesThatShouldBeKept)230 TEST_F(EventModelImplTest, InitializeShouldOnlyLoadEntriesThatShouldBeKept) {
231 // Back to day 5, i.e. no entries.
232 storage_validator_->SetMaxKeepAge("foo", 1u);
233
234 // Back to day 2, i.e. 2 events.
235 storage_validator_->SetMaxKeepAge("bar", 4u);
236
237 // Back to day epoch, i.e. all events.
238 storage_validator_->SetMaxKeepAge("qux", 10u);
239
240 model_->Initialize(
241 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
242 base::Unretained(this)),
243 5u);
244 task_runner_->RunUntilIdle();
245 EXPECT_TRUE(model_->IsReady());
246 EXPECT_TRUE(got_initialize_callback_);
247 EXPECT_TRUE(initialize_callback_result_);
248
249 // Verify that all the data matches what was put into the store in
250 // CreateStore(), minus the events that should no longer exist.
251 const Event* foo_event = model_->GetEvent("foo");
252 EXPECT_EQ(nullptr, foo_event);
253 EXPECT_EQ("foo", store_->GetLastDeletedEvent());
254
255 const Event* bar_event = model_->GetEvent("bar");
256 EXPECT_EQ("bar", bar_event->name());
257 EXPECT_EQ(2, bar_event->events_size());
258 test::VerifyEventCount(bar_event, 2u, 3u);
259 test::VerifyEventCount(bar_event, 5u, 5u);
260 test::VerifyEventsEqual(bar_event, store_->GetLastWrittenEvent());
261
262 // Nothing has changed for 'qux', so nothing will be written to EventStore.
263 const Event* qux_event = model_->GetEvent("qux");
264 EXPECT_EQ("qux", qux_event->name());
265 EXPECT_EQ(3, qux_event->events_size());
266 test::VerifyEventCount(qux_event, 1u, 5u);
267 test::VerifyEventCount(qux_event, 2u, 1u);
268 test::VerifyEventCount(qux_event, 3u, 2u);
269
270 // In total, only two operations should have happened, the update of "bar",
271 // and the delete of "foo".
272 EXPECT_EQ(2u, store_->GetStoreOperationCount());
273 }
274
TEST_F(EventModelImplTest,RetrievingNewEventsShouldYieldNullptr)275 TEST_F(EventModelImplTest, RetrievingNewEventsShouldYieldNullptr) {
276 model_->Initialize(
277 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
278 base::Unretained(this)),
279 1000u);
280 task_runner_->RunUntilIdle();
281 EXPECT_TRUE(model_->IsReady());
282
283 const Event* no_event = model_->GetEvent("no");
284 EXPECT_EQ(nullptr, no_event);
285 test::VerifyEventsEqual(nullptr, store_->GetLastWrittenEvent());
286 }
287
TEST_F(EventModelImplTest,IncrementingNonExistingEvent)288 TEST_F(EventModelImplTest, IncrementingNonExistingEvent) {
289 model_->Initialize(
290 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
291 base::Unretained(this)),
292 1000u);
293 task_runner_->RunUntilIdle();
294 EXPECT_TRUE(model_->IsReady());
295
296 // Incrementing the event should work even if it does not exist.
297 model_->IncrementEvent("nonexisting", 1u);
298 const Event* event1 = model_->GetEvent("nonexisting");
299 ASSERT_NE(nullptr, event1);
300 EXPECT_EQ("nonexisting", event1->name());
301 EXPECT_EQ(1, event1->events_size());
302 test::VerifyEventCount(event1, 1u, 1u);
303 test::VerifyEventsEqual(event1, store_->GetLastWrittenEvent());
304
305 // Incrementing the event after it has been initialized to 1, it should now
306 // have a count of 2 for the given day.
307 model_->IncrementEvent("nonexisting", 1u);
308 const Event* event2 = model_->GetEvent("nonexisting");
309 ASSERT_NE(nullptr, event2);
310 Event_Count event2_count = event2->events(0);
311 EXPECT_EQ(1, event2->events_size());
312 test::VerifyEventCount(event2, 1u, 2u);
313 test::VerifyEventsEqual(event2, store_->GetLastWrittenEvent());
314 }
315
TEST_F(EventModelImplTest,IncrementingNonExistingEventMultipleDays)316 TEST_F(EventModelImplTest, IncrementingNonExistingEventMultipleDays) {
317 model_->Initialize(
318 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
319 base::Unretained(this)),
320 1000u);
321 task_runner_->RunUntilIdle();
322 EXPECT_TRUE(model_->IsReady());
323
324 model_->IncrementEvent("nonexisting", 1u);
325 model_->IncrementEvent("nonexisting", 2u);
326 model_->IncrementEvent("nonexisting", 2u);
327 model_->IncrementEvent("nonexisting", 3u);
328 const Event* event = model_->GetEvent("nonexisting");
329 ASSERT_NE(nullptr, event);
330 EXPECT_EQ(3, event->events_size());
331 test::VerifyEventCount(event, 1u, 1u);
332 test::VerifyEventCount(event, 2u, 2u);
333 test::VerifyEventCount(event, 3u, 1u);
334 test::VerifyEventsEqual(event, store_->GetLastWrittenEvent());
335 }
336
TEST_F(EventModelImplTest,IncrementingNonExistingEventWithoutStoring)337 TEST_F(EventModelImplTest, IncrementingNonExistingEventWithoutStoring) {
338 model_->Initialize(
339 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
340 base::Unretained(this)),
341 1000u);
342 task_runner_->RunUntilIdle();
343 EXPECT_TRUE(model_->IsReady());
344
345 storage_validator_->SetShouldStore(false);
346
347 // Incrementing the event should not be written or stored in-memory.
348 model_->IncrementEvent("nonexisting", 1u);
349 const Event* event1 = model_->GetEvent("nonexisting");
350 EXPECT_EQ(nullptr, event1);
351 test::VerifyEventsEqual(nullptr, store_->GetLastWrittenEvent());
352 }
353
TEST_F(EventModelImplTest,IncrementingExistingEventWithoutStoring)354 TEST_F(EventModelImplTest, IncrementingExistingEventWithoutStoring) {
355 model_->Initialize(
356 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
357 base::Unretained(this)),
358 1000u);
359 task_runner_->RunUntilIdle();
360 EXPECT_TRUE(model_->IsReady());
361
362 // Write one event before turning off storage.
363 model_->IncrementEvent("nonexisting", 1u);
364 const Event* first_event = model_->GetEvent("nonexisting");
365 ASSERT_NE(nullptr, first_event);
366 test::VerifyEventsEqual(first_event, store_->GetLastWrittenEvent());
367
368 storage_validator_->SetShouldStore(false);
369
370 // Incrementing the event should no longer be written or stored in-memory.
371 model_->IncrementEvent("nonexisting", 1u);
372 const Event* second_event = model_->GetEvent("nonexisting");
373 EXPECT_EQ(first_event, second_event);
374 test::VerifyEventsEqual(first_event, store_->GetLastWrittenEvent());
375 }
376
TEST_F(EventModelImplTest,IncrementingSingleDayExistingEvent)377 TEST_F(EventModelImplTest, IncrementingSingleDayExistingEvent) {
378 model_->Initialize(
379 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
380 base::Unretained(this)),
381 1000u);
382 task_runner_->RunUntilIdle();
383 EXPECT_TRUE(model_->IsReady());
384
385 // |foo| is inserted into the store with a count of 1 at day 1.
386 const Event* foo_event = model_->GetEvent("foo");
387 EXPECT_EQ("foo", foo_event->name());
388 EXPECT_EQ(1, foo_event->events_size());
389 test::VerifyEventCount(foo_event, 1u, 1u);
390
391 // Incrementing |foo| should change count to 2.
392 model_->IncrementEvent("foo", 1u);
393 const Event* foo_event2 = model_->GetEvent("foo");
394 EXPECT_EQ(1, foo_event2->events_size());
395 test::VerifyEventCount(foo_event2, 1u, 2u);
396 test::VerifyEventsEqual(foo_event2, store_->GetLastWrittenEvent());
397 }
398
TEST_F(EventModelImplTest,IncrementingSingleDayExistingEventTwice)399 TEST_F(EventModelImplTest, IncrementingSingleDayExistingEventTwice) {
400 model_->Initialize(
401 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
402 base::Unretained(this)),
403 1000u);
404 task_runner_->RunUntilIdle();
405 EXPECT_TRUE(model_->IsReady());
406
407 // |foo| is inserted into the store with a count of 1 at day 1, so
408 // incrementing twice should lead to 3.
409 model_->IncrementEvent("foo", 1u);
410 model_->IncrementEvent("foo", 1u);
411 const Event* foo_event = model_->GetEvent("foo");
412 EXPECT_EQ(1, foo_event->events_size());
413 test::VerifyEventCount(foo_event, 1u, 3u);
414 test::VerifyEventsEqual(foo_event, store_->GetLastWrittenEvent());
415 }
416
TEST_F(EventModelImplTest,IncrementingExistingMultiDayEvent)417 TEST_F(EventModelImplTest, IncrementingExistingMultiDayEvent) {
418 model_->Initialize(
419 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
420 base::Unretained(this)),
421 1000u);
422 task_runner_->RunUntilIdle();
423 EXPECT_TRUE(model_->IsReady());
424
425 // |bar| is inserted into the store with a count of 3 at day 2. Incrementing
426 // that day should lead to a count of 4.
427 const Event* bar_event = model_->GetEvent("bar");
428 test::VerifyEventCount(bar_event, 2u, 3u);
429 model_->IncrementEvent("bar", 2u);
430 const Event* bar_event2 = model_->GetEvent("bar");
431 test::VerifyEventCount(bar_event2, 2u, 4u);
432 test::VerifyEventsEqual(bar_event2, store_->GetLastWrittenEvent());
433 }
434
TEST_F(EventModelImplTest,IncrementingExistingMultiDayEventNewDay)435 TEST_F(EventModelImplTest, IncrementingExistingMultiDayEventNewDay) {
436 model_->Initialize(
437 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
438 base::Unretained(this)),
439 1000u);
440 task_runner_->RunUntilIdle();
441 EXPECT_TRUE(model_->IsReady());
442
443 // |bar| does not contain entries for day 10, so incrementing should create
444 // the day.
445 model_->IncrementEvent("bar", 10u);
446 const Event* bar_event = model_->GetEvent("bar");
447 test::VerifyEventCount(bar_event, 10u, 1u);
448 test::VerifyEventsEqual(bar_event, store_->GetLastWrittenEvent());
449 model_->IncrementEvent("bar", 10u);
450 const Event* bar_event2 = model_->GetEvent("bar");
451 test::VerifyEventCount(bar_event2, 10u, 2u);
452 test::VerifyEventsEqual(bar_event2, store_->GetLastWrittenEvent());
453 }
454
TEST_F(LoadFailingEventModelImplTest,FailedInitializeInformsCaller)455 TEST_F(LoadFailingEventModelImplTest, FailedInitializeInformsCaller) {
456 model_->Initialize(
457 base::Bind(&EventModelImplTest::OnModelInitializationFinished,
458 base::Unretained(this)),
459 1000u);
460 task_runner_->RunUntilIdle();
461 EXPECT_FALSE(model_->IsReady());
462 EXPECT_TRUE(got_initialize_callback_);
463 EXPECT_FALSE(initialize_callback_result_);
464 }
465
466 } // namespace feature_engagement
467