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.
8 #include "base/basictypes.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "content/public/test/async_file_test_helper.h"
15 #include "content/public/test/test_file_system_context.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/request_priority.h"
18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_job.h"
21 #include "net/url_request/url_request_job_factory.h"
22 #include "net/url_request/url_request_status.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/file_system_quota_util.h"
25 #include "storage/browser/fileapi/file_writer_delegate.h"
26 #include "storage/browser/fileapi/sandbox_file_stream_writer.h"
27 #include "testing/platform_test.h"
30 using content::AsyncFileTestHelper
;
31 using storage::FileSystemURL
;
32 using storage::FileWriterDelegate
;
38 const GURL
kOrigin("http://example.com");
39 const storage::FileSystemType kFileSystemType
= storage::kFileSystemTypeTest
;
41 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
42 const int kDataSize
= arraysize(kData
) - 1;
47 : status_(base::File::FILE_OK
),
49 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
51 base::File::Error
status() const { return status_
; }
52 int64
bytes_written() const { return bytes_written_
; }
53 FileWriterDelegate::WriteProgressStatus
write_status() const {
57 void DidWrite(base::File::Error status
, int64 bytes
,
58 FileWriterDelegate::WriteProgressStatus write_status
) {
59 write_status_
= write_status
;
60 if (status
== base::File::FILE_OK
) {
61 bytes_written_
+= bytes
;
62 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
63 base::MessageLoop::current()->Quit();
65 EXPECT_EQ(base::File::FILE_OK
, status_
);
67 base::MessageLoop::current()->Quit();
72 // For post-operation status.
73 base::File::Error status_
;
75 FileWriterDelegate::WriteProgressStatus write_status_
;
78 class BlobURLRequestJobFactory
;
80 } // namespace (anonymous)
82 class FileWriterDelegateTest
: public PlatformTest
{
84 FileWriterDelegateTest() {}
87 void SetUp() override
;
88 void TearDown() override
;
91 return file_system_context_
->GetQuotaUtil(kFileSystemType
)
92 ->GetOriginUsageOnFileTaskRunner(
93 file_system_context_
.get(), kOrigin
, kFileSystemType
);
96 int64
GetFileSizeOnDisk(const char* test_file_path
) {
97 // There might be in-flight flush/write.
98 base::MessageLoop::current()->PostTask(
99 FROM_HERE
, base::Bind(&base::DoNothing
));
100 base::RunLoop().RunUntilIdle();
102 FileSystemURL url
= GetFileSystemURL(test_file_path
);
103 base::File::Info file_info
;
104 EXPECT_EQ(base::File::FILE_OK
,
105 AsyncFileTestHelper::GetMetadata(
106 file_system_context_
.get(), url
, &file_info
));
107 return file_info
.size
;
110 FileSystemURL
GetFileSystemURL(const char* file_name
) const {
111 return file_system_context_
->CreateCrackedFileSystemURL(
112 kOrigin
, kFileSystemType
, base::FilePath().FromUTF8Unsafe(file_name
));
115 FileWriterDelegate
* CreateWriterDelegate(
116 const char* test_file_path
,
118 int64 allowed_growth
) {
119 storage::SandboxFileStreamWriter
* writer
=
120 new storage::SandboxFileStreamWriter(
121 file_system_context_
.get(),
122 GetFileSystemURL(test_file_path
),
124 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
125 writer
->set_default_quota(allowed_growth
);
126 return new FileWriterDelegate(scoped_ptr
<storage::FileStreamWriter
>(writer
),
127 FileWriterDelegate::FLUSH_ON_COMPLETION
);
130 FileWriterDelegate::DelegateWriteCallback
GetWriteCallback(Result
* result
) {
131 return base::Bind(&Result::DidWrite
, base::Unretained(result
));
134 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
135 // and creates a new FileWriterDelegate for the file.
136 void PrepareForWrite(const char* test_file_path
,
137 const GURL
& blob_url
,
139 int64 allowed_growth
) {
140 file_writer_delegate_
.reset(
141 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
));
142 request_
= empty_context_
.CreateRequest(
143 blob_url
, net::DEFAULT_PRIORITY
, file_writer_delegate_
.get(), NULL
);
146 // This should be alive until the very end of this instance.
147 base::MessageLoopForIO loop_
;
149 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
151 net::URLRequestContext empty_context_
;
152 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
153 scoped_ptr
<net::URLRequest
> request_
;
154 scoped_ptr
<BlobURLRequestJobFactory
> job_factory_
;
156 base::ScopedTempDir dir_
;
158 static const char* content_
;
161 const char* FileWriterDelegateTest::content_
= NULL
;
165 static std::string g_content
;
167 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
169 FileWriterDelegateTestJob(net::URLRequest
* request
,
170 net::NetworkDelegate
* network_delegate
,
171 const std::string
& content
)
172 : net::URLRequestJob(request
, network_delegate
),
174 remaining_bytes_(content
.length()),
178 void Start() override
{
179 base::MessageLoop::current()->PostTask(
181 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
184 bool ReadRawData(net::IOBuffer
* buf
, int buf_size
, int* bytes_read
) override
{
185 if (remaining_bytes_
< buf_size
)
186 buf_size
= static_cast<int>(remaining_bytes_
);
188 for (int i
= 0; i
< buf_size
; ++i
)
189 buf
->data()[i
] = content_
[cursor_
++];
190 remaining_bytes_
-= buf_size
;
192 SetStatus(net::URLRequestStatus());
193 *bytes_read
= buf_size
;
197 int GetResponseCode() const override
{ return 200; }
200 ~FileWriterDelegateTestJob() override
{}
203 std::string content_
;
204 int remaining_bytes_
;
208 class BlobURLRequestJobFactory
: public net::URLRequestJobFactory
{
210 explicit BlobURLRequestJobFactory(const char** content_data
)
211 : content_data_(content_data
) {
214 net::URLRequestJob
* MaybeCreateJobWithProtocolHandler(
215 const std::string
& scheme
,
216 net::URLRequest
* request
,
217 net::NetworkDelegate
* network_delegate
) const override
{
218 return new FileWriterDelegateTestJob(
219 request
, network_delegate
, *content_data_
);
222 net::URLRequestJob
* MaybeInterceptRedirect(
223 net::URLRequest
* request
,
224 net::NetworkDelegate
* network_delegate
,
225 const GURL
& location
) const override
{
229 net::URLRequestJob
* MaybeInterceptResponse(
230 net::URLRequest
* request
,
231 net::NetworkDelegate
* network_delegate
) const override
{
235 bool IsHandledProtocol(const std::string
& scheme
) const override
{
236 return scheme
== "blob";
239 bool IsHandledURL(const GURL
& url
) const override
{
240 return url
.SchemeIs("blob");
243 bool IsSafeRedirectTarget(const GURL
& location
) const override
{
248 const char** content_data_
;
250 DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory
);
253 } // namespace (anonymous)
255 void FileWriterDelegateTest::SetUp() {
256 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
258 file_system_context_
= CreateFileSystemContextForTesting(
260 ASSERT_EQ(base::File::FILE_OK
,
261 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
262 GetFileSystemURL("test")));
263 job_factory_
.reset(new BlobURLRequestJobFactory(&content_
));
264 empty_context_
.set_job_factory(job_factory_
.get());
267 void FileWriterDelegateTest::TearDown() {
268 file_system_context_
= NULL
;
269 base::RunLoop().RunUntilIdle();
272 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
273 const GURL
kBlobURL("blob:nolimit");
276 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
279 ASSERT_EQ(0, usage());
280 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
281 base::MessageLoop::current()->Run();
283 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
284 file_writer_delegate_
.reset();
286 ASSERT_EQ(kDataSize
, usage());
287 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
288 EXPECT_EQ(kDataSize
, result
.bytes_written());
289 EXPECT_EQ(base::File::FILE_OK
, result
.status());
292 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
293 const GURL
kBlobURL("blob:just");
295 const int64 kAllowedGrowth
= kDataSize
;
296 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
299 ASSERT_EQ(0, usage());
300 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
301 base::MessageLoop::current()->Run();
302 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
303 file_writer_delegate_
.reset();
305 ASSERT_EQ(kAllowedGrowth
, usage());
306 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
308 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
309 EXPECT_EQ(base::File::FILE_OK
, result
.status());
312 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
313 const GURL
kBlobURL("blob:failure");
315 const int64 kAllowedGrowth
= kDataSize
- 1;
316 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
319 ASSERT_EQ(0, usage());
320 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
321 base::MessageLoop::current()->Run();
322 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
323 file_writer_delegate_
.reset();
325 ASSERT_EQ(kAllowedGrowth
, usage());
326 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
328 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
329 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
330 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
333 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
334 const GURL
kBlobURL("blob:zero");
336 int64 kAllowedGrowth
= 0;
337 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
340 ASSERT_EQ(0, usage());
341 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
342 base::MessageLoop::current()->Run();
343 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
344 file_writer_delegate_
.reset();
346 ASSERT_EQ(kAllowedGrowth
, usage());
347 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
349 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
350 EXPECT_EQ(base::File::FILE_OK
, result
.status());
351 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
354 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
355 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
356 scoped_ptr
<net::URLRequest
> request2
;
358 ASSERT_EQ(base::File::FILE_OK
,
359 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
360 GetFileSystemURL("test2")));
362 const GURL
kBlobURL("blob:nolimitconcurrent");
363 const GURL
kBlobURL2("blob:nolimitconcurrent2");
366 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
368 // Credate another FileWriterDelegate for concurrent write.
369 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
370 request2
= empty_context_
.CreateRequest(
371 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get(), NULL
);
373 Result result
, result2
;
374 ASSERT_EQ(0, usage());
375 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
376 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
377 base::MessageLoop::current()->Run();
378 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
379 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
380 base::MessageLoop::current()->Run();
382 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
383 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
384 file_writer_delegate_
.reset();
385 file_writer_delegate2
.reset();
387 ASSERT_EQ(kDataSize
* 2, usage());
388 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
390 EXPECT_EQ(kDataSize
, result
.bytes_written());
391 EXPECT_EQ(base::File::FILE_OK
, result
.status());
392 EXPECT_EQ(kDataSize
, result2
.bytes_written());
393 EXPECT_EQ(base::File::FILE_OK
, result2
.status());
396 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
397 const GURL
kBlobURL("blob:failure-with-updated-quota");
400 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
402 int64 allowed_growth
= 100;
403 ASSERT_LT(kDataSize
, allowed_growth
);
404 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
408 ASSERT_EQ(0, usage());
409 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
410 base::MessageLoop::current()->Run();
411 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
412 file_writer_delegate_
.reset();
414 ASSERT_EQ(kDataSize
, usage());
415 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
416 EXPECT_EQ(kDataSize
, result
.bytes_written());
417 EXPECT_EQ(base::File::FILE_OK
, result
.status());
420 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
423 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
427 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
428 base::MessageLoop::current()->Run();
429 EXPECT_EQ(kDataSize
, usage());
430 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
431 EXPECT_EQ(kDataSize
, result
.bytes_written());
432 EXPECT_EQ(base::File::FILE_OK
, result
.status());
433 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
436 // Trying to write kDataSize bytes data from offset 25 while
437 // allowed_growth is 55.
440 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
444 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
445 base::MessageLoop::current()->Run();
446 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
447 file_writer_delegate_
.reset();
449 EXPECT_EQ(offset
+ kDataSize
, usage());
450 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
451 EXPECT_EQ(kDataSize
, result
.bytes_written());
452 EXPECT_EQ(base::File::FILE_OK
, result
.status());
455 // Trying to overwrite 45 bytes data while allowed_growth is -20.
457 allowed_growth
= -20;
458 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
459 int64 pre_write_usage
= GetFileSizeOnDisk("test");
463 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
464 base::MessageLoop::current()->Run();
465 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
466 file_writer_delegate_
.reset();
468 EXPECT_EQ(pre_write_usage
, usage());
469 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
470 EXPECT_EQ(kDataSize
, result
.bytes_written());
471 EXPECT_EQ(base::File::FILE_OK
, result
.status());
474 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
475 // while allowed_growth is 10.
476 const int kOverlap
= 20;
477 offset
= pre_write_usage
- kOverlap
;
479 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
483 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
484 base::MessageLoop::current()->Run();
485 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
486 file_writer_delegate_
.reset();
488 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
489 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
490 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
491 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
495 } // namespace content