|  | // Copyright 2018 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/optimization_guide/hint_cache.h" | 
|  |  | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/files/scoped_temp_dir.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/test/metrics/histogram_tester.h" | 
|  | #include "base/test/task_environment.h" | 
|  | #include "components/optimization_guide/optimization_guide_features.h" | 
|  | #include "components/optimization_guide/optimization_guide_store.h" | 
|  | #include "components/optimization_guide/proto_database_provider_test_base.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | namespace optimization_guide { | 
|  | namespace { | 
|  |  | 
|  | std::string GetHostDomainOrg(int index) { | 
|  | return "host.domain" + base::NumberToString(index) + ".org"; | 
|  | } | 
|  |  | 
|  | class HintCacheTest : public ProtoDatabaseProviderTestBase { | 
|  | public: | 
|  | HintCacheTest() : loaded_hint_(nullptr) {} | 
|  |  | 
|  | ~HintCacheTest() override {} | 
|  |  | 
|  | void SetUp() override { ProtoDatabaseProviderTestBase::SetUp(); } | 
|  |  | 
|  | void TearDown() override { | 
|  | ProtoDatabaseProviderTestBase::TearDown(); | 
|  | DestroyHintCache(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Creates and initializes the hint cache and optimization guide store and | 
|  | // waits for the callback indicating that initialization is complete. | 
|  | void CreateAndInitializeHintCache(int memory_cache_size, | 
|  | bool purge_existing_data = false) { | 
|  | auto database_path = temp_dir_.GetPath(); | 
|  | auto database_task_runner = task_environment_.GetMainThreadTaskRunner(); | 
|  | hint_cache_ = std::make_unique<HintCache>( | 
|  | std::make_unique<OptimizationGuideStore>( | 
|  | db_provider_.get(), database_path, database_task_runner), | 
|  | memory_cache_size); | 
|  | is_store_initialized_ = false; | 
|  | hint_cache_->Initialize(purge_existing_data, | 
|  | base::BindOnce(&HintCacheTest::OnStoreInitialized, | 
|  | base::Unretained(this))); | 
|  | while (!is_store_initialized_) { | 
|  | RunUntilIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DestroyHintCache() { | 
|  | hint_cache_.reset(); | 
|  | loaded_hint_ = nullptr; | 
|  | is_store_initialized_ = false; | 
|  | are_component_hints_updated_ = false; | 
|  | on_load_hint_callback_called_ = false; | 
|  | are_fetched_hints_updated_ = false; | 
|  |  | 
|  | RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | HintCache* hint_cache() { return hint_cache_.get(); } | 
|  |  | 
|  | bool are_fetched_hints_updated() { return are_fetched_hints_updated_; } | 
|  |  | 
|  | // Updates the cache with |component_data| and waits for callback indicating | 
|  | // that the update is complete. | 
|  | void UpdateComponentHints(std::unique_ptr<StoreUpdateData> component_data) { | 
|  | are_component_hints_updated_ = false; | 
|  | hint_cache_->UpdateComponentHints( | 
|  | std::move(component_data), | 
|  | base::BindOnce(&HintCacheTest::OnUpdateComponentHints, | 
|  | base::Unretained(this))); | 
|  | while (!are_component_hints_updated_) { | 
|  | RunUntilIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void UpdateFetchedHintsAndWait( | 
|  | std::unique_ptr<proto::GetHintsResponse> get_hints_response, | 
|  | base::Time stored_time) { | 
|  | are_fetched_hints_updated_ = false; | 
|  | hint_cache_->UpdateFetchedHints( | 
|  | std::move(get_hints_response), stored_time, | 
|  | base::BindOnce(&HintCacheTest::OnHintsUpdated, base::Unretained(this))); | 
|  |  | 
|  | while (!are_fetched_hints_updated_) | 
|  | RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | void OnHintsUpdated() { are_fetched_hints_updated_ = true; } | 
|  |  | 
|  | // Loads hint for the specified host from the cache and waits for callback | 
|  | // indicating that loading the hint is complete. | 
|  | void LoadHint(const std::string& host) { | 
|  | on_load_hint_callback_called_ = false; | 
|  | loaded_hint_ = nullptr; | 
|  | hint_cache_->LoadHint(host, base::BindOnce(&HintCacheTest::OnLoadHint, | 
|  | base::Unretained(this))); | 
|  | while (!on_load_hint_callback_called_) { | 
|  | RunUntilIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | const proto::Hint* GetLoadedHint() const { return loaded_hint_; } | 
|  |  | 
|  | private: | 
|  | void RunUntilIdle() { | 
|  | task_environment_.RunUntilIdle(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | void OnStoreInitialized() { is_store_initialized_ = true; } | 
|  | void OnUpdateComponentHints() { are_component_hints_updated_ = true; } | 
|  | void OnLoadHint(const proto::Hint* hint) { | 
|  | on_load_hint_callback_called_ = true; | 
|  | loaded_hint_ = hint; | 
|  | } | 
|  |  | 
|  | base::test::TaskEnvironment task_environment_; | 
|  |  | 
|  | std::unique_ptr<HintCache> hint_cache_; | 
|  | const proto::Hint* loaded_hint_; | 
|  |  | 
|  | bool is_store_initialized_; | 
|  | bool are_component_hints_updated_; | 
|  | bool on_load_hint_callback_called_; | 
|  | bool are_fetched_hints_updated_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(HintCacheTest); | 
|  | }; | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentUpdate) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version("2.0.0"); | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | proto::Hint hint1; | 
|  | hint1.set_key("subdomain.domain.org"); | 
|  | hint1.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint2; | 
|  | hint2.set_key("host.domain.org"); | 
|  | hint2.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint3; | 
|  | hint3.set_key("otherhost.subdomain.domain.org"); | 
|  | hint3.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint1)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint2)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint3)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org")); | 
|  |  | 
|  | // Matched | 
|  | EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org")); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentUpdateWithSameVersionIgnored) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version("2.0.0"); | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | EXPECT_FALSE(hint_cache()->MaybeCreateUpdateDataForComponentHints(version)); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentUpdateWithEarlierVersionIgnored) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version_1("1.0.0"); | 
|  | base::Version version_2("2.0.0"); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version_2); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | EXPECT_FALSE(hint_cache()->MaybeCreateUpdateDataForComponentHints(version_1)); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentUpdateWithLaterVersionProcessed) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version_1("1.0.0"); | 
|  | base::Version version_2("2.0.0"); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data_1 = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version_1); | 
|  | ASSERT_TRUE(update_data_1); | 
|  |  | 
|  | proto::Hint hint1; | 
|  | hint1.set_key("subdomain.domain.org"); | 
|  | hint1.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint2; | 
|  | hint2.set_key("host.domain.org"); | 
|  | hint2.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint3; | 
|  | hint3.set_key("otherhost.subdomain.domain.org"); | 
|  | hint3.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data_1->MoveHintIntoUpdateData(std::move(hint1)); | 
|  | update_data_1->MoveHintIntoUpdateData(std::move(hint2)); | 
|  | update_data_1->MoveHintIntoUpdateData(std::move(hint3)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data_1)); | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org")); | 
|  |  | 
|  | // Matched | 
|  | EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org")); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data_2 = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version_2); | 
|  | ASSERT_TRUE(update_data_2); | 
|  |  | 
|  | proto::Hint hint4; | 
|  | hint4.set_key("subdomain.domain2.org"); | 
|  | hint4.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint5; | 
|  | hint5.set_key("host.domain2.org"); | 
|  | hint5.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint6; | 
|  | hint6.set_key("otherhost.subdomain.domain2.org"); | 
|  | hint6.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data_2->MoveHintIntoUpdateData(std::move(hint4)); | 
|  | update_data_2->MoveHintIntoUpdateData(std::move(hint5)); | 
|  | update_data_2->MoveHintIntoUpdateData(std::move(hint6)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data_2)); | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("otherhost.subdomain.domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("host.subdomain.domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("subhost.host.subdomain.domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain2.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain2.org")); | 
|  |  | 
|  | // Matched | 
|  | EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain2.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain2.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain2.org")); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentHintsAvailableAfterRestart) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize, | 
|  | false /*=purge_existing_data*/); | 
|  |  | 
|  | base::Version version("2.0.0"); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | if (i == 0) { | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | proto::Hint hint1; | 
|  | hint1.set_key("subdomain.domain.org"); | 
|  | hint1.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint2; | 
|  | hint2.set_key("host.domain.org"); | 
|  | hint2.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint3; | 
|  | hint3.set_key("otherhost.subdomain.domain.org"); | 
|  | hint3.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint1)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint2)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint3)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  | } else { | 
|  | EXPECT_FALSE(update_data); | 
|  | } | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org")); | 
|  |  | 
|  | // Matched | 
|  | EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org")); | 
|  |  | 
|  | DestroyHintCache(); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentHintsUpdatableAfterRestartWithPurge) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize, | 
|  | true /*=purge_existing_data*/); | 
|  |  | 
|  | base::Version version("2.0.0"); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | proto::Hint hint1; | 
|  | hint1.set_key("subdomain.domain.org"); | 
|  | hint1.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint2; | 
|  | hint2.set_key("host.domain.org"); | 
|  | hint2.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint3; | 
|  | hint3.set_key("otherhost.subdomain.domain.org"); | 
|  | hint3.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint1)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint2)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint3)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org")); | 
|  |  | 
|  | // Matched | 
|  | EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org")); | 
|  | EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org")); | 
|  |  | 
|  | DestroyHintCache(); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ComponentHintsNotRetainedAfterRestartWithPurge) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize, | 
|  | true /*=purge_existing_data*/); | 
|  |  | 
|  | base::Version version("2.0.0"); | 
|  |  | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | if (i == 0) { | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | proto::Hint hint1; | 
|  | hint1.set_key("subdomain.domain.org"); | 
|  | hint1.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint2; | 
|  | hint2.set_key("host.domain.org"); | 
|  | hint2.set_key_representation(proto::HOST_SUFFIX); | 
|  | proto::Hint hint3; | 
|  | hint3.set_key("otherhost.subdomain.domain.org"); | 
|  | hint3.set_key_representation(proto::HOST_SUFFIX); | 
|  |  | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint1)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint2)); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint3)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  | } else { | 
|  | EXPECT_TRUE(update_data); | 
|  | } | 
|  |  | 
|  | // Not matched | 
|  | EXPECT_FALSE(hint_cache()->HasHint("domain.org")); | 
|  | EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org")); | 
|  |  | 
|  | // Maybe matched | 
|  | bool should_match = (i == 0); | 
|  | EXPECT_EQ(hint_cache()->HasHint("otherhost.subdomain.domain.org"), | 
|  | should_match); | 
|  | EXPECT_EQ(hint_cache()->HasHint("host.subdomain.domain.org"), should_match); | 
|  | EXPECT_EQ(hint_cache()->HasHint("subhost.host.subdomain.domain.org"), | 
|  | should_match); | 
|  |  | 
|  | DestroyHintCache(); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, TestMemoryCacheLeastRecentlyUsedPurge) { | 
|  | const int kTestHintCount = 10; | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version("1.0.0"); | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | for (int i = 0; i < kTestHintCount; ++i) { | 
|  | proto::Hint hint; | 
|  | hint.set_key(GetHostDomainOrg(i)); | 
|  | hint.set_key_representation(proto::HOST_SUFFIX); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint)); | 
|  | } | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | for (int i = kTestHintCount - 1; i >= 0; --i) { | 
|  | std::string host = GetHostDomainOrg(i); | 
|  | EXPECT_TRUE(hint_cache()->HasHint(host)); | 
|  | LoadHint(host); | 
|  | ASSERT_TRUE(GetLoadedHint()); | 
|  | EXPECT_EQ(GetLoadedHint()->key(), host); | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < kTestHintCount; ++i) { | 
|  | std::string host = GetHostDomainOrg(i); | 
|  | if (i < kMemoryCacheSize) { | 
|  | ASSERT_TRUE(hint_cache()->GetHintIfLoaded(host)); | 
|  | EXPECT_EQ(GetHostDomainOrg(i), | 
|  | hint_cache()->GetHintIfLoaded(host)->key()); | 
|  | } else { | 
|  | EXPECT_FALSE(hint_cache()->GetHintIfLoaded(host)); | 
|  | } | 
|  | EXPECT_TRUE(hint_cache()->HasHint(host)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, TestHostNotInCache) { | 
|  | const int kTestHintCount = 10; | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version("1.0.0"); | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | for (int i = 0; i < kTestHintCount; ++i) { | 
|  | proto::Hint hint; | 
|  | hint.set_key(GetHostDomainOrg(i)); | 
|  | hint.set_key_representation(proto::HOST_SUFFIX); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint)); | 
|  | } | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | EXPECT_FALSE(hint_cache()->HasHint(GetHostDomainOrg(kTestHintCount))); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, TestMemoryCacheLoadCallback) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Version version("1.0.0"); | 
|  | std::unique_ptr<StoreUpdateData> update_data = | 
|  | hint_cache()->MaybeCreateUpdateDataForComponentHints(version); | 
|  | ASSERT_TRUE(update_data); | 
|  |  | 
|  | std::string hint_key = "subdomain.domain.org"; | 
|  | proto::Hint hint; | 
|  | hint.set_key(hint_key); | 
|  | hint.set_key_representation(proto::HOST_SUFFIX); | 
|  | update_data->MoveHintIntoUpdateData(std::move(hint)); | 
|  |  | 
|  | UpdateComponentHints(std::move(update_data)); | 
|  |  | 
|  | EXPECT_FALSE(hint_cache()->GetHintIfLoaded("host.subdomain.domain.org")); | 
|  | LoadHint("host.subdomain.domain.org"); | 
|  | EXPECT_TRUE(hint_cache()->GetHintIfLoaded("host.subdomain.domain.org")); | 
|  |  | 
|  | EXPECT_TRUE(GetLoadedHint()); | 
|  | EXPECT_EQ(hint_key, GetLoadedHint()->key()); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, StoreValidFetchedHints) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | // Default update time for empty optimization guide store is base::Time(). | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); | 
|  |  | 
|  | std::unique_ptr<proto::GetHintsResponse> get_hints_response = | 
|  | std::make_unique<proto::GetHintsResponse>(); | 
|  |  | 
|  | proto::Hint* hint = get_hints_response->add_hints(); | 
|  | hint->set_key_representation(proto::HOST_SUFFIX); | 
|  | hint->set_key("host.domain.org"); | 
|  | proto::PageHint* page_hint = hint->add_page_hints(); | 
|  | page_hint->set_page_pattern("page pattern"); | 
|  |  | 
|  | base::Time stored_time = base::Time().Now(); | 
|  | UpdateFetchedHintsAndWait(std::move(get_hints_response), stored_time); | 
|  | EXPECT_TRUE(are_fetched_hints_updated()); | 
|  |  | 
|  | // Next update time for hints should be updated. | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, ParseEmptyFetchedHints) { | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | base::Time stored_time = base::Time().Now() + base::TimeDelta().FromDays(1); | 
|  | std::unique_ptr<proto::GetHintsResponse> get_hints_response = | 
|  | std::make_unique<proto::GetHintsResponse>(); | 
|  |  | 
|  | UpdateFetchedHintsAndWait(std::move(get_hints_response), stored_time); | 
|  | // Empty Fetched Hints causes the metadata entry to be updated. | 
|  | EXPECT_TRUE(are_fetched_hints_updated()); | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, StoreValidFetchedHintsWithServerProvidedExpiryTime) { | 
|  | base::HistogramTester histogram_tester; | 
|  | const int kMemoryCacheSize = 5; | 
|  | const int kFetchedHintExpirationSecs = 60; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | // Default update time for empty optimization guide store is base::Time(). | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); | 
|  |  | 
|  | std::unique_ptr<proto::GetHintsResponse> get_hints_response = | 
|  | std::make_unique<proto::GetHintsResponse>(); | 
|  |  | 
|  | // Set server-provided expiration time. | 
|  | get_hints_response->mutable_max_cache_duration()->set_seconds( | 
|  | kFetchedHintExpirationSecs); | 
|  |  | 
|  | proto::Hint* hint = get_hints_response->add_hints(); | 
|  | hint->set_key_representation(proto::HOST_SUFFIX); | 
|  | hint->set_key("host.domain.org"); | 
|  | proto::PageHint* page_hint = hint->add_page_hints(); | 
|  | page_hint->set_page_pattern("page pattern"); | 
|  |  | 
|  | base::Time stored_time = base::Time().Now(); | 
|  | UpdateFetchedHintsAndWait(std::move(get_hints_response), stored_time); | 
|  | EXPECT_TRUE(are_fetched_hints_updated()); | 
|  |  | 
|  | // Next update time for hints should be updated. | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); | 
|  |  | 
|  | LoadHint("host.domain.org"); | 
|  | // HISTOGRAM TEST! | 
|  | histogram_tester.ExpectTimeBucketCount( | 
|  | "OptimizationGuide.HintCache.FetchedHint.TimeToExpiration", | 
|  | base::TimeDelta::FromSeconds(kFetchedHintExpirationSecs), 1); | 
|  | } | 
|  |  | 
|  | TEST_F(HintCacheTest, StoreValidFetchedHintsWithDefaultExpiryTime) { | 
|  | base::HistogramTester histogram_tester; | 
|  | const int kMemoryCacheSize = 5; | 
|  | CreateAndInitializeHintCache(kMemoryCacheSize); | 
|  |  | 
|  | // Default update time for empty optimization guide store is base::Time(). | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), base::Time()); | 
|  |  | 
|  | std::unique_ptr<proto::GetHintsResponse> get_hints_response = | 
|  | std::make_unique<proto::GetHintsResponse>(); | 
|  |  | 
|  | proto::Hint* hint = get_hints_response->add_hints(); | 
|  | hint->set_key_representation(proto::HOST_SUFFIX); | 
|  | hint->set_key("host.domain.org"); | 
|  | proto::PageHint* page_hint = hint->add_page_hints(); | 
|  | page_hint->set_page_pattern("page pattern"); | 
|  |  | 
|  | base::Time stored_time = base::Time().Now(); | 
|  | UpdateFetchedHintsAndWait(std::move(get_hints_response), stored_time); | 
|  | EXPECT_TRUE(are_fetched_hints_updated()); | 
|  |  | 
|  | // Next update time for hints should be updated. | 
|  | EXPECT_EQ(hint_cache()->GetFetchedHintsUpdateTime(), stored_time); | 
|  |  | 
|  | LoadHint("host.domain.org"); | 
|  | histogram_tester.ExpectTimeBucketCount( | 
|  | "OptimizationGuide.HintCache.FetchedHint.TimeToExpiration", | 
|  | optimization_guide::features::StoredFetchedHintsFreshnessDuration(), 1); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace optimization_guide |