| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit) |
| // rather than as part of test_shell_tests because they rely on being able |
| // to instantiate a MessageLoop of type TYPE_IO. test_shell_tests uses |
| // TYPE_UI, which URLRequest doesn't allow. |
| // |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/file_util_proxy.h" |
| #include "base/message_loop.h" |
| #include "base/scoped_temp_dir.h" |
| #include "googleurl/src/gurl.h" |
| #include "net/base/io_buffer.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_job.h" |
| #include "net/url_request/url_request_status.h" |
| #include "testing/platform_test.h" |
| #include "webkit/fileapi/file_system_callback_dispatcher.h" |
| #include "webkit/fileapi/file_system_context.h" |
| #include "webkit/fileapi/file_system_operation.h" |
| #include "webkit/fileapi/file_system_operation_context.h" |
| #include "webkit/fileapi/file_system_path_manager.h" |
| #include "webkit/fileapi/file_system_test_helper.h" |
| #include "webkit/fileapi/file_system_usage_cache.h" |
| #include "webkit/fileapi/file_writer_delegate.h" |
| #include "webkit/fileapi/quota_file_util.h" |
| #include "webkit/fileapi/sandbox_mount_point_provider.h" |
| |
| namespace fileapi { |
| |
| namespace { |
| |
| class Result { |
| public: |
| Result() |
| : status_(base::PLATFORM_FILE_OK), |
| bytes_written_(0), |
| complete_(false) {} |
| |
| void set_failure_status(base::PlatformFileError status) { |
| EXPECT_FALSE(complete_); |
| EXPECT_EQ(status_, base::PLATFORM_FILE_OK); |
| EXPECT_NE(status, base::PLATFORM_FILE_OK); |
| complete_ = true; |
| status_ = status; |
| } |
| base::PlatformFileError status() const { return status_; } |
| void add_bytes_written(int64 bytes, bool complete) { |
| bytes_written_ += bytes; |
| EXPECT_FALSE(complete_); |
| complete_ = complete; |
| } |
| int64 bytes_written() const { return bytes_written_; } |
| bool complete() const { return complete_; } |
| |
| private: |
| // For post-operation status. |
| base::PlatformFileError status_; |
| int64 bytes_written_; |
| bool complete_; |
| }; |
| |
| const char kData[] = "The quick brown fox jumps over the lazy dog.\n"; |
| const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1; |
| |
| } // namespace (anonymous) |
| |
| class FileWriterDelegateTest : public PlatformTest { |
| public: |
| FileWriterDelegateTest() |
| : loop_(MessageLoop::TYPE_IO), |
| file_(base::kInvalidPlatformFileValue) {} |
| |
| protected: |
| virtual void SetUp(); |
| virtual void TearDown(); |
| |
| virtual void SetUpTestHelper(const FilePath& base_dir) { |
| quota_file_util_.reset(QuotaFileUtil::CreateDefault()); |
| test_helper_.SetUp(base_dir, quota_file_util_.get()); |
| } |
| |
| int64 ComputeCurrentOriginUsage() { |
| base::FlushPlatformFile(file_); |
| return test_helper_.ComputeCurrentOriginUsage(); |
| } |
| |
| // Creates and sets up a FileWriterDelegate for writing the given |blob_url| |
| // to a file (file_) from |offset| with |allowed_growth| quota setting. |
| void PrepareForWrite(const GURL& blob_url, |
| int64 offset, |
| int64 allowed_growth) { |
| bool created; |
| base::PlatformFileError error_code; |
| file_ = base::CreatePlatformFile( |
| file_path_, |
| base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | |
| base::PLATFORM_FILE_ASYNC, |
| &created, &error_code); |
| ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); |
| |
| result_.reset(new Result()); |
| file_writer_delegate_.reset(new FileWriterDelegate( |
| CreateNewOperation(result_.get(), allowed_growth), |
| offset, base::MessageLoopProxy::current())); |
| request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get())); |
| } |
| |
| FileSystemOperation* CreateNewOperation(Result* result, int64 quota); |
| |
| static net::URLRequest::ProtocolFactory Factory; |
| |
| scoped_ptr<QuotaFileUtil> quota_file_util_; |
| scoped_ptr<FileWriterDelegate> file_writer_delegate_; |
| scoped_ptr<net::URLRequest> request_; |
| scoped_ptr<Result> result_; |
| FileSystemTestOriginHelper test_helper_; |
| |
| MessageLoop loop_; |
| ScopedTempDir dir_; |
| FilePath file_path_; |
| PlatformFile file_; |
| |
| static const char* content_; |
| }; |
| const char* FileWriterDelegateTest::content_ = NULL; |
| |
| namespace { |
| |
| static std::string g_content; |
| |
| class FileWriterDelegateTestJob : public net::URLRequestJob { |
| public: |
| FileWriterDelegateTestJob(net::URLRequest* request, |
| const std::string& content) |
| : net::URLRequestJob(request), |
| content_(content), |
| remaining_bytes_(content.length()), |
| cursor_(0) { |
| } |
| |
| void Start() { |
| MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete, this)); |
| } |
| |
| bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read) { |
| if (remaining_bytes_ < buf_size) |
| buf_size = static_cast<int>(remaining_bytes_); |
| |
| for (int i = 0; i < buf_size; ++i) |
| buf->data()[i] = content_[cursor_++]; |
| remaining_bytes_ -= buf_size; |
| |
| SetStatus(net::URLRequestStatus()); |
| *bytes_read = buf_size; |
| return true; |
| } |
| |
| private: |
| std::string content_; |
| int remaining_bytes_; |
| int cursor_; |
| }; |
| |
| class MockDispatcher : public FileSystemCallbackDispatcher { |
| public: |
| explicit MockDispatcher(Result* result) : result_(result) {} |
| |
| virtual void DidFail(base::PlatformFileError status) { |
| result_->set_failure_status(status); |
| MessageLoop::current()->Quit(); |
| } |
| |
| virtual void DidSucceed() { |
| ADD_FAILURE(); |
| } |
| |
| virtual void DidReadMetadata( |
| const base::PlatformFileInfo& info, |
| const FilePath& platform_path) { |
| ADD_FAILURE(); |
| } |
| |
| virtual void DidReadDirectory( |
| const std::vector<base::FileUtilProxy::Entry>& entries, |
| bool /* has_more */) { |
| ADD_FAILURE(); |
| } |
| |
| virtual void DidOpenFileSystem(const std::string&, const GURL&) { |
| ADD_FAILURE(); |
| } |
| |
| virtual void DidWrite(int64 bytes, bool complete) { |
| result_->add_bytes_written(bytes, complete); |
| if (complete) |
| MessageLoop::current()->Quit(); |
| } |
| |
| private: |
| Result* result_; |
| }; |
| |
| } // namespace (anonymous) |
| |
| // static |
| net::URLRequestJob* FileWriterDelegateTest::Factory( |
| net::URLRequest* request, |
| const std::string& scheme) { |
| return new FileWriterDelegateTestJob( |
| request, FileWriterDelegateTest::content_); |
| } |
| |
| void FileWriterDelegateTest::SetUp() { |
| ASSERT_TRUE(dir_.CreateUniqueTempDir()); |
| FilePath base_dir = dir_.path().AppendASCII("filesystem"); |
| SetUpTestHelper(base_dir); |
| ASSERT_TRUE(file_util::CreateTemporaryFileInDir( |
| test_helper_.GetOriginRootPath(), &file_path_)); |
| net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory); |
| } |
| |
| void FileWriterDelegateTest::TearDown() { |
| net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL); |
| base::ClosePlatformFile(file_); |
| test_helper_.TearDown(); |
| } |
| |
| FileSystemOperation* FileWriterDelegateTest::CreateNewOperation( |
| Result* result, int64 quota) { |
| FileSystemOperation* operation = test_helper_.NewOperation( |
| new MockDispatcher(result)); |
| FileSystemOperationContext* context = |
| operation->file_system_operation_context(); |
| context->set_allowed_bytes_growth(quota); |
| return operation; |
| } |
| |
| TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { |
| const GURL kBlobURL("blob:nolimit"); |
| content_ = kData; |
| |
| PrepareForWrite(kBlobURL, 0, QuotaFileUtil::kNoLimit); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| |
| file_writer_delegate_.reset(); |
| } |
| |
| TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) { |
| const GURL kBlobURL("blob:just"); |
| content_ = kData; |
| const int64 kAllowedGrowth = kDataSize; |
| PrepareForWrite(kBlobURL, 0, kAllowedGrowth); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| |
| file_writer_delegate_.reset(); |
| |
| EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| } |
| |
| TEST_F(FileWriterDelegateTest, WriteFailureByQuota) { |
| const GURL kBlobURL("blob:failure"); |
| content_ = kData; |
| const int64 kAllowedGrowth = kDataSize - 1; |
| PrepareForWrite(kBlobURL, 0, kAllowedGrowth); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| |
| file_writer_delegate_.reset(); |
| |
| EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| } |
| |
| TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) { |
| const GURL kBlobURL("blob:zero"); |
| content_ = ""; |
| int64 kAllowedGrowth = 0; |
| PrepareForWrite(kBlobURL, 0, kAllowedGrowth); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| |
| file_writer_delegate_.reset(); |
| |
| EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| } |
| |
| TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { |
| scoped_ptr<FileWriterDelegate> file_writer_delegate2; |
| scoped_ptr<net::URLRequest> request2; |
| scoped_ptr<Result> result2; |
| |
| FilePath file_path2; |
| PlatformFile file2; |
| bool created; |
| base::PlatformFileError error_code; |
| ASSERT_TRUE(file_util::CreateTemporaryFileInDir( |
| test_helper_.GetOriginRootPath(), &file_path2)); |
| file2 = base::CreatePlatformFile( |
| file_path2, |
| base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | |
| base::PLATFORM_FILE_ASYNC, |
| &created, &error_code); |
| ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); |
| |
| const GURL kBlobURL("blob:nolimitconcurrent"); |
| const GURL kBlobURL2("blob:nolimitconcurrent2"); |
| content_ = kData; |
| |
| PrepareForWrite(kBlobURL, 0, QuotaFileUtil::kNoLimit); |
| |
| // Credate another FileWriterDelegate for concurrent write. |
| result2.reset(new Result()); |
| file_writer_delegate2.reset(new FileWriterDelegate( |
| CreateNewOperation(result2.get(), QuotaFileUtil::kNoLimit), |
| 0, base::MessageLoopProxy::current())); |
| request2.reset(new net::URLRequest(kBlobURL2, file_writer_delegate2.get())); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| file_writer_delegate2->Start(file2, request2.get()); |
| MessageLoop::current()->Run(); |
| if (!result_->complete() || !result2->complete()) |
| MessageLoop::current()->Run(); |
| |
| ASSERT_EQ(kDataSize * 2, test_helper_.GetCachedOriginUsage()); |
| base::FlushPlatformFile(file2); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| |
| file_writer_delegate_.reset(); |
| |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| EXPECT_EQ(kDataSize, result2->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result2->status()); |
| EXPECT_TRUE(result2->complete()); |
| |
| base::ClosePlatformFile(file2); |
| } |
| |
| TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { |
| const GURL kBlobURL("blob:failure-with-updated-quota"); |
| content_ = kData; |
| |
| // Writing kDataSize (=45) bytes data while allowed_growth is 100. |
| int64 offset = 0; |
| int64 allowed_growth = 100; |
| ASSERT_LT(kDataSize, allowed_growth); |
| PrepareForWrite(kBlobURL, offset, allowed_growth); |
| |
| ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| |
| // Trying to overwrite kDataSize bytes data while allowed_growth is 20. |
| offset = 0; |
| allowed_growth = 20; |
| PrepareForWrite(kBlobURL, offset, allowed_growth); |
| |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| EXPECT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| |
| // Trying to write kDataSize bytes data from offset 25 while |
| // allowed_growth is 55. |
| offset = 25; |
| allowed_growth = 55; |
| PrepareForWrite(kBlobURL, offset, allowed_growth); |
| |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| EXPECT_EQ(offset + kDataSize, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| |
| // Trying to overwrite 45 bytes data while allowed_growth is -20. |
| offset = 0; |
| allowed_growth = -20; |
| PrepareForWrite(kBlobURL, offset, allowed_growth); |
| |
| int64 pre_write_usage = ComputeCurrentOriginUsage(); |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| EXPECT_EQ(pre_write_usage, test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(kDataSize, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| |
| // Trying to overwrite 45 bytes data with offset pre_write_usage - 20, |
| // while allowed_growth is 10. |
| const int kOverlap = 20; |
| offset = pre_write_usage - kOverlap; |
| allowed_growth = 10; |
| PrepareForWrite(kBlobURL, offset, allowed_growth); |
| |
| file_writer_delegate_->Start(file_, request_.get()); |
| MessageLoop::current()->Run(); |
| EXPECT_EQ(pre_write_usage + allowed_growth, |
| test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); |
| EXPECT_EQ(kOverlap + allowed_growth, result_->bytes_written()); |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); |
| EXPECT_TRUE(result_->complete()); |
| } |
| |
| class FileWriterDelegateUnlimitedTest : public FileWriterDelegateTest { |
| protected: |
| virtual void SetUpTestHelper(const FilePath& path) OVERRIDE; |
| }; |
| |
| } // namespace fileapi |