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.
6 #include "base/file_util.h"
7 #include "base/files/file_path.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/run_loop.h"
12 #include "base/time/time.h"
13 #include "content/public/test/test_file_system_context.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/request_priority.h"
16 #include "net/http/http_byte_range.h"
17 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/url_request/url_request.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_job_factory_impl.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "webkit/browser/blob/blob_url_request_job.h"
24 #include "webkit/browser/fileapi/async_file_test_helper.h"
25 #include "webkit/browser/fileapi/file_system_context.h"
26 #include "webkit/browser/fileapi/file_system_operation_context.h"
27 #include "webkit/browser/fileapi/file_system_url.h"
28 #include "webkit/common/blob/blob_data.h"
30 namespace webkit_blob
{
34 const int kBufferSize
= 1024;
35 const char kTestData1
[] = "Hello";
36 const char kTestData2
[] = "Here it is data.";
37 const char kTestFileData1
[] = "0123456789";
38 const char kTestFileData2
[] = "This is sample file.";
39 const char kTestFileSystemFileData1
[] = "abcdefghijklmnop";
40 const char kTestFileSystemFileData2
[] = "File system file test data.";
41 const char kTestContentType
[] = "foo/bar";
42 const char kTestContentDisposition
[] = "attachment; filename=foo.txt";
44 const char kFileSystemURLOrigin
[] = "http://remote";
45 const fileapi::FileSystemType kFileSystemType
=
46 fileapi::kFileSystemTypeTemporary
;
50 class BlobURLRequestJobTest
: public testing::Test
{
53 // Test Harness -------------------------------------------------------------
54 // TODO(jianli): share this test harness with AppCacheURLRequestJobTest
56 class MockURLRequestDelegate
: public net::URLRequest::Delegate
{
58 MockURLRequestDelegate()
59 : received_data_(new net::IOBuffer(kBufferSize
)) {}
61 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
{
62 if (request
->status().is_success()) {
63 EXPECT_TRUE(request
->response_headers());
70 virtual void OnReadCompleted(net::URLRequest
* request
,
71 int bytes_read
) OVERRIDE
{
73 ReceiveData(request
, bytes_read
);
78 const std::string
& response_data() const { return response_data_
; }
81 void ReadSome(net::URLRequest
* request
) {
82 if (!request
->is_pending()) {
88 if (!request
->Read(received_data_
.get(), kBufferSize
, &bytes_read
)) {
89 if (!request
->status().is_io_pending()) {
95 ReceiveData(request
, bytes_read
);
98 void ReceiveData(net::URLRequest
* request
, int bytes_read
) {
100 response_data_
.append(received_data_
->data(),
101 static_cast<size_t>(bytes_read
));
108 void RequestComplete() {
109 base::MessageLoop::current()->Quit();
112 scoped_refptr
<net::IOBuffer
> received_data_
;
113 std::string response_data_
;
116 // A simple ProtocolHandler implementation to create BlobURLRequestJob.
117 class MockProtocolHandler
:
118 public net::URLRequestJobFactory::ProtocolHandler
{
120 MockProtocolHandler(BlobURLRequestJobTest
* test
) : test_(test
) {}
122 // net::URLRequestJobFactory::ProtocolHandler override.
123 virtual net::URLRequestJob
* MaybeCreateJob(
124 net::URLRequest
* request
,
125 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
126 return new BlobURLRequestJob(request
,
128 test_
->blob_data_
.get(),
129 test_
->file_system_context_
.get(),
130 base::MessageLoopProxy::current().get());
134 BlobURLRequestJobTest
* test_
;
137 BlobURLRequestJobTest()
138 : blob_data_(new BlobData()),
139 expected_status_code_(0) {}
141 virtual void SetUp() {
142 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
144 temp_file1_
= temp_dir_
.path().AppendASCII("BlobFile1.dat");
145 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
146 file_util::WriteFile(temp_file1_
, kTestFileData1
,
147 arraysize(kTestFileData1
) - 1));
148 base::PlatformFileInfo file_info1
;
149 base::GetFileInfo(temp_file1_
, &file_info1
);
150 temp_file_modification_time1_
= file_info1
.last_modified
;
152 temp_file2_
= temp_dir_
.path().AppendASCII("BlobFile2.dat");
153 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
154 file_util::WriteFile(temp_file2_
, kTestFileData2
,
155 arraysize(kTestFileData2
) - 1));
156 base::PlatformFileInfo file_info2
;
157 base::GetFileInfo(temp_file2_
, &file_info2
);
158 temp_file_modification_time2_
= file_info2
.last_modified
;
160 url_request_job_factory_
.SetProtocolHandler("blob",
161 new MockProtocolHandler(this));
162 url_request_context_
.set_job_factory(&url_request_job_factory_
);
165 virtual void TearDown() {
168 void SetUpFileSystem() {
169 // Prepare file system.
170 file_system_context_
= fileapi::CreateFileSystemContextForTesting(
171 NULL
, temp_dir_
.path());
173 file_system_context_
->OpenFileSystem(
174 GURL(kFileSystemURLOrigin
),
176 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
177 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem
,
178 base::Unretained(this)));
179 base::RunLoop().RunUntilIdle();
180 ASSERT_TRUE(file_system_root_url_
.is_valid());
182 // Prepare files on file system.
183 const char kFilename1
[] = "FileSystemFile1.dat";
184 temp_file_system_file1_
= GetFileSystemURL(kFilename1
);
185 WriteFileSystemFile(kFilename1
, kTestFileSystemFileData1
,
186 arraysize(kTestFileSystemFileData1
) - 1,
187 &temp_file_system_file_modification_time1_
);
188 const char kFilename2
[] = "FileSystemFile2.dat";
189 temp_file_system_file2_
= GetFileSystemURL(kFilename2
);
190 WriteFileSystemFile(kFilename2
, kTestFileSystemFileData2
,
191 arraysize(kTestFileSystemFileData2
) - 1,
192 &temp_file_system_file_modification_time2_
);
195 GURL
GetFileSystemURL(const std::string
& filename
) {
196 return GURL(file_system_root_url_
.spec() + filename
);
199 void WriteFileSystemFile(const std::string
& filename
,
200 const char* buf
, int buf_size
,
201 base::Time
* modification_time
) {
202 fileapi::FileSystemURL url
=
203 file_system_context_
->CreateCrackedFileSystemURL(
204 GURL(kFileSystemURLOrigin
),
206 base::FilePath().AppendASCII(filename
));
208 ASSERT_EQ(base::PLATFORM_FILE_OK
,
209 fileapi::AsyncFileTestHelper::CreateFileWithData(
210 file_system_context_
, url
, buf
, buf_size
));
212 base::PlatformFileInfo file_info
;
213 ASSERT_EQ(base::PLATFORM_FILE_OK
,
214 fileapi::AsyncFileTestHelper::GetMetadata(
215 file_system_context_
, url
, &file_info
));
216 if (modification_time
)
217 *modification_time
= file_info
.last_modified
;
220 void OnValidateFileSystem(const GURL
& root
,
221 const std::string
& name
,
222 base::PlatformFileError result
) {
223 ASSERT_EQ(base::PLATFORM_FILE_OK
, result
);
224 ASSERT_TRUE(root
.is_valid());
225 file_system_root_url_
= root
;
228 void TestSuccessRequest(const std::string
& expected_response
) {
229 expected_status_code_
= 200;
230 expected_response_
= expected_response
;
231 TestRequest("GET", net::HttpRequestHeaders());
234 void TestErrorRequest(int expected_status_code
) {
235 expected_status_code_
= expected_status_code
;
236 expected_response_
= "";
237 TestRequest("GET", net::HttpRequestHeaders());
240 void TestRequest(const std::string
& method
,
241 const net::HttpRequestHeaders
& extra_headers
) {
242 request_
= url_request_context_
.CreateRequest(
243 GURL("blob:blah"), net::DEFAULT_PRIORITY
, &url_request_delegate_
);
244 request_
->set_method(method
);
245 if (!extra_headers
.IsEmpty())
246 request_
->SetExtraRequestHeaders(extra_headers
);
249 base::MessageLoop::current()->Run();
252 EXPECT_TRUE(request_
->status().is_success());
253 EXPECT_EQ(expected_status_code_
,
254 request_
->response_headers()->response_code());
255 EXPECT_EQ(expected_response_
, url_request_delegate_
.response_data());
258 void BuildComplicatedData(std::string
* expected_result
) {
259 blob_data_
->AppendData(kTestData1
+ 1, 2);
260 blob_data_
->AppendFile(temp_file1_
, 2, 3, temp_file_modification_time1_
);
261 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 3, 4,
262 temp_file_system_file_modification_time1_
);
263 blob_data_
->AppendData(kTestData2
+ 4, 5);
264 blob_data_
->AppendFile(temp_file2_
, 5, 6, temp_file_modification_time2_
);
265 blob_data_
->AppendFileSystemFile(temp_file_system_file2_
, 6, 7,
266 temp_file_system_file_modification_time2_
);
267 *expected_result
= std::string(kTestData1
+ 1, 2);
268 *expected_result
+= std::string(kTestFileData1
+ 2, 3);
269 *expected_result
+= std::string(kTestFileSystemFileData1
+ 3, 4);
270 *expected_result
+= std::string(kTestData2
+ 4, 5);
271 *expected_result
+= std::string(kTestFileData2
+ 5, 6);
272 *expected_result
+= std::string(kTestFileSystemFileData2
+ 6, 7);
276 base::ScopedTempDir temp_dir_
;
277 base::FilePath temp_file1_
;
278 base::FilePath temp_file2_
;
279 base::Time temp_file_modification_time1_
;
280 base::Time temp_file_modification_time2_
;
281 GURL file_system_root_url_
;
282 GURL temp_file_system_file1_
;
283 GURL temp_file_system_file2_
;
284 base::Time temp_file_system_file_modification_time1_
;
285 base::Time temp_file_system_file_modification_time2_
;
287 base::MessageLoopForIO message_loop_
;
288 scoped_refptr
<fileapi::FileSystemContext
> file_system_context_
;
289 scoped_refptr
<BlobData
> blob_data_
;
290 net::URLRequestJobFactoryImpl url_request_job_factory_
;
291 net::URLRequestContext url_request_context_
;
292 MockURLRequestDelegate url_request_delegate_
;
293 scoped_ptr
<net::URLRequest
> request_
;
295 int expected_status_code_
;
296 std::string expected_response_
;
299 TEST_F(BlobURLRequestJobTest
, TestGetSimpleDataRequest
) {
300 blob_data_
->AppendData(kTestData1
);
301 TestSuccessRequest(kTestData1
);
304 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileRequest
) {
305 blob_data_
->AppendFile(temp_file1_
, 0, -1, base::Time());
306 TestSuccessRequest(kTestFileData1
);
309 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileRequest
) {
310 base::FilePath large_temp_file
=
311 temp_dir_
.path().AppendASCII("LargeBlob.dat");
312 std::string large_data
;
313 large_data
.reserve(kBufferSize
* 5);
314 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
315 large_data
.append(1, static_cast<char>(i
% 256));
316 ASSERT_EQ(static_cast<int>(large_data
.size()),
317 file_util::WriteFile(large_temp_file
, large_data
.data(),
319 blob_data_
->AppendFile(large_temp_file
, 0, -1, base::Time());
320 TestSuccessRequest(large_data
);
323 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileRequest
) {
324 base::FilePath non_existent_file
=
325 temp_file1_
.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
326 blob_data_
->AppendFile(non_existent_file
, 0, -1, base::Time());
327 TestErrorRequest(404);
330 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileRequest
) {
331 base::Time old_time
=
332 temp_file_modification_time1_
- base::TimeDelta::FromSeconds(10);
333 blob_data_
->AppendFile(temp_file1_
, 0, 3, old_time
);
334 TestErrorRequest(404);
337 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileRequest
) {
338 blob_data_
->AppendFile(temp_file1_
, 2, 4, temp_file_modification_time1_
);
339 std::string
result(kTestFileData1
+ 2, 4);
340 TestSuccessRequest(result
);
343 TEST_F(BlobURLRequestJobTest
, TestGetSimpleFileSystemFileRequest
) {
345 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, -1,
347 TestSuccessRequest(kTestFileSystemFileData1
);
350 TEST_F(BlobURLRequestJobTest
, TestGetLargeFileSystemFileRequest
) {
352 std::string large_data
;
353 large_data
.reserve(kBufferSize
* 5);
354 for (int i
= 0; i
< kBufferSize
* 5; ++i
)
355 large_data
.append(1, static_cast<char>(i
% 256));
357 const char kFilename
[] = "LargeBlob.dat";
358 WriteFileSystemFile(kFilename
, large_data
.data(), large_data
.size(), NULL
);
360 blob_data_
->AppendFileSystemFile(GetFileSystemURL(kFilename
),
361 0, -1, base::Time());
362 TestSuccessRequest(large_data
);
365 TEST_F(BlobURLRequestJobTest
, TestGetNonExistentFileSystemFileRequest
) {
367 GURL non_existent_file
= GetFileSystemURL("non-existent.dat");
368 blob_data_
->AppendFileSystemFile(non_existent_file
, 0, -1, base::Time());
369 TestErrorRequest(404);
372 TEST_F(BlobURLRequestJobTest
, TestGetChangedFileSystemFileRequest
) {
374 base::Time old_time
=
375 temp_file_system_file_modification_time1_
-
376 base::TimeDelta::FromSeconds(10);
377 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 0, 3, old_time
);
378 TestErrorRequest(404);
381 TEST_F(BlobURLRequestJobTest
, TestGetSlicedFileSystemFileRequest
) {
383 blob_data_
->AppendFileSystemFile(temp_file_system_file1_
, 2, 4,
384 temp_file_system_file_modification_time1_
);
385 std::string
result(kTestFileSystemFileData1
+ 2, 4);
386 TestSuccessRequest(result
);
389 TEST_F(BlobURLRequestJobTest
, TestGetComplicatedDataAndFileRequest
) {
392 BuildComplicatedData(&result
);
393 TestSuccessRequest(result
);
396 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest1
) {
399 BuildComplicatedData(&result
);
400 net::HttpRequestHeaders extra_headers
;
401 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
402 net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
403 expected_status_code_
= 206;
404 expected_response_
= result
.substr(5, 10 - 5 + 1);
405 TestRequest("GET", extra_headers
);
408 TEST_F(BlobURLRequestJobTest
, TestGetRangeRequest2
) {
411 BuildComplicatedData(&result
);
412 net::HttpRequestHeaders extra_headers
;
413 extra_headers
.SetHeader(net::HttpRequestHeaders::kRange
,
414 net::HttpByteRange::Suffix(10).GetHeaderValue());
415 expected_status_code_
= 206;
416 expected_response_
= result
.substr(result
.length() - 10);
417 TestRequest("GET", extra_headers
);
420 TEST_F(BlobURLRequestJobTest
, TestExtraHeaders
) {
421 blob_data_
->set_content_type(kTestContentType
);
422 blob_data_
->set_content_disposition(kTestContentDisposition
);
423 blob_data_
->AppendData(kTestData1
);
424 expected_status_code_
= 200;
425 expected_response_
= kTestData1
;
426 TestRequest("GET", net::HttpRequestHeaders());
428 std::string content_type
;
429 EXPECT_TRUE(request_
->response_headers()->GetMimeType(&content_type
));
430 EXPECT_EQ(kTestContentType
, content_type
);
432 std::string content_disposition
;
433 EXPECT_TRUE(request_
->response_headers()->EnumerateHeader(
434 &iter
, "Content-Disposition", &content_disposition
));
435 EXPECT_EQ(kTestContentDisposition
, content_disposition
);
438 } // namespace webkit_blob