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"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/location.h"
17 #include "base/metrics/histogram.h"
18 #include "base/metrics/histogram_samples.h"
19 #include "base/metrics/statistics_recorder.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "components/history/core/browser/history_service.h"
23 #include "components/history/core/browser/history_types.h"
24 #include "components/precache/core/precache_switches.h"
25 #include "content/public/test/test_browser_context.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "net/http/http_status_code.h"
28 #include "net/url_request/test_url_fetcher_factory.h"
29 #include "net/url_request/url_request_status.h"
30 #include "net/url_request/url_request_test_util.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
40 using ::testing::SaveArg
;
42 // A map of histogram names to the total sample counts.
43 typedef std::map
<std::string
, base::HistogramBase::Count
> HistogramCountMap
;
45 const char kConfigURL
[] = "http://config-url.com";
46 const char kManifestURLPrefix
[] = "http://manifest-url-prefix.com/";
47 const char kGoodManifestURL
[] =
48 "http://manifest-url-prefix.com/good-manifest.com";
50 base::HistogramBase::Count
GetHistogramTotalCount(const char* histogram_name
) {
51 base::HistogramBase
* histogram
=
52 base::StatisticsRecorder::FindHistogram(histogram_name
);
53 return histogram
? histogram
->SnapshotSamples()->TotalCount() : 0;
56 HistogramCountMap
GetHistogramCountMap() {
57 // Note that the PrecacheManager tests don't care about the ".Cellular"
59 const char* kHistogramNames
[] = {"Precache.DownloadedPrecacheMotivated",
60 "Precache.DownloadedNonPrecache",
63 HistogramCountMap histogram_count_map
;
64 for (size_t i
= 0; i
< arraysize(kHistogramNames
); ++i
) {
65 histogram_count_map
[kHistogramNames
[i
]] =
66 GetHistogramTotalCount(kHistogramNames
[i
]);
68 return histogram_count_map
;
71 class TestURLFetcherCallback
{
73 scoped_ptr
<net::FakeURLFetcher
> CreateURLFetcher(
74 const GURL
& url
, net::URLFetcherDelegate
* delegate
,
75 const std::string
& response_data
, net::HttpStatusCode response_code
,
76 net::URLRequestStatus::Status status
) {
77 scoped_ptr
<net::FakeURLFetcher
> fetcher(new net::FakeURLFetcher(
78 url
, delegate
, response_data
, response_code
, status
));
80 requested_urls_
.insert(url
);
81 return fetcher
.Pass();
84 const std::multiset
<GURL
>& requested_urls() const {
85 return requested_urls_
;
89 // Multiset with one entry for each URL requested.
90 std::multiset
<GURL
> requested_urls_
;
93 class MockHistoryService
: public history::HistoryService
{
95 MOCK_CONST_METHOD2(TopHosts
,
96 void(int num_hosts
, const TopHostsCallback
& callback
));
99 ACTION_P(ReturnHosts
, starting_hosts
) {
100 arg1
.Run(starting_hosts
);
103 class TestPrecacheCompletionCallback
{
105 TestPrecacheCompletionCallback() : was_on_done_called_(false) {}
108 was_on_done_called_
= true;
111 PrecacheManager::PrecacheCompletionCallback
GetCallback() {
112 return base::Bind(&TestPrecacheCompletionCallback::OnDone
,
113 base::Unretained(this));
116 bool was_on_done_called() const {
117 return was_on_done_called_
;
121 bool was_on_done_called_
;
124 class PrecacheManagerTest
: public testing::Test
{
126 PrecacheManagerTest()
127 : precache_manager_(&browser_context_
),
128 factory_(NULL
, base::Bind(&TestURLFetcherCallback::CreateURLFetcher
,
129 base::Unretained(&url_callback_
))) {}
132 void SetUp() override
{
133 base::StatisticsRecorder::Initialize();
135 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
136 switches::kPrecacheConfigSettingsURL
, kConfigURL
);
137 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
138 switches::kPrecacheManifestURLPrefix
, kManifestURLPrefix
);
140 // Make the fetch of the precache configuration settings fail. Precaching
141 // should still complete normally in this case.
142 factory_
.SetFakeResponse(GURL(kConfigURL
), "",
143 net::HTTP_INTERNAL_SERVER_ERROR
,
144 net::URLRequestStatus::FAILED
);
147 content::TestBrowserThreadBundle test_browser_thread_bundle_
;
148 content::TestBrowserContext browser_context_
;
149 PrecacheManager precache_manager_
;
150 TestURLFetcherCallback url_callback_
;
151 net::FakeURLFetcherFactory factory_
;
152 TestPrecacheCompletionCallback precache_callback_
;
155 TEST_F(PrecacheManagerTest
, StartAndFinishPrecaching
) {
156 EXPECT_FALSE(precache_manager_
.IsPrecaching());
158 MockHistoryService history_service
;
159 MockHistoryService::TopHostsCallback top_hosts_callback
;
160 EXPECT_CALL(history_service
, TopHosts(NumTopHosts(), _
))
161 .WillOnce(SaveArg
<1>(&top_hosts_callback
));
163 factory_
.SetFakeResponse(GURL(kGoodManifestURL
), "", net::HTTP_OK
,
164 net::URLRequestStatus::SUCCESS
);
166 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
169 EXPECT_TRUE(precache_manager_
.IsPrecaching());
171 top_hosts_callback
.Run(
172 history::TopHostsList(1, std::make_pair("good-manifest.com", 1)));
173 base::MessageLoop::current()->RunUntilIdle(); // For PrecacheFetcher.
174 EXPECT_FALSE(precache_manager_
.IsPrecaching());
175 EXPECT_TRUE(precache_callback_
.was_on_done_called());
177 std::multiset
<GURL
> expected_requested_urls
;
178 expected_requested_urls
.insert(GURL(kConfigURL
));
179 expected_requested_urls
.insert(GURL(kGoodManifestURL
));
180 EXPECT_EQ(expected_requested_urls
, url_callback_
.requested_urls());
183 TEST_F(PrecacheManagerTest
, StartAndCancelPrecachingBeforeURLsReceived
) {
184 EXPECT_FALSE(precache_manager_
.IsPrecaching());
186 MockHistoryService history_service
;
187 MockHistoryService::TopHostsCallback top_hosts_callback
;
188 EXPECT_CALL(history_service
, TopHosts(NumTopHosts(), _
))
189 .WillOnce(SaveArg
<1>(&top_hosts_callback
));
191 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
193 EXPECT_TRUE(precache_manager_
.IsPrecaching());
195 precache_manager_
.CancelPrecaching();
196 EXPECT_FALSE(precache_manager_
.IsPrecaching());
198 top_hosts_callback
.Run(
199 history::TopHostsList(1, std::make_pair("starting-url.com", 1)));
200 base::MessageLoop::current()->RunUntilIdle(); // For PrecacheFetcher.
201 EXPECT_FALSE(precache_manager_
.IsPrecaching());
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 MockHistoryService history_service
;
210 EXPECT_CALL(history_service
, TopHosts(NumTopHosts(), _
))
211 .WillOnce(ReturnHosts(
212 history::TopHostsList(1, std::make_pair("starting-url.com", 1))));
214 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
217 // Since the |history_service| ran the callback immediately, Start() has
218 // been called on the PrecacheFetcher, and the precache config settings have
219 // been requested. The response has not yet been received though, so
220 // precaching is still in progress.
221 EXPECT_TRUE(precache_manager_
.IsPrecaching());
223 precache_manager_
.CancelPrecaching();
224 EXPECT_FALSE(precache_manager_
.IsPrecaching());
226 base::MessageLoop::current()->RunUntilIdle(); // For PrecacheFetcher.
227 EXPECT_FALSE(precache_manager_
.IsPrecaching());
228 EXPECT_FALSE(precache_callback_
.was_on_done_called());
230 // Even though the response for the precache config settings should not have
231 // been received, the request should still have been made.
232 std::multiset
<GURL
> expected_requested_urls
;
233 expected_requested_urls
.insert(GURL(kConfigURL
));
234 EXPECT_EQ(expected_requested_urls
, url_callback_
.requested_urls());
237 TEST_F(PrecacheManagerTest
, RecordStatsForFetchWithIrrelevantFetches
) {
238 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
240 // Fetches with size 0 should be ignored.
241 precache_manager_
.RecordStatsForFetch(GURL("http://url.com"), base::Time(), 0,
243 base::MessageLoop::current()->RunUntilIdle();
244 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
246 // Fetches for URLs with schemes other than HTTP or HTTPS should be ignored.
247 precache_manager_
.RecordStatsForFetch(GURL("ftp://ftp.com"), base::Time(),
249 base::MessageLoop::current()->RunUntilIdle();
250 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
252 // Fetches for empty URLs should be ignored.
253 precache_manager_
.RecordStatsForFetch(GURL(), base::Time(), 1000, false);
254 base::MessageLoop::current()->RunUntilIdle();
255 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
258 TEST_F(PrecacheManagerTest
, RecordStatsForFetchDuringPrecaching
) {
259 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
261 MockHistoryService history_service
;
262 EXPECT_CALL(history_service
, TopHosts(NumTopHosts(), _
))
263 .WillOnce(ReturnHosts(history::TopHostsList()));
265 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
268 EXPECT_TRUE(precache_manager_
.IsPrecaching());
269 precache_manager_
.RecordStatsForFetch(GURL("http://url.com"), base::Time(),
272 precache_manager_
.CancelPrecaching();
274 // For PrecacheFetcher and RecordURLPrecached.
275 base::MessageLoop::current()->RunUntilIdle();
276 expected_histogram_count_map
["Precache.DownloadedPrecacheMotivated"]++;
277 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
280 TEST_F(PrecacheManagerTest
, RecordStatsForFetchHTTP
) {
281 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
283 precache_manager_
.RecordStatsForFetch(GURL("http://http-url.com"),
284 base::Time(), 1000, false);
285 base::MessageLoop::current()->RunUntilIdle();
287 expected_histogram_count_map
["Precache.DownloadedNonPrecache"]++;
288 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
291 TEST_F(PrecacheManagerTest
, RecordStatsForFetchHTTPS
) {
292 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
294 precache_manager_
.RecordStatsForFetch(GURL("https://https-url.com"),
295 base::Time(), 1000, false);
296 base::MessageLoop::current()->RunUntilIdle();
298 expected_histogram_count_map
["Precache.DownloadedNonPrecache"]++;
299 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
302 TEST_F(PrecacheManagerTest
, DeleteExpiredPrecacheHistory
) {
303 // This test has to use Time::Now() because StartPrecaching uses Time::Now().
304 const base::Time kCurrentTime
= base::Time::Now();
305 HistogramCountMap expected_histogram_count_map
= GetHistogramCountMap();
307 MockHistoryService history_service
;
308 EXPECT_CALL(history_service
, TopHosts(NumTopHosts(), _
))
310 .WillRepeatedly(ReturnHosts(history::TopHostsList()));
312 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
314 EXPECT_TRUE(precache_manager_
.IsPrecaching());
316 // Precache a bunch of URLs, with different fetch times.
317 precache_manager_
.RecordStatsForFetch(
318 GURL("http://old-fetch.com"),
319 kCurrentTime
- base::TimeDelta::FromDays(61), 1000, false);
320 precache_manager_
.RecordStatsForFetch(
321 GURL("http://recent-fetch.com"),
322 kCurrentTime
- base::TimeDelta::FromDays(59), 1000, false);
323 precache_manager_
.RecordStatsForFetch(
324 GURL("http://yesterday-fetch.com"),
325 kCurrentTime
- base::TimeDelta::FromDays(1), 1000, false);
326 expected_histogram_count_map
["Precache.DownloadedPrecacheMotivated"] += 3;
328 precache_manager_
.CancelPrecaching();
329 // For PrecacheFetcher and RecordURLPrecached.
330 base::MessageLoop::current()->RunUntilIdle();
331 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
333 // The expired precache will be deleted during precaching this time.
334 precache_manager_
.StartPrecaching(precache_callback_
.GetCallback(),
336 EXPECT_TRUE(precache_manager_
.IsPrecaching());
338 precache_manager_
.CancelPrecaching();
339 // For PrecacheFetcher and RecordURLPrecached.
340 base::MessageLoop::current()->RunUntilIdle();
341 EXPECT_FALSE(precache_manager_
.IsPrecaching());
343 // A fetch for the same URL as the expired precache was served from the cache,
344 // but it isn't reported as saved bytes because it had expired in the precache
346 precache_manager_
.RecordStatsForFetch(
347 GURL("http://old-fetch.com"),
348 kCurrentTime
, 1000, true);
350 base::MessageLoop::current()->RunUntilIdle();
351 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
353 // The other precaches should not have expired, so the following fetches from
354 // the cache should count as saved bytes.
355 precache_manager_
.RecordStatsForFetch(
356 GURL("http://recent-fetch.com"),
357 kCurrentTime
, 1000, true);
358 precache_manager_
.RecordStatsForFetch(
359 GURL("http://yesterday-fetch.com"),
360 kCurrentTime
, 1000, true);
361 expected_histogram_count_map
["Precache.Saved"] += 2;
363 base::MessageLoop::current()->RunUntilIdle();
364 EXPECT_EQ(expected_histogram_count_map
, GetHistogramCountMap());
369 } // namespace precache