// Copyright 2013 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/precache/core/precache_database.h" #include <stdint.h> #include <map> #include "base/containers/hash_tables.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_base.h" #include "base/test/histogram_tester.h" #include "base/time/time.h" #include "components/history/core/browser/history_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace { using ::testing::ContainerEq; using ::testing::ElementsAre; using base::Bucket; const GURL kURL("http://url.com"); const base::TimeDelta kLatency = base::TimeDelta::FromMilliseconds(5); const base::Time kFetchTime = base::Time() + base::TimeDelta::FromHours(1000); const base::Time kOldFetchTime = kFetchTime - base::TimeDelta::FromDays(1); const int64_t kSize = 5000; std::map<GURL, base::Time> BuildURLTableMap(const GURL& url, const base::Time& precache_time) { std::map<GURL, base::Time> url_table_map; url_table_map[url] = precache_time; return url_table_map; } } // namespace namespace precache { class PrecacheDatabaseTest : public testing::Test { public: PrecacheDatabaseTest() {} ~PrecacheDatabaseTest() override {} protected: void SetUp() override { precache_database_.reset(new PrecacheDatabase()); ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); base::FilePath db_path = scoped_temp_dir_.path().Append( base::FilePath(FILE_PATH_LITERAL("precache_database"))); precache_database_->Init(db_path); } std::map<GURL, base::Time> GetActualURLTableMap() { // Flush any buffered writes so that the URL table will be up to date. precache_database_->Flush(); std::map<GURL, base::Time> url_table_map; precache_url_table()->GetAllDataForTesting(&url_table_map); return url_table_map; } PrecacheURLTable* precache_url_table() { return &precache_database_->precache_url_table_; } // Convenience methods for recording different types of URL fetches. These // exist to improve the readability of the tests. void RecordPrecacheFromNetwork(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size); void RecordPrecacheFromCache(const GURL& url, const base::Time& fetch_time, int64_t size); void RecordFetchFromNetwork(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size); void RecordFetchFromNetwork(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size, int host_rank); void RecordFetchFromNetworkCellular(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size); void RecordFetchFromCache(const GURL& url, const base::Time& fetch_time, int64_t size); void RecordFetchFromCacheCellular(const GURL& url, const base::Time& fetch_time, int64_t size); // Must be declared first so that it is destroyed last. base::ScopedTempDir scoped_temp_dir_; // Having this MessageLoop member variable causes base::MessageLoop::current() // to be set properly. base::MessageLoopForUI loop_; scoped_ptr<PrecacheDatabase> precache_database_; base::HistogramTester histograms_; base::HistogramTester::CountsMap expected_histogram_counts_; void ExpectNewSample(const std::string& histogram_name, base::HistogramBase::Sample sample) { histograms_.ExpectUniqueSample(histogram_name, sample, 1); expected_histogram_counts_[histogram_name]++; } void ExpectNoOtherSamples() { EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."), ContainerEq(expected_histogram_counts_)); } }; void PrecacheDatabaseTest::RecordPrecacheFromNetwork( const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLPrefetch(url, latency, fetch_time, size, false /* was_cached */); } void PrecacheDatabaseTest::RecordPrecacheFromCache(const GURL& url, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLPrefetch(url, base::TimeDelta() /* latency */, fetch_time, size, true /* was_cached */); } void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLNonPrefetch( url, latency, fetch_time, size, false /* was_cached */, history::kMaxTopHosts, false /* is_connection_cellular */); } void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size, int host_rank) { precache_database_->RecordURLNonPrefetch(url, latency, fetch_time, size, false /* was_cached */, host_rank, false /* is_connection_cellular */); } void PrecacheDatabaseTest::RecordFetchFromNetworkCellular( const GURL& url, base::TimeDelta latency, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLNonPrefetch( url, latency, fetch_time, size, false /* was_cached */, history::kMaxTopHosts, true /* is_connection_cellular */); } void PrecacheDatabaseTest::RecordFetchFromCache(const GURL& url, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLNonPrefetch( url, base::TimeDelta() /* latency */, fetch_time, size, true /* was_cached */, history::kMaxTopHosts, false /* is_connection_cellular */); } void PrecacheDatabaseTest::RecordFetchFromCacheCellular( const GURL& url, const base::Time& fetch_time, int64_t size) { precache_database_->RecordURLNonPrefetch( url, base::TimeDelta() /* latency */, fetch_time, size, true /* was_cached */, history::kMaxTopHosts, true /* is_connection_cellular */); } namespace { TEST_F(PrecacheDatabaseTest, PrecacheOverNetwork) { RecordPrecacheFromNetwork(kURL, kLatency, kFetchTime, kSize); EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap()); ExpectNewSample("Precache.DownloadedPrecacheMotivated", kSize); ExpectNewSample("Precache.Latency.Prefetch", kLatency.InMilliseconds()); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithURLTableEntry) { precache_url_table()->AddURL(kURL, kOldFetchTime); RecordPrecacheFromCache(kURL, kFetchTime, kSize); // The URL table entry should have been updated to have |kFetchTime| as the // timestamp. EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap()); ExpectNewSample("Precache.Latency.Prefetch", 0); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithoutURLTableEntry) { RecordPrecacheFromCache(kURL, kFetchTime, kSize); EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.Latency.Prefetch", 0); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular) { RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize); EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.DownloadedNonPrecache", kSize); ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds()); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", kLatency.InMilliseconds()); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular_TopHosts) { RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize, 0 /* host_rank */); EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.DownloadedNonPrecache", kSize); ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds()); ExpectNewSample("Precache.Latency.NonPrefetch.TopHosts", kLatency.InMilliseconds()); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchOverNetwork_Cellular) { RecordFetchFromNetworkCellular(kURL, kLatency, kFetchTime, kSize); EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.DownloadedNonPrecache", kSize); ExpectNewSample("Precache.DownloadedNonPrecache.Cellular", kSize); ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds()); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", kLatency.InMilliseconds()); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchOverNetworkWithURLTableEntry) { precache_url_table()->AddURL(kURL, kOldFetchTime); RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize); // The URL table entry should have been deleted. EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.DownloadedNonPrecache", kSize); ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds()); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", kLatency.InMilliseconds()); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_NonCellular) { precache_url_table()->AddURL(kURL, kOldFetchTime); RecordFetchFromCache(kURL, kFetchTime, kSize); // The URL table entry should have been deleted. EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.Latency.NonPrefetch", 0); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0); ExpectNewSample("Precache.Saved", kSize); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_Cellular) { precache_url_table()->AddURL(kURL, kOldFetchTime); RecordFetchFromCacheCellular(kURL, kFetchTime, kSize); // The URL table entry should have been deleted. EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.Latency.NonPrefetch", 0); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0); ExpectNewSample("Precache.Saved", kSize); ExpectNewSample("Precache.Saved.Cellular", kSize); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, FetchFromCacheWithoutURLTableEntry) { RecordFetchFromCache(kURL, kFetchTime, kSize); EXPECT_TRUE(GetActualURLTableMap().empty()); ExpectNewSample("Precache.Latency.NonPrefetch", 0); ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0); ExpectNoOtherSamples(); } TEST_F(PrecacheDatabaseTest, DeleteExpiredPrecacheHistory) { const base::Time kToday = base::Time() + base::TimeDelta::FromDays(1000); const base::Time k59DaysAgo = kToday - base::TimeDelta::FromDays(59); const base::Time k61DaysAgo = kToday - base::TimeDelta::FromDays(61); precache_url_table()->AddURL(GURL("http://expired-precache.com"), k61DaysAgo); precache_url_table()->AddURL(GURL("http://old-precache.com"), k59DaysAgo); precache_database_->DeleteExpiredPrecacheHistory(kToday); EXPECT_EQ(BuildURLTableMap(GURL("http://old-precache.com"), k59DaysAgo), GetActualURLTableMap()); } TEST_F(PrecacheDatabaseTest, SampleInteraction) { const GURL kURL1("http://url1.com"); const int64_t kSize1 = 1; const GURL kURL2("http://url2.com"); const int64_t kSize2 = 2; const GURL kURL3("http://url3.com"); const int64_t kSize3 = 3; const GURL kURL4("http://url4.com"); const int64_t kSize4 = 4; const GURL kURL5("http://url5.com"); const int64_t kSize5 = 5; RecordPrecacheFromNetwork(kURL1, kLatency, kFetchTime, kSize1); RecordPrecacheFromNetwork(kURL2, kLatency, kFetchTime, kSize2); RecordPrecacheFromNetwork(kURL3, kLatency, kFetchTime, kSize3); RecordPrecacheFromNetwork(kURL4, kLatency, kFetchTime, kSize4); RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1); RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1); RecordFetchFromNetworkCellular(kURL2, kLatency, kFetchTime, kSize2); RecordFetchFromNetworkCellular(kURL5, kLatency, kFetchTime, kSize5); RecordFetchFromCacheCellular(kURL5, kFetchTime, kSize5); RecordPrecacheFromCache(kURL1, kFetchTime, kSize1); RecordPrecacheFromNetwork(kURL2, kLatency, kFetchTime, kSize2); RecordPrecacheFromCache(kURL3, kFetchTime, kSize3); RecordPrecacheFromCache(kURL4, kFetchTime, kSize4); RecordFetchFromCache(kURL1, kFetchTime, kSize1); RecordFetchFromNetwork(kURL2, kLatency, kFetchTime, kSize2); RecordFetchFromCache(kURL3, kFetchTime, kSize3); RecordFetchFromCache(kURL5, kFetchTime, kSize5); EXPECT_THAT(histograms_.GetAllSamples("Precache.DownloadedPrecacheMotivated"), ElementsAre(Bucket(kSize1, 1), Bucket(kSize2, 2), Bucket(kSize3, 1), Bucket(kSize4, 1))); EXPECT_THAT(histograms_.GetAllSamples("Precache.DownloadedNonPrecache"), ElementsAre(Bucket(kSize2, 2), Bucket(kSize5, 1))); EXPECT_THAT( histograms_.GetAllSamples("Precache.DownloadedNonPrecache.Cellular"), ElementsAre(Bucket(kSize2, 1), Bucket(kSize5, 1))); EXPECT_THAT(histograms_.GetAllSamples("Precache.Latency.Prefetch"), ElementsAre(Bucket(0, 3), Bucket(kLatency.InMilliseconds(), 5))); EXPECT_THAT(histograms_.GetAllSamples("Precache.Latency.NonPrefetch"), ElementsAre(Bucket(0, 6), Bucket(kLatency.InMilliseconds(), 3))); EXPECT_THAT(histograms_.GetAllSamples("Precache.Saved"), ElementsAre(Bucket(kSize1, 1), Bucket(kSize3, 1))); EXPECT_THAT(histograms_.GetAllSamples("Precache.Saved.Cellular"), ElementsAre(Bucket(kSize1, 1))); } } // namespace } // namespace precache