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
26namespace precache {
27
28namespace {
29
30class TestURLFetcherCallback {
31 public:
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
50  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
59class TestPrecacheDelegate : public PrecacheFetcher::PrecacheDelegate {
60 public:
61  TestPrecacheDelegate() : was_on_done_called_(false) {}
62
63  virtual void OnDone() OVERRIDE {
64    was_on_done_called_ = true;
65  }
66
67  bool was_on_done_called() const {
68    return was_on_done_called_;
69  }
70
71 private:
72  bool was_on_done_called_;
73};
74
75class PrecacheFetcherTest : public testing::Test {
76 public:
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
91const char kConfigURL[] = "http://config-url.com";
92const char kManfiestURLPrefix[] = "http://manifest-url-prefix.com/";
93const char kManifestFetchFailureURL[] =
94    "http://manifest-url-prefix.com/"
95    "http%253A%252F%252Fmanifest-fetch-failure.com%252F";
96const char kBadManifestURL[] =
97    "http://manifest-url-prefix.com/http%253A%252F%252Fbad-manifest.com%252F";
98const char kGoodManifestURL[] =
99    "http://manifest-url-prefix.com/http%253A%252F%252Fgood-manifest.com%252F";
100const char kResourceFetchFailureURL[] = "http://resource-fetch-failure.com";
101const char kGoodResourceURL[] = "http://good-resource.com";
102const char kForcedStartingURLManifestURL[] =
103    "http://manifest-url-prefix.com/"
104    "http%253A%252F%252Fforced-starting-url.com%252F";
105
106TEST_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
168TEST_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
191TEST_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
213TEST_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.
246TEST_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.
275TEST_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