| // 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. |
| |
| #include <deque> |
| #include <limits> |
| #include <string> |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/platform_file.h" |
| #include "content/renderer/pepper/quota_file_io.h" |
| #include "content/test/ppapi_unittest.h" |
| |
| using base::MessageLoopProxy; |
| using base::PlatformFile; |
| using base::PlatformFileError; |
| |
| namespace content { |
| |
| namespace { |
| class QuotaMockDelegate : public QuotaFileIO::Delegate { |
| public: |
| typedef QuotaFileIO::Delegate::AvailableSpaceCallback Callback; |
| |
| QuotaMockDelegate() |
| : available_space_(0), |
| will_update_count_(0), |
| file_thread_(MessageLoopProxy::current()), |
| weak_factory_(this) { |
| } |
| virtual ~QuotaMockDelegate() {} |
| |
| virtual void QueryAvailableSpace( |
| const GURL& origin, |
| quota::StorageType type, |
| const Callback& callback) OVERRIDE { |
| DCHECK_EQ(false, callback.is_null()); |
| MessageLoopProxy::current()->PostTask( |
| FROM_HERE, base::Bind( |
| &QuotaMockDelegate::RunAvailableSpaceCallback, |
| weak_factory_.GetWeakPtr(), callback)); |
| } |
| |
| virtual void WillUpdateFile(const GURL& file_path) OVERRIDE { |
| file_path_ = file_path; |
| ++will_update_count_; |
| } |
| |
| virtual void DidUpdateFile(const GURL& file_path, int64_t delta) OVERRIDE { |
| ASSERT_EQ(file_path_, file_path); |
| ASSERT_GT(will_update_count_, 0); |
| --will_update_count_; |
| available_space_ -= delta; |
| } |
| |
| virtual scoped_refptr<base::MessageLoopProxy> |
| GetFileThreadMessageLoopProxy() OVERRIDE { |
| return file_thread_; |
| } |
| |
| void set_available_space(int64 available) { available_space_ = available; } |
| int64_t available_space() const { return available_space_; } |
| |
| private: |
| void RunAvailableSpaceCallback(const Callback& callback) { |
| callback.Run(available_space_); |
| } |
| |
| int64_t available_space_; |
| int will_update_count_; |
| GURL file_path_; |
| scoped_refptr<MessageLoopProxy> file_thread_; |
| base::WeakPtrFactory<QuotaMockDelegate> weak_factory_; |
| }; |
| } // namespace |
| |
| class QuotaFileIOTest : public PpapiUnittest { |
| public: |
| QuotaFileIOTest() |
| : delegate_(NULL), |
| weak_factory_(this) {} |
| |
| virtual void SetUp() OVERRIDE { |
| PpapiUnittest::SetUp(); |
| ASSERT_TRUE(dir_.CreateUniqueTempDir()); |
| base::FilePath path; |
| ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_.path(), &path)); |
| int file_flags = base::PLATFORM_FILE_OPEN | |
| base::PLATFORM_FILE_READ | |
| base::PLATFORM_FILE_WRITE | |
| base::PLATFORM_FILE_WRITE_ATTRIBUTES; |
| bool created = false; |
| file_ = base::kInvalidPlatformFileValue; |
| PlatformFileError error = base::PLATFORM_FILE_OK; |
| file_ = base::CreatePlatformFile(path, file_flags, &created, &error); |
| ASSERT_EQ(base::PLATFORM_FILE_OK, error); |
| ASSERT_NE(base::kInvalidPlatformFileValue, file_); |
| ASSERT_FALSE(created); |
| delegate_ = new QuotaMockDelegate; // Owned by QuotaFileIO. |
| quota_file_io_.reset(new QuotaFileIO( |
| delegate_, file_, GURL(), PP_FILESYSTEMTYPE_LOCALTEMPORARY)); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| quota_file_io_.reset(); |
| if (file_ != base::kInvalidPlatformFileValue) |
| base::ClosePlatformFile(file_); |
| PpapiUnittest::TearDown(); |
| } |
| |
| protected: |
| void WriteTestBody() { |
| // Attempt to write zero bytes. |
| EXPECT_FALSE(quota_file_io_->Write( |
| 0, "data", 0, |
| base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); |
| // Attempt to write negative number of bytes. |
| EXPECT_FALSE(quota_file_io_->Write( |
| 0, "data", std::numeric_limits<int32_t>::min(), |
| base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); |
| |
| delegate()->set_available_space(100); |
| std::string read_buffer; |
| |
| // Write 8 bytes at offset 0 (-> length=8). |
| std::string data("12345678"); |
| Write(0, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 8, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(8, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ(data, read_buffer); |
| |
| // Write 5 bytes at offset 5 (-> length=10). |
| data = "55555"; |
| Write(5, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 10, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(10, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ("1234555555", read_buffer); |
| |
| // Write 7 bytes at offset 8 (-> length=15). |
| data = "9012345"; |
| Write(8, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 15, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(15, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ("123455559012345", read_buffer); |
| |
| // Write 2 bytes at offset 2 (-> length=15). |
| data = "33"; |
| Write(2, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 15, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(15, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ("123355559012345", read_buffer); |
| |
| // Write 4 bytes at offset 20 (-> length=24). |
| data = "XXXX"; |
| Write(20, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 24, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(24, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ(std::string("123355559012345\0\0\0\0\0XXXX", 24), read_buffer); |
| |
| delegate()->set_available_space(5); |
| |
| // Quota error case. Write 7 bytes at offset 23 (-> length is unchanged) |
| data = "ABCDEFG"; |
| Write(23, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); |
| EXPECT_EQ(5, delegate()->available_space()); |
| reset_results(); |
| |
| // Overlapping write. Write 6 bytes at offset 2 (-> length is unchanged) |
| data = "ABCDEF"; |
| Write(2, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(5, delegate()->available_space()); |
| reset_results(); |
| |
| // Overlapping + extending the file size, but within the quota. |
| // Write 6 bytes at offset 23 (-> length=29). |
| Write(23, data); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(0, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(29, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ(std::string("12ABCDEF9012345\0\0\0\0\0XXXABCDEF", 29), |
| read_buffer); |
| } |
| |
| void SetLengthTestBody() { |
| delegate()->set_available_space(100); |
| |
| SetLength(0); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(0, GetPlatformFileSize()); |
| EXPECT_EQ(100, delegate()->available_space()); |
| reset_results(); |
| |
| SetLength(8); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 8, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(8, GetPlatformFileSize()); |
| |
| SetLength(16); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 16, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(16, GetPlatformFileSize()); |
| |
| SetLength(4); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100 - 4, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(4, GetPlatformFileSize()); |
| |
| SetLength(0); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| EXPECT_EQ(100, delegate()->available_space()); |
| reset_results(); |
| |
| EXPECT_EQ(0, GetPlatformFileSize()); |
| |
| delegate()->set_available_space(5); |
| |
| // Quota error case. |
| SetLength(7); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(1U, num_results()); |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); |
| EXPECT_EQ(5, delegate()->available_space()); |
| reset_results(); |
| } |
| |
| QuotaMockDelegate* delegate() { |
| return delegate_; |
| } |
| |
| void Write(int64_t offset, const std::string& data) { |
| ASSERT_TRUE(quota_file_io_->Write( |
| offset, data.c_str(), data.size(), |
| base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); |
| } |
| |
| void SetLength(int64_t length) { |
| ASSERT_TRUE(quota_file_io_->SetLength( |
| length, |
| base::Bind(&QuotaFileIOTest::DidSetLength, |
| weak_factory_.GetWeakPtr()))); |
| } |
| |
| void DidWrite(PlatformFileError status, int bytes_written) { |
| status_.push_back(status); |
| bytes_written_.push_back(bytes_written); |
| } |
| |
| void DidSetLength(PlatformFileError status) { |
| status_.push_back(status); |
| } |
| |
| size_t num_results() const { return status_.size(); } |
| const std::deque<int>& bytes_written() const { return bytes_written_; } |
| const std::deque<PlatformFileError>& status() const { return status_; } |
| |
| void reset_results() { |
| bytes_written_.clear(); |
| status_.clear(); |
| } |
| |
| void pop_result() { |
| bytes_written_.pop_front(); |
| status_.pop_front(); |
| } |
| |
| void ReadPlatformFile(std::string* data) { |
| data->clear(); |
| char buf[256]; |
| int32_t read_offset = 0; |
| for (;;) { |
| int rv = base::ReadPlatformFile(file_, read_offset, buf, sizeof(buf)); |
| ASSERT_GE(rv, 0); |
| if (rv == 0) |
| break; |
| read_offset += rv; |
| data->append(buf, rv); |
| } |
| } |
| |
| int64_t GetPlatformFileSize() { |
| base::PlatformFileInfo info; |
| EXPECT_TRUE(base::GetPlatformFileInfo(file_, &info)); |
| return info.size; |
| } |
| |
| void SetPlatformFileSize(int64_t length) { |
| EXPECT_TRUE(base::TruncatePlatformFile(file_, length)); |
| } |
| |
| private: |
| base::ScopedTempDir dir_; |
| PlatformFile file_; |
| scoped_ptr<QuotaFileIO> quota_file_io_; |
| std::deque<int> bytes_written_; |
| std::deque<PlatformFileError> status_; |
| QuotaMockDelegate* delegate_; |
| base::WeakPtrFactory<QuotaFileIOTest> weak_factory_; |
| }; |
| |
| TEST_F(QuotaFileIOTest, Write) { |
| WriteTestBody(); |
| } |
| |
| TEST_F(QuotaFileIOTest, SetLength) { |
| SetLengthTestBody(); |
| } |
| |
| TEST_F(QuotaFileIOTest, ParallelWrites) { |
| delegate()->set_available_space(22); |
| std::string read_buffer; |
| |
| const std::string data1[] = { |
| std::string("12345678"), |
| std::string("55555"), |
| std::string("9012345"), |
| }; |
| Write(0, data1[0]); |
| Write(5, data1[1]); |
| Write(8, data1[2]); |
| base::MessageLoop::current()->RunUntilIdle(); |
| |
| ASSERT_EQ(ARRAYSIZE_UNSAFE(data1), num_results()); |
| for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data1); ++i) { |
| EXPECT_EQ(static_cast<int>(data1[i].size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| pop_result(); |
| } |
| |
| EXPECT_EQ(22 - 15, delegate()->available_space()); |
| EXPECT_EQ(15, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ("123455559012345", read_buffer); |
| |
| // The second write will fail for quota error. |
| const std::string data2[] = { |
| std::string("33"), |
| std::string("XXXX"), |
| }; |
| Write(2, data2[0]); |
| Write(20, data2[1]); |
| base::MessageLoop::current()->RunUntilIdle(); |
| |
| ASSERT_EQ(ARRAYSIZE_UNSAFE(data2), num_results()); |
| EXPECT_EQ(static_cast<int>(data2[0].size()), bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); |
| pop_result(); |
| EXPECT_EQ(0, bytes_written().front()); |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); |
| pop_result(); |
| |
| EXPECT_EQ(22 - 15, delegate()->available_space()); |
| EXPECT_EQ(15, GetPlatformFileSize()); |
| ReadPlatformFile(&read_buffer); |
| EXPECT_EQ("123355559012345", read_buffer); |
| } |
| |
| } // namespace content |