summaryrefslogtreecommitdiff
path: root/chromium/components/feature_engagement/internal/event_model_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/feature_engagement/internal/event_model_impl_unittest.cc')
-rw-r--r--chromium/components/feature_engagement/internal/event_model_impl_unittest.cc467
1 files changed, 467 insertions, 0 deletions
diff --git a/chromium/components/feature_engagement/internal/event_model_impl_unittest.cc b/chromium/components/feature_engagement/internal/event_model_impl_unittest.cc
new file mode 100644
index 00000000000..818dd48042e
--- /dev/null
+++ b/chromium/components/feature_engagement/internal/event_model_impl_unittest.cc
@@ -0,0 +1,467 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feature_engagement/internal/event_model_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/feature_engagement/internal/editable_configuration.h"
+#include "components/feature_engagement/internal/in_memory_event_store.h"
+#include "components/feature_engagement/internal/never_event_storage_validator.h"
+#include "components/feature_engagement/internal/proto/event.pb.h"
+#include "components/feature_engagement/internal/test/event_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feature_engagement {
+
+namespace {
+
+// A test-only implementation of InMemoryEventStore that tracks calls to
+// WriteEvent(...).
+class TestInMemoryEventStore : public InMemoryEventStore {
+ public:
+ TestInMemoryEventStore(std::unique_ptr<std::vector<Event>> events,
+ bool load_should_succeed)
+ : InMemoryEventStore(std::move(events)),
+ store_operation_count_(0),
+ load_should_succeed_(load_should_succeed) {}
+
+ void Load(const OnLoadedCallback& callback) override {
+ HandleLoadResult(callback, load_should_succeed_);
+ }
+
+ void WriteEvent(const Event& event) override {
+ ++store_operation_count_;
+ last_written_event_.reset(new Event(event));
+ }
+
+ void DeleteEvent(const std::string& event_name) override {
+ ++store_operation_count_;
+ last_deleted_event_ = event_name;
+ }
+
+ const Event* GetLastWrittenEvent() { return last_written_event_.get(); }
+
+ const std::string GetLastDeletedEvent() { return last_deleted_event_; }
+
+ uint32_t GetStoreOperationCount() { return store_operation_count_; }
+
+ private:
+ // Temporary store the last written event.
+ std::unique_ptr<Event> last_written_event_;
+
+ // Temporary store the last deleted event.
+ std::string last_deleted_event_;
+
+ // Tracks the number of operations performed on the store.
+ uint32_t store_operation_count_;
+
+ // Denotes whether the call to Load(...) should succeed or not. This impacts
+ // both the ready-state and the result for the OnLoadedCallback.
+ bool load_should_succeed_;
+};
+
+class TestEventStorageValidator : public EventStorageValidator {
+ public:
+ TestEventStorageValidator() : should_store_(true) {}
+
+ bool ShouldStore(const std::string& event_name) const override {
+ return should_store_;
+ }
+
+ bool ShouldKeep(const std::string& event_name,
+ uint32_t event_day,
+ uint32_t current_day) const override {
+ auto search = max_keep_ages_.find(event_name);
+ if (search == max_keep_ages_.end())
+ return false;
+
+ return (current_day - event_day) < search->second;
+ }
+
+ void SetShouldStore(bool should_store) { should_store_ = should_store; }
+
+ void SetMaxKeepAge(const std::string& event_name, uint32_t age) {
+ max_keep_ages_[event_name] = age;
+ }
+
+ private:
+ bool should_store_;
+ std::map<std::string, uint32_t> max_keep_ages_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestEventStorageValidator);
+};
+
+// Creates a TestInMemoryEventStore containing three hard coded events.
+std::unique_ptr<TestInMemoryEventStore> CreatePrefilledStore() {
+ std::unique_ptr<std::vector<Event>> events =
+ base::MakeUnique<std::vector<Event>>();
+
+ Event foo;
+ foo.set_name("foo");
+ test::SetEventCountForDay(&foo, 1, 1);
+ events->push_back(foo);
+
+ Event bar;
+ bar.set_name("bar");
+ test::SetEventCountForDay(&bar, 1, 3);
+ test::SetEventCountForDay(&bar, 2, 3);
+ test::SetEventCountForDay(&bar, 5, 5);
+ events->push_back(bar);
+
+ Event qux;
+ qux.set_name("qux");
+ test::SetEventCountForDay(&qux, 1, 5);
+ test::SetEventCountForDay(&qux, 2, 1);
+ test::SetEventCountForDay(&qux, 3, 2);
+ events->push_back(qux);
+
+ return base::MakeUnique<TestInMemoryEventStore>(std::move(events), true);
+}
+
+class EventModelImplTest : public ::testing::Test {
+ public:
+ EventModelImplTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ handle_(task_runner_),
+ got_initialize_callback_(false),
+ initialize_callback_result_(false) {}
+
+ void SetUp() override {
+ std::unique_ptr<TestInMemoryEventStore> store = CreateStore();
+ store_ = store.get();
+
+ auto storage_validator = base::MakeUnique<TestEventStorageValidator>();
+ storage_validator_ = storage_validator.get();
+
+ model_.reset(
+ new EventModelImpl(std::move(store), std::move(storage_validator)));
+
+ // By default store all events for a very long time.
+ storage_validator_->SetMaxKeepAge("foo", 10000u);
+ storage_validator_->SetMaxKeepAge("bar", 10000u);
+ storage_validator_->SetMaxKeepAge("qux", 10000u);
+ }
+
+ virtual std::unique_ptr<TestInMemoryEventStore> CreateStore() {
+ return CreatePrefilledStore();
+ }
+
+ void OnModelInitializationFinished(bool success) {
+ got_initialize_callback_ = true;
+ initialize_callback_result_ = success;
+ }
+
+ protected:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle handle_;
+
+ std::unique_ptr<EventModelImpl> model_;
+ TestInMemoryEventStore* store_;
+ TestEventStorageValidator* storage_validator_;
+ bool got_initialize_callback_;
+ bool initialize_callback_result_;
+};
+
+class LoadFailingEventModelImplTest : public EventModelImplTest {
+ public:
+ LoadFailingEventModelImplTest() : EventModelImplTest() {}
+
+ std::unique_ptr<TestInMemoryEventStore> CreateStore() override {
+ return base::MakeUnique<TestInMemoryEventStore>(
+ base::MakeUnique<std::vector<Event>>(), false);
+ }
+};
+
+} // namespace
+
+TEST_F(EventModelImplTest, InitializeShouldBeReadyImmediatelyAfterCallback) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+
+ // Only run pending tasks on the queue. Do not run any subsequently queued
+ // tasks that result from running the current pending tasks.
+ task_runner_->RunPendingTasks();
+
+ EXPECT_TRUE(got_initialize_callback_);
+ EXPECT_TRUE(model_->IsReady());
+}
+
+TEST_F(EventModelImplTest, InitializeShouldLoadEntries) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+ EXPECT_TRUE(got_initialize_callback_);
+ EXPECT_TRUE(initialize_callback_result_);
+
+ // Verify that all the data matches what was put into the store in
+ // CreateStore().
+ const Event* foo_event = model_->GetEvent("foo");
+ EXPECT_EQ("foo", foo_event->name());
+ EXPECT_EQ(1, foo_event->events_size());
+ test::VerifyEventCount(foo_event, 1u, 1u);
+
+ const Event* bar_event = model_->GetEvent("bar");
+ EXPECT_EQ("bar", bar_event->name());
+ EXPECT_EQ(3, bar_event->events_size());
+ test::VerifyEventCount(bar_event, 1u, 3u);
+ test::VerifyEventCount(bar_event, 2u, 3u);
+ test::VerifyEventCount(bar_event, 5u, 5u);
+
+ const Event* qux_event = model_->GetEvent("qux");
+ EXPECT_EQ("qux", qux_event->name());
+ EXPECT_EQ(3, qux_event->events_size());
+ test::VerifyEventCount(qux_event, 1u, 5u);
+ test::VerifyEventCount(qux_event, 2u, 1u);
+ test::VerifyEventCount(qux_event, 3u, 2u);
+}
+
+TEST_F(EventModelImplTest, InitializeShouldOnlyLoadEntriesThatShouldBeKept) {
+ // Back to day 5, i.e. no entries.
+ storage_validator_->SetMaxKeepAge("foo", 1u);
+
+ // Back to day 2, i.e. 2 events.
+ storage_validator_->SetMaxKeepAge("bar", 4u);
+
+ // Back to day epoch, i.e. all events.
+ storage_validator_->SetMaxKeepAge("qux", 10u);
+
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 5u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+ EXPECT_TRUE(got_initialize_callback_);
+ EXPECT_TRUE(initialize_callback_result_);
+
+ // Verify that all the data matches what was put into the store in
+ // CreateStore(), minus the events that should no longer exist.
+ const Event* foo_event = model_->GetEvent("foo");
+ EXPECT_EQ(nullptr, foo_event);
+ EXPECT_EQ("foo", store_->GetLastDeletedEvent());
+
+ const Event* bar_event = model_->GetEvent("bar");
+ EXPECT_EQ("bar", bar_event->name());
+ EXPECT_EQ(2, bar_event->events_size());
+ test::VerifyEventCount(bar_event, 2u, 3u);
+ test::VerifyEventCount(bar_event, 5u, 5u);
+ test::VerifyEventsEqual(bar_event, store_->GetLastWrittenEvent());
+
+ // Nothing has changed for 'qux', so nothing will be written to EventStore.
+ const Event* qux_event = model_->GetEvent("qux");
+ EXPECT_EQ("qux", qux_event->name());
+ EXPECT_EQ(3, qux_event->events_size());
+ test::VerifyEventCount(qux_event, 1u, 5u);
+ test::VerifyEventCount(qux_event, 2u, 1u);
+ test::VerifyEventCount(qux_event, 3u, 2u);
+
+ // In total, only two operations should have happened, the update of "bar",
+ // and the delete of "foo".
+ EXPECT_EQ(2u, store_->GetStoreOperationCount());
+}
+
+TEST_F(EventModelImplTest, RetrievingNewEventsShouldYieldNullptr) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ const Event* no_event = model_->GetEvent("no");
+ EXPECT_EQ(nullptr, no_event);
+ test::VerifyEventsEqual(nullptr, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingNonExistingEvent) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // Incrementing the event should work even if it does not exist.
+ model_->IncrementEvent("nonexisting", 1u);
+ const Event* event1 = model_->GetEvent("nonexisting");
+ ASSERT_NE(nullptr, event1);
+ EXPECT_EQ("nonexisting", event1->name());
+ EXPECT_EQ(1, event1->events_size());
+ test::VerifyEventCount(event1, 1u, 1u);
+ test::VerifyEventsEqual(event1, store_->GetLastWrittenEvent());
+
+ // Incrementing the event after it has been initialized to 1, it should now
+ // have a count of 2 for the given day.
+ model_->IncrementEvent("nonexisting", 1u);
+ const Event* event2 = model_->GetEvent("nonexisting");
+ ASSERT_NE(nullptr, event2);
+ Event_Count event2_count = event2->events(0);
+ EXPECT_EQ(1, event2->events_size());
+ test::VerifyEventCount(event2, 1u, 2u);
+ test::VerifyEventsEqual(event2, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingNonExistingEventMultipleDays) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ model_->IncrementEvent("nonexisting", 1u);
+ model_->IncrementEvent("nonexisting", 2u);
+ model_->IncrementEvent("nonexisting", 2u);
+ model_->IncrementEvent("nonexisting", 3u);
+ const Event* event = model_->GetEvent("nonexisting");
+ ASSERT_NE(nullptr, event);
+ EXPECT_EQ(3, event->events_size());
+ test::VerifyEventCount(event, 1u, 1u);
+ test::VerifyEventCount(event, 2u, 2u);
+ test::VerifyEventCount(event, 3u, 1u);
+ test::VerifyEventsEqual(event, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingNonExistingEventWithoutStoring) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ storage_validator_->SetShouldStore(false);
+
+ // Incrementing the event should not be written or stored in-memory.
+ model_->IncrementEvent("nonexisting", 1u);
+ const Event* event1 = model_->GetEvent("nonexisting");
+ EXPECT_EQ(nullptr, event1);
+ test::VerifyEventsEqual(nullptr, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingExistingEventWithoutStoring) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // Write one event before turning off storage.
+ model_->IncrementEvent("nonexisting", 1u);
+ const Event* first_event = model_->GetEvent("nonexisting");
+ ASSERT_NE(nullptr, first_event);
+ test::VerifyEventsEqual(first_event, store_->GetLastWrittenEvent());
+
+ storage_validator_->SetShouldStore(false);
+
+ // Incrementing the event should no longer be written or stored in-memory.
+ model_->IncrementEvent("nonexisting", 1u);
+ const Event* second_event = model_->GetEvent("nonexisting");
+ EXPECT_EQ(first_event, second_event);
+ test::VerifyEventsEqual(first_event, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingSingleDayExistingEvent) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // |foo| is inserted into the store with a count of 1 at day 1.
+ const Event* foo_event = model_->GetEvent("foo");
+ EXPECT_EQ("foo", foo_event->name());
+ EXPECT_EQ(1, foo_event->events_size());
+ test::VerifyEventCount(foo_event, 1u, 1u);
+
+ // Incrementing |foo| should change count to 2.
+ model_->IncrementEvent("foo", 1u);
+ const Event* foo_event2 = model_->GetEvent("foo");
+ EXPECT_EQ(1, foo_event2->events_size());
+ test::VerifyEventCount(foo_event2, 1u, 2u);
+ test::VerifyEventsEqual(foo_event2, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingSingleDayExistingEventTwice) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // |foo| is inserted into the store with a count of 1 at day 1, so
+ // incrementing twice should lead to 3.
+ model_->IncrementEvent("foo", 1u);
+ model_->IncrementEvent("foo", 1u);
+ const Event* foo_event = model_->GetEvent("foo");
+ EXPECT_EQ(1, foo_event->events_size());
+ test::VerifyEventCount(foo_event, 1u, 3u);
+ test::VerifyEventsEqual(foo_event, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingExistingMultiDayEvent) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // |bar| is inserted into the store with a count of 3 at day 2. Incrementing
+ // that day should lead to a count of 4.
+ const Event* bar_event = model_->GetEvent("bar");
+ test::VerifyEventCount(bar_event, 2u, 3u);
+ model_->IncrementEvent("bar", 2u);
+ const Event* bar_event2 = model_->GetEvent("bar");
+ test::VerifyEventCount(bar_event2, 2u, 4u);
+ test::VerifyEventsEqual(bar_event2, store_->GetLastWrittenEvent());
+}
+
+TEST_F(EventModelImplTest, IncrementingExistingMultiDayEventNewDay) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(model_->IsReady());
+
+ // |bar| does not contain entries for day 10, so incrementing should create
+ // the day.
+ model_->IncrementEvent("bar", 10u);
+ const Event* bar_event = model_->GetEvent("bar");
+ test::VerifyEventCount(bar_event, 10u, 1u);
+ test::VerifyEventsEqual(bar_event, store_->GetLastWrittenEvent());
+ model_->IncrementEvent("bar", 10u);
+ const Event* bar_event2 = model_->GetEvent("bar");
+ test::VerifyEventCount(bar_event2, 10u, 2u);
+ test::VerifyEventsEqual(bar_event2, store_->GetLastWrittenEvent());
+}
+
+TEST_F(LoadFailingEventModelImplTest, FailedInitializeInformsCaller) {
+ model_->Initialize(
+ base::Bind(&EventModelImplTest::OnModelInitializationFinished,
+ base::Unretained(this)),
+ 1000u);
+ task_runner_->RunUntilIdle();
+ EXPECT_FALSE(model_->IsReady());
+ EXPECT_TRUE(got_initialize_callback_);
+ EXPECT_FALSE(initialize_callback_result_);
+}
+
+} // namespace feature_engagement