Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / fileapi / file_writer_delegate_unittest.cc
blobaf60d3fcfa4dc148c4acf2a151dac55864378cde
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.
5 #include <string>
6 #include <vector>
8 #include "base/basictypes.h"
9 #include "base/bind.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"
31 #include "url/gurl.h"
33 using content::AsyncFileTestHelper;
34 using storage::FileSystemURL;
35 using storage::FileWriterDelegate;
37 namespace content {
39 namespace {
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;
47 class Result {
48 public:
49 Result()
50 : status_(base::File::FILE_OK),
51 bytes_written_(0),
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 {
57 return write_status_;
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();
67 } else {
68 EXPECT_EQ(base::File::FILE_OK, status_);
69 status_ = status;
70 base::MessageLoop::current()->Quit();
74 private:
75 // For post-operation status.
76 base::File::Error status_;
77 int64 bytes_written_;
78 FileWriterDelegate::WriteProgressStatus write_status_;
81 class BlobURLRequestJobFactory;
83 } // namespace (anonymous)
85 class FileWriterDelegateTest : public PlatformTest {
86 public:
87 FileWriterDelegateTest() {}
89 protected:
90 void SetUp() override;
91 void TearDown() override;
93 int64 usage() {
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,
120 int64 offset,
121 int64 allowed_growth) {
122 storage::SandboxFileStreamWriter* writer =
123 new storage::SandboxFileStreamWriter(
124 file_system_context_.get(),
125 GetFileSystemURL(test_file_path),
126 offset,
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,
141 int64 offset,
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;
166 namespace {
168 static std::string g_content;
170 class FileWriterDelegateTestJob : public net::URLRequestJob {
171 public:
172 FileWriterDelegateTestJob(net::URLRequest* request,
173 net::NetworkDelegate* network_delegate,
174 const std::string& content)
175 : net::URLRequestJob(request, network_delegate),
176 content_(content),
177 remaining_bytes_(content.length()),
178 cursor_(0) {
181 void Start() override {
182 base::ThreadTaskRunnerHandle::Get()->PostTask(
183 FROM_HERE,
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;
197 return true;
200 int GetResponseCode() const override { return 200; }
202 protected:
203 ~FileWriterDelegateTestJob() override {}
205 private:
206 std::string content_;
207 int remaining_bytes_;
208 int cursor_;
211 class BlobURLRequestJobFactory : public net::URLRequestJobFactory {
212 public:
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 {
229 return nullptr;
232 net::URLRequestJob* MaybeInterceptResponse(
233 net::URLRequest* request,
234 net::NetworkDelegate* network_delegate) const override {
235 return nullptr;
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 {
247 return true;
250 private:
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(
262 NULL, dir_.path());
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");
277 content_ = kData;
279 PrepareForWrite("test", kBlobURL, 0, kint64max);
281 Result result;
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");
297 content_ = kData;
298 const int64 kAllowedGrowth = kDataSize;
299 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
301 Result result;
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");
317 content_ = kData;
318 const int64 kAllowedGrowth = kDataSize - 1;
319 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
321 Result result;
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");
338 content_ = "";
339 int64 kAllowedGrowth = 0;
340 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
342 Result result;
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");
367 content_ = kData;
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");
401 content_ = kData;
403 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
404 int64 offset = 0;
405 int64 allowed_growth = 100;
406 ASSERT_LT(kDataSize, allowed_growth);
407 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
410 Result result;
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.
424 offset = 0;
425 allowed_growth = 20;
426 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
429 Result result;
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.
441 offset = 25;
442 allowed_growth = 55;
443 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
446 Result result;
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.
459 offset = 0;
460 allowed_growth = -20;
461 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
462 int64 pre_write_usage = GetFileSizeOnDisk("test");
465 Result result;
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;
481 allowed_growth = 10;
482 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
485 Result result;
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