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 "storage/common/fileapi/file_system_mount_option.h"
28 #include "testing/platform_test.h"
31 using content::AsyncFileTestHelper
;
32 using storage::FileSystemURL
;
33 using storage::FileWriterDelegate
;
39 const GURL
kOrigin("http://example.com");
40 const storage::FileSystemType kFileSystemType
= storage::kFileSystemTypeTest
;
42 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
43 const int kDataSize
= arraysize(kData
) - 1;
48 : status_(base::File::FILE_OK
),
50 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
52 base::File::Error
status() const { return status_
; }
53 int64
bytes_written() const { return bytes_written_
; }
54 FileWriterDelegate::WriteProgressStatus
write_status() const {
58 void DidWrite(base::File::Error status
, int64 bytes
,
59 FileWriterDelegate::WriteProgressStatus write_status
) {
60 write_status_
= write_status
;
61 if (status
== base::File::FILE_OK
) {
62 bytes_written_
+= bytes
;
63 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
64 base::MessageLoop::current()->Quit();
66 EXPECT_EQ(base::File::FILE_OK
, status_
);
68 base::MessageLoop::current()->Quit();
73 // For post-operation status.
74 base::File::Error status_
;
76 FileWriterDelegate::WriteProgressStatus write_status_
;
79 class BlobURLRequestJobFactory
;
81 } // namespace (anonymous)
83 class FileWriterDelegateTest
: public PlatformTest
{
85 FileWriterDelegateTest() {}
88 void SetUp() override
;
89 void TearDown() override
;
92 return file_system_context_
->GetQuotaUtil(kFileSystemType
)
93 ->GetOriginUsageOnFileTaskRunner(
94 file_system_context_
.get(), kOrigin
, kFileSystemType
);
97 int64
GetFileSizeOnDisk(const char* test_file_path
) {
98 // There might be in-flight flush/write.
99 base::MessageLoop::current()->PostTask(
100 FROM_HERE
, base::Bind(&base::DoNothing
));
101 base::RunLoop().RunUntilIdle();
103 FileSystemURL url
= GetFileSystemURL(test_file_path
);
104 base::File::Info file_info
;
105 EXPECT_EQ(base::File::FILE_OK
,
106 AsyncFileTestHelper::GetMetadata(
107 file_system_context_
.get(), url
, &file_info
));
108 return file_info
.size
;
111 FileSystemURL
GetFileSystemURL(const char* file_name
) const {
112 return file_system_context_
->CreateCrackedFileSystemURL(
113 kOrigin
, kFileSystemType
, base::FilePath().FromUTF8Unsafe(file_name
));
116 FileWriterDelegate
* CreateWriterDelegate(
117 const char* test_file_path
,
119 int64 allowed_growth
) {
120 storage::SandboxFileStreamWriter
* writer
=
121 new storage::SandboxFileStreamWriter(
122 file_system_context_
.get(),
123 GetFileSystemURL(test_file_path
),
125 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
126 writer
->set_default_quota(allowed_growth
);
127 return new FileWriterDelegate(scoped_ptr
<storage::FileStreamWriter
>(writer
),
128 storage::FlushPolicy::FLUSH_ON_COMPLETION
);
131 FileWriterDelegate::DelegateWriteCallback
GetWriteCallback(Result
* result
) {
132 return base::Bind(&Result::DidWrite
, base::Unretained(result
));
135 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
136 // and creates a new FileWriterDelegate for the file.
137 void PrepareForWrite(const char* test_file_path
,
138 const GURL
& blob_url
,
140 int64 allowed_growth
) {
141 file_writer_delegate_
.reset(
142 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
));
143 request_
= empty_context_
.CreateRequest(
144 blob_url
, net::DEFAULT_PRIORITY
, file_writer_delegate_
.get(), NULL
);
147 // This should be alive until the very end of this instance.
148 base::MessageLoopForIO loop_
;
150 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
152 net::URLRequestContext empty_context_
;
153 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
154 scoped_ptr
<net::URLRequest
> request_
;
155 scoped_ptr
<BlobURLRequestJobFactory
> job_factory_
;
157 base::ScopedTempDir dir_
;
159 static const char* content_
;
162 const char* FileWriterDelegateTest::content_
= NULL
;
166 static std::string g_content
;
168 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
170 FileWriterDelegateTestJob(net::URLRequest
* request
,
171 net::NetworkDelegate
* network_delegate
,
172 const std::string
& content
)
173 : net::URLRequestJob(request
, network_delegate
),
175 remaining_bytes_(content
.length()),
179 void Start() override
{
180 base::MessageLoop::current()->PostTask(
182 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
185 bool ReadRawData(net::IOBuffer
* buf
, int buf_size
, int* bytes_read
) override
{
186 if (remaining_bytes_
< buf_size
)
187 buf_size
= static_cast<int>(remaining_bytes_
);
189 for (int i
= 0; i
< buf_size
; ++i
)
190 buf
->data()[i
] = content_
[cursor_
++];
191 remaining_bytes_
-= buf_size
;
193 SetStatus(net::URLRequestStatus());
194 *bytes_read
= buf_size
;
198 int GetResponseCode() const override
{ return 200; }
201 ~FileWriterDelegateTestJob() override
{}
204 std::string content_
;
205 int remaining_bytes_
;
209 class BlobURLRequestJobFactory
: public net::URLRequestJobFactory
{
211 explicit BlobURLRequestJobFactory(const char** content_data
)
212 : content_data_(content_data
) {
215 net::URLRequestJob
* MaybeCreateJobWithProtocolHandler(
216 const std::string
& scheme
,
217 net::URLRequest
* request
,
218 net::NetworkDelegate
* network_delegate
) const override
{
219 return new FileWriterDelegateTestJob(
220 request
, network_delegate
, *content_data_
);
223 net::URLRequestJob
* MaybeInterceptRedirect(
224 net::URLRequest
* request
,
225 net::NetworkDelegate
* network_delegate
,
226 const GURL
& location
) const override
{
230 net::URLRequestJob
* MaybeInterceptResponse(
231 net::URLRequest
* request
,
232 net::NetworkDelegate
* network_delegate
) const override
{
236 bool IsHandledProtocol(const std::string
& scheme
) const override
{
237 return scheme
== "blob";
240 bool IsHandledURL(const GURL
& url
) const override
{
241 return url
.SchemeIs("blob");
244 bool IsSafeRedirectTarget(const GURL
& location
) const override
{
249 const char** content_data_
;
251 DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory
);
254 } // namespace (anonymous)
256 void FileWriterDelegateTest::SetUp() {
257 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
259 file_system_context_
= CreateFileSystemContextForTesting(
261 ASSERT_EQ(base::File::FILE_OK
,
262 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
263 GetFileSystemURL("test")));
264 job_factory_
.reset(new BlobURLRequestJobFactory(&content_
));
265 empty_context_
.set_job_factory(job_factory_
.get());
268 void FileWriterDelegateTest::TearDown() {
269 file_system_context_
= NULL
;
270 base::RunLoop().RunUntilIdle();
273 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
274 const GURL
kBlobURL("blob:nolimit");
277 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
280 ASSERT_EQ(0, usage());
281 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
282 base::MessageLoop::current()->Run();
284 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
285 file_writer_delegate_
.reset();
287 ASSERT_EQ(kDataSize
, usage());
288 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
289 EXPECT_EQ(kDataSize
, result
.bytes_written());
290 EXPECT_EQ(base::File::FILE_OK
, result
.status());
293 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
294 const GURL
kBlobURL("blob:just");
296 const int64 kAllowedGrowth
= kDataSize
;
297 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
300 ASSERT_EQ(0, usage());
301 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
302 base::MessageLoop::current()->Run();
303 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
304 file_writer_delegate_
.reset();
306 ASSERT_EQ(kAllowedGrowth
, usage());
307 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
309 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
310 EXPECT_EQ(base::File::FILE_OK
, result
.status());
313 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
314 const GURL
kBlobURL("blob:failure");
316 const int64 kAllowedGrowth
= kDataSize
- 1;
317 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
320 ASSERT_EQ(0, usage());
321 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
322 base::MessageLoop::current()->Run();
323 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
324 file_writer_delegate_
.reset();
326 ASSERT_EQ(kAllowedGrowth
, usage());
327 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
329 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
330 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
331 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
334 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
335 const GURL
kBlobURL("blob:zero");
337 int64 kAllowedGrowth
= 0;
338 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
341 ASSERT_EQ(0, usage());
342 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
343 base::MessageLoop::current()->Run();
344 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
345 file_writer_delegate_
.reset();
347 ASSERT_EQ(kAllowedGrowth
, usage());
348 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
350 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
351 EXPECT_EQ(base::File::FILE_OK
, result
.status());
352 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
355 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
356 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
357 scoped_ptr
<net::URLRequest
> request2
;
359 ASSERT_EQ(base::File::FILE_OK
,
360 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
361 GetFileSystemURL("test2")));
363 const GURL
kBlobURL("blob:nolimitconcurrent");
364 const GURL
kBlobURL2("blob:nolimitconcurrent2");
367 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
369 // Credate another FileWriterDelegate for concurrent write.
370 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
371 request2
= empty_context_
.CreateRequest(
372 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get(), NULL
);
374 Result result
, result2
;
375 ASSERT_EQ(0, usage());
376 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
377 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
378 base::MessageLoop::current()->Run();
379 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
380 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
381 base::MessageLoop::current()->Run();
383 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
384 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
385 file_writer_delegate_
.reset();
386 file_writer_delegate2
.reset();
388 ASSERT_EQ(kDataSize
* 2, usage());
389 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
391 EXPECT_EQ(kDataSize
, result
.bytes_written());
392 EXPECT_EQ(base::File::FILE_OK
, result
.status());
393 EXPECT_EQ(kDataSize
, result2
.bytes_written());
394 EXPECT_EQ(base::File::FILE_OK
, result2
.status());
397 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
398 const GURL
kBlobURL("blob:failure-with-updated-quota");
401 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
403 int64 allowed_growth
= 100;
404 ASSERT_LT(kDataSize
, allowed_growth
);
405 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
409 ASSERT_EQ(0, usage());
410 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
411 base::MessageLoop::current()->Run();
412 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
413 file_writer_delegate_
.reset();
415 ASSERT_EQ(kDataSize
, usage());
416 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
417 EXPECT_EQ(kDataSize
, result
.bytes_written());
418 EXPECT_EQ(base::File::FILE_OK
, result
.status());
421 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
424 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
428 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
429 base::MessageLoop::current()->Run();
430 EXPECT_EQ(kDataSize
, usage());
431 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
432 EXPECT_EQ(kDataSize
, result
.bytes_written());
433 EXPECT_EQ(base::File::FILE_OK
, result
.status());
434 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
437 // Trying to write kDataSize bytes data from offset 25 while
438 // allowed_growth is 55.
441 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
445 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
446 base::MessageLoop::current()->Run();
447 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
448 file_writer_delegate_
.reset();
450 EXPECT_EQ(offset
+ kDataSize
, usage());
451 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
452 EXPECT_EQ(kDataSize
, result
.bytes_written());
453 EXPECT_EQ(base::File::FILE_OK
, result
.status());
456 // Trying to overwrite 45 bytes data while allowed_growth is -20.
458 allowed_growth
= -20;
459 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
460 int64 pre_write_usage
= GetFileSizeOnDisk("test");
464 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
465 base::MessageLoop::current()->Run();
466 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
467 file_writer_delegate_
.reset();
469 EXPECT_EQ(pre_write_usage
, usage());
470 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
471 EXPECT_EQ(kDataSize
, result
.bytes_written());
472 EXPECT_EQ(base::File::FILE_OK
, result
.status());
475 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
476 // while allowed_growth is 10.
477 const int kOverlap
= 20;
478 offset
= pre_write_usage
- kOverlap
;
480 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
484 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
485 base::MessageLoop::current()->Run();
486 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
487 file_writer_delegate_
.reset();
489 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
490 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
491 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
492 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
496 } // namespace content