blob: 3f9253c744af757c3272e89fc7f0eceeb8b34204 [file] [log] [blame]
// 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 <memory>
#include "base/containers/hash_tables.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.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_;
std::unique_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