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_UNSAFE(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 virtual void SetUp() OVERRIDE
;
88 virtual 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 virtual void Start() OVERRIDE
{
179 base::MessageLoop::current()->PostTask(
181 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
184 virtual bool ReadRawData(net::IOBuffer
* buf
,
186 int *bytes_read
) OVERRIDE
{
187 if (remaining_bytes_
< buf_size
)
188 buf_size
= static_cast<int>(remaining_bytes_
);
190 for (int i
= 0; i
< buf_size
; ++i
)
191 buf
->data()[i
] = content_
[cursor_
++];
192 remaining_bytes_
-= buf_size
;
194 SetStatus(net::URLRequestStatus());
195 *bytes_read
= buf_size
;
199 virtual int GetResponseCode() const OVERRIDE
{
204 virtual ~FileWriterDelegateTestJob() {}
207 std::string content_
;
208 int remaining_bytes_
;
212 class BlobURLRequestJobFactory
: public net::URLRequestJobFactory
{
214 explicit BlobURLRequestJobFactory(const char** content_data
)
215 : content_data_(content_data
) {
218 virtual net::URLRequestJob
* MaybeCreateJobWithProtocolHandler(
219 const std::string
& scheme
,
220 net::URLRequest
* request
,
221 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
222 return new FileWriterDelegateTestJob(
223 request
, network_delegate
, *content_data_
);
226 virtual bool IsHandledProtocol(const std::string
& scheme
) const OVERRIDE
{
227 return scheme
== "blob";
230 virtual bool IsHandledURL(const GURL
& url
) const OVERRIDE
{
231 return url
.SchemeIs("blob");
234 virtual bool IsSafeRedirectTarget(const GURL
& location
) const OVERRIDE
{
239 const char** content_data_
;
241 DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory
);
244 } // namespace (anonymous)
246 void FileWriterDelegateTest::SetUp() {
247 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
249 file_system_context_
= CreateFileSystemContextForTesting(
251 ASSERT_EQ(base::File::FILE_OK
,
252 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
253 GetFileSystemURL("test")));
254 job_factory_
.reset(new BlobURLRequestJobFactory(&content_
));
255 empty_context_
.set_job_factory(job_factory_
.get());
258 void FileWriterDelegateTest::TearDown() {
259 file_system_context_
= NULL
;
260 base::RunLoop().RunUntilIdle();
263 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
264 const GURL
kBlobURL("blob:nolimit");
267 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
270 ASSERT_EQ(0, usage());
271 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
272 base::MessageLoop::current()->Run();
274 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
275 file_writer_delegate_
.reset();
277 ASSERT_EQ(kDataSize
, usage());
278 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
279 EXPECT_EQ(kDataSize
, result
.bytes_written());
280 EXPECT_EQ(base::File::FILE_OK
, result
.status());
283 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
284 const GURL
kBlobURL("blob:just");
286 const int64 kAllowedGrowth
= kDataSize
;
287 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
290 ASSERT_EQ(0, usage());
291 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
292 base::MessageLoop::current()->Run();
293 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
294 file_writer_delegate_
.reset();
296 ASSERT_EQ(kAllowedGrowth
, usage());
297 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
299 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
300 EXPECT_EQ(base::File::FILE_OK
, result
.status());
303 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
304 const GURL
kBlobURL("blob:failure");
306 const int64 kAllowedGrowth
= kDataSize
- 1;
307 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
310 ASSERT_EQ(0, usage());
311 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
312 base::MessageLoop::current()->Run();
313 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
314 file_writer_delegate_
.reset();
316 ASSERT_EQ(kAllowedGrowth
, usage());
317 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
319 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
320 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
321 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
324 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
325 const GURL
kBlobURL("blob:zero");
327 int64 kAllowedGrowth
= 0;
328 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
331 ASSERT_EQ(0, usage());
332 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
333 base::MessageLoop::current()->Run();
334 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
335 file_writer_delegate_
.reset();
337 ASSERT_EQ(kAllowedGrowth
, usage());
338 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
340 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
341 EXPECT_EQ(base::File::FILE_OK
, result
.status());
342 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
345 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
346 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
347 scoped_ptr
<net::URLRequest
> request2
;
349 ASSERT_EQ(base::File::FILE_OK
,
350 AsyncFileTestHelper::CreateFile(file_system_context_
.get(),
351 GetFileSystemURL("test2")));
353 const GURL
kBlobURL("blob:nolimitconcurrent");
354 const GURL
kBlobURL2("blob:nolimitconcurrent2");
357 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
359 // Credate another FileWriterDelegate for concurrent write.
360 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
361 request2
= empty_context_
.CreateRequest(
362 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get(), NULL
);
364 Result result
, result2
;
365 ASSERT_EQ(0, usage());
366 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
367 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
368 base::MessageLoop::current()->Run();
369 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
370 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
371 base::MessageLoop::current()->Run();
373 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
374 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
375 file_writer_delegate_
.reset();
376 file_writer_delegate2
.reset();
378 ASSERT_EQ(kDataSize
* 2, usage());
379 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
381 EXPECT_EQ(kDataSize
, result
.bytes_written());
382 EXPECT_EQ(base::File::FILE_OK
, result
.status());
383 EXPECT_EQ(kDataSize
, result2
.bytes_written());
384 EXPECT_EQ(base::File::FILE_OK
, result2
.status());
387 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
388 const GURL
kBlobURL("blob:failure-with-updated-quota");
391 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
393 int64 allowed_growth
= 100;
394 ASSERT_LT(kDataSize
, allowed_growth
);
395 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
399 ASSERT_EQ(0, usage());
400 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
401 base::MessageLoop::current()->Run();
402 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
403 file_writer_delegate_
.reset();
405 ASSERT_EQ(kDataSize
, usage());
406 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
407 EXPECT_EQ(kDataSize
, result
.bytes_written());
408 EXPECT_EQ(base::File::FILE_OK
, result
.status());
411 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
414 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
418 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
419 base::MessageLoop::current()->Run();
420 EXPECT_EQ(kDataSize
, usage());
421 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
422 EXPECT_EQ(kDataSize
, result
.bytes_written());
423 EXPECT_EQ(base::File::FILE_OK
, result
.status());
424 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
427 // Trying to write kDataSize bytes data from offset 25 while
428 // allowed_growth is 55.
431 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
435 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
436 base::MessageLoop::current()->Run();
437 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
438 file_writer_delegate_
.reset();
440 EXPECT_EQ(offset
+ kDataSize
, usage());
441 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
442 EXPECT_EQ(kDataSize
, result
.bytes_written());
443 EXPECT_EQ(base::File::FILE_OK
, result
.status());
446 // Trying to overwrite 45 bytes data while allowed_growth is -20.
448 allowed_growth
= -20;
449 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
450 int64 pre_write_usage
= GetFileSizeOnDisk("test");
454 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
455 base::MessageLoop::current()->Run();
456 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
457 file_writer_delegate_
.reset();
459 EXPECT_EQ(pre_write_usage
, usage());
460 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
461 EXPECT_EQ(kDataSize
, result
.bytes_written());
462 EXPECT_EQ(base::File::FILE_OK
, result
.status());
465 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
466 // while allowed_growth is 10.
467 const int kOverlap
= 20;
468 offset
= pre_write_usage
- kOverlap
;
470 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
474 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
475 base::MessageLoop::current()->Run();
476 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
477 file_writer_delegate_
.reset();
479 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
480 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
481 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
482 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
486 } // namespace content