• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/content/precache_manager.h"
6 
7 #include <list>
8 #include <map>
9 #include <set>
10 #include <string>
11 
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/compiler_specific.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/metrics/histogram_samples.h"
20 #include "base/metrics/statistics_recorder.h"
21 #include "components/precache/core/precache_switches.h"
22 #include "components/precache/core/url_list_provider.h"
23 #include "content/public/test/test_browser_context.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "net/http/http_status_code.h"
26 #include "net/url_request/test_url_fetcher_factory.h"
27 #include "net/url_request/url_request_status.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31 
32 namespace precache {
33 
34 namespace {
35 
36 // A map of histogram names to the total sample counts.
37 typedef std::map<std::string, base::HistogramBase::Count> HistogramCountMap;
38 
39 const char kConfigURL[] = "http://config-url.com";
40 const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/";
41 
GetHistogramTotalCount(const char * histogram_name)42 base::HistogramBase::Count GetHistogramTotalCount(const char* histogram_name) {
43   base::HistogramBase* histogram =
44       base::StatisticsRecorder::FindHistogram(histogram_name);
45   return histogram ? histogram->SnapshotSamples()->TotalCount() : 0;
46 }
47 
GetHistogramCountMap()48 HistogramCountMap GetHistogramCountMap() {
49   // Note that the PrecacheManager tests don't care about the ".Cellular"
50   // histograms.
51   const char* kHistogramNames[] = {"Precache.DownloadedPrecacheMotivated",
52                                    "Precache.DownloadedNonPrecache",
53                                    "Precache.Saved"};
54 
55   HistogramCountMap histogram_count_map;
56   for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
57     histogram_count_map[kHistogramNames[i]] =
58         GetHistogramTotalCount(kHistogramNames[i]);
59   }
60   return histogram_count_map;
61 }
62 
63 class TestURLFetcherCallback {
64  public:
CreateURLFetcher(const GURL & url,net::URLFetcherDelegate * delegate,const std::string & response_data,net::HttpStatusCode response_code,net::URLRequestStatus::Status status)65   scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
66       const GURL& url, net::URLFetcherDelegate* delegate,
67       const std::string& response_data, net::HttpStatusCode response_code,
68       net::URLRequestStatus::Status status) {
69     scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
70         url, delegate, response_data, response_code, status));
71 
72     requested_urls_.insert(url);
73     return fetcher.Pass();
74   }
75 
requested_urls() const76   const std::multiset<GURL>& requested_urls() const {
77     return requested_urls_;
78   }
79 
80  private:
81   // Multiset with one entry for each URL requested.
82   std::multiset<GURL> requested_urls_;
83 };
84 
85 class FakeURLListProvider : public URLListProvider {
86  public:
FakeURLListProvider(const std::list<GURL> & urls,bool run_immediately)87   FakeURLListProvider(const std::list<GURL>& urls, bool run_immediately)
88       : urls_(urls),
89         run_immediately_(run_immediately),
90         was_get_urls_called_(false) {}
91 
GetURLs(const GetURLsCallback & callback)92   virtual void GetURLs(const GetURLsCallback& callback) OVERRIDE {
93     was_get_urls_called_ = true;
94 
95     if (run_immediately_) {
96       callback.Run(urls_);
97     } else {
98       // Post the callback to be run later in the message loop.
99       base::MessageLoop::current()->PostTask(FROM_HERE,
100                                              base::Bind(callback, urls_));
101     }
102   }
103 
was_get_urls_called() const104   bool was_get_urls_called() const {
105     return was_get_urls_called_;
106   }
107 
108  private:
109   const std::list<GURL> urls_;
110   const bool run_immediately_;
111   bool was_get_urls_called_;
112 };
113 
114 class TestPrecacheCompletionCallback {
115  public:
TestPrecacheCompletionCallback()116   TestPrecacheCompletionCallback() : was_on_done_called_(false) {}
117 
OnDone()118   void OnDone() {
119     was_on_done_called_ = true;
120   }
121 
GetCallback()122   PrecacheManager::PrecacheCompletionCallback GetCallback() {
123     return base::Bind(&TestPrecacheCompletionCallback::OnDone,
124                       base::Unretained(this));
125   }
126 
was_on_done_called() const127   bool was_on_done_called() const {
128     return was_on_done_called_;
129   }
130 
131  private:
132   bool was_on_done_called_;
133 };
134 
135 class PrecacheManagerTest : public testing::Test {
136  public:
PrecacheManagerTest()137   PrecacheManagerTest()
138       : precache_manager_(&browser_context_),
139         factory_(NULL, base::Bind(&TestURLFetcherCallback::CreateURLFetcher,
140                                   base::Unretained(&url_callback_))) {}
141 
142  protected:
SetUp()143   virtual void SetUp() OVERRIDE {
144     base::StatisticsRecorder::Initialize();
145 
146     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
147         switches::kPrecacheConfigSettingsURL, kConfigURL);
148     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
149         switches::kPrecacheManifestURLPrefix, kManifestURLPrefix);
150 
151     // Make the fetch of the precache configuration settings fail. Precaching
152     // should still complete normally in this case.
153     factory_.SetFakeResponse(GURL(kConfigURL), "",
154                              net::HTTP_INTERNAL_SERVER_ERROR,
155                              net::URLRequestStatus::FAILED);
156   }
157 
158   content::TestBrowserThreadBundle test_browser_thread_bundle_;
159   content::TestBrowserContext browser_context_;
160   PrecacheManager precache_manager_;
161   TestURLFetcherCallback url_callback_;
162   net::FakeURLFetcherFactory factory_;
163   TestPrecacheCompletionCallback precache_callback_;
164 };
165 
TEST_F(PrecacheManagerTest,StartAndFinishPrecaching)166 TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
167   EXPECT_FALSE(precache_manager_.IsPrecaching());
168 
169   FakeURLListProvider url_list_provider(
170       std::list<GURL>(1, GURL("http://starting-url.com")), false);
171   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
172                                     &url_list_provider);
173 
174   EXPECT_TRUE(precache_manager_.IsPrecaching());
175 
176   base::MessageLoop::current()->RunUntilIdle();
177   EXPECT_FALSE(precache_manager_.IsPrecaching());
178   EXPECT_TRUE(url_list_provider.was_get_urls_called());
179   EXPECT_TRUE(precache_callback_.was_on_done_called());
180 
181   std::multiset<GURL> expected_requested_urls;
182   expected_requested_urls.insert(GURL(kConfigURL));
183   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
184 }
185 
TEST_F(PrecacheManagerTest,StartAndCancelPrecachingBeforeURLsReceived)186 TEST_F(PrecacheManagerTest, StartAndCancelPrecachingBeforeURLsReceived) {
187   EXPECT_FALSE(precache_manager_.IsPrecaching());
188 
189   FakeURLListProvider url_list_provider(
190       std::list<GURL>(1, GURL("http://starting-url.com")), false);
191 
192   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
193                                     &url_list_provider);
194   EXPECT_TRUE(precache_manager_.IsPrecaching());
195 
196   precache_manager_.CancelPrecaching();
197   EXPECT_FALSE(precache_manager_.IsPrecaching());
198 
199   base::MessageLoop::current()->RunUntilIdle();
200   EXPECT_FALSE(precache_manager_.IsPrecaching());
201   EXPECT_TRUE(url_list_provider.was_get_urls_called());
202   EXPECT_FALSE(precache_callback_.was_on_done_called());
203   EXPECT_TRUE(url_callback_.requested_urls().empty());
204 }
205 
TEST_F(PrecacheManagerTest,StartAndCancelPrecachingAfterURLsReceived)206 TEST_F(PrecacheManagerTest, StartAndCancelPrecachingAfterURLsReceived) {
207   EXPECT_FALSE(precache_manager_.IsPrecaching());
208 
209   FakeURLListProvider url_list_provider(
210       std::list<GURL>(1, GURL("http://starting-url.com")), true);
211 
212   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
213                                     &url_list_provider);
214 
215   // Since the |url_list_provider| ran the callback immediately, Start() has
216   // been called on the PrecacheFetcher, and the precache config settings have
217   // been requested. The response has not yet been received though, so
218   // precaching is still in progress.
219   EXPECT_TRUE(precache_manager_.IsPrecaching());
220 
221   precache_manager_.CancelPrecaching();
222   EXPECT_FALSE(precache_manager_.IsPrecaching());
223 
224   base::MessageLoop::current()->RunUntilIdle();
225   EXPECT_FALSE(precache_manager_.IsPrecaching());
226   EXPECT_TRUE(url_list_provider.was_get_urls_called());
227   EXPECT_FALSE(precache_callback_.was_on_done_called());
228 
229   // Even though the response for the precache config settings should not have
230   // been received, the request should still have been made.
231   std::multiset<GURL> expected_requested_urls;
232   expected_requested_urls.insert(GURL(kConfigURL));
233   EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
234 }
235 
TEST_F(PrecacheManagerTest,RecordStatsForFetchWithIrrelevantFetches)236 TEST_F(PrecacheManagerTest, RecordStatsForFetchWithIrrelevantFetches) {
237   HistogramCountMap expected_histogram_count_map = GetHistogramCountMap();
238 
239   // Fetches with size 0 should be ignored.
240   precache_manager_.RecordStatsForFetch(GURL("http://url.com"), base::Time(), 0,
241                                         false);
242   base::MessageLoop::current()->RunUntilIdle();
243   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
244 
245   // Fetches for URLs with schemes other than HTTP or HTTPS should be ignored.
246   precache_manager_.RecordStatsForFetch(GURL("ftp://ftp.com"), base::Time(),
247                                         1000, false);
248   base::MessageLoop::current()->RunUntilIdle();
249   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
250 
251   // Fetches for empty URLs should be ignored.
252   precache_manager_.RecordStatsForFetch(GURL(), base::Time(), 1000, false);
253   base::MessageLoop::current()->RunUntilIdle();
254   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
255 }
256 
TEST_F(PrecacheManagerTest,RecordStatsForFetchDuringPrecaching)257 TEST_F(PrecacheManagerTest, RecordStatsForFetchDuringPrecaching) {
258   HistogramCountMap expected_histogram_count_map = GetHistogramCountMap();
259 
260   FakeURLListProvider url_list_provider(std::list<GURL>(), false);
261   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
262                                     &url_list_provider);
263 
264   EXPECT_TRUE(precache_manager_.IsPrecaching());
265   precache_manager_.RecordStatsForFetch(GURL("http://url.com"), base::Time(),
266                                         1000, false);
267 
268   precache_manager_.CancelPrecaching();
269 
270   base::MessageLoop::current()->RunUntilIdle();
271   expected_histogram_count_map["Precache.DownloadedPrecacheMotivated"]++;
272   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
273 }
274 
TEST_F(PrecacheManagerTest,RecordStatsForFetchHTTP)275 TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTP) {
276   HistogramCountMap expected_histogram_count_map = GetHistogramCountMap();
277 
278   precache_manager_.RecordStatsForFetch(GURL("http://http-url.com"),
279                                         base::Time(), 1000, false);
280   base::MessageLoop::current()->RunUntilIdle();
281 
282   expected_histogram_count_map["Precache.DownloadedNonPrecache"]++;
283   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
284 }
285 
TEST_F(PrecacheManagerTest,RecordStatsForFetchHTTPS)286 TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTPS) {
287   HistogramCountMap expected_histogram_count_map = GetHistogramCountMap();
288 
289   precache_manager_.RecordStatsForFetch(GURL("https://https-url.com"),
290                                         base::Time(), 1000, false);
291   base::MessageLoop::current()->RunUntilIdle();
292 
293   expected_histogram_count_map["Precache.DownloadedNonPrecache"]++;
294   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
295 }
296 
TEST_F(PrecacheManagerTest,DeleteExpiredPrecacheHistory)297 TEST_F(PrecacheManagerTest, DeleteExpiredPrecacheHistory) {
298   // This test has to use Time::Now() because StartPrecaching uses Time::Now().
299   const base::Time kCurrentTime = base::Time::Now();
300   HistogramCountMap expected_histogram_count_map = GetHistogramCountMap();
301 
302   FakeURLListProvider url_list_provider(std::list<GURL>(), false);
303   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
304                                     &url_list_provider);
305   EXPECT_TRUE(precache_manager_.IsPrecaching());
306 
307   // Precache a bunch of URLs, with different fetch times.
308   precache_manager_.RecordStatsForFetch(
309       GURL("http://old-fetch.com"),
310       kCurrentTime - base::TimeDelta::FromDays(61), 1000, false);
311   precache_manager_.RecordStatsForFetch(
312       GURL("http://recent-fetch.com"),
313       kCurrentTime - base::TimeDelta::FromDays(59), 1000, false);
314   precache_manager_.RecordStatsForFetch(
315       GURL("http://yesterday-fetch.com"),
316       kCurrentTime - base::TimeDelta::FromDays(1), 1000, false);
317   expected_histogram_count_map["Precache.DownloadedPrecacheMotivated"] += 3;
318 
319   precache_manager_.CancelPrecaching();
320   base::MessageLoop::current()->RunUntilIdle();
321   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
322 
323   // The expired precache will be deleted during precaching this time.
324   precache_manager_.StartPrecaching(precache_callback_.GetCallback(),
325                                     &url_list_provider);
326   EXPECT_TRUE(precache_manager_.IsPrecaching());
327 
328   precache_manager_.CancelPrecaching();
329   base::MessageLoop::current()->RunUntilIdle();
330   EXPECT_FALSE(precache_manager_.IsPrecaching());
331 
332   // A fetch for the same URL as the expired precache was served from the cache,
333   // but it isn't reported as saved bytes because it had expired in the precache
334   // history.
335   precache_manager_.RecordStatsForFetch(
336       GURL("http://old-fetch.com"),
337       kCurrentTime, 1000, true);
338 
339   base::MessageLoop::current()->RunUntilIdle();
340   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
341 
342   // The other precaches should not have expired, so the following fetches from
343   // the cache should count as saved bytes.
344   precache_manager_.RecordStatsForFetch(
345       GURL("http://recent-fetch.com"),
346       kCurrentTime, 1000, true);
347   precache_manager_.RecordStatsForFetch(
348       GURL("http://yesterday-fetch.com"),
349       kCurrentTime, 1000, true);
350   expected_histogram_count_map["Precache.Saved"] += 2;
351 
352   base::MessageLoop::current()->RunUntilIdle();
353   EXPECT_EQ(expected_histogram_count_map, GetHistogramCountMap());
354 }
355 
356 }  // namespace
357 
358 }  // namespace precache
359