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
20namespace {
21
22const GURL kURL("http://url.com");
23const base::Time kFetchTime = base::Time() + base::TimeDelta::FromHours(1000);
24const base::Time kOldFetchTime = kFetchTime - base::TimeDelta::FromDays(1);
25const int64 kSize = 5000;
26
27const char* kHistogramNames[] = {"Precache.DownloadedPrecacheMotivated",
28                                 "Precache.DownloadedNonPrecache",
29                                 "Precache.DownloadedNonPrecache.Cellular",
30                                 "Precache.Saved",
31                                 "Precache.Saved.Cellular"};
32
33scoped_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
43std::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
52namespace precache {
53
54class 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
148void 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
154void 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
161void 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
169void 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
176void 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
184void 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
191namespace {
192
193TEST_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
205TEST_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
220TEST_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
232TEST_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
244TEST_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
256TEST_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
270TEST_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
284TEST_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
298TEST_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
310TEST_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
324TEST_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