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_fetcher.h"
6
7 #include <list>
8 #include <set>
9 #include <string>
10
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/message_loop/message_loop.h"
17 #include "components/precache/core/precache_switches.h"
18 #include "components/precache/core/proto/precache.pb.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/http/http_status_code.h"
21 #include "net/url_request/test_url_fetcher_factory.h"
22 #include "net/url_request/url_request_status.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace precache {
27
28 namespace {
29
30 class TestURLFetcherCallback {
31 public:
CreateURLFetcher(const GURL & url,net::URLFetcherDelegate * delegate,const std::string & response_data,net::HttpStatusCode response_code,net::URLRequestStatus::Status status)32 scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
33 const GURL& url, net::URLFetcherDelegate* delegate,
34 const std::string& response_data, net::HttpStatusCode response_code,
35 net::URLRequestStatus::Status status) {
36 scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
37 url, delegate, response_data, response_code, status));
38
39 if (response_code == net::HTTP_OK) {
40 scoped_refptr<net::HttpResponseHeaders> download_headers =
41 new net::HttpResponseHeaders("");
42 download_headers->AddHeader("Content-Type: text/html");
43 fetcher->set_response_headers(download_headers);
44 }
45
46 requested_urls_.insert(url);
47 return fetcher.Pass();
48 }
49
requested_urls() const50 const std::multiset<GURL>& requested_urls() const {
51 return requested_urls_;
52 }
53
54 private:
55 // Multiset with one entry for each URL requested.
56 std::multiset<GURL> requested_urls_;
57 };
58
59 class TestPrecacheDelegate : public PrecacheFetcher::PrecacheDelegate {
60 public:
TestPrecacheDelegate()61 TestPrecacheDelegate() : was_on_done_called_(false) {}
62
OnDone()63 virtual void OnDone() OVERRIDE {
64 was_on_done_called_ = true;
65 }
66
was_on_done_called() const67 bool was_on_done_called() const {
68 return was_on_done_called_;
69 }
70
71 private:
72 bool was_on_done_called_;
73 };
74
75 class PrecacheFetcherTest : public testing::Test {
76 public:
PrecacheFetcherTest()77 PrecacheFetcherTest()
78 : request_context_(new net::TestURLRequestContextGetter(
79 base::MessageLoopProxy::current())),
80 factory_(NULL, base::Bind(&TestURLFetcherCallback::CreateURLFetcher,
81 base::Unretained(&url_callback_))) {}
82
83 protected:
84 base::MessageLoopForUI loop_;
85 scoped_refptr<net::TestURLRequestContextGetter> request_context_;
86 TestURLFetcherCallback url_callback_;
87 net::FakeURLFetcherFactory factory_;
88 TestPrecacheDelegate precache_delegate_;
89 };
90
91 const char kConfigURL[] = "http://config-url.com";
92 const char kManfiestURLPrefix[] = "http://manifest-url-prefix.com/";
93 const char kManifestFetchFailureURL[] =
94 "http://manifest-url-prefix.com/"
95 "http%253A%252F%252Fmanifest-fetch-failure.com%252F";
96 const char kBadManifestURL[] =
97 "http://manifest-url-prefix.com/http%253A%252F%252Fbad-manifest.com%252F";
98 const char kGoodManifestURL[] =
99 "http://manifest-url-prefix.com/http%253A%252F%252Fgood-manifest.com%252F";
100 const char kResourceFetchFailureURL[] = "http://resource-fetch-failure.com";
101 const char kGoodResourceURL[] = "http://good-resource.com";
102 const char kForcedStartingURLManifestURL[] =
103 "http://manifest-url-prefix.com/"
104 "http%253A%252F%252Fforced-starting-url.com%252F";
105
TEST_F(PrecacheFetcherTest,FullPrecache)106 TEST_F(PrecacheFetcherTest, FullPrecache) {
107 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
108 switches::kPrecacheConfigSettingsURL, kConfigURL);
109 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
110 switches::kPrecacheManifestURLPrefix, kManfiestURLPrefix);
111
112 std::list<GURL> starting_urls;
113 starting_urls.push_back(GURL("http://manifest-fetch-failure.com"));
114 starting_urls.push_back(GURL("http://bad-manifest.com"));
115 starting_urls.push_back(GURL("http://good-manifest.com"));
116 starting_urls.push_back(GURL("http://not-in-top-3.com"));
117
118 PrecacheConfigurationSettings config;
119 config.set_top_sites_count(3);
120 config.add_forced_starting_url("http://forced-starting-url.com");
121 // Duplicate starting URL, the manifest for this should only be fetched once.
122 config.add_forced_starting_url("http://good-manifest.com");
123
124 PrecacheManifest good_manifest;
125 good_manifest.add_resource()->set_url(kResourceFetchFailureURL);
126 good_manifest.add_resource(); // Resource with no URL, should not be fetched.
127 good_manifest.add_resource()->set_url(kGoodResourceURL);
128
129 factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
130 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
131 factory_.SetFakeResponse(GURL(kManifestFetchFailureURL), "",
132 net::HTTP_INTERNAL_SERVER_ERROR,
133 net::URLRequestStatus::FAILED);
134 factory_.SetFakeResponse(GURL(kBadManifestURL), "bad protobuf", net::HTTP_OK,
135 net::URLRequestStatus::SUCCESS);
136 factory_.SetFakeResponse(GURL(kGoodManifestURL),
137 good_manifest.SerializeAsString(), net::HTTP_OK,
138 net::URLRequestStatus::SUCCESS);
139 factory_.SetFakeResponse(GURL(kResourceFetchFailureURL),
140 "", net::HTTP_INTERNAL_SERVER_ERROR,
141 net::URLRequestStatus::FAILED);
142 factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
143 net::URLRequestStatus::SUCCESS);
144 factory_.SetFakeResponse(GURL(kForcedStartingURLManifestURL),
145 PrecacheManifest().SerializeAsString(), net::HTTP_OK,
146 net::URLRequestStatus::SUCCESS);
147
148 PrecacheFetcher precache_fetcher(starting_urls, request_context_.get(),
149 &precache_delegate_);
150 precache_fetcher.Start();
151
152 base::MessageLoop::current()->RunUntilIdle();
153
154 std::multiset<GURL> expected_requested_urls;
155 expected_requested_urls.insert(GURL(kConfigURL));
156 expected_requested_urls.insert(GURL(kManifestFetchFailureURL));
157 expected_requested_urls.insert(GURL(kBadManifestURL));
158 expected_requested_urls.insert(GURL(kGoodManifestURL));
159 expected_requested_urls.insert(GURL(kResourceFetchFailureURL));
160 expected_requested_urls.insert(GURL(kGoodResourceURL));
161 expected_requested_urls.insert(GURL(kForcedStartingURLManifestURL));
162
163 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
164
165 EXPECT_TRUE(precache_delegate_.was_on_done_called());
166 }
167
TEST_F(PrecacheFetcherTest,ConfigFetchFailure)168 TEST_F(PrecacheFetcherTest, ConfigFetchFailure) {
169 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
170 switches::kPrecacheConfigSettingsURL, kConfigURL);
171
172 std::list<GURL> starting_urls(1, GURL("http://starting-url.com"));
173
174 factory_.SetFakeResponse(GURL(kConfigURL), "",
175 net::HTTP_INTERNAL_SERVER_ERROR,
176 net::URLRequestStatus::FAILED);
177
178 PrecacheFetcher precache_fetcher(starting_urls, request_context_.get(),
179 &precache_delegate_);
180 precache_fetcher.Start();
181
182 base::MessageLoop::current()->RunUntilIdle();
183
184 std::multiset<GURL> expected_requested_urls;
185 expected_requested_urls.insert(GURL(kConfigURL));
186 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
187
188 EXPECT_TRUE(precache_delegate_.was_on_done_called());
189 }
190
TEST_F(PrecacheFetcherTest,BadConfig)191 TEST_F(PrecacheFetcherTest, BadConfig) {
192 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
193 switches::kPrecacheConfigSettingsURL, kConfigURL);
194
195 std::list<GURL> starting_urls(1, GURL("http://starting-url.com"));
196
197 factory_.SetFakeResponse(GURL(kConfigURL), "bad protobuf", net::HTTP_OK,
198 net::URLRequestStatus::SUCCESS);
199
200 PrecacheFetcher precache_fetcher(starting_urls, request_context_.get(),
201 &precache_delegate_);
202 precache_fetcher.Start();
203
204 base::MessageLoop::current()->RunUntilIdle();
205
206 std::multiset<GURL> expected_requested_urls;
207 expected_requested_urls.insert(GURL(kConfigURL));
208 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
209
210 EXPECT_TRUE(precache_delegate_.was_on_done_called());
211 }
212
TEST_F(PrecacheFetcherTest,Cancel)213 TEST_F(PrecacheFetcherTest, Cancel) {
214 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
215 switches::kPrecacheConfigSettingsURL, kConfigURL);
216
217 std::list<GURL> starting_urls(1, GURL("http://starting-url.com"));
218
219 PrecacheConfigurationSettings config;
220 config.set_top_sites_count(1);
221
222 factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
223 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
224
225 scoped_ptr<PrecacheFetcher> precache_fetcher(new PrecacheFetcher(
226 starting_urls, request_context_.get(), &precache_delegate_));
227 precache_fetcher->Start();
228
229 // Destroy the PrecacheFetcher to cancel precaching. This should not cause
230 // OnDone to be called on the precache delegate.
231 precache_fetcher.reset();
232
233 base::MessageLoop::current()->RunUntilIdle();
234
235 std::multiset<GURL> expected_requested_urls;
236 expected_requested_urls.insert(GURL(kConfigURL));
237 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
238
239 EXPECT_FALSE(precache_delegate_.was_on_done_called());
240 }
241
242 #if defined(PRECACHE_CONFIG_SETTINGS_URL)
243
244 // If the default precache configuration settings URL is defined, then test that
245 // it works with the PrecacheFetcher.
TEST_F(PrecacheFetcherTest,PrecacheUsingDefaultConfigSettingsURL)246 TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultConfigSettingsURL) {
247 std::list<GURL> starting_urls(1, GURL("http://starting-url.com"));
248
249 PrecacheConfigurationSettings config;
250 config.set_top_sites_count(0);
251
252 factory_.SetFakeResponse(GURL(PRECACHE_CONFIG_SETTINGS_URL),
253 config.SerializeAsString(), net::HTTP_OK,
254 net::URLRequestStatus::SUCCESS);
255
256 PrecacheFetcher precache_fetcher(starting_urls, request_context_.get(),
257 &precache_delegate_);
258 precache_fetcher.Start();
259
260 base::MessageLoop::current()->RunUntilIdle();
261
262 std::multiset<GURL> expected_requested_urls;
263 expected_requested_urls.insert(GURL(PRECACHE_CONFIG_SETTINGS_URL));
264 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
265
266 EXPECT_TRUE(precache_delegate_.was_on_done_called());
267 }
268
269 #endif // PRECACHE_CONFIG_SETTINGS_URL
270
271 #if defined(PRECACHE_MANIFEST_URL_PREFIX)
272
273 // If the default precache manifest URL prefix is defined, then test that it
274 // works with the PrecacheFetcher.
TEST_F(PrecacheFetcherTest,PrecacheUsingDefaultManifestURLPrefix)275 TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultManifestURLPrefix) {
276 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
277 switches::kPrecacheConfigSettingsURL, kConfigURL);
278
279 std::list<GURL> starting_urls(1, GURL("http://starting-url.com"));
280
281 PrecacheConfigurationSettings config;
282 config.set_top_sites_count(1);
283
284 GURL manifest_url(PRECACHE_MANIFEST_URL_PREFIX
285 "http%253A%252F%252Fstarting-url.com%252F");
286
287 factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
288 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
289 factory_.SetFakeResponse(manifest_url, PrecacheManifest().SerializeAsString(),
290 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
291
292 PrecacheFetcher precache_fetcher(starting_urls, request_context_.get(),
293 &precache_delegate_);
294 precache_fetcher.Start();
295
296 base::MessageLoop::current()->RunUntilIdle();
297
298 std::multiset<GURL> expected_requested_urls;
299 expected_requested_urls.insert(GURL(kConfigURL));
300 expected_requested_urls.insert(manifest_url);
301 EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
302
303 EXPECT_TRUE(precache_delegate_.was_on_done_called());
304 }
305
306 #endif // PRECACHE_MANIFEST_URL_PREFIX
307
308 } // namespace
309
310 } // namespace precache
311