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.
5 #include "components/precache/content/precache_manager.h"
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"
36 // A map of histogram names to the total sample counts.
37 typedef std::map
<std::string
, base::HistogramBase::Count
> HistogramCountMap
;
39 const char kConfigURL
[] = "http://config-url.com";
40 const char kManifestURLPrefix
[] = "http://manifest-url-prefix.com/";
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;
48 HistogramCountMap
GetHistogramCountMap() {
49 // Note that the PrecacheManager tests don't care about the ".Cellular"
51 const char* kHistogramNames
[] = {"Precache.DownloadedPrecacheMotivated",
52 "Precache.DownloadedNonPrecache",
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
]);
60 return histogram_count_map
;
63 class TestURLFetcherCallback
{
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
));
72 requested_urls_
.insert(url
);
73 return fetcher
.Pass();
76 const std::multiset
<GURL
>& requested_urls() const {
77 return requested_urls_
;
81 // Multiset with one entry for each URL requested.
82 std::multiset
<GURL
> requested_urls_
;
85 class FakeURLListProvider
: public URLListProvider
{
87 FakeURLListProvider(const std::list
<GURL
>& urls
, bool run_immediately
)
89 run_immediately_(run_immediately
),
90 was_get_urls_called_(false) {}
92 void GetURLs(const GetURLsCallback
& callback
) override
{
93 was_get_urls_called_
= true;
95 if (run_immediately_
) {
98 // Post the callback to be run later in the message loop.
99 base::MessageLoop::current()->PostTask(FROM_HERE
,
100 base::Bind(callback
, urls_
));
104 bool was_get_urls_called() const {
105 return was_get_urls_called_
;
109 const std::list
<GURL
> urls_
;
110 const bool run_immediately_
;
111 bool was_get_urls_called_
;
114 class TestPrecacheCompletionCallback
{
116 TestPrecacheCompletionCallback() : was_on_done_called_(false) {}
119 was_on_done_called_
= true;
122 PrecacheManager::PrecacheCompletionCallback
GetCallback() {
123 return base::Bind(&TestPrecacheCompletionCallback::OnDone
,
124 base::Unretained(this));
127 bool was_on_done_called() const {
128 return was_on_done_called_
;
132 bool was_on_done_called_
;
135 class PrecacheManagerTest
: public testing::Test
{
137 PrecacheManagerTest()
138 : precache_manager_(&browser_context_
),
139 factory_(NULL
, base::Bind(&TestURLFetcherCallback::CreateURLFetcher
,
140 base::Unretained(&url_callback_
))) {}
143 void SetUp() override
{
144 base::StatisticsRecorder::Initialize();
146 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
147 switches::kPrecacheConfigSettingsURL
, kConfigURL
);
148 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
149 switches::kPrecacheManifestURLPrefix
, kManifestURLPrefix
);
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
);
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_
;
166 TEST_F(PrecacheManagerTest
, StartAndFinishPrecaching
) {
167 EXPECT_FALSE(precache_manager_
.IsPrecaching());
169 FakeURLListProvider
url_list_provider(
170 std::list
<GURL
>(1, GURL("http://starting-url.com")), false);
171 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
174 EXPECT_TRUE(precache_manager_
.IsPrecaching());
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());
181 std::multiset
<GURL
> expected_requested_urls
;
182 expected_requested_urls
.insert(GURL(kConfigURL
));
183 EXPECT_EQ(expected_requested_urls
, url_callback_
.requested_urls());
186 TEST_F(PrecacheManagerTest
, StartAndCancelPrecachingBeforeURLsReceived
) {
187 EXPECT_FALSE(precache_manager_
.IsPrecaching());
189 FakeURLListProvider
url_list_provider(
190 std::list
<GURL
>(1, GURL("http://starting-url.com")), false);
192 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
194 EXPECT_TRUE(precache_manager_
.IsPrecaching());
196 precache_manager_
.CancelPrecaching();
197 EXPECT_FALSE(precache_manager_
.IsPrecaching());
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());
206 TEST_F(PrecacheManagerTest
, StartAndCancelPrecachingAfterURLsReceived
) {
207 EXPECT_FALSE(precache_manager_
.IsPrecaching());
209 FakeURLListProvider
url_list_provider(
210 std::list
<GURL
>(1, GURL("http://starting-url.com")), true);
212 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
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());
221 precache_manager_
.CancelPrecaching();
222 EXPECT_FALSE(precache_manager_
.IsPrecaching());
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());
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());
236 TEST_F(PrecacheManagerTest
, RecordStatsForFetchWithIrrelevantFetches
) {
237 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
239 // Fetches with size 0 should be ignored.
240 precache_manager_
.RecordStatsForFetch(GURL("http://url.com"), base::Time(), 0,
242 base::MessageLoop::current()->RunUntilIdle();
243 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
245 // Fetches for URLs with schemes other than HTTP or HTTPS should be ignored.
246 precache_manager_
.RecordStatsForFetch(GURL("ftp://ftp.com"), base::Time(),
248 base::MessageLoop::current()->RunUntilIdle();
249 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
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());
257 TEST_F(PrecacheManagerTest
, RecordStatsForFetchDuringPrecaching
) {
258 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
260 FakeURLListProvider
url_list_provider(std::list
<GURL
>(), false);
261 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
264 EXPECT_TRUE(precache_manager_
.IsPrecaching());
265 precache_manager_
.RecordStatsForFetch(GURL("http://url.com"), base::Time(),
268 precache_manager_
.CancelPrecaching();
270 base::MessageLoop::current()->RunUntilIdle();
271 expected_histogram_count_map
["Precache.DownloadedPrecacheMotivated"]++;
272 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
275 TEST_F(PrecacheManagerTest
, RecordStatsForFetchHTTP
) {
276 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
278 precache_manager_
.RecordStatsForFetch(GURL("http://http-url.com"),
279 base::Time(), 1000, false);
280 base::MessageLoop::current()->RunUntilIdle();
282 expected_histogram_count_map
["Precache.DownloadedNonPrecache"]++;
283 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
286 TEST_F(PrecacheManagerTest
, RecordStatsForFetchHTTPS
) {
287 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
289 precache_manager_
.RecordStatsForFetch(GURL("https://https-url.com"),
290 base::Time(), 1000, false);
291 base::MessageLoop::current()->RunUntilIdle();
293 expected_histogram_count_map
["Precache.DownloadedNonPrecache"]++;
294 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
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();
302 FakeURLListProvider
url_list_provider(std::list
<GURL
>(), false);
303 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
305 EXPECT_TRUE(precache_manager_
.IsPrecaching());
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;
319 precache_manager_
.CancelPrecaching();
320 base::MessageLoop::current()->RunUntilIdle();
321 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
323 // The expired precache will be deleted during precaching this time.
324 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
326 EXPECT_TRUE(precache_manager_
.IsPrecaching());
328 precache_manager_
.CancelPrecaching();
329 base::MessageLoop::current()->RunUntilIdle();
330 EXPECT_FALSE(precache_manager_
.IsPrecaching());
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
335 precache_manager_
.RecordStatsForFetch(
336 GURL("http://old-fetch.com"),
337 kCurrentTime
, 1000, true);
339 base::MessageLoop::current()->RunUntilIdle();
340 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
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;
352 base::MessageLoop::current()->RunUntilIdle();
353 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
358 } // namespace precache