blob: f1d7a59aec793e2e39abe6f5d1ee2a07e4c16ad6 [file] [log] [blame]
// 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