Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / fileapi / file_writer_delegate_unittest.cc
blobd907843695a9c25c9c0b25039182e07e8125f0e8
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/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"
28 #include "url/gurl.h"
30 using content::AsyncFileTestHelper;
31 using storage::FileSystemURL;
32 using storage::FileWriterDelegate;
34 namespace content {
36 namespace {
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(kData) - 1;
44 class Result {
45 public:
46 Result()
47 : status_(base::File::FILE_OK),
48 bytes_written_(0),
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 {
54 return write_status_;
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();
64 } else {
65 EXPECT_EQ(base::File::FILE_OK, status_);
66 status_ = status;
67 base::MessageLoop::current()->Quit();
71 private:
72 // For post-operation status.
73 base::File::Error status_;
74 int64 bytes_written_;
75 FileWriterDelegate::WriteProgressStatus write_status_;
78 class BlobURLRequestJobFactory;
80 } // namespace (anonymous)
82 class FileWriterDelegateTest : public PlatformTest {
83 public:
84 FileWriterDelegateTest() {}
86 protected:
87 void SetUp() override;
88 void TearDown() override;
90 int64 usage() {
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,
117 int64 offset,
118 int64 allowed_growth) {
119 storage::SandboxFileStreamWriter* writer =
120 new storage::SandboxFileStreamWriter(
121 file_system_context_.get(),
122 GetFileSystemURL(test_file_path),
123 offset,
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,
138 int64 offset,
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;
163 namespace {
165 static std::string g_content;
167 class FileWriterDelegateTestJob : public net::URLRequestJob {
168 public:
169 FileWriterDelegateTestJob(net::URLRequest* request,
170 net::NetworkDelegate* network_delegate,
171 const std::string& content)
172 : net::URLRequestJob(request, network_delegate),
173 content_(content),
174 remaining_bytes_(content.length()),
175 cursor_(0) {
178 void Start() override {
179 base::MessageLoop::current()->PostTask(
180 FROM_HERE,
181 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete, this));
184 bool ReadRawData(net::IOBuffer* buf, int buf_size, 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;
194 return true;
197 int GetResponseCode() const override { return 200; }
199 protected:
200 ~FileWriterDelegateTestJob() override {}
202 private:
203 std::string content_;
204 int remaining_bytes_;
205 int cursor_;
208 class BlobURLRequestJobFactory : public net::URLRequestJobFactory {
209 public:
210 explicit BlobURLRequestJobFactory(const char** content_data)
211 : content_data_(content_data) {
214 net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
215 const std::string& scheme,
216 net::URLRequest* request,
217 net::NetworkDelegate* network_delegate) const override {
218 return new FileWriterDelegateTestJob(
219 request, network_delegate, *content_data_);
222 net::URLRequestJob* MaybeInterceptRedirect(
223 net::URLRequest* request,
224 net::NetworkDelegate* network_delegate,
225 const GURL& location) const override {
226 return nullptr;
229 net::URLRequestJob* MaybeInterceptResponse(
230 net::URLRequest* request,
231 net::NetworkDelegate* network_delegate) const override {
232 return nullptr;
235 bool IsHandledProtocol(const std::string& scheme) const override {
236 return scheme == "blob";
239 bool IsHandledURL(const GURL& url) const override {
240 return url.SchemeIs("blob");
243 bool IsSafeRedirectTarget(const GURL& location) const override {
244 return true;
247 private:
248 const char** content_data_;
250 DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory);
253 } // namespace (anonymous)
255 void FileWriterDelegateTest::SetUp() {
256 ASSERT_TRUE(dir_.CreateUniqueTempDir());
258 file_system_context_ = CreateFileSystemContextForTesting(
259 NULL, dir_.path());
260 ASSERT_EQ(base::File::FILE_OK,
261 AsyncFileTestHelper::CreateFile(file_system_context_.get(),
262 GetFileSystemURL("test")));
263 job_factory_.reset(new BlobURLRequestJobFactory(&content_));
264 empty_context_.set_job_factory(job_factory_.get());
267 void FileWriterDelegateTest::TearDown() {
268 file_system_context_ = NULL;
269 base::RunLoop().RunUntilIdle();
272 TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
273 const GURL kBlobURL("blob:nolimit");
274 content_ = kData;
276 PrepareForWrite("test", kBlobURL, 0, kint64max);
278 Result result;
279 ASSERT_EQ(0, usage());
280 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
281 base::MessageLoop::current()->Run();
283 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
284 file_writer_delegate_.reset();
286 ASSERT_EQ(kDataSize, usage());
287 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
288 EXPECT_EQ(kDataSize, result.bytes_written());
289 EXPECT_EQ(base::File::FILE_OK, result.status());
292 TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
293 const GURL kBlobURL("blob:just");
294 content_ = kData;
295 const int64 kAllowedGrowth = kDataSize;
296 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
298 Result result;
299 ASSERT_EQ(0, usage());
300 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
301 base::MessageLoop::current()->Run();
302 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
303 file_writer_delegate_.reset();
305 ASSERT_EQ(kAllowedGrowth, usage());
306 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
308 EXPECT_EQ(kAllowedGrowth, result.bytes_written());
309 EXPECT_EQ(base::File::FILE_OK, result.status());
312 TEST_F(FileWriterDelegateTest, DISABLED_WriteFailureByQuota) {
313 const GURL kBlobURL("blob:failure");
314 content_ = kData;
315 const int64 kAllowedGrowth = kDataSize - 1;
316 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
318 Result result;
319 ASSERT_EQ(0, usage());
320 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
321 base::MessageLoop::current()->Run();
322 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
323 file_writer_delegate_.reset();
325 ASSERT_EQ(kAllowedGrowth, usage());
326 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
328 EXPECT_EQ(kAllowedGrowth, result.bytes_written());
329 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, result.status());
330 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
333 TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) {
334 const GURL kBlobURL("blob:zero");
335 content_ = "";
336 int64 kAllowedGrowth = 0;
337 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
339 Result result;
340 ASSERT_EQ(0, usage());
341 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
342 base::MessageLoop::current()->Run();
343 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
344 file_writer_delegate_.reset();
346 ASSERT_EQ(kAllowedGrowth, usage());
347 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
349 EXPECT_EQ(kAllowedGrowth, result.bytes_written());
350 EXPECT_EQ(base::File::FILE_OK, result.status());
351 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
354 TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
355 scoped_ptr<FileWriterDelegate> file_writer_delegate2;
356 scoped_ptr<net::URLRequest> request2;
358 ASSERT_EQ(base::File::FILE_OK,
359 AsyncFileTestHelper::CreateFile(file_system_context_.get(),
360 GetFileSystemURL("test2")));
362 const GURL kBlobURL("blob:nolimitconcurrent");
363 const GURL kBlobURL2("blob:nolimitconcurrent2");
364 content_ = kData;
366 PrepareForWrite("test", kBlobURL, 0, kint64max);
368 // Credate another FileWriterDelegate for concurrent write.
369 file_writer_delegate2.reset(CreateWriterDelegate("test2", 0, kint64max));
370 request2 = empty_context_.CreateRequest(
371 kBlobURL2, net::DEFAULT_PRIORITY, file_writer_delegate2.get(), NULL);
373 Result result, result2;
374 ASSERT_EQ(0, usage());
375 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
376 file_writer_delegate2->Start(request2.Pass(), GetWriteCallback(&result2));
377 base::MessageLoop::current()->Run();
378 if (result.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING ||
379 result2.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING)
380 base::MessageLoop::current()->Run();
382 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
383 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result2.write_status());
384 file_writer_delegate_.reset();
385 file_writer_delegate2.reset();
387 ASSERT_EQ(kDataSize * 2, usage());
388 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
390 EXPECT_EQ(kDataSize, result.bytes_written());
391 EXPECT_EQ(base::File::FILE_OK, result.status());
392 EXPECT_EQ(kDataSize, result2.bytes_written());
393 EXPECT_EQ(base::File::FILE_OK, result2.status());
396 TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
397 const GURL kBlobURL("blob:failure-with-updated-quota");
398 content_ = kData;
400 // Writing kDataSize (=45) bytes data while allowed_growth is 100.
401 int64 offset = 0;
402 int64 allowed_growth = 100;
403 ASSERT_LT(kDataSize, allowed_growth);
404 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
407 Result result;
408 ASSERT_EQ(0, usage());
409 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
410 base::MessageLoop::current()->Run();
411 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
412 file_writer_delegate_.reset();
414 ASSERT_EQ(kDataSize, usage());
415 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
416 EXPECT_EQ(kDataSize, result.bytes_written());
417 EXPECT_EQ(base::File::FILE_OK, result.status());
420 // Trying to overwrite kDataSize bytes data while allowed_growth is 20.
421 offset = 0;
422 allowed_growth = 20;
423 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
426 Result result;
427 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
428 base::MessageLoop::current()->Run();
429 EXPECT_EQ(kDataSize, usage());
430 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
431 EXPECT_EQ(kDataSize, result.bytes_written());
432 EXPECT_EQ(base::File::FILE_OK, result.status());
433 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
436 // Trying to write kDataSize bytes data from offset 25 while
437 // allowed_growth is 55.
438 offset = 25;
439 allowed_growth = 55;
440 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
443 Result result;
444 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
445 base::MessageLoop::current()->Run();
446 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
447 file_writer_delegate_.reset();
449 EXPECT_EQ(offset + kDataSize, usage());
450 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
451 EXPECT_EQ(kDataSize, result.bytes_written());
452 EXPECT_EQ(base::File::FILE_OK, result.status());
455 // Trying to overwrite 45 bytes data while allowed_growth is -20.
456 offset = 0;
457 allowed_growth = -20;
458 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
459 int64 pre_write_usage = GetFileSizeOnDisk("test");
462 Result result;
463 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
464 base::MessageLoop::current()->Run();
465 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
466 file_writer_delegate_.reset();
468 EXPECT_EQ(pre_write_usage, usage());
469 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
470 EXPECT_EQ(kDataSize, result.bytes_written());
471 EXPECT_EQ(base::File::FILE_OK, result.status());
474 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
475 // while allowed_growth is 10.
476 const int kOverlap = 20;
477 offset = pre_write_usage - kOverlap;
478 allowed_growth = 10;
479 PrepareForWrite("test", kBlobURL, offset, allowed_growth);
482 Result result;
483 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
484 base::MessageLoop::current()->Run();
485 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
486 file_writer_delegate_.reset();
488 EXPECT_EQ(pre_write_usage + allowed_growth, usage());
489 EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
490 EXPECT_EQ(kOverlap + allowed_growth, result.bytes_written());
491 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, result.status());
495 } // namespace content