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/location.h"
13 #include "base/run_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "content/public/test/async_file_test_helper.h"
17 #include "content/public/test/test_file_system_context.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/request_priority.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_context.h"
22 #include "net/url_request/url_request_job.h"
23 #include "net/url_request/url_request_job_factory.h"
24 #include "net/url_request/url_request_status.h"
25 #include "storage/browser/fileapi/file_system_context.h"
26 #include "storage/browser/fileapi/file_system_quota_util.h"
27 #include "storage/browser/fileapi/file_writer_delegate.h"
28 #include "storage/browser/fileapi/sandbox_file_stream_writer.h"
29 #include "storage/common/fileapi/file_system_mount_option.h"
30 #include "testing/platform_test.h"
33 using content::AsyncFileTestHelper
;
34 using storage::FileSystemURL
;
35 using storage::FileWriterDelegate
;
41 const GURL
kOrigin("http://example.com");
42 const storage::FileSystemType kFileSystemType
= storage::kFileSystemTypeTest
;
44 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
45 const int kDataSize
= arraysize(kData
) - 1;
50 : status_(base::File::FILE_OK
),
52 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
54 base::File::Error
status() const { return status_
; }
55 int64
bytes_written() const { return bytes_written_
; }
56 FileWriterDelegate::WriteProgressStatus
write_status() const {
60 void DidWrite(base::File::Error status
, int64 bytes
,
61 FileWriterDelegate::WriteProgressStatus write_status
) {
62 write_status_
= write_status
;
63 if (status
== base::File::FILE_OK
) {
64 bytes_written_
+= bytes
;
65 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
66 base::MessageLoop::current()->Quit();
68 EXPECT_EQ(base::File::FILE_OK
, status_
);
70 base::MessageLoop::current()->Quit();
75 // For post-operation status.
76 base::File::Error status_
;
78 FileWriterDelegate::WriteProgressStatus write_status_
;
81 class BlobURLRequestJobFactory
;
83 } // namespace (anonymous)
85 class FileWriterDelegateTest
: public PlatformTest
{
87 FileWriterDelegateTest() {}
90 void SetUp() override
;
91 void TearDown() override
;
94 return file_system_context_
->GetQuotaUtil(kFileSystemType
)
95 ->GetOriginUsageOnFileTaskRunner(
96 file_system_context_
.get(), kOrigin
, kFileSystemType
);
99 int64
GetFileSizeOnDisk(const char* test_file_path
) {
100 // There might be in-flight flush/write.
101 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
102 base::Bind(&base::DoNothing
));
103 base::RunLoop().RunUntilIdle();
105 FileSystemURL url
= GetFileSystemURL(test_file_path
);
106 base::File::Info file_info
;
107 EXPECT_EQ(base::File::FILE_OK
,
108 AsyncFileTestHelper::GetMetadata(
109 file_system_context_
.get(), url
, &file_info
));
110 return file_info
.size
;
113 FileSystemURL
GetFileSystemURL(const char* file_name
) const {
114 return file_system_context_
->CreateCrackedFileSystemURL(
115 kOrigin
, kFileSystemType
, base::FilePath().FromUTF8Unsafe(file_name
));
118 FileWriterDelegate
* CreateWriterDelegate(
119 const char* test_file_path
,
121 int64 allowed_growth
) {
122 storage::SandboxFileStreamWriter
* writer
=
123 new storage::SandboxFileStreamWriter(
124 file_system_context_
.get(),
125 GetFileSystemURL(test_file_path
),
127 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
128 writer
->set_default_quota(allowed_growth
);
129 return new FileWriterDelegate(scoped_ptr
<storage::FileStreamWriter
>(writer
),
130 storage::FlushPolicy::FLUSH_ON_COMPLETION
);
133 FileWriterDelegate::DelegateWriteCallback
GetWriteCallback(Result
* result
) {
134 return base::Bind(&Result::DidWrite
, base::Unretained(result
));
137 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
138 // and creates a new FileWriterDelegate for the file.
139 void PrepareForWrite(const char* test_file_path
,
140 const GURL
& blob_url
,
142 int64 allowed_growth
) {
143 file_writer_delegate_
.reset(
144 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
));
145 request_
= empty_context_
.CreateRequest(
146 blob_url
, net::DEFAULT_PRIORITY
, file_writer_delegate_
.get());
149 // This should be alive until the very end of this instance.
150 base::MessageLoopForIO loop_
;
152 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
154 net::URLRequestContext empty_context_
;
155 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
156 scoped_ptr
<net::URLRequest
> request_
;
157 scoped_ptr
<BlobURLRequestJobFactory
> job_factory_
;
159 base::ScopedTempDir dir_
;
161 static const char* content_
;
164 const char* FileWriterDelegateTest::content_
= NULL
;
168 static std::string g_content
;
170 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
172 FileWriterDelegateTestJob(net::URLRequest
* request
,
173 net::NetworkDelegate
* network_delegate
,
174 const std::string
& content
)
175 : net::URLRequestJob(request
, network_delegate
),
177 remaining_bytes_(content
.length()),
181 void Start() override
{
182 base::ThreadTaskRunnerHandle::Get()->PostTask(
184 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
187 bool ReadRawData(net::IOBuffer
* buf
, int buf_size
, int* bytes_read
) override
{
188 if (remaining_bytes_
< buf_size
)
189 buf_size
= static_cast<int>(remaining_bytes_
);
191 for (int i
= 0; i
< buf_size
; ++i
)
192 buf
->data()[i
] = content_
[cursor_
++];
193 remaining_bytes_
-= buf_size
;
195 SetStatus(net::URLRequestStatus());
196 *bytes_read
= buf_size
;
200 int GetResponseCode() const override
{ return 200; }
203 ~FileWriterDelegateTestJob() override
{}
206 std::string content_
;
207 int remaining_bytes_
;
211 class BlobURLRequestJobFactory
: public net::URLRequestJobFactory
{
213 explicit BlobURLRequestJobFactory(const char** content_data
)
214 : content_data_(content_data
) {
217 net::URLRequestJob
* MaybeCreateJobWithProtocolHandler(
218 const std::string
& scheme
,
219 net::URLRequest
* request
,
220 net::NetworkDelegate
* network_delegate
) const override
{
221 return new FileWriterDelegateTestJob(
222 request
, network_delegate
, *content_data_
);
225 net::URLRequestJob
* MaybeInterceptRedirect(
226 net::URLRequest
* request
,
227 net::NetworkDelegate
* network_delegate
,
228 const GURL
& location
) const override
{
232 net::URLRequestJob
* MaybeInterceptResponse(
233 net::URLRequest
* request
,
234 net::NetworkDelegate
* network_delegate
) const override
{
238 bool IsHandledProtocol(const std::string
& scheme
) const override
{
239 return scheme
== "blob";
242 bool IsHandledURL(const GURL
& url
) const override
{
243 return url
.SchemeIs("blob");
246 bool IsSafeRedirectTarget(const GURL
& location
) const override
{
251 const char** content_data_
;
253 DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory
);
256 } // namespace (anonymous)
258 void FileWriterDelegateTest::SetUp() {
259 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
261 file_system_context_
= CreateFileSystemContextForTesting(
263 ASSERT_EQ(base::File::FILE_OK
,
264 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
265 GetFileSystemURL("test")));
266 job_factory_
.reset(new BlobURLRequestJobFactory(&content_
));
267 empty_context_
.set_job_factory(job_factory_
.get());
270 void FileWriterDelegateTest::TearDown() {
271 file_system_context_
= NULL
;
272 base::RunLoop().RunUntilIdle();
275 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
276 const GURL
kBlobURL("blob:nolimit");
279 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
282 ASSERT_EQ(0, usage());
283 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
284 base::MessageLoop::current()->Run();
286 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
287 file_writer_delegate_
.reset();
289 ASSERT_EQ(kDataSize
, usage());
290 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
291 EXPECT_EQ(kDataSize
, result
.bytes_written());
292 EXPECT_EQ(base::File::FILE_OK
, result
.status());
295 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
296 const GURL
kBlobURL("blob:just");
298 const int64 kAllowedGrowth
= kDataSize
;
299 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
302 ASSERT_EQ(0, usage());
303 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
304 base::MessageLoop::current()->Run();
305 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
306 file_writer_delegate_
.reset();
308 ASSERT_EQ(kAllowedGrowth
, usage());
309 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
311 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
312 EXPECT_EQ(base::File::FILE_OK
, result
.status());
315 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
316 const GURL
kBlobURL("blob:failure");
318 const int64 kAllowedGrowth
= kDataSize
- 1;
319 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
322 ASSERT_EQ(0, usage());
323 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
324 base::MessageLoop::current()->Run();
325 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
326 file_writer_delegate_
.reset();
328 ASSERT_EQ(kAllowedGrowth
, usage());
329 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
331 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
332 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
333 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
336 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
337 const GURL
kBlobURL("blob:zero");
339 int64 kAllowedGrowth
= 0;
340 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
343 ASSERT_EQ(0, usage());
344 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
345 base::MessageLoop::current()->Run();
346 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
347 file_writer_delegate_
.reset();
349 ASSERT_EQ(kAllowedGrowth
, usage());
350 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
352 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
353 EXPECT_EQ(base::File::FILE_OK
, result
.status());
354 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
357 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
358 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
359 scoped_ptr
<net::URLRequest
> request2
;
361 ASSERT_EQ(base::File::FILE_OK
,
362 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
363 GetFileSystemURL("test2")));
365 const GURL
kBlobURL("blob:nolimitconcurrent");
366 const GURL
kBlobURL2("blob:nolimitconcurrent2");
369 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
371 // Credate another FileWriterDelegate for concurrent write.
372 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
373 request2
= empty_context_
.CreateRequest(
374 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get());
376 Result result
, result2
;
377 ASSERT_EQ(0, usage());
378 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
379 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
380 base::MessageLoop::current()->Run();
381 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
382 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
383 base::MessageLoop::current()->Run();
385 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
386 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
387 file_writer_delegate_
.reset();
388 file_writer_delegate2
.reset();
390 ASSERT_EQ(kDataSize
* 2, usage());
391 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
393 EXPECT_EQ(kDataSize
, result
.bytes_written());
394 EXPECT_EQ(base::File::FILE_OK
, result
.status());
395 EXPECT_EQ(kDataSize
, result2
.bytes_written());
396 EXPECT_EQ(base::File::FILE_OK
, result2
.status());
399 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
400 const GURL
kBlobURL("blob:failure-with-updated-quota");
403 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
405 int64 allowed_growth
= 100;
406 ASSERT_LT(kDataSize
, allowed_growth
);
407 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
411 ASSERT_EQ(0, usage());
412 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
413 base::MessageLoop::current()->Run();
414 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
415 file_writer_delegate_
.reset();
417 ASSERT_EQ(kDataSize
, usage());
418 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
419 EXPECT_EQ(kDataSize
, result
.bytes_written());
420 EXPECT_EQ(base::File::FILE_OK
, result
.status());
423 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
426 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
430 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
431 base::MessageLoop::current()->Run();
432 EXPECT_EQ(kDataSize
, usage());
433 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
434 EXPECT_EQ(kDataSize
, result
.bytes_written());
435 EXPECT_EQ(base::File::FILE_OK
, result
.status());
436 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
439 // Trying to write kDataSize bytes data from offset 25 while
440 // allowed_growth is 55.
443 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
447 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
448 base::MessageLoop::current()->Run();
449 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
450 file_writer_delegate_
.reset();
452 EXPECT_EQ(offset
+ kDataSize
, usage());
453 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
454 EXPECT_EQ(kDataSize
, result
.bytes_written());
455 EXPECT_EQ(base::File::FILE_OK
, result
.status());
458 // Trying to overwrite 45 bytes data while allowed_growth is -20.
460 allowed_growth
= -20;
461 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
462 int64 pre_write_usage
= GetFileSizeOnDisk("test");
466 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
467 base::MessageLoop::current()->Run();
468 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
469 file_writer_delegate_
.reset();
471 EXPECT_EQ(pre_write_usage
, usage());
472 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
473 EXPECT_EQ(kDataSize
, result
.bytes_written());
474 EXPECT_EQ(base::File::FILE_OK
, result
.status());
477 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
478 // while allowed_growth is 10.
479 const int kOverlap
= 20;
480 offset
= pre_write_usage
- kOverlap
;
482 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
486 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
487 base::MessageLoop::current()->Run();
488 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
489 file_writer_delegate_
.reset();
491 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
492 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
493 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
494 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
498 } // namespace content