// 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 #include #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 quota_file_util_; scoped_ptr file_writer_delegate_; scoped_ptr request_; scoped_ptr 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(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& 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 file_writer_delegate2; scoped_ptr request2; scoped_ptr 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