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/test_file_system_context.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/request_priority.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_job.h"
20 #include "net/url_request/url_request_status.h"
21 #include "testing/platform_test.h"
23 #include "webkit/browser/fileapi/async_file_test_helper.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"
33 const GURL
kOrigin("http://example.com");
34 const FileSystemType kFileSystemType
= kFileSystemTypeTest
;
36 const char kData
[] = "The quick brown fox jumps over the lazy dog.\n";
37 const int kDataSize
= ARRAYSIZE_UNSAFE(kData
) - 1;
42 : status_(base::PLATFORM_FILE_OK
),
44 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING
) {}
46 base::PlatformFileError
status() const { return status_
; }
47 int64
bytes_written() const { return bytes_written_
; }
48 FileWriterDelegate::WriteProgressStatus
write_status() const {
52 void DidWrite(base::PlatformFileError status
, int64 bytes
,
53 FileWriterDelegate::WriteProgressStatus write_status
) {
54 write_status_
= write_status
;
55 if (status
== base::PLATFORM_FILE_OK
) {
56 bytes_written_
+= bytes
;
57 if (write_status_
!= FileWriterDelegate::SUCCESS_IO_PENDING
)
58 base::MessageLoop::current()->Quit();
60 EXPECT_EQ(base::PLATFORM_FILE_OK
, status_
);
62 base::MessageLoop::current()->Quit();
67 // For post-operation status.
68 base::PlatformFileError status_
;
70 FileWriterDelegate::WriteProgressStatus write_status_
;
73 } // namespace (anonymous)
75 class FileWriterDelegateTest
: public PlatformTest
{
77 FileWriterDelegateTest() {}
80 virtual void SetUp() OVERRIDE
;
81 virtual void TearDown() OVERRIDE
;
84 return file_system_context_
->GetQuotaUtil(kFileSystemType
)
85 ->GetOriginUsageOnFileTaskRunner(
86 file_system_context_
.get(), kOrigin
, kFileSystemType
);
89 int64
GetFileSizeOnDisk(const char* test_file_path
) {
90 // There might be in-flight flush/write.
91 base::MessageLoop::current()->PostTask(
92 FROM_HERE
, base::Bind(&base::DoNothing
));
93 base::RunLoop().RunUntilIdle();
95 FileSystemURL url
= GetFileSystemURL(test_file_path
);
96 base::PlatformFileInfo file_info
;
97 EXPECT_EQ(base::PLATFORM_FILE_OK
,
98 AsyncFileTestHelper::GetMetadata(
99 file_system_context_
, url
, &file_info
));
100 return file_info
.size
;
103 FileSystemURL
GetFileSystemURL(const char* file_name
) const {
104 return file_system_context_
->CreateCrackedFileSystemURL(
105 kOrigin
, kFileSystemType
, base::FilePath().FromUTF8Unsafe(file_name
));
108 FileWriterDelegate
* CreateWriterDelegate(
109 const char* test_file_path
,
111 int64 allowed_growth
) {
112 SandboxFileStreamWriter
* writer
= new SandboxFileStreamWriter(
113 file_system_context_
.get(),
114 GetFileSystemURL(test_file_path
),
116 *file_system_context_
->GetUpdateObservers(kFileSystemType
));
117 writer
->set_default_quota(allowed_growth
);
118 return new FileWriterDelegate(scoped_ptr
<FileStreamWriter
>(writer
));
121 FileWriterDelegate::DelegateWriteCallback
GetWriteCallback(Result
* result
) {
122 return base::Bind(&Result::DidWrite
, base::Unretained(result
));
125 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
126 // and creates a new FileWriterDelegate for the file.
127 void PrepareForWrite(const char* test_file_path
,
128 const GURL
& blob_url
,
130 int64 allowed_growth
) {
131 file_writer_delegate_
.reset(
132 CreateWriterDelegate(test_file_path
, offset
, allowed_growth
));
133 request_
= empty_context_
.CreateRequest(
134 blob_url
, net::DEFAULT_PRIORITY
, file_writer_delegate_
.get());
137 static net::URLRequest::ProtocolFactory Factory
;
139 // This should be alive until the very end of this instance.
140 base::MessageLoopForIO loop_
;
142 scoped_refptr
<FileSystemContext
> file_system_context_
;
144 net::URLRequestContext empty_context_
;
145 scoped_ptr
<FileWriterDelegate
> file_writer_delegate_
;
146 scoped_ptr
<net::URLRequest
> request_
;
148 base::ScopedTempDir dir_
;
150 static const char* content_
;
153 const char* FileWriterDelegateTest::content_
= NULL
;
157 static std::string g_content
;
159 class FileWriterDelegateTestJob
: public net::URLRequestJob
{
161 FileWriterDelegateTestJob(net::URLRequest
* request
,
162 net::NetworkDelegate
* network_delegate
,
163 const std::string
& content
)
164 : net::URLRequestJob(request
, network_delegate
),
166 remaining_bytes_(content
.length()),
170 virtual void Start() OVERRIDE
{
171 base::MessageLoop::current()->PostTask(
173 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete
, this));
176 virtual bool ReadRawData(net::IOBuffer
* buf
,
178 int *bytes_read
) OVERRIDE
{
179 if (remaining_bytes_
< buf_size
)
180 buf_size
= static_cast<int>(remaining_bytes_
);
182 for (int i
= 0; i
< buf_size
; ++i
)
183 buf
->data()[i
] = content_
[cursor_
++];
184 remaining_bytes_
-= buf_size
;
186 SetStatus(net::URLRequestStatus());
187 *bytes_read
= buf_size
;
191 virtual int GetResponseCode() const OVERRIDE
{
196 virtual ~FileWriterDelegateTestJob() {}
199 std::string content_
;
200 int remaining_bytes_
;
204 } // namespace (anonymous)
207 net::URLRequestJob
* FileWriterDelegateTest::Factory(
208 net::URLRequest
* request
,
209 net::NetworkDelegate
* network_delegate
,
210 const std::string
& scheme
) {
211 return new FileWriterDelegateTestJob(
212 request
, network_delegate
, FileWriterDelegateTest::content_
);
215 void FileWriterDelegateTest::SetUp() {
216 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
218 file_system_context_
= CreateFileSystemContextForTesting(
220 ASSERT_EQ(base::PLATFORM_FILE_OK
,
221 AsyncFileTestHelper::CreateFile(
222 file_system_context_
, GetFileSystemURL("test")));
223 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory
);
226 void FileWriterDelegateTest::TearDown() {
227 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL
);
228 file_system_context_
= NULL
;
229 base::RunLoop().RunUntilIdle();
232 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimit
) {
233 const GURL
kBlobURL("blob:nolimit");
236 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
239 ASSERT_EQ(0, usage());
240 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
241 base::MessageLoop::current()->Run();
243 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
244 file_writer_delegate_
.reset();
246 ASSERT_EQ(kDataSize
, usage());
247 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
248 EXPECT_EQ(kDataSize
, result
.bytes_written());
249 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
252 TEST_F(FileWriterDelegateTest
, WriteSuccessWithJustQuota
) {
253 const GURL
kBlobURL("blob:just");
255 const int64 kAllowedGrowth
= kDataSize
;
256 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
259 ASSERT_EQ(0, usage());
260 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
261 base::MessageLoop::current()->Run();
262 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
263 file_writer_delegate_
.reset();
265 ASSERT_EQ(kAllowedGrowth
, usage());
266 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
268 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
269 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
272 TEST_F(FileWriterDelegateTest
, DISABLED_WriteFailureByQuota
) {
273 const GURL
kBlobURL("blob:failure");
275 const int64 kAllowedGrowth
= kDataSize
- 1;
276 PrepareForWrite("test", kBlobURL
, 0, kAllowedGrowth
);
279 ASSERT_EQ(0, usage());
280 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
281 base::MessageLoop::current()->Run();
282 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
283 file_writer_delegate_
.reset();
285 ASSERT_EQ(kAllowedGrowth
, usage());
286 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
288 EXPECT_EQ(kAllowedGrowth
, result
.bytes_written());
289 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE
, result
.status());
290 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
293 TEST_F(FileWriterDelegateTest
, WriteZeroBytesSuccessfullyWithZeroQuota
) {
294 const GURL
kBlobURL("blob:zero");
296 int64 kAllowedGrowth
= 0;
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::PLATFORM_FILE_OK
, result
.status());
311 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
314 TEST_F(FileWriterDelegateTest
, WriteSuccessWithoutQuotaLimitConcurrent
) {
315 scoped_ptr
<FileWriterDelegate
> file_writer_delegate2
;
316 scoped_ptr
<net::URLRequest
> request2
;
318 ASSERT_EQ(base::PLATFORM_FILE_OK
,
319 AsyncFileTestHelper::CreateFile(
320 file_system_context_
, GetFileSystemURL("test2")));
322 const GURL
kBlobURL("blob:nolimitconcurrent");
323 const GURL
kBlobURL2("blob:nolimitconcurrent2");
326 PrepareForWrite("test", kBlobURL
, 0, kint64max
);
328 // Credate another FileWriterDelegate for concurrent write.
329 file_writer_delegate2
.reset(CreateWriterDelegate("test2", 0, kint64max
));
330 request2
= empty_context_
.CreateRequest(
331 kBlobURL2
, net::DEFAULT_PRIORITY
, file_writer_delegate2
.get());
333 Result result
, result2
;
334 ASSERT_EQ(0, usage());
335 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
336 file_writer_delegate2
->Start(request2
.Pass(), GetWriteCallback(&result2
));
337 base::MessageLoop::current()->Run();
338 if (result
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
||
339 result2
.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING
)
340 base::MessageLoop::current()->Run();
342 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
343 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result2
.write_status());
344 file_writer_delegate_
.reset();
345 file_writer_delegate2
.reset();
347 ASSERT_EQ(kDataSize
* 2, usage());
348 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
350 EXPECT_EQ(kDataSize
, result
.bytes_written());
351 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
352 EXPECT_EQ(kDataSize
, result2
.bytes_written());
353 EXPECT_EQ(base::PLATFORM_FILE_OK
, result2
.status());
356 TEST_F(FileWriterDelegateTest
, WritesWithQuotaAndOffset
) {
357 const GURL
kBlobURL("blob:failure-with-updated-quota");
360 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
362 int64 allowed_growth
= 100;
363 ASSERT_LT(kDataSize
, allowed_growth
);
364 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
368 ASSERT_EQ(0, usage());
369 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
370 base::MessageLoop::current()->Run();
371 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
372 file_writer_delegate_
.reset();
374 ASSERT_EQ(kDataSize
, usage());
375 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
376 EXPECT_EQ(kDataSize
, result
.bytes_written());
377 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
380 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
383 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
387 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
388 base::MessageLoop::current()->Run();
389 EXPECT_EQ(kDataSize
, usage());
390 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
391 EXPECT_EQ(kDataSize
, result
.bytes_written());
392 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
393 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
396 // Trying to write kDataSize bytes data from offset 25 while
397 // allowed_growth is 55.
400 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
404 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
405 base::MessageLoop::current()->Run();
406 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
407 file_writer_delegate_
.reset();
409 EXPECT_EQ(offset
+ kDataSize
, usage());
410 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
411 EXPECT_EQ(kDataSize
, result
.bytes_written());
412 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
415 // Trying to overwrite 45 bytes data while allowed_growth is -20.
417 allowed_growth
= -20;
418 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
419 int64 pre_write_usage
= GetFileSizeOnDisk("test");
423 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
424 base::MessageLoop::current()->Run();
425 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED
, result
.write_status());
426 file_writer_delegate_
.reset();
428 EXPECT_EQ(pre_write_usage
, usage());
429 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
430 EXPECT_EQ(kDataSize
, result
.bytes_written());
431 EXPECT_EQ(base::PLATFORM_FILE_OK
, result
.status());
434 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
435 // while allowed_growth is 10.
436 const int kOverlap
= 20;
437 offset
= pre_write_usage
- kOverlap
;
439 PrepareForWrite("test", kBlobURL
, offset
, allowed_growth
);
443 file_writer_delegate_
->Start(request_
.Pass(), GetWriteCallback(&result
));
444 base::MessageLoop::current()->Run();
445 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED
, result
.write_status());
446 file_writer_delegate_
.reset();
448 EXPECT_EQ(pre_write_usage
+ allowed_growth
, usage());
449 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
450 EXPECT_EQ(kOverlap
+ allowed_growth
, result
.bytes_written());
451 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE
, result
.status());
455 } // namespace fileapi