1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/precache/core/precache_database.h" 6 7 #include <map> 8 9 #include "base/files/file_path.h" 10 #include "base/files/scoped_temp_dir.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram_samples.h" 14 #include "base/metrics/statistics_recorder.h" 15 #include "base/time/time.h" 16 #include "sql/connection.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 #include "url/gurl.h" 19 20 namespace { 21 22 const GURL kURL("http://url.com"); 23 const base::Time kFetchTime = base::Time() + base::TimeDelta::FromHours(1000); 24 const base::Time kOldFetchTime = kFetchTime - base::TimeDelta::FromDays(1); 25 const int64 kSize = 5000; 26 27 const char* kHistogramNames[] = {"Precache.DownloadedPrecacheMotivated", 28 "Precache.DownloadedNonPrecache", 29 "Precache.DownloadedNonPrecache.Cellular", 30 "Precache.Saved", 31 "Precache.Saved.Cellular"}; 32 33 scoped_ptr<base::HistogramSamples> GetHistogramSamples( 34 const char* histogram_name) { 35 base::HistogramBase* histogram = 36 base::StatisticsRecorder::FindHistogram(histogram_name); 37 38 EXPECT_NE(static_cast<base::HistogramBase*>(NULL), histogram); 39 40 return histogram->SnapshotSamples().Pass(); 41 } 42 43 std::map<GURL, base::Time> BuildURLTableMap(const GURL& url, 44 const base::Time& precache_time) { 45 std::map<GURL, base::Time> url_table_map; 46 url_table_map[url] = precache_time; 47 return url_table_map; 48 } 49 50 } // namespace 51 52 namespace precache { 53 54 class PrecacheDatabaseTest : public testing::Test { 55 public: 56 PrecacheDatabaseTest() {} 57 virtual ~PrecacheDatabaseTest() {} 58 59 protected: 60 virtual void SetUp() OVERRIDE { 61 base::StatisticsRecorder::Initialize(); 62 precache_database_ = new PrecacheDatabase(); 63 64 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); 65 base::FilePath db_path = scoped_temp_dir_.path().Append( 66 base::FilePath(FILE_PATH_LITERAL("precache_database"))); 67 precache_database_->Init(db_path); 68 69 // Log a sample for each histogram, to ensure that they are all created. 70 // This has to be done here, and not in the for loop below, because of the 71 // way that UMA_HISTOGRAM_COUNTS uses static variables. 72 UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated", 0); 73 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", 0); 74 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular", 0); 75 UMA_HISTOGRAM_COUNTS("Precache.Saved", 0); 76 UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", 0); 77 78 for (size_t i = 0; i < arraysize(kHistogramNames); i++) { 79 initial_histogram_samples_[i] = 80 GetHistogramSamples(kHistogramNames[i]).Pass(); 81 initial_histogram_samples_map_[kHistogramNames[i]] = 82 initial_histogram_samples_[i].get(); 83 } 84 } 85 86 std::map<GURL, base::Time> GetActualURLTableMap() { 87 // Flush any buffered writes so that the URL table will be up to date. 88 precache_database_->Flush(); 89 90 std::map<GURL, base::Time> url_table_map; 91 precache_url_table()->GetAllDataForTesting(&url_table_map); 92 return url_table_map; 93 } 94 95 PrecacheURLTable* precache_url_table() { 96 return &precache_database_->precache_url_table_; 97 } 98 99 scoped_ptr<base::HistogramSamples> GetHistogramSamplesDelta( 100 const char* histogram_name) { 101 scoped_ptr<base::HistogramSamples> delta_samples( 102 GetHistogramSamples(histogram_name)); 103 delta_samples->Subtract(*initial_histogram_samples_map_[histogram_name]); 104 105 return delta_samples.Pass(); 106 } 107 108 void ExpectNewSample(const char* histogram_name, 109 base::HistogramBase::Sample sample) { 110 scoped_ptr<base::HistogramSamples> delta_samples( 111 GetHistogramSamplesDelta(histogram_name)); 112 EXPECT_EQ(1, delta_samples->TotalCount()); 113 EXPECT_EQ(1, delta_samples->GetCount(sample)); 114 } 115 116 void ExpectNoNewSamples(const char* histogram_name) { 117 scoped_ptr<base::HistogramSamples> delta_samples( 118 GetHistogramSamplesDelta(histogram_name)); 119 EXPECT_EQ(0, delta_samples->TotalCount()); 120 } 121 122 // Convenience methods for recording different types of URL fetches. These 123 // exist to improve the readability of the tests. 124 void RecordPrecacheFromNetwork(const GURL& url, const base::Time& fetch_time, 125 int64 size); 126 void RecordPrecacheFromCache(const GURL& url, const base::Time& fetch_time, 127 int64 size); 128 void RecordFetchFromNetwork(const GURL& url, const base::Time& fetch_time, 129 int64 size); 130 void RecordFetchFromNetworkCellular(const GURL& url, 131 const base::Time& fetch_time, int64 size); 132 void RecordFetchFromCache(const GURL& url, const base::Time& fetch_time, 133 int64 size); 134 void RecordFetchFromCacheCellular(const GURL& url, 135 const base::Time& fetch_time, int64 size); 136 137 // Having this MessageLoop member variable causes base::MessageLoop::current() 138 // to be set properly. 139 base::MessageLoopForUI loop_; 140 141 scoped_refptr<PrecacheDatabase> precache_database_; 142 base::ScopedTempDir scoped_temp_dir_; 143 scoped_ptr<base::HistogramSamples> initial_histogram_samples_ 144 [arraysize(kHistogramNames)]; 145 std::map<std::string, base::HistogramSamples*> initial_histogram_samples_map_; 146 }; 147 148 void PrecacheDatabaseTest::RecordPrecacheFromNetwork( 149 const GURL& url, const base::Time& fetch_time, int64 size) { 150 precache_database_->RecordURLPrecached(url, fetch_time, size, 151 false /* was_cached */); 152 } 153 154 void PrecacheDatabaseTest::RecordPrecacheFromCache(const GURL& url, 155 const base::Time& fetch_time, 156 int64 size) { 157 precache_database_->RecordURLPrecached(url, fetch_time, size, 158 true /* was_cached */); 159 } 160 161 void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url, 162 const base::Time& fetch_time, 163 int64 size) { 164 precache_database_->RecordURLFetched(url, fetch_time, size, 165 false /* was_cached */, 166 false /* is_connection_cellular */); 167 } 168 169 void PrecacheDatabaseTest::RecordFetchFromNetworkCellular( 170 const GURL& url, const base::Time& fetch_time, int64 size) { 171 precache_database_->RecordURLFetched(url, fetch_time, size, 172 false /* was_cached */, 173 true /* is_connection_cellular */); 174 } 175 176 void PrecacheDatabaseTest::RecordFetchFromCache(const GURL& url, 177 const base::Time& fetch_time, 178 int64 size) { 179 precache_database_->RecordURLFetched(url, fetch_time, size, 180 true /* was_cached */, 181 false /* is_connection_cellular */); 182 } 183 184 void PrecacheDatabaseTest::RecordFetchFromCacheCellular( 185 const GURL& url, const base::Time& fetch_time, int64 size) { 186 precache_database_->RecordURLFetched(url, fetch_time, size, 187 true /* was_cached */, 188 true /* is_connection_cellular */); 189 } 190 191 namespace { 192 193 TEST_F(PrecacheDatabaseTest, PrecacheOverNetwork) { 194 RecordPrecacheFromNetwork(kURL, kFetchTime, kSize); 195 196 EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap()); 197 198 ExpectNewSample("Precache.DownloadedPrecacheMotivated", kSize); 199 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 200 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 201 ExpectNoNewSamples("Precache.Saved"); 202 ExpectNoNewSamples("Precache.Saved.Cellular"); 203 } 204 205 TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithURLTableEntry) { 206 precache_url_table()->AddURL(kURL, kOldFetchTime); 207 RecordPrecacheFromCache(kURL, kFetchTime, kSize); 208 209 // The URL table entry should have been updated to have |kFetchTime| as the 210 // timestamp. 211 EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap()); 212 213 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 214 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 215 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 216 ExpectNoNewSamples("Precache.Saved"); 217 ExpectNoNewSamples("Precache.Saved.Cellular"); 218 } 219 220 TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithoutURLTableEntry) { 221 RecordPrecacheFromCache(kURL, kFetchTime, kSize); 222 223 EXPECT_TRUE(GetActualURLTableMap().empty()); 224 225 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 226 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 227 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 228 ExpectNoNewSamples("Precache.Saved"); 229 ExpectNoNewSamples("Precache.Saved.Cellular"); 230 } 231 232 TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular) { 233 RecordFetchFromNetwork(kURL, kFetchTime, kSize); 234 235 EXPECT_TRUE(GetActualURLTableMap().empty()); 236 237 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 238 ExpectNewSample("Precache.DownloadedNonPrecache", kSize); 239 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 240 ExpectNoNewSamples("Precache.Saved"); 241 ExpectNoNewSamples("Precache.Saved.Cellular"); 242 } 243 244 TEST_F(PrecacheDatabaseTest, FetchOverNetwork_Cellular) { 245 RecordFetchFromNetworkCellular(kURL, kFetchTime, kSize); 246 247 EXPECT_TRUE(GetActualURLTableMap().empty()); 248 249 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 250 ExpectNewSample("Precache.DownloadedNonPrecache", kSize); 251 ExpectNewSample("Precache.DownloadedNonPrecache.Cellular", kSize); 252 ExpectNoNewSamples("Precache.Saved"); 253 ExpectNoNewSamples("Precache.Saved.Cellular"); 254 } 255 256 TEST_F(PrecacheDatabaseTest, FetchOverNetworkWithURLTableEntry) { 257 precache_url_table()->AddURL(kURL, kOldFetchTime); 258 RecordFetchFromNetwork(kURL, kFetchTime, kSize); 259 260 // The URL table entry should have been deleted. 261 EXPECT_TRUE(GetActualURLTableMap().empty()); 262 263 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 264 ExpectNewSample("Precache.DownloadedNonPrecache", kSize); 265 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 266 ExpectNoNewSamples("Precache.Saved"); 267 ExpectNoNewSamples("Precache.Saved.Cellular"); 268 } 269 270 TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_NonCellular) { 271 precache_url_table()->AddURL(kURL, kOldFetchTime); 272 RecordFetchFromCache(kURL, kFetchTime, kSize); 273 274 // The URL table entry should have been deleted. 275 EXPECT_TRUE(GetActualURLTableMap().empty()); 276 277 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 278 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 279 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 280 ExpectNewSample("Precache.Saved", kSize); 281 ExpectNoNewSamples("Precache.Saved.Cellular"); 282 } 283 284 TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_Cellular) { 285 precache_url_table()->AddURL(kURL, kOldFetchTime); 286 RecordFetchFromCacheCellular(kURL, kFetchTime, kSize); 287 288 // The URL table entry should have been deleted. 289 EXPECT_TRUE(GetActualURLTableMap().empty()); 290 291 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 292 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 293 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 294 ExpectNewSample("Precache.Saved", kSize); 295 ExpectNewSample("Precache.Saved.Cellular", kSize); 296 } 297 298 TEST_F(PrecacheDatabaseTest, FetchFromCacheWithoutURLTableEntry) { 299 RecordFetchFromCache(kURL, kFetchTime, kSize); 300 301 EXPECT_TRUE(GetActualURLTableMap().empty()); 302 303 ExpectNoNewSamples("Precache.DownloadedPrecacheMotivated"); 304 ExpectNoNewSamples("Precache.DownloadedNonPrecache"); 305 ExpectNoNewSamples("Precache.DownloadedNonPrecache.Cellular"); 306 ExpectNoNewSamples("Precache.Saved"); 307 ExpectNoNewSamples("Precache.Saved.Cellular"); 308 } 309 310 TEST_F(PrecacheDatabaseTest, DeleteExpiredPrecacheHistory) { 311 const base::Time kToday = base::Time() + base::TimeDelta::FromDays(1000); 312 const base::Time k59DaysAgo = kToday - base::TimeDelta::FromDays(59); 313 const base::Time k61DaysAgo = kToday - base::TimeDelta::FromDays(61); 314 315 precache_url_table()->AddURL(GURL("http://expired-precache.com"), k61DaysAgo); 316 precache_url_table()->AddURL(GURL("http://old-precache.com"), k59DaysAgo); 317 318 precache_database_->DeleteExpiredPrecacheHistory(kToday); 319 320 EXPECT_EQ(BuildURLTableMap(GURL("http://old-precache.com"), k59DaysAgo), 321 GetActualURLTableMap()); 322 } 323 324 TEST_F(PrecacheDatabaseTest, SampleInteraction) { 325 const GURL kURL1("http://url1.com"); 326 const int64 kSize1 = 1000; 327 const GURL kURL2("http://url2.com"); 328 const int64 kSize2 = 2000; 329 const GURL kURL3("http://url3.com"); 330 const int64 kSize3 = 3000; 331 const GURL kURL4("http://url4.com"); 332 const int64 kSize4 = 4000; 333 const GURL kURL5("http://url5.com"); 334 const int64 kSize5 = 5000; 335 336 RecordPrecacheFromNetwork(kURL1, kFetchTime, kSize1); 337 RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2); 338 RecordPrecacheFromNetwork(kURL3, kFetchTime, kSize3); 339 RecordPrecacheFromNetwork(kURL4, kFetchTime, kSize4); 340 341 RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1); 342 RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1); 343 RecordFetchFromNetworkCellular(kURL2, kFetchTime, kSize2); 344 RecordFetchFromNetworkCellular(kURL5, kFetchTime, kSize5); 345 RecordFetchFromCacheCellular(kURL5, kFetchTime, kSize5); 346 347 RecordPrecacheFromCache(kURL1, kFetchTime, kSize1); 348 RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2); 349 RecordPrecacheFromCache(kURL3, kFetchTime, kSize3); 350 RecordPrecacheFromCache(kURL4, kFetchTime, kSize4); 351 352 RecordFetchFromCache(kURL1, kFetchTime, kSize1); 353 RecordFetchFromNetwork(kURL2, kFetchTime, kSize2); 354 RecordFetchFromCache(kURL3, kFetchTime, kSize3); 355 RecordFetchFromCache(kURL5, kFetchTime, kSize5); 356 357 scoped_ptr<base::HistogramSamples> downloaded_precache_motivated_bytes( 358 GetHistogramSamplesDelta("Precache.DownloadedPrecacheMotivated")); 359 EXPECT_EQ(5, downloaded_precache_motivated_bytes->TotalCount()); 360 EXPECT_EQ(1, downloaded_precache_motivated_bytes->GetCount(kSize1)); 361 EXPECT_EQ(2, downloaded_precache_motivated_bytes->GetCount(kSize2)); 362 EXPECT_EQ(1, downloaded_precache_motivated_bytes->GetCount(kSize3)); 363 EXPECT_EQ(1, downloaded_precache_motivated_bytes->GetCount(kSize4)); 364 365 scoped_ptr<base::HistogramSamples> downloaded_non_precache_bytes( 366 GetHistogramSamplesDelta("Precache.DownloadedNonPrecache")); 367 EXPECT_EQ(3, downloaded_non_precache_bytes->TotalCount()); 368 EXPECT_EQ(2, downloaded_non_precache_bytes->GetCount(kSize2)); 369 EXPECT_EQ(1, downloaded_non_precache_bytes->GetCount(kSize5)); 370 371 scoped_ptr<base::HistogramSamples> downloaded_non_precache_bytes_cellular( 372 GetHistogramSamplesDelta("Precache.DownloadedNonPrecache.Cellular")); 373 EXPECT_EQ(2, downloaded_non_precache_bytes_cellular->TotalCount()); 374 EXPECT_EQ(1, downloaded_non_precache_bytes_cellular->GetCount(kSize2)); 375 EXPECT_EQ(1, downloaded_non_precache_bytes_cellular->GetCount(kSize5)); 376 377 scoped_ptr<base::HistogramSamples> saved_bytes( 378 GetHistogramSamplesDelta("Precache.Saved")); 379 EXPECT_EQ(2, saved_bytes->TotalCount()); 380 EXPECT_EQ(1, saved_bytes->GetCount(kSize1)); 381 EXPECT_EQ(1, saved_bytes->GetCount(kSize3)); 382 383 scoped_ptr<base::HistogramSamples> saved_bytes_cellular( 384 GetHistogramSamplesDelta("Precache.Saved.Cellular")); 385 EXPECT_EQ(1, saved_bytes_cellular->TotalCount()); 386 EXPECT_EQ(1, saved_bytes_cellular->GetCount(kSize1)); 387 } 388 389 } // namespace 390 391 } // namespace precache 392