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