1 // Copyright (c) 2012 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.h"
13 #include "googleurl/src/gurl.h"
14 #include "net/base/io_buffer.h"
15 #include "net/url_request/url_request.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_job.h"
18 #include "net/url_request/url_request_status.h"
19 #include "testing/platform_test.h"
20 #include "webkit/browser/fileapi/file_system_quota_util.h"
21 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
22 #include "webkit/fileapi/file_system_context.h"
23 #include "webkit/fileapi/file_system_file_util.h"
24 #include "webkit/fileapi/file_system_operation_context.h"
25 #include "webkit/fileapi/file_writer_delegate.h"
26 #include "webkit/fileapi/local_file_system_operation.h"
27 #include "webkit/fileapi/mock_file_system_context.h"
33 const GURL
kOrigin("http://example.com");
34 const FileSystemType kFileSystemType
= kFileSystemTypeTest
;
39 : status_(base::PLATFORM_FILE_OK
),
41 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
43 base::PlatformFileError
status() const { return status_
; }
44 int64
bytes_written() const { return bytes_written_
; }
45 FileWriterDelegate::WriteProgressStatus
write_status() const {
49 void DidWrite(base::PlatformFileError status
, int64 bytes
,
50 FileWriterDelegate::WriteProgressStatus write_status
) {
51 write_status_
= write_status
;
52 if (status
== base::PLATFORM_FILE_OK
) {
53 bytes_written_
+= bytes
;
54 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
55 base::MessageLoop::current()->Quit();
57 EXPECT_EQ(base::PLATFORM_FILE_OK
, status_
);
59 base::MessageLoop::current()->Quit();
64 // For post-operation status.
65 base::PlatformFileError status_
;
67 FileWriterDelegate::WriteProgressStatus write_status_
;
70 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
71 const int kDataSize
= ARRAYSIZE_UNSAFE(kData
) - 1;
73 } // namespace (anonymous)
75 class FileWriterDelegateTest
: public PlatformTest
{
77 FileWriterDelegateTest()
78 : loop_(base::MessageLoop::TYPE_IO
) {}
81 virtual void SetUp() OVERRIDE
;
82 virtual void TearDown() OVERRIDE
;
84 FileSystemFileUtil
* file_util() {
85 return file_system_context_
->GetFileUtil(kFileSystemType
);
89 return file_system_context_
->GetQuotaUtil(kFileSystemType
)->
90 GetOriginUsageOnFileThread(file_system_context_
,
95 int64
GetFileSizeOnDisk(const char* test_file_path
) {
96 // There might be in-flight flush/write.
97 base::MessageLoop::current()->PostTask(
98 FROM_HERE
, base::Bind(&base::DoNothing
));
99 base::MessageLoop::current()->RunUntilIdle();
101 FileSystemURL url
= GetFileSystemURL(test_file_path
);
102 base::PlatformFileInfo file_info
;
103 base::FilePath platform_path
;
104 EXPECT_EQ(base::PLATFORM_FILE_OK
,
105 file_util()->GetFileInfo(NewOperationContext().get(), url
,
106 &file_info
, &platform_path
));
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 scoped_ptr
<FileSystemOperationContext
> NewOperationContext() {
116 FileSystemOperationContext
* context
=
117 new FileSystemOperationContext(file_system_context_
);
118 context
->set_update_observers(
119 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
120 return make_scoped_ptr(context
);
123 FileWriterDelegate
* CreateWriterDelegate(
124 const char* test_file_path
,
126 int64 allowed_growth
,
128 SandboxFileStreamWriter
* writer
= new SandboxFileStreamWriter(
129 file_system_context_
,
130 GetFileSystemURL(test_file_path
),
132 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
133 writer
->set_default_quota(allowed_growth
);
134 return new FileWriterDelegate(
135 base::Bind(&Result::DidWrite
, base::Unretained(result
)),
136 scoped_ptr
<FileStreamWriter
>(writer
));
139 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
140 // and creates a new FileWriterDelegate for the file.
141 void PrepareForWrite(const char* test_file_path
,
142 const GURL
& blob_url
,
144 int64 allowed_growth
) {
145 result_
.reset(new Result());
146 file_writer_delegate_
.reset(
147 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
,
149 request_
.reset(empty_context_
.CreateRequest(
150 blob_url
, file_writer_delegate_
.get()));
153 static net::URLRequest::ProtocolFactory Factory
;
155 // This should be alive until the very end of this instance.
156 base::MessageLoop loop_
;
158 scoped_refptr
<FileSystemContext
> file_system_context_
;
160 net::URLRequestContext empty_context_
;
161 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
162 scoped_ptr
<net::URLRequest
> request_
;
163 scoped_ptr
<Result
> result_
;
165 base::ScopedTempDir dir_
;
167 static const char* content_
;
170 const char* FileWriterDelegateTest::content_
= NULL
;
174 static std::string g_content
;
176 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
178 FileWriterDelegateTestJob(net::URLRequest
* request
,
179 net::NetworkDelegate
* network_delegate
,
180 const std::string
& content
)
181 : net::URLRequestJob(request
, network_delegate
),
183 remaining_bytes_(content
.length()),
187 virtual void Start() OVERRIDE
{
188 base::MessageLoop::current()->PostTask(
190 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
193 virtual bool ReadRawData(net::IOBuffer
* buf
,
195 int *bytes_read
) OVERRIDE
{
196 if (remaining_bytes_
< buf_size
)
197 buf_size
= static_cast<int>(remaining_bytes_
);
199 for (int i
= 0; i
< buf_size
; ++i
)
200 buf
->data()[i
] = content_
[cursor_
++];
201 remaining_bytes_
-= buf_size
;
203 SetStatus(net::URLRequestStatus());
204 *bytes_read
= buf_size
;
208 virtual int GetResponseCode() const OVERRIDE
{
213 virtual ~FileWriterDelegateTestJob() {}
216 std::string content_
;
217 int remaining_bytes_
;
221 } // namespace (anonymous)
224 net::URLRequestJob
* FileWriterDelegateTest::Factory(
225 net::URLRequest
* request
,
226 net::NetworkDelegate
* network_delegate
,
227 const std::string
& scheme
) {
228 return new FileWriterDelegateTestJob(
229 request
, network_delegate
, FileWriterDelegateTest::content_
);
232 void FileWriterDelegateTest::SetUp() {
233 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
235 file_system_context_
= CreateFileSystemContextForTesting(
238 bool created
= false;
239 scoped_ptr
<FileSystemOperationContext
> context
= NewOperationContext();
240 context
->set_allowed_bytes_growth(kint64max
);
241 base::PlatformFileError error
= file_util()->EnsureFileExists(
243 GetFileSystemURL("test"),
245 ASSERT_EQ(base::PLATFORM_FILE_OK
, error
);
246 ASSERT_TRUE(created
);
247 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory
);
250 void FileWriterDelegateTest::TearDown() {
251 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL
);
252 file_system_context_
= NULL
;
253 base::MessageLoop::current()->RunUntilIdle();
256 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
257 const GURL
kBlobURL("blob:nolimit");
260 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
262 ASSERT_EQ(0, usage());
263 file_writer_delegate_
->Start(request_
.Pass());
264 base::MessageLoop::current()->Run();
266 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
267 file_writer_delegate_
.reset();
269 ASSERT_EQ(kDataSize
, usage());
270 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
271 EXPECT_EQ(kDataSize
, result_
->bytes_written());
272 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
275 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
276 const GURL
kBlobURL("blob:just");
278 const int64 kAllowedGrowth
= kDataSize
;
279 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
281 ASSERT_EQ(0, usage());
282 file_writer_delegate_
->Start(request_
.Pass());
283 base::MessageLoop::current()->Run();
284 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
285 file_writer_delegate_
.reset();
287 ASSERT_EQ(kAllowedGrowth
, usage());
288 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
290 EXPECT_EQ(kAllowedGrowth
, result_
->bytes_written());
291 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
294 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
295 const GURL
kBlobURL("blob:failure");
297 const int64 kAllowedGrowth
= kDataSize
- 1;
298 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
300 ASSERT_EQ(0, usage());
301 file_writer_delegate_
->Start(request_
.Pass());
302 base::MessageLoop::current()->Run();
303 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, 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::PLATFORM_FILE_ERROR_NO_SPACE
, result_
->status());
311 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result_
->write_status());
314 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
315 const GURL
kBlobURL("blob:zero");
317 int64 kAllowedGrowth
= 0;
318 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
320 ASSERT_EQ(0, usage());
321 file_writer_delegate_
->Start(request_
.Pass());
322 base::MessageLoop::current()->Run();
323 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, 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::PLATFORM_FILE_OK
, result_
->status());
331 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
334 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
335 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
336 scoped_ptr
<net::URLRequest
> request2
;
337 scoped_ptr
<Result
> result2
;
339 bool created
= false;
340 file_util()->EnsureFileExists(NewOperationContext().get(),
341 GetFileSystemURL("test2"),
343 ASSERT_TRUE(created
);
345 const GURL
kBlobURL("blob:nolimitconcurrent");
346 const GURL
kBlobURL2("blob:nolimitconcurrent2");
349 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
351 // Credate another FileWriterDelegate for concurrent write.
352 result2
.reset(new Result());
353 file_writer_delegate2
.reset(CreateWriterDelegate(
354 "test2", 0, kint64max
, result2
.get()));
355 request2
.reset(empty_context_
.CreateRequest(
356 kBlobURL2
, file_writer_delegate2
.get()));
358 ASSERT_EQ(0, usage());
359 file_writer_delegate_
->Start(request_
.Pass());
360 file_writer_delegate2
->Start(request2
.Pass());
361 base::MessageLoop::current()->Run();
362 if (result_
->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
363 result2
->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
364 base::MessageLoop::current()->Run();
366 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
367 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
->write_status());
368 file_writer_delegate_
.reset();
369 file_writer_delegate2
.reset();
371 ASSERT_EQ(kDataSize
* 2, usage());
372 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
374 EXPECT_EQ(kDataSize
, result_
->bytes_written());
375 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
376 EXPECT_EQ(kDataSize
, result2
->bytes_written());
377 EXPECT_EQ(base::PLATFORM_FILE_OK
, result2
->status());
380 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
381 const GURL
kBlobURL("blob:failure-with-updated-quota");
384 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
386 int64 allowed_growth
= 100;
387 ASSERT_LT(kDataSize
, allowed_growth
);
388 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
390 ASSERT_EQ(0, usage());
391 file_writer_delegate_
->Start(request_
.Pass());
392 base::MessageLoop::current()->Run();
393 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
394 file_writer_delegate_
.reset();
396 ASSERT_EQ(kDataSize
, usage());
397 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
398 EXPECT_EQ(kDataSize
, result_
->bytes_written());
399 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
401 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
404 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
406 file_writer_delegate_
->Start(request_
.Pass());
407 base::MessageLoop::current()->Run();
408 EXPECT_EQ(kDataSize
, usage());
409 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
410 EXPECT_EQ(kDataSize
, result_
->bytes_written());
411 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
412 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
414 // Trying to write kDataSize bytes data from offset 25 while
415 // allowed_growth is 55.
418 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
420 file_writer_delegate_
->Start(request_
.Pass());
421 base::MessageLoop::current()->Run();
422 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
423 file_writer_delegate_
.reset();
425 EXPECT_EQ(offset
+ kDataSize
, usage());
426 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
427 EXPECT_EQ(kDataSize
, result_
->bytes_written());
428 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
430 // Trying to overwrite 45 bytes data while allowed_growth is -20.
432 allowed_growth
= -20;
433 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
435 int64 pre_write_usage
= GetFileSizeOnDisk("test");
436 file_writer_delegate_
->Start(request_
.Pass());
437 base::MessageLoop::current()->Run();
438 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result_
->write_status());
439 file_writer_delegate_
.reset();
441 EXPECT_EQ(pre_write_usage
, usage());
442 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
443 EXPECT_EQ(kDataSize
, result_
->bytes_written());
444 EXPECT_EQ(base::PLATFORM_FILE_OK
, result_
->status());
446 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
447 // while allowed_growth is 10.
448 const int kOverlap
= 20;
449 offset
= pre_write_usage
- kOverlap
;
451 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
453 file_writer_delegate_
->Start(request_
.Pass());
454 base::MessageLoop::current()->Run();
455 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result_
->write_status());
456 file_writer_delegate_
.reset();
458 EXPECT_EQ(pre_write_usage
+ allowed_growth
,
460 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
461 EXPECT_EQ(kOverlap
+ allowed_growth
, result_
->bytes_written());
462 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE
, result_
->status());
465 } // namespace fileapi