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_status.h"
22 #include "testing/platform_test.h"
24 #include "webkit/browser/fileapi/file_system_context.h"
25 #include "webkit/browser/fileapi/file_system_quota_util.h"
26 #include "webkit/browser/fileapi/file_writer_delegate.h"
27 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
29 using content::AsyncFileTestHelper
;
30 using fileapi::FileSystemURL
;
31 using fileapi::FileWriterDelegate
;
37 const GURL
kOrigin("http://example.com");
38 const fileapi::FileSystemType kFileSystemType
= fileapi::kFileSystemTypeTest
;
40 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
41 const int kDataSize
= ARRAYSIZE_UNSAFE(kData
) - 1;
46 : status_(base::File::FILE_OK
),
48 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
50 base::File::Error
status() const { return status_
; }
51 int64
bytes_written() const { return bytes_written_
; }
52 FileWriterDelegate::WriteProgressStatus
write_status() const {
56 void DidWrite(base::File::Error status
, int64 bytes
,
57 FileWriterDelegate::WriteProgressStatus write_status
) {
58 write_status_
= write_status
;
59 if (status
== base::File::FILE_OK
) {
60 bytes_written_
+= bytes
;
61 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
62 base::MessageLoop::current()->Quit();
64 EXPECT_EQ(base::File::FILE_OK
, status_
);
66 base::MessageLoop::current()->Quit();
71 // For post-operation status.
72 base::File::Error status_
;
74 FileWriterDelegate::WriteProgressStatus write_status_
;
77 } // namespace (anonymous)
79 class FileWriterDelegateTest
: public PlatformTest
{
81 FileWriterDelegateTest() {}
84 virtual void SetUp() OVERRIDE
;
85 virtual void TearDown() OVERRIDE
;
88 return file_system_context_
->GetQuotaUtil(kFileSystemType
)
89 ->GetOriginUsageOnFileTaskRunner(
90 file_system_context_
.get(), kOrigin
, kFileSystemType
);
93 int64
GetFileSizeOnDisk(const char* test_file_path
) {
94 // There might be in-flight flush/write.
95 base::MessageLoop::current()->PostTask(
96 FROM_HERE
, base::Bind(&base::DoNothing
));
97 base::RunLoop().RunUntilIdle();
99 FileSystemURL url
= GetFileSystemURL(test_file_path
);
100 base::File::Info file_info
;
101 EXPECT_EQ(base::File::FILE_OK
,
102 AsyncFileTestHelper::GetMetadata(
103 file_system_context_
, url
, &file_info
));
104 return file_info
.size
;
107 FileSystemURL
GetFileSystemURL(const char* file_name
) const {
108 return file_system_context_
->CreateCrackedFileSystemURL(
109 kOrigin
, kFileSystemType
, base::FilePath().FromUTF8Unsafe(file_name
));
112 FileWriterDelegate
* CreateWriterDelegate(
113 const char* test_file_path
,
115 int64 allowed_growth
) {
116 fileapi::SandboxFileStreamWriter
* writer
=
117 new fileapi::SandboxFileStreamWriter(
118 file_system_context_
.get(),
119 GetFileSystemURL(test_file_path
),
121 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
122 writer
->set_default_quota(allowed_growth
);
123 return new FileWriterDelegate(
124 scoped_ptr
<fileapi::FileStreamWriter
>(writer
));
127 FileWriterDelegate::DelegateWriteCallback
GetWriteCallback(Result
* result
) {
128 return base::Bind(&Result::DidWrite
, base::Unretained(result
));
131 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
132 // and creates a new FileWriterDelegate for the file.
133 void PrepareForWrite(const char* test_file_path
,
134 const GURL
& blob_url
,
136 int64 allowed_growth
) {
137 file_writer_delegate_
.reset(
138 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
));
139 request_
= empty_context_
.CreateRequest(
140 blob_url
, net::DEFAULT_PRIORITY
, file_writer_delegate_
.get(), NULL
);
143 static net::URLRequest::ProtocolFactory Factory
;
145 // This should be alive until the very end of this instance.
146 base::MessageLoopForIO loop_
;
148 scoped_refptr
<fileapi::FileSystemContext
> file_system_context_
;
150 net::URLRequestContext empty_context_
;
151 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
152 scoped_ptr
<net::URLRequest
> request_
;
154 base::ScopedTempDir dir_
;
156 static const char* content_
;
159 const char* FileWriterDelegateTest::content_
= NULL
;
163 static std::string g_content
;
165 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
167 FileWriterDelegateTestJob(net::URLRequest
* request
,
168 net::NetworkDelegate
* network_delegate
,
169 const std::string
& content
)
170 : net::URLRequestJob(request
, network_delegate
),
172 remaining_bytes_(content
.length()),
176 virtual void Start() OVERRIDE
{
177 base::MessageLoop::current()->PostTask(
179 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
182 virtual bool ReadRawData(net::IOBuffer
* buf
,
184 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 virtual int GetResponseCode() const OVERRIDE
{
202 virtual ~FileWriterDelegateTestJob() {}
205 std::string content_
;
206 int remaining_bytes_
;
210 } // namespace (anonymous)
213 net::URLRequestJob
* FileWriterDelegateTest::Factory(
214 net::URLRequest
* request
,
215 net::NetworkDelegate
* network_delegate
,
216 const std::string
& scheme
) {
217 return new FileWriterDelegateTestJob(
218 request
, network_delegate
, FileWriterDelegateTest::content_
);
221 void FileWriterDelegateTest::SetUp() {
222 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
224 file_system_context_
= CreateFileSystemContextForTesting(
226 ASSERT_EQ(base::File::FILE_OK
,
227 AsyncFileTestHelper::CreateFile(
228 file_system_context_
, GetFileSystemURL("test")));
229 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory
);
232 void FileWriterDelegateTest::TearDown() {
233 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL
);
234 file_system_context_
= NULL
;
235 base::RunLoop().RunUntilIdle();
238 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
239 const GURL
kBlobURL("blob:nolimit");
242 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
245 ASSERT_EQ(0, usage());
246 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
247 base::MessageLoop::current()->Run();
249 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
250 file_writer_delegate_
.reset();
252 ASSERT_EQ(kDataSize
, usage());
253 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
254 EXPECT_EQ(kDataSize
, result
.bytes_written());
255 EXPECT_EQ(base::File::FILE_OK
, result
.status());
258 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
259 const GURL
kBlobURL("blob:just");
261 const int64 kAllowedGrowth
= kDataSize
;
262 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
265 ASSERT_EQ(0, usage());
266 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
267 base::MessageLoop::current()->Run();
268 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
269 file_writer_delegate_
.reset();
271 ASSERT_EQ(kAllowedGrowth
, usage());
272 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
274 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
275 EXPECT_EQ(base::File::FILE_OK
, result
.status());
278 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
279 const GURL
kBlobURL("blob:failure");
281 const int64 kAllowedGrowth
= kDataSize
- 1;
282 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
285 ASSERT_EQ(0, usage());
286 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
287 base::MessageLoop::current()->Run();
288 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
289 file_writer_delegate_
.reset();
291 ASSERT_EQ(kAllowedGrowth
, usage());
292 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
294 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
295 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
296 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
299 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
300 const GURL
kBlobURL("blob:zero");
302 int64 kAllowedGrowth
= 0;
303 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
306 ASSERT_EQ(0, usage());
307 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
308 base::MessageLoop::current()->Run();
309 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
310 file_writer_delegate_
.reset();
312 ASSERT_EQ(kAllowedGrowth
, usage());
313 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
315 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
316 EXPECT_EQ(base::File::FILE_OK
, result
.status());
317 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
320 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
321 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
322 scoped_ptr
<net::URLRequest
> request2
;
324 ASSERT_EQ(base::File::FILE_OK
,
325 AsyncFileTestHelper::CreateFile(
326 file_system_context_
, GetFileSystemURL("test2")));
328 const GURL
kBlobURL("blob:nolimitconcurrent");
329 const GURL
kBlobURL2("blob:nolimitconcurrent2");
332 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
334 // Credate another FileWriterDelegate for concurrent write.
335 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
336 request2
= empty_context_
.CreateRequest(
337 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get(), NULL
);
339 Result result
, result2
;
340 ASSERT_EQ(0, usage());
341 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
342 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
343 base::MessageLoop::current()->Run();
344 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
345 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
346 base::MessageLoop::current()->Run();
348 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
349 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
350 file_writer_delegate_
.reset();
351 file_writer_delegate2
.reset();
353 ASSERT_EQ(kDataSize
* 2, usage());
354 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
356 EXPECT_EQ(kDataSize
, result
.bytes_written());
357 EXPECT_EQ(base::File::FILE_OK
, result
.status());
358 EXPECT_EQ(kDataSize
, result2
.bytes_written());
359 EXPECT_EQ(base::File::FILE_OK
, result2
.status());
362 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
363 const GURL
kBlobURL("blob:failure-with-updated-quota");
366 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
368 int64 allowed_growth
= 100;
369 ASSERT_LT(kDataSize
, allowed_growth
);
370 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
374 ASSERT_EQ(0, usage());
375 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
376 base::MessageLoop::current()->Run();
377 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
378 file_writer_delegate_
.reset();
380 ASSERT_EQ(kDataSize
, usage());
381 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
382 EXPECT_EQ(kDataSize
, result
.bytes_written());
383 EXPECT_EQ(base::File::FILE_OK
, result
.status());
386 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
389 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
393 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
394 base::MessageLoop::current()->Run();
395 EXPECT_EQ(kDataSize
, usage());
396 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
397 EXPECT_EQ(kDataSize
, result
.bytes_written());
398 EXPECT_EQ(base::File::FILE_OK
, result
.status());
399 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
402 // Trying to write kDataSize bytes data from offset 25 while
403 // allowed_growth is 55.
406 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
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 EXPECT_EQ(offset
+ 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 45 bytes data while allowed_growth is -20.
423 allowed_growth
= -20;
424 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
425 int64 pre_write_usage
= GetFileSizeOnDisk("test");
429 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
430 base::MessageLoop::current()->Run();
431 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
432 file_writer_delegate_
.reset();
434 EXPECT_EQ(pre_write_usage
, usage());
435 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
436 EXPECT_EQ(kDataSize
, result
.bytes_written());
437 EXPECT_EQ(base::File::FILE_OK
, result
.status());
440 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
441 // while allowed_growth is 10.
442 const int kOverlap
= 20;
443 offset
= pre_write_usage
- kOverlap
;
445 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
449 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
450 base::MessageLoop::current()->Run();
451 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
452 file_writer_delegate_
.reset();
454 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
455 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
456 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
457 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, result
.status());
461 } // namespace content