diff options
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.cc | 467 |
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 |