Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(725)

Side by Side Diff: content/browser/download/url_downloader_unittest.cc

Issue 23496076: WIP - Refactor programmatic downloads Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 <vector>
6
7 #include "base/bind.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/browser/byte_stream.h"
13 #include "content/browser/download/download_create_info.h"
14 #include "content/browser/download/download_interrupt_reasons_impl.h"
15 #include "content/browser/download/url_downloader.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/download_interrupt_reasons.h"
18 #include "content/public/browser/download_url_parameters.h"
19 #include "content/public/test/test_browser_context.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "net/base/net_errors.h"
22 #include "net/test/embedded_test_server/embedded_test_server.h"
23 #include "net/test/embedded_test_server/http_request.h"
24 #include "net/test/embedded_test_server/http_response.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using ::testing::AllOf;
29 using ::testing::Return;
30 using ::testing::_;
31
32 namespace content {
33 namespace {
34
35 class ByteStreamSlurper {
36 public:
37 typedef base::Callback<void(scoped_ptr<ByteStreamSlurper>)>
38 CompletionCallback;
39
40 ByteStreamSlurper(scoped_ptr<ByteStreamReader> reader,
41 const CompletionCallback& callback);
42
43 bool is_done() const { return is_done_; }
44 int state() const { return state_; }
45 const std::string& content() const { return content_; }
46
47 void Start();
48
49 private:
50 void Slurp();
51
52 scoped_ptr<ByteStreamReader> reader_;
53 CompletionCallback callback_;
54 bool is_done_;
55 int state_;
56 std::string content_;
57 };
58
59 ByteStreamSlurper::ByteStreamSlurper(scoped_ptr<ByteStreamReader> reader,
60 const CompletionCallback& callback)
61 : reader_(reader.Pass()),
62 callback_(callback),
63 is_done_(false),
64 state_(0) {
65 }
66
67 void ByteStreamSlurper::Start() {
68 reader_->RegisterCallback(
69 base::Bind(&ByteStreamSlurper::Slurp,
70 base::Unretained(this)));
71 // Initial slurp.
72 Slurp();
73 }
74
75 void ByteStreamSlurper::Slurp() {
76 while (!is_done_) {
77 scoped_refptr<net::IOBuffer> buffer;
78 size_t length = 0;
79 ByteStreamReader::StreamState state = reader_->Read(&buffer, &length);
80 switch (state) {
81 case ByteStreamReader::STREAM_EMPTY:
82 return; // We should get called when there's data available.
83
84 case ByteStreamReader::STREAM_COMPLETE: {
85 DCHECK_EQ(0u, length);
86 is_done_ = true;
87 state_ = reader_->GetStatus();
88 scoped_ptr<ByteStreamSlurper> owned_this(this);
89 BrowserThread::PostTask(
90 BrowserThread::UI,
91 FROM_HERE,
92 base::Bind(callback_, base::Passed(&owned_this)));
93 break;
94 }
95
96 case ByteStreamReader::STREAM_HAS_DATA:
97 content_.append(buffer->data(), length);
98 break;
99 }
100 }
101 }
102
103 class Response : public net::test_server::HttpResponse {
104 public:
105 Response() : data_(new Data()) {}
106 Response(const Response& that) : data_(that.data_) {}
107 Response& Code(net::HttpStatusCode code) {
108 data_->response_.set_code(code);
109 return *this;
110 }
111 Response& Header(const char* name, const char* value) {
112 data_->response_.AddCustomHeader(name, value);
113 return *this;
114 }
115 Response& Content(const char* content) {
116 data_->response_.set_content(content);
117 return *this;
118 }
119 Response& ContentType(const char* content_type) {
120 data_->response_.set_content_type(content_type);
121 return *this;
122 }
123
124 virtual std::string ToResponseString() const OVERRIDE {
125 return data_->response_.ToResponseString();
126 }
127
128 private:
129 class Data : public base::RefCounted<Data> {
130 public:
131 Data() {}
132 net::test_server::BasicHttpResponse response_;
133
134 private:
135 friend class base::RefCounted<Data>;
136 virtual ~Data() {}
137 };
138
139 scoped_refptr<Data> data_;
140 };
141
142 class RequestHandler {
143 public:
144 net::test_server::EmbeddedTestServer::HandleRequestCallback GetCallback() {
145 return base::Bind(&RequestHandler::HandleRequest, base::Unretained(this));
146 }
147
148 MOCK_METHOD3(Request,
149 Response(const std::string&,
150 const std::map<std::string, std::string>&,
151 const std::string&));
152
153 private:
154 scoped_ptr<net::test_server::HttpResponse> HandleRequest(
155 const net::test_server::HttpRequest& request);
156 };
157
158 MATCHER_P2(HasHeader, name, value, "") {
159 return arg.find(name) != arg.end() && arg.find(name)->second == value;
160 }
161
162 scoped_ptr<net::test_server::HttpResponse> RequestHandler::HandleRequest(
163 const net::test_server::HttpRequest& request) {
164 scoped_ptr<Response> response(new Response(
165 Request(request.relative_url, request.headers, request.content)));
166 return response.PassAs<net::test_server::HttpResponse>();
167 }
168
169 class UrlDownloaderTest : public testing::Test {
170 public:
171 UrlDownloaderTest() {}
172 virtual ~UrlDownloaderTest() {}
173
174 virtual void SetUp() OVERRIDE {
175 test_server_.reset(new net::test_server::EmbeddedTestServer(
176 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)));
177 test_server_->RegisterRequestHandler(handler_.GetCallback());
178 }
179
180 virtual void TearDown() OVERRIDE {
181 ASSERT_TRUE(test_server_->ShutdownAndWaitUntilComplete());
182 base::RunLoop run_loop;
183 run_loop.RunUntilIdle();
184 }
185
186 DownloadInterruptReason StartRequestAndWaitForCallback(
187 DownloadRequestHandle* request_handle,
188 scoped_ptr<DownloadCreateInfo>* info) {
189 base::RunLoop run_loop;
190 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
191 request_handle->Start(base::Bind(&UrlDownloaderTest::StartRequestCallback,
192 &result,
193 info,
194 run_loop.QuitClosure()));
195 run_loop.Run();
196 return result;
197 }
198
199 DownloadInterruptReason SlurpByteStream(
200 scoped_ptr<ByteStreamReader> stream_reader,
201 std::string* content) {
202 int status = 0;
203 base::RunLoop run_loop;
204 scoped_ptr<ByteStreamSlurper> slurper(
205 new ByteStreamSlurper(stream_reader.Pass(),
206 base::Bind(&UrlDownloaderTest::SlurpDoneCallback,
207 &status,
208 content,
209 run_loop.QuitClosure())));
210 BrowserThread::PostTask(BrowserThread::IO,
211 FROM_HERE,
212 base::Bind(&ByteStreamSlurper::Start,
213 base::Unretained(slurper.release())));
214 run_loop.Run();
215 return static_cast<DownloadInterruptReason>(status);
216 }
217
218 TestBrowserContext* test_browser_context() { return &test_browser_context_; }
219
220 RequestHandler* request_handler() { return &handler_; }
221
222 net::test_server::EmbeddedTestServer* test_server() {
223 return test_server_.get();
224 }
225
226 private:
227 static void StartRequestCallback(DownloadInterruptReason* store_result,
228 scoped_ptr<DownloadCreateInfo>* store_info,
229 const base::Closure& closure,
230 DownloadInterruptReason result,
231 scoped_ptr<DownloadCreateInfo> create_info) {
232 *store_result = result;
233 store_info->swap(create_info);
234 closure.Run();
235 }
236
237 static void SlurpDoneCallback(int* status,
238 std::string* content,
239 const base::Closure& closure,
240 scoped_ptr<ByteStreamSlurper> slurper) {
241 *status = slurper->state();
242 *content = slurper->content();
243 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, slurper.release());
244 closure.Run();
245 }
246
247 TestBrowserThreadBundle thread_bundle_;
248 TestBrowserContext test_browser_context_;
249 scoped_ptr<net::test_server::EmbeddedTestServer> test_server_;
250 RequestHandler handler_;
251 };
252
253 const char* const kDummyURL = "/foo";
254 const char* const kTextContentType = "text/plain";
255 const char* const kDummyContent = "Hello world";
256
257 TEST_F(UrlDownloaderTest, UrlDownloader_Start) {
258 const char* kETag = "abcd";
259 ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
260 GURL url = test_server()->GetURL(kDummyURL);
261 scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
262 url, -1, -1, test_browser_context()->GetResourceContext()));
263 scoped_ptr<DownloadRequestHandle> request_handle(
264 UrlDownloader::CreateDownloadRequest(params.Pass()));
265
266 EXPECT_CALL(*request_handler(), Request(kDummyURL, _, _))
267 .WillOnce(Return(Response()
268 .Code(net::HTTP_OK)
269 .Header("etag", kETag)
270 .ContentType(kTextContentType)
271 .Content(kDummyContent)));
272 scoped_ptr<DownloadCreateInfo> info;
273 DownloadInterruptReason result =
274 StartRequestAndWaitForCallback(request_handle.get(), &info);
275 ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, result);
276 ASSERT_TRUE(info);
277 EXPECT_STREQ(kETag, info->etag.c_str());
278 EXPECT_STREQ(kTextContentType, info->mime_type.c_str());
279
280 std::string content;
281 ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
282 SlurpByteStream(info->stream_reader.Pass(), &content));
283 EXPECT_EQ(kDummyContent, content);
284 }
285
286 TEST_F(UrlDownloaderTest, UrlDownloader_ValidRedirects) {
287 ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
288 static const char* const kURL0 = "/foo";
289 static const char* const kURL1 = "/bar";
290 static const char* const kURL2 = "/baz";
291 std::vector<GURL> urls;
292 urls.push_back(test_server()->GetURL(kURL0));
293 urls.push_back(test_server()->GetURL(kURL1));
294 urls.push_back(test_server()->GetURL(kURL2));
295
296 scoped_ptr<DownloadUrlParameters> params(
297 new DownloadUrlParameters(urls[0], -1, -1,
298 test_browser_context()->GetResourceContext()));
299 scoped_ptr<DownloadRequestHandle> request_handle(
300 UrlDownloader::CreateDownloadRequest(params.Pass()));
301
302 EXPECT_CALL(*request_handler(), Request(kURL0, _, _))
303 .WillOnce(Return(Response()
304 .Code(net::HTTP_TEMPORARY_REDIRECT)
305 .Header("location", urls[1].spec().c_str())));
306 EXPECT_CALL(*request_handler(), Request(kURL1, _, _))
307 .WillOnce(Return(Response()
308 .Code(net::HTTP_TEMPORARY_REDIRECT)
309 .Header("location", urls[2].spec().c_str())));
310 EXPECT_CALL(*request_handler(), Request(kURL2, _, _))
311 .WillOnce(Return(Response()
312 .Code(net::HTTP_OK)
313 .ContentType(kTextContentType)
314 .Content(kDummyContent)));
315 scoped_ptr<DownloadCreateInfo> info;
316 DownloadInterruptReason result =
317 StartRequestAndWaitForCallback(request_handle.get(), &info);
318 ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, result);
319 ASSERT_TRUE(info);
320 EXPECT_EQ(urls, info->url_chain);
321 }
322
323 TEST_F(UrlDownloaderTest, UrlDownloader_InvalidRedirects) {
324 ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
325 static const char* const kURL0 = "/foo";
326 static const char* const kURL1 = "/bar";
327 static const char* const kURL2 = "notavalidscheme://baz";
328 std::vector<GURL> urls;
329 urls.push_back(test_server()->GetURL(kURL0));
330 urls.push_back(test_server()->GetURL(kURL1));
331 urls.push_back(GURL(kURL2));
332
333 scoped_ptr<DownloadUrlParameters> params(
334 new DownloadUrlParameters(urls[0], -1, -1,
335 test_browser_context()->GetResourceContext()));
336 scoped_ptr<DownloadRequestHandle> request_handle(
337 UrlDownloader::CreateDownloadRequest(params.Pass()));
338
339 EXPECT_CALL(*request_handler(), Request(kURL0, _, _))
340 .WillOnce(Return(Response()
341 .Code(net::HTTP_TEMPORARY_REDIRECT)
342 .Header("location", urls[1].spec().c_str())));
343 EXPECT_CALL(*request_handler(), Request(kURL1, _, _))
344 .WillOnce(Return(Response()
345 .Code(net::HTTP_TEMPORARY_REDIRECT)
346 .Header("location", urls[2].spec().c_str())));
347 scoped_ptr<DownloadCreateInfo> info;
348 DownloadInterruptReason result =
349 StartRequestAndWaitForCallback(request_handle.get(), &info);
350 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, result);
351 EXPECT_FALSE(info);
352 }
353
354 TEST_F(UrlDownloaderTest, UrlDownloader_404Handling) {
355 ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
356 scoped_ptr<DownloadUrlParameters> params(
357 new DownloadUrlParameters(test_server()->GetURL(kDummyURL),
358 -1,
359 -1,
360 test_browser_context()->GetResourceContext()));
361 scoped_ptr<DownloadRequestHandle> request_handle(
362 UrlDownloader::CreateDownloadRequest(params.Pass()));
363 EXPECT_CALL(*request_handler(), Request(kDummyURL, _, _))
364 .WillOnce(Return(Response().Code(net::HTTP_NOT_FOUND)));
365 scoped_ptr<DownloadCreateInfo> info;
366 DownloadInterruptReason reason =
367 StartRequestAndWaitForCallback(request_handle.get(), &info);
368 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
369 EXPECT_TRUE(info);
370
371 std::string content;
372 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
373 SlurpByteStream(info->stream_reader.Pass(), &content));
374 EXPECT_TRUE(content.empty());
375 }
376
377 TEST_F(UrlDownloaderTest, UrlDownloader_InvalidScheme) {
378 GURL url("notarealscheme://foo/bar");
379 scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
380 url, -1, -1, test_browser_context()->GetResourceContext()));
381 scoped_ptr<DownloadRequestHandle> request_handle(
382 UrlDownloader::CreateDownloadRequest(params.Pass()));
383 scoped_ptr<DownloadCreateInfo> info;
384 DownloadInterruptReason reason =
385 StartRequestAndWaitForCallback(request_handle.get(), &info);
386 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, reason);
387 EXPECT_FALSE(info);
388 }
389
390 // TODO(asanka): Test that back pressure is commuted correctly.
391 // TODO(asanka): Test that a successful response results in a
392 // DownloadCreateInfo and that the response data is available.
393 // TODO(asanka): Test that responses with errors are handled correctly.
394 // TODO(asanka): Test that request headers are set correctly.
395 // TODO(asanka): Test that pausing and resuming work correctly.
396 } // namespace
397 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/download/url_downloader.cc ('k') | content/browser/indexed_db/indexed_db_internals_ui.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698