blob: 06176adc19c3bc5eb2bf82819313eaea3c10131e [file] [log] [blame]
// Copyright (c) 2012 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 <string>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.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_context.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_context.h"
#include "webkit/fileapi/file_writer_delegate.h"
#include "webkit/fileapi/local_file_system_operation.h"
#include "webkit/fileapi/local_file_system_test_helper.h"
#include "webkit/fileapi/sandbox_file_stream_writer.h"
namespace fileapi {
namespace {
class Result {
public:
Result()
: status_(base::PLATFORM_FILE_OK),
bytes_written_(0),
write_status_(FileWriterDelegate::SUCCESS_IO_PENDING) {}
base::PlatformFileError status() const { return status_; }
int64 bytes_written() const { return bytes_written_; }
FileWriterDelegate::WriteProgressStatus write_status() const {
return write_status_;
}
void DidWrite(base::PlatformFileError status, int64 bytes,
FileWriterDelegate::WriteProgressStatus write_status) {
write_status_ = write_status;
if (status == base::PLATFORM_FILE_OK) {
bytes_written_ += bytes;
if (write_status_ != FileWriterDelegate::SUCCESS_IO_PENDING)
MessageLoop::current()->Quit();
} else {
EXPECT_EQ(base::PLATFORM_FILE_OK, status_);
status_ = status;
MessageLoop::current()->Quit();
}
}
private:
// For post-operation status.
base::PlatformFileError status_;
int64 bytes_written_;
FileWriterDelegate::WriteProgressStatus write_status_;
};
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),
test_helper_(GURL("http://example.com"), kFileSystemTypeTest) {}
protected:
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
FileSystemFileUtil* file_util() {
return test_helper_.file_util();
}
int64 ComputeCurrentOriginUsage() {
return test_helper_.ComputeCurrentOriginUsage();
}
FileSystemURL GetFileSystemURL(const char* file_name) const {
return test_helper_.CreateURLFromUTF8(file_name);
}
FileWriterDelegate* CreateWriterDelegate(
const char* test_file_path,
int64 offset,
int64 allowed_growth,
Result* result) {
SandboxFileStreamWriter* writer = new SandboxFileStreamWriter(
test_helper_.file_system_context(),
GetFileSystemURL(test_file_path),
offset,
*test_helper_.file_system_context()->GetUpdateObservers(
test_helper_.type()));
writer->set_default_quota(allowed_growth);
return new FileWriterDelegate(
base::Bind(&Result::DidWrite, base::Unretained(result)),
scoped_ptr<FileStreamWriter>(writer));
}
// Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
// and creates a new FileWriterDelegate for the file.
void PrepareForWrite(const GURL& blob_url,
int64 offset,
int64 allowed_growth) {
result_.reset(new Result());
file_writer_delegate_.reset(
CreateWriterDelegate("test", offset, allowed_growth, result_.get()));
request_.reset(empty_context_.CreateRequest(
blob_url, file_writer_delegate_.get()));
}
static net::URLRequest::ProtocolFactory Factory;
// This should be alive until the very end of this instance.
MessageLoop loop_;
net::URLRequestContext empty_context_;
scoped_ptr<FileWriterDelegate> file_writer_delegate_;
scoped_ptr<net::URLRequest> request_;
scoped_ptr<Result> result_;
LocalFileSystemTestOriginHelper test_helper_;
ScopedTempDir dir_;
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,
net::NetworkDelegate* network_delegate,
const std::string& content)
: net::URLRequestJob(request, network_delegate),
content_(content),
remaining_bytes_(content.length()),
cursor_(0) {
}
virtual void Start() OVERRIDE {
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete, this));
}
virtual bool ReadRawData(net::IOBuffer* buf,
int buf_size,
int *bytes_read) OVERRIDE {
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;
}
virtual int GetResponseCode() const OVERRIDE {
return 200;
}
protected:
virtual ~FileWriterDelegateTestJob() {}
private:
std::string content_;
int remaining_bytes_;
int cursor_;
};
} // namespace (anonymous)
// static
net::URLRequestJob* FileWriterDelegateTest::Factory(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& scheme) {
return new FileWriterDelegateTestJob(
request, network_delegate, FileWriterDelegateTest::content_);
}
void FileWriterDelegateTest::SetUp() {
ASSERT_TRUE(dir_.CreateUniqueTempDir());
FilePath base_dir = dir_.path().AppendASCII("filesystem");
test_helper_.SetUp(base_dir, NULL);
scoped_ptr<FileSystemOperationContext> context(
test_helper_.NewOperationContext());
context->set_allowed_bytes_growth(kint64max);
bool created = false;
base::PlatformFileError error = file_util()->EnsureFileExists(
context.get(),
GetFileSystemURL("test"),
&created);
ASSERT_EQ(base::PLATFORM_FILE_OK, error);
ASSERT_TRUE(created);
net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory);
}
void FileWriterDelegateTest::TearDown() {
net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL);
test_helper_.TearDown();
}
// FileWriterDelegateTest.WriteSuccessWithoutQuotaLimit is flaky on windows
// http://crbug.com/130401
#if defined(OS_WIN)
#define MAYBE_WriteSuccessWithoutQuotaLimit DISABLED_WriteSuccessWithoutQuotaLimit
#else
#define MAYBE_WriteSuccessWithoutQuotaLimit WriteSuccessWithoutQuotaLimit
#endif
TEST_F(FileWriterDelegateTest, MAYBE_WriteSuccessWithoutQuotaLimit) {
const GURL kBlobURL("blob:nolimit");
content_ = kData;
PrepareForWrite(kBlobURL, 0, quota::QuotaManager::kNoLimit);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
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());
}
// FileWriterDelegateTest.WriteSuccessWithJustQuota is flaky on windows
// http://crbug.com/130401
#if defined(OS_WIN)
#define MAYBE_WriteSuccessWithJustQuota DISABLED_WriteSuccessWithJustQuota
#else
#define MAYBE_WriteSuccessWithJustQuota WriteSuccessWithJustQuota
#endif
TEST_F(FileWriterDelegateTest, MAYBE_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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
}
TEST_F(FileWriterDelegateTest, DISABLED_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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status());
file_writer_delegate_.reset();
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status());
ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status());
}
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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
}
#if defined(OS_WIN)
// See http://crbug.com/129264
#define MAYBE_WriteSuccessWithoutQuotaLimitConcurrent \
DISABLED_WriteSuccessWithoutQuotaLimitConcurrent
#else
#define MAYBE_WriteSuccessWithoutQuotaLimitConcurrent \
WriteSuccessWithoutQuotaLimitConcurrent
#endif
TEST_F(FileWriterDelegateTest, MAYBE_WriteSuccessWithoutQuotaLimitConcurrent) {
scoped_ptr<FileWriterDelegate> file_writer_delegate2;
scoped_ptr<net::URLRequest> request2;
scoped_ptr<Result> result2;
scoped_ptr<FileSystemOperationContext> context(
test_helper_.NewOperationContext());
bool created = false;
file_util()->EnsureFileExists(context.get(),
GetFileSystemURL("test2"),
&created);
ASSERT_TRUE(created);
const GURL kBlobURL("blob:nolimitconcurrent");
const GURL kBlobURL2("blob:nolimitconcurrent2");
content_ = kData;
PrepareForWrite(kBlobURL, 0, quota::QuotaManager::kNoLimit);
// Credate another FileWriterDelegate for concurrent write.
result2.reset(new Result());
file_writer_delegate2.reset(CreateWriterDelegate(
"test2", 0, quota::QuotaManager::kNoLimit, result2.get()));
request2.reset(empty_context_.CreateRequest(
kBlobURL2, file_writer_delegate2.get()));
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
file_writer_delegate_->Start(request_.Pass());
file_writer_delegate2->Start(request2.Pass());
MessageLoop::current()->Run();
if (result_->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING ||
result2->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING)
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result2->write_status());
file_writer_delegate_.reset();
file_writer_delegate2.reset();
ASSERT_EQ(kDataSize * 2, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
EXPECT_EQ(kDataSize, result2->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result2->status());
}
#if defined(OS_WIN)
// See http://crbug.com/129264
#define MAYBE_WritesWithQuotaAndOffset \
DISABLED_WritesWithQuotaAndOffset
#else
#define MAYBE_WritesWithQuotaAndOffset \
WritesWithQuotaAndOffset
#endif
TEST_F(FileWriterDelegateTest, MAYBE_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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
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());
// 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(request_.Pass());
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());
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
// 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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
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());
// 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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status());
file_writer_delegate_.reset();
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());
// 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(request_.Pass());
MessageLoop::current()->Run();
ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status());
file_writer_delegate_.reset();
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());
}
} // namespace fileapi