summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/fileapi
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/content/browser/fileapi
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/content/browser/fileapi')
-rw-r--r--chromium/content/browser/fileapi/blob_url_request_job_unittest.cc438
-rw-r--r--chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc326
-rw-r--r--chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc866
-rw-r--r--chromium/content/browser/fileapi/dragged_file_util_unittest.cc544
-rw-r--r--chromium/content/browser/fileapi/file_system_browsertest.cc4
-rw-r--r--chromium/content/browser/fileapi/file_system_context_unittest.cc374
-rw-r--r--chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc321
-rw-r--r--chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc266
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc1241
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc328
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc162
-rw-r--r--chromium/content/browser/fileapi/file_system_quota_client_unittest.cc561
-rw-r--r--chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc368
-rw-r--r--chromium/content/browser/fileapi/file_writer_delegate_unittest.cc455
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter.cc216
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter.h42
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc25
-rw-r--r--chromium/content/browser/fileapi/local_file_util_unittest.cc388
-rw-r--r--chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc2490
-rw-r--r--chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc147
-rw-r--r--chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc280
-rw-r--r--chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc82
-rw-r--r--chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc319
-rw-r--r--chromium/content/browser/fileapi/transient_file_util_unittest.cc121
-rw-r--r--chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc278
25 files changed, 10400 insertions, 242 deletions
diff --git a/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc b/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc
new file mode 100644
index 00000000000..a09456b607f
--- /dev/null
+++ b/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc
@@ -0,0 +1,438 @@
+// Copyright 2013 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 "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_byte_range.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/blob/blob_url_request_job.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/common/blob/blob_data.h"
+
+namespace webkit_blob {
+
+namespace {
+
+const int kBufferSize = 1024;
+const char kTestData1[] = "Hello";
+const char kTestData2[] = "Here it is data.";
+const char kTestFileData1[] = "0123456789";
+const char kTestFileData2[] = "This is sample file.";
+const char kTestFileSystemFileData1[] = "abcdefghijklmnop";
+const char kTestFileSystemFileData2[] = "File system file test data.";
+const char kTestContentType[] = "foo/bar";
+const char kTestContentDisposition[] = "attachment; filename=foo.txt";
+
+const char kFileSystemURLOrigin[] = "http://remote";
+const fileapi::FileSystemType kFileSystemType =
+ fileapi::kFileSystemTypeTemporary;
+
+} // namespace
+
+class BlobURLRequestJobTest : public testing::Test {
+ public:
+
+ // Test Harness -------------------------------------------------------------
+ // TODO(jianli): share this test harness with AppCacheURLRequestJobTest
+
+ class MockURLRequestDelegate : public net::URLRequest::Delegate {
+ public:
+ MockURLRequestDelegate()
+ : received_data_(new net::IOBuffer(kBufferSize)) {}
+
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
+ if (request->status().is_success()) {
+ EXPECT_TRUE(request->response_headers());
+ ReadSome(request);
+ } else {
+ RequestComplete();
+ }
+ }
+
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE {
+ if (bytes_read > 0)
+ ReceiveData(request, bytes_read);
+ else
+ RequestComplete();
+ }
+
+ const std::string& response_data() const { return response_data_; }
+
+ private:
+ void ReadSome(net::URLRequest* request) {
+ if (!request->is_pending()) {
+ RequestComplete();
+ return;
+ }
+
+ int bytes_read = 0;
+ if (!request->Read(received_data_.get(), kBufferSize, &bytes_read)) {
+ if (!request->status().is_io_pending()) {
+ RequestComplete();
+ }
+ return;
+ }
+
+ ReceiveData(request, bytes_read);
+ }
+
+ void ReceiveData(net::URLRequest* request, int bytes_read) {
+ if (bytes_read) {
+ response_data_.append(received_data_->data(),
+ static_cast<size_t>(bytes_read));
+ ReadSome(request);
+ } else {
+ RequestComplete();
+ }
+ }
+
+ void RequestComplete() {
+ base::MessageLoop::current()->Quit();
+ }
+
+ scoped_refptr<net::IOBuffer> received_data_;
+ std::string response_data_;
+ };
+
+ // A simple ProtocolHandler implementation to create BlobURLRequestJob.
+ class MockProtocolHandler :
+ public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ MockProtocolHandler(BlobURLRequestJobTest* test) : test_(test) {}
+
+ // net::URLRequestJobFactory::ProtocolHandler override.
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return new BlobURLRequestJob(request,
+ network_delegate,
+ test_->blob_data_.get(),
+ test_->file_system_context_.get(),
+ base::MessageLoopProxy::current().get());
+ }
+
+ private:
+ BlobURLRequestJobTest* test_;
+ };
+
+ BlobURLRequestJobTest()
+ : blob_data_(new BlobData()),
+ expected_status_code_(0) {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ temp_file1_ = temp_dir_.path().AppendASCII("BlobFile1.dat");
+ ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1),
+ file_util::WriteFile(temp_file1_, kTestFileData1,
+ arraysize(kTestFileData1) - 1));
+ base::PlatformFileInfo file_info1;
+ base::GetFileInfo(temp_file1_, &file_info1);
+ temp_file_modification_time1_ = file_info1.last_modified;
+
+ temp_file2_ = temp_dir_.path().AppendASCII("BlobFile2.dat");
+ ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1),
+ file_util::WriteFile(temp_file2_, kTestFileData2,
+ arraysize(kTestFileData2) - 1));
+ base::PlatformFileInfo file_info2;
+ base::GetFileInfo(temp_file2_, &file_info2);
+ temp_file_modification_time2_ = file_info2.last_modified;
+
+ url_request_job_factory_.SetProtocolHandler("blob",
+ new MockProtocolHandler(this));
+ url_request_context_.set_job_factory(&url_request_job_factory_);
+ }
+
+ virtual void TearDown() {
+ }
+
+ void SetUpFileSystem() {
+ // Prepare file system.
+ file_system_context_ = fileapi::CreateFileSystemContextForTesting(
+ NULL, temp_dir_.path());
+
+ file_system_context_->OpenFileSystem(
+ GURL(kFileSystemURLOrigin),
+ kFileSystemType,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem,
+ base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(file_system_root_url_.is_valid());
+
+ // Prepare files on file system.
+ const char kFilename1[] = "FileSystemFile1.dat";
+ temp_file_system_file1_ = GetFileSystemURL(kFilename1);
+ WriteFileSystemFile(kFilename1, kTestFileSystemFileData1,
+ arraysize(kTestFileSystemFileData1) - 1,
+ &temp_file_system_file_modification_time1_);
+ const char kFilename2[] = "FileSystemFile2.dat";
+ temp_file_system_file2_ = GetFileSystemURL(kFilename2);
+ WriteFileSystemFile(kFilename2, kTestFileSystemFileData2,
+ arraysize(kTestFileSystemFileData2) - 1,
+ &temp_file_system_file_modification_time2_);
+ }
+
+ GURL GetFileSystemURL(const std::string& filename) {
+ return GURL(file_system_root_url_.spec() + filename);
+ }
+
+ void WriteFileSystemFile(const std::string& filename,
+ const char* buf, int buf_size,
+ base::Time* modification_time) {
+ fileapi::FileSystemURL url =
+ file_system_context_->CreateCrackedFileSystemURL(
+ GURL(kFileSystemURLOrigin),
+ kFileSystemType,
+ base::FilePath().AppendASCII(filename));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ fileapi::AsyncFileTestHelper::CreateFileWithData(
+ file_system_context_, url, buf, buf_size));
+
+ base::PlatformFileInfo file_info;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ fileapi::AsyncFileTestHelper::GetMetadata(
+ file_system_context_, url, &file_info));
+ if (modification_time)
+ *modification_time = file_info.last_modified;
+ }
+
+ void OnValidateFileSystem(const GURL& root,
+ const std::string& name,
+ base::PlatformFileError result) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ ASSERT_TRUE(root.is_valid());
+ file_system_root_url_ = root;
+ }
+
+ void TestSuccessRequest(const std::string& expected_response) {
+ expected_status_code_ = 200;
+ expected_response_ = expected_response;
+ TestRequest("GET", net::HttpRequestHeaders());
+ }
+
+ void TestErrorRequest(int expected_status_code) {
+ expected_status_code_ = expected_status_code;
+ expected_response_ = "";
+ TestRequest("GET", net::HttpRequestHeaders());
+ }
+
+ void TestRequest(const std::string& method,
+ const net::HttpRequestHeaders& extra_headers) {
+ request_ = url_request_context_.CreateRequest(
+ GURL("blob:blah"), net::DEFAULT_PRIORITY, &url_request_delegate_);
+ request_->set_method(method);
+ if (!extra_headers.IsEmpty())
+ request_->SetExtraRequestHeaders(extra_headers);
+ request_->Start();
+
+ base::MessageLoop::current()->Run();
+
+ // Verify response.
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_EQ(expected_status_code_,
+ request_->response_headers()->response_code());
+ EXPECT_EQ(expected_response_, url_request_delegate_.response_data());
+ }
+
+ void BuildComplicatedData(std::string* expected_result) {
+ blob_data_->AppendData(kTestData1 + 1, 2);
+ blob_data_->AppendFile(temp_file1_, 2, 3, temp_file_modification_time1_);
+ blob_data_->AppendFileSystemFile(temp_file_system_file1_, 3, 4,
+ temp_file_system_file_modification_time1_);
+ blob_data_->AppendData(kTestData2 + 4, 5);
+ blob_data_->AppendFile(temp_file2_, 5, 6, temp_file_modification_time2_);
+ blob_data_->AppendFileSystemFile(temp_file_system_file2_, 6, 7,
+ temp_file_system_file_modification_time2_);
+ *expected_result = std::string(kTestData1 + 1, 2);
+ *expected_result += std::string(kTestFileData1 + 2, 3);
+ *expected_result += std::string(kTestFileSystemFileData1 + 3, 4);
+ *expected_result += std::string(kTestData2 + 4, 5);
+ *expected_result += std::string(kTestFileData2 + 5, 6);
+ *expected_result += std::string(kTestFileSystemFileData2 + 6, 7);
+ }
+
+ protected:
+ base::ScopedTempDir temp_dir_;
+ base::FilePath temp_file1_;
+ base::FilePath temp_file2_;
+ base::Time temp_file_modification_time1_;
+ base::Time temp_file_modification_time2_;
+ GURL file_system_root_url_;
+ GURL temp_file_system_file1_;
+ GURL temp_file_system_file2_;
+ base::Time temp_file_system_file_modification_time1_;
+ base::Time temp_file_system_file_modification_time2_;
+
+ base::MessageLoopForIO message_loop_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_refptr<BlobData> blob_data_;
+ net::URLRequestJobFactoryImpl url_request_job_factory_;
+ net::URLRequestContext url_request_context_;
+ MockURLRequestDelegate url_request_delegate_;
+ scoped_ptr<net::URLRequest> request_;
+
+ int expected_status_code_;
+ std::string expected_response_;
+};
+
+TEST_F(BlobURLRequestJobTest, TestGetSimpleDataRequest) {
+ blob_data_->AppendData(kTestData1);
+ TestSuccessRequest(kTestData1);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetSimpleFileRequest) {
+ blob_data_->AppendFile(temp_file1_, 0, -1, base::Time());
+ TestSuccessRequest(kTestFileData1);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetLargeFileRequest) {
+ base::FilePath large_temp_file =
+ temp_dir_.path().AppendASCII("LargeBlob.dat");
+ std::string large_data;
+ large_data.reserve(kBufferSize * 5);
+ for (int i = 0; i < kBufferSize * 5; ++i)
+ large_data.append(1, static_cast<char>(i % 256));
+ ASSERT_EQ(static_cast<int>(large_data.size()),
+ file_util::WriteFile(large_temp_file, large_data.data(),
+ large_data.size()));
+ blob_data_->AppendFile(large_temp_file, 0, -1, base::Time());
+ TestSuccessRequest(large_data);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
+ base::FilePath non_existent_file =
+ temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
+ blob_data_->AppendFile(non_existent_file, 0, -1, base::Time());
+ TestErrorRequest(404);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetChangedFileRequest) {
+ base::Time old_time =
+ temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10);
+ blob_data_->AppendFile(temp_file1_, 0, 3, old_time);
+ TestErrorRequest(404);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
+ blob_data_->AppendFile(temp_file1_, 2, 4, temp_file_modification_time1_);
+ std::string result(kTestFileData1 + 2, 4);
+ TestSuccessRequest(result);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) {
+ SetUpFileSystem();
+ blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, -1,
+ base::Time());
+ TestSuccessRequest(kTestFileSystemFileData1);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
+ SetUpFileSystem();
+ std::string large_data;
+ large_data.reserve(kBufferSize * 5);
+ for (int i = 0; i < kBufferSize * 5; ++i)
+ large_data.append(1, static_cast<char>(i % 256));
+
+ const char kFilename[] = "LargeBlob.dat";
+ WriteFileSystemFile(kFilename, large_data.data(), large_data.size(), NULL);
+
+ blob_data_->AppendFileSystemFile(GetFileSystemURL(kFilename),
+ 0, -1, base::Time());
+ TestSuccessRequest(large_data);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
+ SetUpFileSystem();
+ GURL non_existent_file = GetFileSystemURL("non-existent.dat");
+ blob_data_->AppendFileSystemFile(non_existent_file, 0, -1, base::Time());
+ TestErrorRequest(404);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
+ SetUpFileSystem();
+ base::Time old_time =
+ temp_file_system_file_modification_time1_ -
+ base::TimeDelta::FromSeconds(10);
+ blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, 3, old_time);
+ TestErrorRequest(404);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
+ SetUpFileSystem();
+ blob_data_->AppendFileSystemFile(temp_file_system_file1_, 2, 4,
+ temp_file_system_file_modification_time1_);
+ std::string result(kTestFileSystemFileData1 + 2, 4);
+ TestSuccessRequest(result);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataAndFileRequest) {
+ SetUpFileSystem();
+ std::string result;
+ BuildComplicatedData(&result);
+ TestSuccessRequest(result);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetRangeRequest1) {
+ SetUpFileSystem();
+ std::string result;
+ BuildComplicatedData(&result);
+ net::HttpRequestHeaders extra_headers;
+ extra_headers.SetHeader(net::HttpRequestHeaders::kRange,
+ net::HttpByteRange::Bounded(5, 10).GetHeaderValue());
+ expected_status_code_ = 206;
+ expected_response_ = result.substr(5, 10 - 5 + 1);
+ TestRequest("GET", extra_headers);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetRangeRequest2) {
+ SetUpFileSystem();
+ std::string result;
+ BuildComplicatedData(&result);
+ net::HttpRequestHeaders extra_headers;
+ extra_headers.SetHeader(net::HttpRequestHeaders::kRange,
+ net::HttpByteRange::Suffix(10).GetHeaderValue());
+ expected_status_code_ = 206;
+ expected_response_ = result.substr(result.length() - 10);
+ TestRequest("GET", extra_headers);
+}
+
+TEST_F(BlobURLRequestJobTest, TestExtraHeaders) {
+ blob_data_->set_content_type(kTestContentType);
+ blob_data_->set_content_disposition(kTestContentDisposition);
+ blob_data_->AppendData(kTestData1);
+ expected_status_code_ = 200;
+ expected_response_ = kTestData1;
+ TestRequest("GET", net::HttpRequestHeaders());
+
+ std::string content_type;
+ EXPECT_TRUE(request_->response_headers()->GetMimeType(&content_type));
+ EXPECT_EQ(kTestContentType, content_type);
+ void* iter = NULL;
+ std::string content_disposition;
+ EXPECT_TRUE(request_->response_headers()->EnumerateHeader(
+ &iter, "Content-Disposition", &content_disposition));
+ EXPECT_EQ(kTestContentDisposition, content_disposition);
+}
+
+} // namespace webkit_blob
diff --git a/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc b/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc
new file mode 100644
index 00000000000..1dd3f986552
--- /dev/null
+++ b/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc
@@ -0,0 +1,326 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_backend.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+namespace fileapi {
+
+namespace {
+
+const FileSystemType kNoValidatorType = kFileSystemTypeTemporary;
+const FileSystemType kWithValidatorType = kFileSystemTypeTest;
+
+void ExpectOk(const GURL& origin_url,
+ const std::string& name,
+ base::PlatformFileError error) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+}
+
+class CopyOrMoveFileValidatorTestHelper {
+ public:
+ CopyOrMoveFileValidatorTestHelper(
+ const GURL& origin,
+ FileSystemType src_type,
+ FileSystemType dest_type)
+ : origin_(origin),
+ src_type_(src_type),
+ dest_type_(dest_type) {}
+
+ ~CopyOrMoveFileValidatorTestHelper() {
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void SetUp() {
+ ASSERT_TRUE(base_.CreateUniqueTempDir());
+ base::FilePath base_dir = base_.path();
+
+ file_system_context_ = CreateFileSystemContextForTesting(NULL, base_dir);
+
+ // Set up TestFileSystemBackend to require CopyOrMoveFileValidator.
+ FileSystemBackend* test_file_system_backend =
+ file_system_context_->GetFileSystemBackend(kWithValidatorType);
+ static_cast<TestFileSystemBackend*>(test_file_system_backend)->
+ set_require_copy_or_move_validator(true);
+
+ // Sets up source.
+ FileSystemBackend* src_file_system_backend =
+ file_system_context_->GetFileSystemBackend(src_type_);
+ src_file_system_backend->OpenFileSystem(
+ origin_, src_type_,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&ExpectOk));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(SourceURL("")));
+
+ // Sets up dest.
+ DCHECK_EQ(kWithValidatorType, dest_type_);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(DestURL("")));
+
+ copy_src_ = SourceURL("copy_src.jpg");
+ move_src_ = SourceURL("move_src.jpg");
+ copy_dest_ = DestURL("copy_dest.jpg");
+ move_dest_ = DestURL("move_dest.jpg");
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(copy_src_, 10));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(move_src_, 10));
+
+ ASSERT_TRUE(FileExists(copy_src_, 10));
+ ASSERT_TRUE(FileExists(move_src_, 10));
+ ASSERT_FALSE(FileExists(copy_dest_, 10));
+ ASSERT_FALSE(FileExists(move_dest_, 10));
+ }
+
+ void SetMediaCopyOrMoveFileValidatorFactory(
+ scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+ TestFileSystemBackend* backend = static_cast<TestFileSystemBackend*>(
+ file_system_context_->GetFileSystemBackend(kWithValidatorType));
+ backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
+ }
+
+ void CopyTest(base::PlatformFileError expected) {
+ ASSERT_TRUE(FileExists(copy_src_, 10));
+ ASSERT_FALSE(FileExists(copy_dest_, 10));
+
+ EXPECT_EQ(expected,
+ AsyncFileTestHelper::Copy(
+ file_system_context_.get(), copy_src_, copy_dest_));
+
+ EXPECT_TRUE(FileExists(copy_src_, 10));
+ if (expected == base::PLATFORM_FILE_OK)
+ EXPECT_TRUE(FileExists(copy_dest_, 10));
+ else
+ EXPECT_FALSE(FileExists(copy_dest_, 10));
+ };
+
+ void MoveTest(base::PlatformFileError expected) {
+ ASSERT_TRUE(FileExists(move_src_, 10));
+ ASSERT_FALSE(FileExists(move_dest_, 10));
+
+ EXPECT_EQ(expected,
+ AsyncFileTestHelper::Move(
+ file_system_context_.get(), move_src_, move_dest_));
+
+ if (expected == base::PLATFORM_FILE_OK) {
+ EXPECT_FALSE(FileExists(move_src_, 10));
+ EXPECT_TRUE(FileExists(move_dest_, 10));
+ } else {
+ EXPECT_TRUE(FileExists(move_src_, 10));
+ EXPECT_FALSE(FileExists(move_dest_, 10));
+ }
+ };
+
+ private:
+ FileSystemURL SourceURL(const std::string& path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ origin_, src_type_,
+ base::FilePath().AppendASCII("src").AppendASCII(path));
+ }
+
+ FileSystemURL DestURL(const std::string& path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ origin_, dest_type_,
+ base::FilePath().AppendASCII("dest").AppendASCII(path));
+ }
+
+ base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) {
+ base::PlatformFileError result =
+ AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
+ if (result != base::PLATFORM_FILE_OK)
+ return result;
+ return AsyncFileTestHelper::TruncateFile(
+ file_system_context_.get(), url, size);
+ }
+
+ base::PlatformFileError CreateDirectory(const FileSystemURL& url) {
+ return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
+ url);
+ }
+
+ bool FileExists(const FileSystemURL& url, int64 expected_size) {
+ return AsyncFileTestHelper::FileExists(
+ file_system_context_.get(), url, expected_size);
+ }
+
+ base::ScopedTempDir base_;
+
+ const GURL origin_;
+
+ const FileSystemType src_type_;
+ const FileSystemType dest_type_;
+ std::string src_fsid_;
+ std::string dest_fsid_;
+
+ base::MessageLoop message_loop_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+
+ FileSystemURL copy_src_;
+ FileSystemURL copy_dest_;
+ FileSystemURL move_src_;
+ FileSystemURL move_dest_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyOrMoveFileValidatorTestHelper);
+};
+
+// For TestCopyOrMoveFileValidatorFactory
+enum Validity {
+ VALID,
+ PRE_WRITE_INVALID,
+ POST_WRITE_INVALID
+};
+
+class TestCopyOrMoveFileValidatorFactory
+ : public CopyOrMoveFileValidatorFactory {
+ public:
+ // A factory that creates validators that accept everything or nothing.
+ // TODO(gbillock): switch args to enum or something
+ explicit TestCopyOrMoveFileValidatorFactory(Validity validity)
+ : validity_(validity) {}
+ virtual ~TestCopyOrMoveFileValidatorFactory() {}
+
+ virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+ const FileSystemURL& /*src_url*/,
+ const base::FilePath& /*platform_path*/) OVERRIDE {
+ return new TestCopyOrMoveFileValidator(validity_);
+ }
+
+ private:
+ class TestCopyOrMoveFileValidator : public CopyOrMoveFileValidator {
+ public:
+ explicit TestCopyOrMoveFileValidator(Validity validity)
+ : result_(validity == VALID || validity == POST_WRITE_INVALID
+ ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_SECURITY),
+ write_result_(validity == VALID || validity == PRE_WRITE_INVALID
+ ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_SECURITY) {
+ }
+ virtual ~TestCopyOrMoveFileValidator() {}
+
+ virtual void StartPreWriteValidation(
+ const ResultCallback& result_callback) OVERRIDE {
+ // Post the result since a real validator must do work asynchronously.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(result_callback, result_));
+ }
+
+ virtual void StartPostWriteValidation(
+ const base::FilePath& dest_platform_path,
+ const ResultCallback& result_callback) OVERRIDE {
+ // Post the result since a real validator must do work asynchronously.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(result_callback, write_result_));
+ }
+
+ private:
+ base::PlatformFileError result_;
+ base::PlatformFileError write_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidator);
+ };
+
+ Validity validity_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidatorFactory);
+};
+
+} // namespace
+
+TEST(CopyOrMoveFileValidatorTest, NoValidatorWithinSameFSType) {
+ // Within a file system type, validation is not expected, so it should
+ // work for kWithValidatorType without a validator set.
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kWithValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ helper.CopyTest(base::PLATFORM_FILE_OK);
+ helper.MoveTest(base::PLATFORM_FILE_OK);
+}
+
+TEST(CopyOrMoveFileValidatorTest, MissingValidator) {
+ // Copying or moving into a kWithValidatorType requires a file
+ // validator. An error is expected if copy is attempted without a validator.
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kNoValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+TEST(CopyOrMoveFileValidatorTest, AcceptAll) {
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kNoValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+ new TestCopyOrMoveFileValidatorFactory(VALID));
+ helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
+
+ helper.CopyTest(base::PLATFORM_FILE_OK);
+ helper.MoveTest(base::PLATFORM_FILE_OK);
+}
+
+TEST(CopyOrMoveFileValidatorTest, AcceptNone) {
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kNoValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+ new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID));
+ helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
+
+ helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+TEST(CopyOrMoveFileValidatorTest, OverrideValidator) {
+ // Once set, you can not override the validator.
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kNoValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ scoped_ptr<CopyOrMoveFileValidatorFactory> reject_factory(
+ new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID));
+ helper.SetMediaCopyOrMoveFileValidatorFactory(reject_factory.Pass());
+
+ scoped_ptr<CopyOrMoveFileValidatorFactory> accept_factory(
+ new TestCopyOrMoveFileValidatorFactory(VALID));
+ helper.SetMediaCopyOrMoveFileValidatorFactory(accept_factory.Pass());
+
+ helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) {
+ CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+ kNoValidatorType,
+ kWithValidatorType);
+ helper.SetUp();
+ scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+ new TestCopyOrMoveFileValidatorFactory(POST_WRITE_INVALID));
+ helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
+
+ helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc b/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc
new file mode 100644
index 00000000000..eb002ab0a0c
--- /dev/null
+++ b/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc
@@ -0,0 +1,866 @@
+// Copyright 2013 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 <map>
+#include <queue>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "content/public/test/test_file_system_backend.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/blob/file_stream_reader.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
+#include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
+#include "webkit/browser/fileapi/file_stream_writer.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/fileapi/test_file_set.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+namespace fileapi {
+
+typedef FileSystemOperation::FileEntryList FileEntryList;
+
+namespace {
+
+void ExpectOk(const GURL& origin_url,
+ const std::string& name,
+ base::PlatformFileError error) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+}
+
+class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
+ public:
+ // A factory that creates validators that accept everything or nothing.
+ TestValidatorFactory() {}
+ virtual ~TestValidatorFactory() {}
+
+ virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+ const FileSystemURL& /*src_url*/,
+ const base::FilePath& /*platform_path*/) OVERRIDE {
+ // Move arg management to TestValidator?
+ return new TestValidator(true, true, std::string("2"));
+ }
+
+ private:
+ class TestValidator : public CopyOrMoveFileValidator {
+ public:
+ explicit TestValidator(bool pre_copy_valid,
+ bool post_copy_valid,
+ const std::string& reject_string)
+ : result_(pre_copy_valid ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_SECURITY),
+ write_result_(post_copy_valid ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_SECURITY),
+ reject_string_(reject_string) {
+ }
+ virtual ~TestValidator() {}
+
+ virtual void StartPreWriteValidation(
+ const ResultCallback& result_callback) OVERRIDE {
+ // Post the result since a real validator must do work asynchronously.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(result_callback, result_));
+ }
+
+ virtual void StartPostWriteValidation(
+ const base::FilePath& dest_platform_path,
+ const ResultCallback& result_callback) OVERRIDE {
+ base::PlatformFileError result = write_result_;
+ std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe();
+ if (unsafe.find(reject_string_) != std::string::npos) {
+ result = base::PLATFORM_FILE_ERROR_SECURITY;
+ }
+ // Post the result since a real validator must do work asynchronously.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(result_callback, result));
+ }
+
+ private:
+ base::PlatformFileError result_;
+ base::PlatformFileError write_result_;
+ std::string reject_string_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestValidator);
+ };
+};
+
+// Records CopyProgressCallback invocations.
+struct ProgressRecord {
+ FileSystemOperation::CopyProgressType type;
+ FileSystemURL source_url;
+ FileSystemURL dest_url;
+ int64 size;
+};
+
+void RecordProgressCallback(std::vector<ProgressRecord>* records,
+ FileSystemOperation::CopyProgressType type,
+ const FileSystemURL& source_url,
+ const FileSystemURL& dest_url,
+ int64 size) {
+ ProgressRecord record;
+ record.type = type;
+ record.source_url = source_url;
+ record.dest_url = dest_url;
+ record.size = size;
+ records->push_back(record);
+}
+
+void RecordFileProgressCallback(std::vector<int64>* records,
+ int64 progress) {
+ records->push_back(progress);
+}
+
+void AssignAndQuit(base::RunLoop* run_loop,
+ base::PlatformFileError* result_out,
+ base::PlatformFileError result) {
+ *result_out = result;
+ run_loop->Quit();
+}
+
+class ScopedThreadStopper {
+ public:
+ ScopedThreadStopper(base::Thread* thread) : thread_(thread) {
+ }
+
+ ~ScopedThreadStopper() {
+ if (thread_) {
+ // Give another chance for deleted streams to perform Close.
+ base::RunLoop run_loop;
+ thread_->message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
+ run_loop.Run();
+ thread_->Stop();
+ }
+ }
+
+ bool is_valid() const { return thread_; }
+
+ private:
+ base::Thread* thread_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper);
+};
+
+} // namespace
+
+class CopyOrMoveOperationTestHelper {
+ public:
+ CopyOrMoveOperationTestHelper(
+ const GURL& origin,
+ FileSystemType src_type,
+ FileSystemType dest_type)
+ : origin_(origin),
+ src_type_(src_type),
+ dest_type_(dest_type) {}
+
+ ~CopyOrMoveOperationTestHelper() {
+ file_system_context_ = NULL;
+ quota_manager_proxy_->SimulateQuotaManagerDestroyed();
+ quota_manager_ = NULL;
+ quota_manager_proxy_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void SetUp() {
+ SetUp(true, true);
+ }
+
+ void SetUpNoValidator() {
+ SetUp(true, false);
+ }
+
+ void SetUp(bool require_copy_or_move_validator,
+ bool init_copy_or_move_validator) {
+ ASSERT_TRUE(base_.CreateUniqueTempDir());
+ base::FilePath base_dir = base_.path();
+ quota_manager_ =
+ new quota::MockQuotaManager(false /* is_incognito */,
+ base_dir,
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ NULL /* special storage policy */);
+ quota_manager_proxy_ = new quota::MockQuotaManagerProxy(
+ quota_manager_.get(), base::MessageLoopProxy::current().get());
+ file_system_context_ =
+ CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir);
+
+ // Prepare the origin's root directory.
+ FileSystemBackend* backend =
+ file_system_context_->GetFileSystemBackend(src_type_);
+ backend->OpenFileSystem(origin_, src_type_,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&ExpectOk));
+ backend = file_system_context_->GetFileSystemBackend(dest_type_);
+ if (dest_type_ == kFileSystemTypeTest) {
+ TestFileSystemBackend* test_backend =
+ static_cast<TestFileSystemBackend*>(backend);
+ scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+ new TestValidatorFactory);
+ test_backend->set_require_copy_or_move_validator(
+ require_copy_or_move_validator);
+ if (init_copy_or_move_validator)
+ test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
+ }
+ backend->OpenFileSystem(origin_, dest_type_,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&ExpectOk));
+ base::RunLoop().RunUntilIdle();
+
+ // Grant relatively big quota initially.
+ quota_manager_->SetQuota(origin_,
+ FileSystemTypeToQuotaStorageType(src_type_),
+ 1024 * 1024);
+ quota_manager_->SetQuota(origin_,
+ FileSystemTypeToQuotaStorageType(dest_type_),
+ 1024 * 1024);
+ }
+
+ int64 GetSourceUsage() {
+ int64 usage = 0;
+ GetUsageAndQuota(src_type_, &usage, NULL);
+ return usage;
+ }
+
+ int64 GetDestUsage() {
+ int64 usage = 0;
+ GetUsageAndQuota(dest_type_, &usage, NULL);
+ return usage;
+ }
+
+ FileSystemURL SourceURL(const std::string& path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ origin_, src_type_, base::FilePath::FromUTF8Unsafe(path));
+ }
+
+ FileSystemURL DestURL(const std::string& path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path));
+ }
+
+ base::PlatformFileError Copy(const FileSystemURL& src,
+ const FileSystemURL& dest) {
+ return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest);
+ }
+
+ base::PlatformFileError CopyWithProgress(
+ const FileSystemURL& src,
+ const FileSystemURL& dest,
+ const AsyncFileTestHelper::CopyProgressCallback& progress_callback) {
+ return AsyncFileTestHelper::CopyWithProgress(
+ file_system_context_.get(), src, dest, progress_callback);
+ }
+
+ base::PlatformFileError Move(const FileSystemURL& src,
+ const FileSystemURL& dest) {
+ return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest);
+ }
+
+ base::PlatformFileError SetUpTestCaseFiles(
+ const FileSystemURL& root,
+ const test::TestCaseRecord* const test_cases,
+ size_t test_case_size) {
+ base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED;
+ for (size_t i = 0; i < test_case_size; ++i) {
+ const test::TestCaseRecord& test_case = test_cases[i];
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ root.origin(),
+ root.mount_type(),
+ root.virtual_path().Append(test_case.path));
+ if (test_case.is_directory)
+ result = CreateDirectory(url);
+ else
+ result = CreateFile(url, test_case.data_file_size);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, result) << url.DebugString();
+ if (result != base::PLATFORM_FILE_OK)
+ return result;
+ }
+ return result;
+ }
+
+ void VerifyTestCaseFiles(
+ const FileSystemURL& root,
+ const test::TestCaseRecord* const test_cases,
+ size_t test_case_size) {
+ std::map<base::FilePath, const test::TestCaseRecord*> test_case_map;
+ for (size_t i = 0; i < test_case_size; ++i) {
+ test_case_map[
+ base::FilePath(test_cases[i].path).NormalizePathSeparators()] =
+ &test_cases[i];
+ }
+
+ std::queue<FileSystemURL> directories;
+ FileEntryList entries;
+ directories.push(root);
+ while (!directories.empty()) {
+ FileSystemURL dir = directories.front();
+ directories.pop();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ReadDirectory(dir, &entries));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ dir.origin(),
+ dir.mount_type(),
+ dir.virtual_path().Append(entries[i].name));
+ base::FilePath relative;
+ root.virtual_path().AppendRelativePath(url.virtual_path(), &relative);
+ relative = relative.NormalizePathSeparators();
+ ASSERT_TRUE(ContainsKey(test_case_map, relative));
+ if (entries[i].is_directory) {
+ EXPECT_TRUE(test_case_map[relative]->is_directory);
+ directories.push(url);
+ } else {
+ EXPECT_FALSE(test_case_map[relative]->is_directory);
+ EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size));
+ }
+ test_case_map.erase(relative);
+ }
+ }
+ EXPECT_TRUE(test_case_map.empty());
+ std::map<base::FilePath, const test::TestCaseRecord*>::const_iterator it;
+ for (it = test_case_map.begin(); it != test_case_map.end(); ++it) {
+ LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName();
+ }
+ }
+
+ base::PlatformFileError ReadDirectory(const FileSystemURL& url,
+ FileEntryList* entries) {
+ return AsyncFileTestHelper::ReadDirectory(
+ file_system_context_.get(), url, entries);
+ }
+
+ base::PlatformFileError CreateDirectory(const FileSystemURL& url) {
+ return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
+ url);
+ }
+
+ base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) {
+ base::PlatformFileError result =
+ AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
+ if (result != base::PLATFORM_FILE_OK)
+ return result;
+ return AsyncFileTestHelper::TruncateFile(
+ file_system_context_.get(), url, size);
+ }
+
+ bool FileExists(const FileSystemURL& url, int64 expected_size) {
+ return AsyncFileTestHelper::FileExists(
+ file_system_context_.get(), url, expected_size);
+ }
+
+ bool DirectoryExists(const FileSystemURL& url) {
+ return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(),
+ url);
+ }
+
+ private:
+ void GetUsageAndQuota(FileSystemType type, int64* usage, int64* quota) {
+ quota::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota(
+ quota_manager_.get(), origin_, type, usage, quota);
+ ASSERT_EQ(quota::kQuotaStatusOk, status);
+ }
+
+ private:
+ base::ScopedTempDir base_;
+
+ const GURL origin_;
+ const FileSystemType src_type_;
+ const FileSystemType dest_type_;
+
+ base::MessageLoopForIO message_loop_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<quota::MockQuotaManagerProxy> quota_manager_proxy_;
+ scoped_refptr<quota::MockQuotaManager> quota_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper);
+};
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source file.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Copy it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest));
+
+ // Verify.
+ ASSERT_TRUE(helper.FileExists(src, 10));
+ ASSERT_TRUE(helper.FileExists(dest, 10));
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source file.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Move it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+
+ // Verify.
+ ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize));
+ ASSERT_TRUE(helper.FileExists(dest, 10));
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Copy it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest));
+
+ // Verify.
+ ASSERT_TRUE(helper.DirectoryExists(src));
+ ASSERT_TRUE(helper.DirectoryExists(dest));
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Move it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+
+ // Verify.
+ ASSERT_FALSE(helper.DirectoryExists(src));
+ ASSERT_TRUE(helper.DirectoryExists(dest));
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.SetUpTestCaseFiles(src,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Copy it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.CopyWithProgress(
+ src, dest,
+ AsyncFileTestHelper::CopyProgressCallback()));
+
+ // Verify.
+ ASSERT_TRUE(helper.DirectoryExists(src));
+ ASSERT_TRUE(helper.DirectoryExists(dest));
+
+ helper.VerifyTestCaseFiles(dest,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize);
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+ int64 src_initial_usage = helper.GetSourceUsage();
+ int64 dest_initial_usage = helper.GetDestUsage();
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.SetUpTestCaseFiles(src,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize));
+ int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
+
+ // Move it.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+
+ // Verify.
+ ASSERT_FALSE(helper.DirectoryExists(src));
+ ASSERT_TRUE(helper.DirectoryExists(dest));
+
+ helper.VerifyTestCaseFiles(dest,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize);
+
+ int64 src_new_usage = helper.GetSourceUsage();
+ ASSERT_EQ(src_initial_usage, src_new_usage);
+
+ int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
+ ASSERT_EQ(src_increase, dest_increase);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest,
+ MoveDirectoryFailPostWriteValidation) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypeTest);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.SetUpTestCaseFiles(src,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize));
+
+ // Move it.
+ helper.Move(src, dest);
+
+ // Verify.
+ ASSERT_TRUE(helper.DirectoryExists(src));
+ ASSERT_TRUE(helper.DirectoryExists(dest));
+
+ test::TestCaseRecord kMoveDirResultCases[] = {
+ {false, FILE_PATH_LITERAL("file 0"), 38},
+ {false, FILE_PATH_LITERAL("file 3"), 0},
+ };
+
+ helper.VerifyTestCaseFiles(dest,
+ kMoveDirResultCases,
+ arraysize(kMoveDirResultCases));
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypeTest);
+ helper.SetUpNoValidator();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+
+ // Set up a source file.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+
+ // The copy attempt should fail with a security error -- getting
+ // the factory returns a security error, and the copy operation must
+ // respect that.
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, helper.Copy(src, dest));
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) {
+ CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent);
+ helper.SetUp();
+
+ FileSystemURL src = helper.SourceURL("a");
+ FileSystemURL dest = helper.DestURL("b");
+
+ // Set up a source directory.
+ ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.SetUpTestCaseFiles(src,
+ test::kRegularTestCases,
+ test::kRegularTestCaseSize));
+
+ std::vector<ProgressRecord> records;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ helper.CopyWithProgress(src, dest,
+ base::Bind(&RecordProgressCallback,
+ base::Unretained(&records))));
+
+ // Verify progress callback.
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+
+ FileSystemURL src_url = helper.SourceURL(
+ std::string("a/") + base::FilePath(test_case.path).AsUTF8Unsafe());
+ FileSystemURL dest_url = helper.DestURL(
+ std::string("b/") + base::FilePath(test_case.path).AsUTF8Unsafe());
+
+ // Find the first and last progress record.
+ size_t begin_index = records.size();
+ size_t end_index = records.size();
+ for (size_t j = 0; j < records.size(); ++j) {
+ if (records[j].source_url == src_url) {
+ if (begin_index == records.size())
+ begin_index = j;
+ end_index = j;
+ }
+ }
+
+ // The record should be found.
+ ASSERT_NE(begin_index, records.size());
+ ASSERT_NE(end_index, records.size());
+ ASSERT_NE(begin_index, end_index);
+
+ EXPECT_EQ(FileSystemOperation::BEGIN_COPY_ENTRY,
+ records[begin_index].type);
+ EXPECT_FALSE(records[begin_index].dest_url.is_valid());
+ EXPECT_EQ(FileSystemOperation::END_COPY_ENTRY, records[end_index].type);
+ EXPECT_EQ(dest_url, records[end_index].dest_url);
+
+ if (test_case.is_directory) {
+ // For directory copy, the progress shouldn't be interlaced.
+ EXPECT_EQ(begin_index + 1, end_index);
+ } else {
+ // PROGRESS event's size should be assending order.
+ int64 current_size = 0;
+ for (size_t j = begin_index + 1; j < end_index; ++j) {
+ if (records[j].source_url == src_url) {
+ EXPECT_EQ(FileSystemOperation::PROGRESS, records[j].type);
+ EXPECT_FALSE(records[j].dest_url.is_valid());
+ EXPECT_GE(records[j].size, current_size);
+ current_size = records[j].size;
+ }
+ }
+ }
+ }
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath source_path = temp_dir.path().AppendASCII("source");
+ const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ file_util::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
+
+ base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
+ // LocalFileWriter requires the file exists. So create an empty file here.
+ file_util::WriteFile(dest_path, "", 0);
+
+ base::MessageLoopForIO message_loop;
+ base::Thread file_thread("file_thread");
+ ASSERT_TRUE(file_thread.Start());
+ ScopedThreadStopper thread_stopper(&file_thread);
+ ASSERT_TRUE(thread_stopper.is_valid());
+
+ scoped_refptr<base::MessageLoopProxy> task_runner =
+ file_thread.message_loop_proxy();
+
+ scoped_ptr<webkit_blob::FileStreamReader> reader(
+ webkit_blob::FileStreamReader::CreateForLocalFile(
+ task_runner.get(), source_path, 0, base::Time()));
+
+ scoped_ptr<FileStreamWriter> writer(
+ FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+
+ std::vector<int64> progress;
+ CopyOrMoveOperationDelegate::StreamCopyHelper helper(
+ reader.Pass(), writer.Pass(),
+ false, // don't need flush
+ 10, // buffer size
+ base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
+ base::TimeDelta()); // For testing, we need all the progress.
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::RunLoop run_loop;
+ helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
+ run_loop.Run();
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(5U, progress.size());
+ EXPECT_EQ(0, progress[0]);
+ EXPECT_EQ(10, progress[1]);
+ EXPECT_EQ(20, progress[2]);
+ EXPECT_EQ(30, progress[3]);
+ EXPECT_EQ(36, progress[4]);
+
+ std::string content;
+ ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
+ EXPECT_EQ(kTestData, content);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
+ // Testing the same configuration as StreamCopyHelper, but with |need_flush|
+ // parameter set to true. Since it is hard to test that the flush is indeed
+ // taking place, this test just only verifies that the file is correctly
+ // written with or without the flag.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath source_path = temp_dir.path().AppendASCII("source");
+ const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ file_util::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
+
+ base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
+ // LocalFileWriter requires the file exists. So create an empty file here.
+ file_util::WriteFile(dest_path, "", 0);
+
+ base::MessageLoopForIO message_loop;
+ base::Thread file_thread("file_thread");
+ ASSERT_TRUE(file_thread.Start());
+ ScopedThreadStopper thread_stopper(&file_thread);
+ ASSERT_TRUE(thread_stopper.is_valid());
+
+ scoped_refptr<base::MessageLoopProxy> task_runner =
+ file_thread.message_loop_proxy();
+
+ scoped_ptr<webkit_blob::FileStreamReader> reader(
+ webkit_blob::FileStreamReader::CreateForLocalFile(
+ task_runner.get(), source_path, 0, base::Time()));
+
+ scoped_ptr<FileStreamWriter> writer(
+ FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+
+ std::vector<int64> progress;
+ CopyOrMoveOperationDelegate::StreamCopyHelper helper(
+ reader.Pass(), writer.Pass(),
+ true, // need flush
+ 10, // buffer size
+ base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
+ base::TimeDelta()); // For testing, we need all the progress.
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::RunLoop run_loop;
+ helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
+ run_loop.Run();
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(5U, progress.size());
+ EXPECT_EQ(0, progress[0]);
+ EXPECT_EQ(10, progress[1]);
+ EXPECT_EQ(20, progress[2]);
+ EXPECT_EQ(30, progress[3]);
+ EXPECT_EQ(36, progress[4]);
+
+ std::string content;
+ ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
+ EXPECT_EQ(kTestData, content);
+}
+
+TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath source_path = temp_dir.path().AppendASCII("source");
+ const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ file_util::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
+
+ base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
+ // LocalFileWriter requires the file exists. So create an empty file here.
+ file_util::WriteFile(dest_path, "", 0);
+
+ base::MessageLoopForIO message_loop;
+ base::Thread file_thread("file_thread");
+ ASSERT_TRUE(file_thread.Start());
+ ScopedThreadStopper thread_stopper(&file_thread);
+ ASSERT_TRUE(thread_stopper.is_valid());
+
+ scoped_refptr<base::MessageLoopProxy> task_runner =
+ file_thread.message_loop_proxy();
+
+ scoped_ptr<webkit_blob::FileStreamReader> reader(
+ webkit_blob::FileStreamReader::CreateForLocalFile(
+ task_runner.get(), source_path, 0, base::Time()));
+
+ scoped_ptr<FileStreamWriter> writer(
+ FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+
+ std::vector<int64> progress;
+ CopyOrMoveOperationDelegate::StreamCopyHelper helper(
+ reader.Pass(), writer.Pass(),
+ false, // need_flush
+ 10, // buffer size
+ base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
+ base::TimeDelta()); // For testing, we need all the progress.
+
+ // Call Cancel() later.
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel,
+ base::Unretained(&helper)));
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::RunLoop run_loop;
+ helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
+ run_loop.Run();
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error);
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/dragged_file_util_unittest.cc b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc
new file mode 100644
index 00000000000..17b29cc6be5
--- /dev/null
+++ b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc
@@ -0,0 +1,544 @@
+// Copyright 2013 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 <map>
+#include <queue>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/dragged_file_util.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+#include "webkit/browser/fileapi/local_file_util.h"
+#include "webkit/browser/fileapi/native_file_util.h"
+#include "webkit/browser/fileapi/test_file_set.h"
+
+namespace fileapi {
+
+namespace {
+
+typedef AsyncFileTestHelper::FileEntryList FileEntryList;
+
+// Used in DraggedFileUtilTest::SimulateDropFiles().
+// Random root paths in which we create each file/directory of the
+// RegularTestCases (so that we can simulate a drop with files/directories
+// from multiple directories).
+static const base::FilePath::CharType* kRootPaths[] = {
+ FILE_PATH_LITERAL("a"),
+ FILE_PATH_LITERAL("b/c"),
+ FILE_PATH_LITERAL("etc"),
+};
+
+base::FilePath GetTopLevelPath(const base::FilePath& path) {
+ std::vector<base::FilePath::StringType> components;
+ path.GetComponents(&components);
+ return base::FilePath(components[0]);
+}
+
+bool IsDirectoryEmpty(FileSystemContext* context, const FileSystemURL& url) {
+ FileEntryList entries;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(context, url, &entries));
+ return entries.empty();
+}
+
+FileSystemURL GetEntryURL(FileSystemContext* file_system_context,
+ const FileSystemURL& dir,
+ const base::FilePath::StringType& name) {
+ return file_system_context->CreateCrackedFileSystemURL(
+ dir.origin(),
+ dir.mount_type(),
+ dir.virtual_path().Append(name));
+}
+
+base::FilePath GetRelativeVirtualPath(const FileSystemURL& root,
+ const FileSystemURL& url) {
+ if (root.virtual_path().empty())
+ return url.virtual_path();
+ base::FilePath relative;
+ const bool success = root.virtual_path().AppendRelativePath(
+ url.virtual_path(), &relative);
+ DCHECK(success);
+ return relative;
+}
+
+FileSystemURL GetOtherURL(FileSystemContext* file_system_context,
+ const FileSystemURL& root,
+ const FileSystemURL& other_root,
+ const FileSystemURL& url) {
+ return file_system_context->CreateCrackedFileSystemURL(
+ other_root.origin(),
+ other_root.mount_type(),
+ other_root.virtual_path().Append(GetRelativeVirtualPath(root, url)));
+}
+
+} // namespace
+
+class DraggedFileUtilTest : public testing::Test {
+ public:
+ DraggedFileUtilTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(partition_dir_.CreateUniqueTempDir());
+ file_util_.reset(new DraggedFileUtil());
+
+ // Register the files/directories of RegularTestCases (with random
+ // root paths) as dropped files.
+ SimulateDropFiles();
+
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL /* quota_manager */,
+ partition_dir_.path());
+
+ isolated_context()->AddReference(filesystem_id_);
+ }
+
+ virtual void TearDown() {
+ isolated_context()->RemoveReference(filesystem_id_);
+ }
+
+ protected:
+ IsolatedContext* isolated_context() const {
+ return IsolatedContext::GetInstance();
+ }
+ const base::FilePath& root_path() const {
+ return data_dir_.path();
+ }
+ FileSystemContext* file_system_context() const {
+ return file_system_context_.get();
+ }
+ FileSystemFileUtil* file_util() const { return file_util_.get(); }
+ std::string filesystem_id() const { return filesystem_id_; }
+
+ base::FilePath GetTestCasePlatformPath(
+ const base::FilePath::StringType& path) {
+ return toplevel_root_map_[GetTopLevelPath(base::FilePath(path))]
+ .Append(path).NormalizePathSeparators();
+ }
+
+ base::FilePath GetTestCaseLocalPath(const base::FilePath& path) {
+ base::FilePath relative;
+ if (data_dir_.path().AppendRelativePath(path, &relative))
+ return relative;
+ return path;
+ }
+
+ FileSystemURL GetFileSystemURL(const base::FilePath& path) const {
+ base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(
+ filesystem_id()).Append(path);
+ return file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://example.com"),
+ kFileSystemTypeIsolated,
+ virtual_path);
+ }
+
+ FileSystemURL GetOtherFileSystemURL(const base::FilePath& path) const {
+ return file_system_context()->CreateCrackedFileSystemURL(
+ GURL("http://example.com"),
+ kFileSystemTypeTemporary,
+ base::FilePath().AppendASCII("dest").Append(path));
+ }
+
+ void VerifyFilesHaveSameContent(const FileSystemURL& url1,
+ const FileSystemURL& url2) {
+ // Get the file info and the platform path for url1.
+ base::PlatformFileInfo info1;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetMetadata(
+ file_system_context(), url1, &info1));
+ base::FilePath platform_path1;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetPlatformPath(
+ file_system_context(), url1, &platform_path1));
+
+ // Get the file info and the platform path for url2.
+ base::PlatformFileInfo info2;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetMetadata(
+ file_system_context(), url2, &info2));
+ base::FilePath platform_path2;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetPlatformPath(
+ file_system_context(), url2, &platform_path2));
+
+ // See if file info matches with the other one.
+ EXPECT_EQ(info1.is_directory, info2.is_directory);
+ EXPECT_EQ(info1.size, info2.size);
+ EXPECT_EQ(info1.is_symbolic_link, info2.is_symbolic_link);
+ EXPECT_NE(platform_path1, platform_path2);
+
+ std::string content1, content2;
+ EXPECT_TRUE(base::ReadFileToString(platform_path1, &content1));
+ EXPECT_TRUE(base::ReadFileToString(platform_path2, &content2));
+ EXPECT_EQ(content1, content2);
+ }
+
+ void VerifyDirectoriesHaveSameContent(const FileSystemURL& root1,
+ const FileSystemURL& root2) {
+ base::FilePath root_path1 = root1.path();
+ base::FilePath root_path2 = root2.path();
+
+ FileEntryList entries;
+ std::queue<FileSystemURL> directories;
+
+ directories.push(root1);
+ std::set<base::FilePath> file_set1;
+ while (!directories.empty()) {
+ FileSystemURL dir = directories.front();
+ directories.pop();
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), dir, &entries));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ FileSystemURL url = GetEntryURL(file_system_context(),
+ dir, entries[i].name);
+ if (entries[i].is_directory) {
+ directories.push(url);
+ continue;
+ }
+ file_set1.insert(GetRelativeVirtualPath(root1, url));
+ }
+ }
+
+ directories.push(root2);
+ while (!directories.empty()) {
+ FileSystemURL dir = directories.front();
+ directories.pop();
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), dir, &entries));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ FileSystemURL url2 = GetEntryURL(file_system_context(),
+ dir, entries[i].name);
+ FileSystemURL url1 = GetOtherURL(file_system_context(),
+ root2, root1, url2);
+ if (entries[i].is_directory) {
+ directories.push(url2);
+ EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1),
+ IsDirectoryEmpty(file_system_context(), url2));
+ continue;
+ }
+ base::FilePath relative = GetRelativeVirtualPath(root2, url2);
+ EXPECT_TRUE(file_set1.find(relative) != file_set1.end());
+ VerifyFilesHaveSameContent(url1, url2);
+ }
+ }
+ }
+
+ scoped_ptr<FileSystemOperationContext> GetOperationContext() {
+ return make_scoped_ptr(
+ new FileSystemOperationContext(file_system_context())).Pass();
+ }
+
+
+ private:
+ void SimulateDropFiles() {
+ size_t root_path_index = 0;
+
+ IsolatedContext::FileInfoSet toplevels;
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ base::FilePath path(test_case.path);
+ base::FilePath toplevel = GetTopLevelPath(path);
+
+ // We create the test case files under one of the kRootPaths
+ // to simulate a drop with multiple directories.
+ if (toplevel_root_map_.find(toplevel) == toplevel_root_map_.end()) {
+ base::FilePath root = root_path().Append(
+ kRootPaths[(root_path_index++) % arraysize(kRootPaths)]);
+ toplevel_root_map_[toplevel] = root;
+ toplevels.AddPath(root.Append(path), NULL);
+ }
+
+ test::SetUpOneTestCase(toplevel_root_map_[toplevel], test_case);
+ }
+
+ // Register the toplevel entries.
+ filesystem_id_ = isolated_context()->RegisterDraggedFileSystem(toplevels);
+ }
+
+ base::ScopedTempDir data_dir_;
+ base::ScopedTempDir partition_dir_;
+ base::MessageLoopForIO message_loop_;
+ std::string filesystem_id_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ std::map<base::FilePath, base::FilePath> toplevel_root_map_;
+ scoped_ptr<DraggedFileUtil> file_util_;
+ DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest);
+};
+
+TEST_F(DraggedFileUtilTest, BasicTest) {
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i);
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+
+ // See if we can query the file info via the isolated FileUtil.
+ // (This should succeed since we have registered all the top-level
+ // entries of the test cases in SetUp())
+ base::PlatformFileInfo info;
+ base::FilePath platform_path;
+ FileSystemOperationContext context(file_system_context());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetFileInfo(&context, url, &info, &platform_path));
+
+ // See if the obtained file info is correct.
+ if (!test_case.is_directory)
+ ASSERT_EQ(test_case.data_file_size, info.size);
+ ASSERT_EQ(test_case.is_directory, info.is_directory);
+ ASSERT_EQ(GetTestCasePlatformPath(test_case.path),
+ platform_path.NormalizePathSeparators());
+ }
+}
+
+TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) {
+ static const fileapi::test::TestCaseRecord kUnregisteredCases[] = {
+ {true, FILE_PATH_LITERAL("nonexistent"), 0},
+ {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
+ {false, FILE_PATH_LITERAL("nonexistent/false"), 0},
+ {false, FILE_PATH_LITERAL("foo"), 30},
+ {false, FILE_PATH_LITERAL("bar"), 20},
+ };
+
+ for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i);
+ const test::TestCaseRecord& test_case = kUnregisteredCases[i];
+
+ // Prepare the test file/directory.
+ SetUpOneTestCase(root_path(), test_case);
+
+ // Make sure regular GetFileInfo succeeds.
+ base::PlatformFileInfo info;
+ ASSERT_TRUE(base::GetFileInfo(root_path().Append(test_case.path), &info));
+ if (!test_case.is_directory)
+ ASSERT_EQ(test_case.data_file_size, info.size);
+ ASSERT_EQ(test_case.is_directory, info.is_directory);
+ }
+
+ for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i);
+ const test::TestCaseRecord& test_case = kUnregisteredCases[i];
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+
+ // We should not be able to get the valid URL for unregistered files.
+ ASSERT_FALSE(url.is_valid());
+ }
+}
+
+TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ if (!test_case.is_directory)
+ continue;
+
+ SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
+ << ": " << test_case.path);
+
+ // Read entries in the directory to construct the expected results map.
+ typedef std::map<base::FilePath::StringType, DirectoryEntry> EntryMap;
+ EntryMap expected_entry_map;
+
+ base::FilePath dir_path = GetTestCasePlatformPath(test_case.path);
+ base::FileEnumerator file_enum(
+ dir_path, false /* not recursive */,
+ base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+ base::FilePath current;
+ while (!(current = file_enum.Next()).empty()) {
+ base::FileEnumerator::FileInfo file_info = file_enum.GetInfo();
+ DirectoryEntry entry;
+ entry.is_directory = file_info.IsDirectory();
+ entry.name = current.BaseName().value();
+ entry.size = file_info.GetSize();
+ entry.last_modified_time = file_info.GetLastModifiedTime();
+ expected_entry_map[entry.name] = entry;
+
+#if defined(OS_POSIX)
+ // Creates a symlink for each file/directory.
+ // They should be ignored by ReadDirectory, so we don't add them
+ // to expected_entry_map.
+ base::CreateSymbolicLink(
+ current,
+ dir_path.Append(current.BaseName().AddExtension(
+ FILE_PATH_LITERAL("link"))));
+#endif
+ }
+
+ // Perform ReadDirectory in the isolated filesystem.
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+ FileEntryList entries;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), url, &entries));
+
+ EXPECT_EQ(expected_entry_map.size(), entries.size());
+ for (size_t i = 0; i < entries.size(); ++i) {
+ const DirectoryEntry& entry = entries[i];
+ EntryMap::iterator found = expected_entry_map.find(entry.name);
+ EXPECT_TRUE(found != expected_entry_map.end());
+ EXPECT_EQ(found->second.name, entry.name);
+ EXPECT_EQ(found->second.is_directory, entry.is_directory);
+ EXPECT_EQ(found->second.size, entry.size);
+ EXPECT_EQ(found->second.last_modified_time.ToDoubleT(),
+ entry.last_modified_time.ToDoubleT());
+ }
+ }
+}
+
+TEST_F(DraggedFileUtilTest, GetLocalFilePathTest) {
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+
+ FileSystemOperationContext context(file_system_context());
+
+ base::FilePath local_file_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetLocalFilePath(&context, url, &local_file_path));
+ EXPECT_EQ(GetTestCasePlatformPath(test_case.path).value(),
+ local_file_path.value());
+ }
+}
+
+TEST_F(DraggedFileUtilTest, CopyOutFileTest) {
+ FileSystemURL src_root = GetFileSystemURL(base::FilePath());
+ FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath());
+
+ FileEntryList entries;
+ std::queue<FileSystemURL> directories;
+ directories.push(src_root);
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateDirectory(file_system_context(),
+ dest_root));
+
+ while (!directories.empty()) {
+ FileSystemURL dir = directories.front();
+ directories.pop();
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(file_system_context(),
+ dir, &entries));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ FileSystemURL src_url = GetEntryURL(file_system_context(),
+ dir, entries[i].name);
+ FileSystemURL dest_url = GetOtherURL(file_system_context(),
+ src_root, dest_root, src_url);
+
+ if (entries[i].is_directory) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateDirectory(file_system_context(),
+ dest_url));
+ directories.push(src_url);
+ continue;
+ }
+ SCOPED_TRACE(testing::Message() << "Testing file copy "
+ << src_url.path().value());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(file_system_context(),
+ src_url, dest_url));
+ VerifyFilesHaveSameContent(src_url, dest_url);
+ }
+ }
+}
+
+TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) {
+ FileSystemURL src_root = GetFileSystemURL(base::FilePath());
+ FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateDirectory(file_system_context(),
+ dest_root));
+
+ FileEntryList entries;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(file_system_context(),
+ src_root, &entries));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ if (!entries[i].is_directory)
+ continue;
+ FileSystemURL src_url = GetEntryURL(file_system_context(),
+ src_root, entries[i].name);
+ FileSystemURL dest_url = GetOtherURL(file_system_context(),
+ src_root, dest_root, src_url);
+ SCOPED_TRACE(testing::Message() << "Testing file copy "
+ << src_url.path().value());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(file_system_context(),
+ src_url, dest_url));
+ VerifyDirectoriesHaveSameContent(src_url, dest_url);
+ }
+}
+
+TEST_F(DraggedFileUtilTest, TouchTest) {
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ if (test_case.is_directory)
+ continue;
+ SCOPED_TRACE(testing::Message() << test_case.path);
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+
+ base::Time last_access_time = base::Time::FromTimeT(1000);
+ base::Time last_modified_time = base::Time::FromTimeT(2000);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Touch(GetOperationContext().get(), url,
+ last_access_time,
+ last_modified_time));
+
+ // Verification.
+ base::PlatformFileInfo info;
+ base::FilePath platform_path;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetFileInfo(GetOperationContext().get(), url,
+ &info, &platform_path));
+ EXPECT_EQ(last_access_time.ToTimeT(), info.last_accessed.ToTimeT());
+ EXPECT_EQ(last_modified_time.ToTimeT(), info.last_modified.ToTimeT());
+ }
+}
+
+TEST_F(DraggedFileUtilTest, TruncateTest) {
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ if (test_case.is_directory)
+ continue;
+
+ SCOPED_TRACE(testing::Message() << test_case.path);
+ FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
+
+ // Truncate to 0.
+ base::PlatformFileInfo info;
+ base::FilePath platform_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(GetOperationContext().get(), url, 0));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetFileInfo(GetOperationContext().get(), url,
+ &info, &platform_path));
+ EXPECT_EQ(0, info.size);
+
+ // Truncate (extend) to 999.
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(GetOperationContext().get(), url, 999));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetFileInfo(GetOperationContext().get(), url,
+ &info, &platform_path));
+ EXPECT_EQ(999, info.size);
+ }
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_browsertest.cc b/chromium/content/browser/fileapi/file_system_browsertest.cc
index eafb1b1cfa4..a3a83efd483 100644
--- a/chromium/content/browser/fileapi/file_system_browsertest.cc
+++ b/chromium/content/browser/fileapi/file_system_browsertest.cc
@@ -33,9 +33,9 @@ class FileSystemBrowserTest : public ContentBrowserTest {
// a #pass or #fail ref.
Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
- LOG(INFO) << "Navigating to URL and blocking.";
+ VLOG(0) << "Navigating to URL and blocking.";
NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
- LOG(INFO) << "Navigation done.";
+ VLOG(0) << "Navigation done.";
std::string result =
the_browser->web_contents()->GetLastCommittedURL().ref();
if (result != "pass") {
diff --git a/chromium/content/browser/fileapi/file_system_context_unittest.cc b/chromium/content/browser/fileapi/file_system_context_unittest.cc
new file mode 100644
index 00000000000..43003ad0dc4
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_context_unittest.cc
@@ -0,0 +1,374 @@
+// Copyright 2013 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 "webkit/browser/fileapi/file_system_context.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/test/test_file_system_options.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+#define DRIVE FPL("C:")
+#else
+#define DRIVE
+#endif
+
+namespace fileapi {
+
+namespace {
+
+const char kTestOrigin[] = "http://chromium.org/";
+
+GURL CreateRawFileSystemURL(const std::string& type_str,
+ const std::string& fs_id) {
+ std::string url_str = base::StringPrintf(
+ "filesystem:http://chromium.org/%s/%s/root/file",
+ type_str.c_str(),
+ fs_id.c_str());
+ return GURL(url_str);
+}
+
+class FileSystemContextTest : public testing::Test {
+ public:
+ FileSystemContextTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+
+ storage_policy_ = new quota::MockSpecialStoragePolicy();
+
+ mock_quota_manager_ =
+ new quota::MockQuotaManager(false /* is_incognito */,
+ data_dir_.path(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ storage_policy_.get());
+ }
+
+ protected:
+ FileSystemContext* CreateFileSystemContextForTest(
+ ExternalMountPoints* external_mount_points) {
+ return new FileSystemContext(base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ external_mount_points,
+ storage_policy_.get(),
+ mock_quota_manager_->proxy(),
+ ScopedVector<FileSystemBackend>(),
+ data_dir_.path(),
+ CreateAllowFileAccessOptions());
+ }
+
+ // Verifies a *valid* filesystem url has expected values.
+ void ExpectFileSystemURLMatches(const FileSystemURL& url,
+ const GURL& expect_origin,
+ FileSystemType expect_mount_type,
+ FileSystemType expect_type,
+ const base::FilePath& expect_path,
+ const base::FilePath& expect_virtual_path,
+ const std::string& expect_filesystem_id) {
+ EXPECT_TRUE(url.is_valid());
+
+ EXPECT_EQ(expect_origin, url.origin());
+ EXPECT_EQ(expect_mount_type, url.mount_type());
+ EXPECT_EQ(expect_type, url.type());
+ EXPECT_EQ(expect_path, url.path());
+ EXPECT_EQ(expect_virtual_path, url.virtual_path());
+ EXPECT_EQ(expect_filesystem_id, url.filesystem_id());
+ }
+
+ private:
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_refptr<quota::SpecialStoragePolicy> storage_policy_;
+ scoped_refptr<quota::MockQuotaManager> mock_quota_manager_;
+};
+
+// It is not valid to pass NULL ExternalMountPoints to FileSystemContext on
+// ChromeOS.
+#if !defined(OS_CHROMEOS)
+TEST_F(FileSystemContextTest, NullExternalMountPoints) {
+ scoped_refptr<FileSystemContext> file_system_context(
+ CreateFileSystemContextForTest(NULL));
+
+ // Cracking system external mount and isolated mount points should work.
+ std::string isolated_name = "root";
+ std::string isolated_id =
+ IsolatedContext::GetInstance()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal,
+ base::FilePath(DRIVE FPL("/test/isolated/root")),
+ &isolated_name);
+ // Register system external mount point.
+ ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ "system",
+ kFileSystemTypeNativeLocal,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/sys/"))));
+
+ FileSystemURL cracked_isolated = file_system_context->CrackURL(
+ CreateRawFileSystemURL("isolated", isolated_id));
+
+ ExpectFileSystemURLMatches(
+ cracked_isolated,
+ GURL(kTestOrigin),
+ kFileSystemTypeIsolated,
+ kFileSystemTypeNativeLocal,
+ base::FilePath(
+ DRIVE FPL("/test/isolated/root/file")).NormalizePathSeparators(),
+ base::FilePath::FromUTF8Unsafe(isolated_id).Append(FPL("root/file")).
+ NormalizePathSeparators(),
+ isolated_id);
+
+ FileSystemURL cracked_external = file_system_context->CrackURL(
+ CreateRawFileSystemURL("external", "system"));
+
+ ExpectFileSystemURLMatches(
+ cracked_external,
+ GURL(kTestOrigin),
+ kFileSystemTypeExternal,
+ kFileSystemTypeNativeLocal,
+ base::FilePath(
+ DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(),
+ base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
+ "system");
+
+
+ IsolatedContext::GetInstance()->RevokeFileSystem(isolated_id);
+ ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
+}
+#endif // !defiend(OS_CHROMEOS)
+
+TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) {
+ scoped_refptr<ExternalMountPoints> mount_points =
+ ExternalMountPoints::CreateRefCounted();
+
+ // Register system external mount point.
+ ASSERT_TRUE(mount_points->RegisterFileSystem(
+ "system",
+ kFileSystemTypeNativeLocal,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/sys/"))));
+
+ scoped_refptr<FileSystemContext> file_system_context(
+ CreateFileSystemContextForTest(mount_points.get()));
+
+ // Release a MountPoints reference created in the test.
+ mount_points = NULL;
+
+ // FileSystemContext should keep a reference to the |mount_points|, so it
+ // should be able to resolve the URL.
+ FileSystemURL cracked_external = file_system_context->CrackURL(
+ CreateRawFileSystemURL("external", "system"));
+
+ ExpectFileSystemURLMatches(
+ cracked_external,
+ GURL(kTestOrigin),
+ kFileSystemTypeExternal,
+ kFileSystemTypeNativeLocal,
+ base::FilePath(
+ DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(),
+ base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
+ "system");
+
+ // No need to revoke the registered filesystem since |mount_points| lifetime
+ // is bound to this test.
+}
+
+TEST_F(FileSystemContextTest, CrackFileSystemURL) {
+ scoped_refptr<ExternalMountPoints> external_mount_points(
+ ExternalMountPoints::CreateRefCounted());
+ scoped_refptr<FileSystemContext> file_system_context(
+ CreateFileSystemContextForTest(external_mount_points.get()));
+
+ // Register an isolated mount point.
+ std::string isolated_file_system_name = "root";
+ const std::string kIsolatedFileSystemID =
+ IsolatedContext::GetInstance()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal,
+ base::FilePath(DRIVE FPL("/test/isolated/root")),
+ &isolated_file_system_name);
+ // Register system external mount point.
+ ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ "system",
+ kFileSystemTypeDrive,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/sys/"))));
+ ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ "ext",
+ kFileSystemTypeNativeLocal,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/ext"))));
+ // Register a system external mount point with the same name/id as the
+ // registered isolated mount point.
+ ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ kIsolatedFileSystemID,
+ kFileSystemTypeRestrictedNativeLocal,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/system/isolated"))));
+ // Add a mount points with the same name as a system mount point to
+ // FileSystemContext's external mount points.
+ ASSERT_TRUE(external_mount_points->RegisterFileSystem(
+ "ext",
+ kFileSystemTypeNativeLocal,
+ FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/test/local/ext/"))));
+
+ const GURL kTestOrigin = GURL("http://chromium.org/");
+ const base::FilePath kVirtualPathNoRoot = base::FilePath(FPL("root/file"));
+
+ struct TestCase {
+ // Test case values.
+ std::string root;
+ std::string type_str;
+
+ // Expected test results.
+ bool expect_is_valid;
+ FileSystemType expect_mount_type;
+ FileSystemType expect_type;
+ const base::FilePath::CharType* expect_path;
+ std::string expect_filesystem_id;
+ };
+
+ const TestCase kTestCases[] = {
+ // Following should not be handled by the url crackers:
+ {
+ "pers_mount", "persistent", true /* is_valid */,
+ kFileSystemTypePersistent, kFileSystemTypePersistent,
+ FPL("pers_mount/root/file"),
+ std::string() /* filesystem id */
+ },
+ {
+ "temp_mount", "temporary", true /* is_valid */,
+ kFileSystemTypeTemporary, kFileSystemTypeTemporary,
+ FPL("temp_mount/root/file"),
+ std::string() /* filesystem id */
+ },
+ // Should be cracked by isolated mount points:
+ {
+ kIsolatedFileSystemID, "isolated", true /* is_valid */,
+ kFileSystemTypeIsolated, kFileSystemTypeNativeLocal,
+ DRIVE FPL("/test/isolated/root/file"),
+ kIsolatedFileSystemID
+ },
+ // Should be cracked by system mount points:
+ {
+ "system", "external", true /* is_valid */,
+ kFileSystemTypeExternal, kFileSystemTypeDrive,
+ DRIVE FPL("/test/sys/root/file"),
+ "system"
+ },
+ {
+ kIsolatedFileSystemID, "external", true /* is_valid */,
+ kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal,
+ DRIVE FPL("/test/system/isolated/root/file"),
+ kIsolatedFileSystemID
+ },
+ // Should be cracked by FileSystemContext's ExternalMountPoints.
+ {
+ "ext", "external", true /* is_valid */,
+ kFileSystemTypeExternal, kFileSystemTypeNativeLocal,
+ DRIVE FPL("/test/local/ext/root/file"),
+ "ext"
+ },
+ // Test for invalid filesystem url (made invalid by adding invalid
+ // filesystem type).
+ {
+ "sytem", "external", false /* is_valid */,
+ // The rest of values will be ignored.
+ kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""),
+ std::string()
+ },
+ // Test for URL with non-existing filesystem id.
+ {
+ "invalid", "external", false /* is_valid */,
+ // The rest of values will be ignored.
+ kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""),
+ std::string()
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ const base::FilePath virtual_path =
+ base::FilePath::FromUTF8Unsafe(
+ kTestCases[i].root).Append(kVirtualPathNoRoot);
+
+ GURL raw_url =
+ CreateRawFileSystemURL(kTestCases[i].type_str, kTestCases[i].root);
+ FileSystemURL cracked_url = file_system_context->CrackURL(raw_url);
+
+ SCOPED_TRACE(testing::Message() << "Test case " << i << ": "
+ << "Cracking URL: " << raw_url);
+
+ EXPECT_EQ(kTestCases[i].expect_is_valid, cracked_url.is_valid());
+ if (!kTestCases[i].expect_is_valid)
+ continue;
+
+ ExpectFileSystemURLMatches(
+ cracked_url,
+ GURL(kTestOrigin),
+ kTestCases[i].expect_mount_type,
+ kTestCases[i].expect_type,
+ base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(),
+ virtual_path.NormalizePathSeparators(),
+ kTestCases[i].expect_filesystem_id);
+ }
+
+ IsolatedContext::GetInstance()->RevokeFileSystemByPath(
+ base::FilePath(DRIVE FPL("/test/isolated/root")));
+ ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
+ ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("ext");
+ ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kIsolatedFileSystemID);
+}
+
+TEST_F(FileSystemContextTest, CanServeURLRequest) {
+ scoped_refptr<ExternalMountPoints> external_mount_points(
+ ExternalMountPoints::CreateRefCounted());
+ scoped_refptr<FileSystemContext> context(
+ CreateFileSystemContextForTest(external_mount_points.get()));
+
+ // A request for a sandbox mount point should be served.
+ FileSystemURL cracked_url =
+ context->CrackURL(CreateRawFileSystemURL("persistent", "pers_mount"));
+ EXPECT_EQ(kFileSystemTypePersistent, cracked_url.mount_type());
+ EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
+
+ // A request for an isolated mount point should NOT be served.
+ std::string isolated_fs_name = "root";
+ std::string isolated_fs_id =
+ IsolatedContext::GetInstance()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal,
+ base::FilePath(DRIVE FPL("/test/isolated/root")),
+ &isolated_fs_name);
+ cracked_url = context->CrackURL(
+ CreateRawFileSystemURL("isolated", isolated_fs_id));
+ EXPECT_EQ(kFileSystemTypeIsolated, cracked_url.mount_type());
+ EXPECT_FALSE(context->CanServeURLRequest(cracked_url));
+
+ // A request for an external mount point should be served.
+ const std::string kExternalMountName = "ext_mount";
+ ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ kExternalMountName, kFileSystemTypeDrive, FileSystemMountOption(),
+ base::FilePath()));
+ cracked_url = context->CrackURL(
+ CreateRawFileSystemURL("external", kExternalMountName));
+ EXPECT_EQ(kFileSystemTypeExternal, cracked_url.mount_type());
+ EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
+
+ ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kExternalMountName);
+ IsolatedContext::GetInstance()->RevokeFileSystem(isolated_fs_id);
+}
+
+} // namespace
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc
new file mode 100644
index 00000000000..874e61b0203
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc
@@ -0,0 +1,321 @@
+// Copyright 2013 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 "webkit/browser/fileapi/file_system_dir_url_request_job.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/format_macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/regex.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+
+namespace fileapi {
+namespace {
+
+// We always use the TEMPORARY FileSystem in this test.
+static const char kFileSystemURLPrefix[] =
+ "filesystem:http://remote/temporary/";
+
+} // namespace
+
+class FileSystemDirURLRequestJobTest : public testing::Test {
+ protected:
+ FileSystemDirURLRequestJobTest()
+ : weak_factory_(this) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ special_storage_policy_ = new quota::MockSpecialStoragePolicy;
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, temp_dir_.path());
+
+ file_system_context_->OpenFileSystem(
+ GURL("http://remote/"), kFileSystemTypeTemporary,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&FileSystemDirURLRequestJobTest::OnOpenFileSystem,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+
+ net::URLRequest::Deprecated::RegisterProtocolFactory(
+ "filesystem", &FileSystemDirURLRequestJobFactory);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // NOTE: order matters, request must die before delegate
+ request_.reset(NULL);
+ delegate_.reset(NULL);
+
+ net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
+ ClearUnusedJob();
+ }
+
+ void OnOpenFileSystem(const GURL& root_url,
+ const std::string& name,
+ base::PlatformFileError result) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ }
+
+ void TestRequestHelper(const GURL& url, bool run_to_completion,
+ FileSystemContext* file_system_context) {
+ delegate_.reset(new net::TestDelegate());
+ delegate_->set_quit_on_redirect(true);
+ request_ = empty_context_.CreateRequest(
+ url, net::DEFAULT_PRIORITY, delegate_.get());
+ job_ = new FileSystemDirURLRequestJob(
+ request_.get(), NULL, file_system_context);
+
+ request_->Start();
+ ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
+ if (run_to_completion)
+ base::MessageLoop::current()->Run();
+ }
+
+ void TestRequest(const GURL& url) {
+ TestRequestHelper(url, true, file_system_context_.get());
+ }
+
+ void TestRequestWithContext(const GURL& url,
+ FileSystemContext* file_system_context) {
+ TestRequestHelper(url, true, file_system_context);
+ }
+
+ void TestRequestNoRun(const GURL& url) {
+ TestRequestHelper(url, false, file_system_context_.get());
+ }
+
+ FileSystemURL CreateURL(const base::FilePath& file_path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://remote"),
+ fileapi::kFileSystemTypeTemporary,
+ file_path);
+ }
+
+ FileSystemOperationContext* NewOperationContext() {
+ FileSystemOperationContext* context(
+ new FileSystemOperationContext(file_system_context_.get()));
+ context->set_allowed_bytes_growth(1024);
+ return context;
+ }
+
+ void CreateDirectory(const base::StringPiece& dir_name) {
+ base::FilePath path = base::FilePath().AppendASCII(dir_name);
+ scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->CreateDirectory(
+ context.get(),
+ CreateURL(path),
+ false /* exclusive */,
+ false /* recursive */));
+ }
+
+ void EnsureFileExists(const base::StringPiece file_name) {
+ base::FilePath path = base::FilePath().AppendASCII(file_name);
+ scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->EnsureFileExists(
+ context.get(), CreateURL(path), NULL));
+ }
+
+ void TruncateFile(const base::StringPiece file_name, int64 length) {
+ base::FilePath path = base::FilePath().AppendASCII(file_name);
+ scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->Truncate(
+ context.get(), CreateURL(path), length));
+ }
+
+ base::PlatformFileError GetFileInfo(const base::FilePath& path,
+ base::PlatformFileInfo* file_info,
+ base::FilePath* platform_file_path) {
+ scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
+ return file_util()->GetFileInfo(context.get(),
+ CreateURL(path),
+ file_info, platform_file_path);
+ }
+
+ void VerifyListingEntry(const std::string& entry_line,
+ const std::string& name,
+ const std::string& url,
+ bool is_directory,
+ int64 size) {
+#define STR "([^\"]*)"
+ icu::UnicodeString pattern("^<script>addRow\\(\"" STR "\",\"" STR
+ "\",(0|1),\"" STR "\",\"" STR "\"\\);</script>");
+#undef STR
+ icu::UnicodeString input(entry_line.c_str());
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::RegexMatcher match(pattern, input, 0, status);
+
+ EXPECT_TRUE(match.find());
+ EXPECT_EQ(5, match.groupCount());
+ EXPECT_EQ(icu::UnicodeString(name.c_str()), match.group(1, status));
+ EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status));
+ EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"),
+ match.group(3, status));
+ icu::UnicodeString size_string(FormatBytesUnlocalized(size).c_str());
+ EXPECT_EQ(size_string, match.group(4, status));
+
+ base::Time date;
+ icu::UnicodeString date_ustr(match.group(5, status));
+ std::string date_str;
+ UTF16ToUTF8(date_ustr.getBuffer(), date_ustr.length(), &date_str);
+ EXPECT_TRUE(base::Time::FromString(date_str.c_str(), &date));
+ EXPECT_FALSE(date.is_null());
+ }
+
+ GURL CreateFileSystemURL(const std::string path) {
+ return GURL(kFileSystemURLPrefix + path);
+ }
+
+ static net::URLRequestJob* FileSystemDirURLRequestJobFactory(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const std::string& scheme) {
+ DCHECK(job_);
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ }
+
+ static void ClearUnusedJob() {
+ if (job_) {
+ scoped_refptr<net::URLRequestJob> deleter = job_;
+ job_ = NULL;
+ }
+ }
+
+ FileSystemFileUtil* file_util() {
+ return file_system_context_->sandbox_delegate()->sync_file_util();
+ }
+
+ // Put the message loop at the top, so that it's the last thing deleted.
+ // Delete all MessageLoopProxy objects before the MessageLoop, to help prevent
+ // leaks caused by tasks posted during shutdown.
+ base::MessageLoopForIO message_loop_;
+
+ base::ScopedTempDir temp_dir_;
+ net::URLRequestContext empty_context_;
+ scoped_ptr<net::TestDelegate> delegate_;
+ scoped_ptr<net::URLRequest> request_;
+ scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ base::WeakPtrFactory<FileSystemDirURLRequestJobTest> weak_factory_;
+
+ static net::URLRequestJob* job_;
+};
+
+// static
+net::URLRequestJob* FileSystemDirURLRequestJobTest::job_ = NULL;
+
+namespace {
+
+TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) {
+ CreateDirectory("foo");
+ CreateDirectory("foo/bar");
+ CreateDirectory("foo/bar/baz");
+
+ EnsureFileExists("foo/bar/hoge");
+ TruncateFile("foo/bar/hoge", 10);
+
+ TestRequest(CreateFileSystemURL("foo/bar/"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_GT(delegate_->bytes_received(), 0);
+
+ std::istringstream in(delegate_->data_received());
+ std::string line;
+ EXPECT_TRUE(std::getline(in, line));
+
+#if defined(OS_WIN)
+ EXPECT_EQ("<script>start(\"foo\\\\bar\");</script>", line);
+#elif defined(OS_POSIX)
+ EXPECT_EQ("<script>start(\"/foo/bar\");</script>", line);
+#endif
+
+ EXPECT_TRUE(std::getline(in, line));
+ VerifyListingEntry(line, "hoge", "hoge", false, 10);
+
+ EXPECT_TRUE(std::getline(in, line));
+ VerifyListingEntry(line, "baz", "baz", true, 0);
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, InvalidURL) {
+ TestRequest(GURL("filesystem:/foo/bar/baz"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error());
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, NoSuchRoot) {
+ TestRequest(GURL("filesystem:http://remote/persistent/somedir/"));
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, NoSuchDirectory) {
+ TestRequest(CreateFileSystemURL("somedir/"));
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, Cancel) {
+ CreateDirectory("foo");
+ TestRequestNoRun(CreateFileSystemURL("foo/"));
+ // Run StartAsync() and only StartAsync().
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release());
+ base::RunLoop().RunUntilIdle();
+ // If we get here, success! we didn't crash!
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, Incognito) {
+ CreateDirectory("foo");
+
+ scoped_refptr<FileSystemContext> file_system_context =
+ CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path());
+
+ TestRequestWithContext(CreateFileSystemURL("/"),
+ file_system_context.get());
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_TRUE(request_->status().is_success());
+
+ std::istringstream in(delegate_->data_received());
+ std::string line;
+ EXPECT_TRUE(std::getline(in, line));
+ EXPECT_FALSE(std::getline(in, line));
+
+ TestRequestWithContext(CreateFileSystemURL("foo"),
+ file_system_context.get());
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+}
+
+} // namespace (anonymous)
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc b/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc
new file mode 100644
index 00000000000..3540d5b8c05
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright 2013 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 "webkit/browser/fileapi/file_system_file_stream_reader.h"
+
+#include <limits>
+#include <string>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+
+namespace fileapi {
+
+namespace {
+
+const char kURLOrigin[] = "http://remote/";
+const char kTestFileName[] = "test.dat";
+const char kTestData[] = "0123456789";
+const int kTestDataSize = arraysize(kTestData) - 1;
+
+void ReadFromReader(FileSystemFileStreamReader* reader,
+ std::string* data,
+ size_t size,
+ int* result) {
+ ASSERT_TRUE(reader != NULL);
+ ASSERT_TRUE(result != NULL);
+ *result = net::OK;
+ net::TestCompletionCallback callback;
+ size_t total_bytes_read = 0;
+ while (total_bytes_read < size) {
+ scoped_refptr<net::IOBufferWithSize> buf(
+ new net::IOBufferWithSize(size - total_bytes_read));
+ int rv = reader->Read(buf.get(), buf->size(), callback.callback());
+ if (rv == net::ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv < 0)
+ *result = rv;
+ if (rv <= 0)
+ break;
+ total_bytes_read += rv;
+ data->append(buf->data(), rv);
+ }
+}
+
+void NeverCalled(int unused) { ADD_FAILURE(); }
+
+} // namespace
+
+class FileSystemFileStreamReaderTest : public testing::Test {
+ public:
+ FileSystemFileStreamReaderTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, temp_dir_.path());
+
+ file_system_context_->OpenFileSystem(
+ GURL(kURLOrigin), kFileSystemTypeTemporary,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&OnOpenFileSystem));
+ base::RunLoop().RunUntilIdle();
+
+ WriteFile(kTestFileName, kTestData, kTestDataSize,
+ &test_file_modification_time_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ FileSystemFileStreamReader* CreateFileReader(
+ const std::string& file_name,
+ int64 initial_offset,
+ const base::Time& expected_modification_time) {
+ return new FileSystemFileStreamReader(file_system_context_.get(),
+ GetFileSystemURL(file_name),
+ initial_offset,
+ expected_modification_time);
+ }
+
+ base::Time test_file_modification_time() const {
+ return test_file_modification_time_;
+ }
+
+ void WriteFile(const std::string& file_name,
+ const char* buf,
+ int buf_size,
+ base::Time* modification_time) {
+ FileSystemURL url = GetFileSystemURL(file_name);
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ fileapi::AsyncFileTestHelper::CreateFileWithData(
+ file_system_context_, url, buf, buf_size));
+
+ base::PlatformFileInfo file_info;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetMetadata(
+ file_system_context_, url, &file_info));
+ if (modification_time)
+ *modification_time = file_info.last_modified;
+ }
+
+ private:
+ static void OnOpenFileSystem(const GURL& root_url,
+ const std::string& name,
+ base::PlatformFileError result) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ }
+
+ FileSystemURL GetFileSystemURL(const std::string& file_name) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ GURL(kURLOrigin),
+ kFileSystemTypeTemporary,
+ base::FilePath().AppendASCII(file_name));
+ }
+
+ base::MessageLoopForIO message_loop_;
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ base::Time test_file_modification_time_;
+};
+
+TEST_F(FileSystemFileStreamReaderTest, NonExistent) {
+ const char kFileName[] = "nonexistent";
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kFileName, 0, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, 10, &result);
+ ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
+ ASSERT_EQ(0U, data.size());
+}
+
+TEST_F(FileSystemFileStreamReaderTest, Empty) {
+ const char kFileName[] = "empty";
+ WriteFile(kFileName, NULL, 0, NULL);
+
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kFileName, 0, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, 10, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(0U, data.size());
+
+ net::TestInt64CompletionCallback callback;
+ int64 length_result = reader->GetLength(callback.callback());
+ if (length_result == net::ERR_IO_PENDING)
+ length_result = callback.WaitForResult();
+ ASSERT_EQ(0, length_result);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, GetLengthNormal) {
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 0, test_file_modification_time()));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, GetLengthAfterModified) {
+ // Pass a fake expected modifictaion time so that the expectation fails.
+ base::Time fake_expected_modification_time =
+ test_file_modification_time() - base::TimeDelta::FromSeconds(10);
+
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+
+ // With NULL expected modification time this should work.
+ reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
+ result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, GetLengthWithOffset) {
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 3, base::Time()));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ // Initial offset does not affect the result of GetLength.
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, ReadNormal) {
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 0, test_file_modification_time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(kTestData, data);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, ReadAfterModified) {
+ // Pass a fake expected modifictaion time so that the expectation fails.
+ base::Time fake_expected_modification_time =
+ test_file_modification_time() - base::TimeDelta::FromSeconds(10);
+
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+ ASSERT_EQ(0U, data.size());
+
+ // With NULL expected modification time this should work.
+ data.clear();
+ reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(kTestData, data);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, ReadWithOffset) {
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 3, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(&kTestData[3], data);
+}
+
+TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) {
+ scoped_ptr<FileSystemFileStreamReader> reader(
+ CreateFileReader(kTestFileName, 0, base::Time()));
+
+ net::TestCompletionCallback callback;
+ scoped_refptr<net::IOBufferWithSize> buf(
+ new net::IOBufferWithSize(kTestDataSize));
+ int rv = reader->Read(buf.get(), buf->size(), base::Bind(&NeverCalled));
+ ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
+
+ // Delete immediately.
+ // Should not crash; nor should NeverCalled be callback.
+ reader.reset();
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc
new file mode 100644
index 00000000000..445657d9a01
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc
@@ -0,0 +1,1241 @@
+// Copyright 2013 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 "webkit/browser/fileapi/file_system_operation_impl.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/test/sandbox_file_system_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_operation_runner.h"
+#include "webkit/browser/fileapi/mock_file_change_observer.h"
+#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using quota::QuotaManager;
+using quota::QuotaManagerProxy;
+using webkit_blob::ShareableFileReference;
+
+namespace fileapi {
+
+namespace {
+
+const int kFileOperationStatusNotSet = 1;
+
+void AssertFileErrorEq(const tracked_objects::Location& from_here,
+ base::PlatformFileError expected,
+ base::PlatformFileError actual) {
+ ASSERT_EQ(expected, actual) << from_here.ToString();
+}
+
+} // namespace (anonymous)
+
+// Test class for FileSystemOperationImpl.
+class FileSystemOperationImplTest
+ : public testing::Test {
+ public:
+ FileSystemOperationImplTest()
+ : status_(kFileOperationStatusNotSet),
+ weak_factory_(this) {}
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ EXPECT_TRUE(base_.CreateUniqueTempDir());
+ change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+
+ base::FilePath base_dir = base_.path().AppendASCII("filesystem");
+ quota_manager_ =
+ new quota::MockQuotaManager(false /* is_incognito */,
+ base_dir,
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ NULL /* special storage policy */);
+ quota_manager_proxy_ = new quota::MockQuotaManagerProxy(
+ quota_manager(), base::MessageLoopProxy::current().get());
+ sandbox_file_system_.SetUp(base_dir, quota_manager_proxy_.get());
+ sandbox_file_system_.AddFileChangeObserver(&change_observer_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Let the client go away before dropping a ref of the quota manager proxy.
+ quota_manager_proxy()->SimulateQuotaManagerDestroyed();
+ quota_manager_ = NULL;
+ quota_manager_proxy_ = NULL;
+ sandbox_file_system_.TearDown();
+ }
+
+ FileSystemOperationRunner* operation_runner() {
+ return sandbox_file_system_.operation_runner();
+ }
+
+ int status() const { return status_; }
+ const base::PlatformFileInfo& info() const { return info_; }
+ const base::FilePath& path() const { return path_; }
+ const std::vector<DirectoryEntry>& entries() const {
+ return entries_;
+ }
+
+ const ShareableFileReference* shareable_file_ref() const {
+ return shareable_file_ref_.get();
+ }
+
+ quota::MockQuotaManager* quota_manager() {
+ return static_cast<quota::MockQuotaManager*>(quota_manager_.get());
+ }
+
+ quota::MockQuotaManagerProxy* quota_manager_proxy() {
+ return static_cast<quota::MockQuotaManagerProxy*>(
+ quota_manager_proxy_.get());
+ }
+
+ FileSystemFileUtil* file_util() {
+ return sandbox_file_system_.file_util();
+ }
+
+ MockFileChangeObserver* change_observer() {
+ return &change_observer_;
+ }
+
+ scoped_ptr<FileSystemOperationContext> NewContext() {
+ FileSystemOperationContext* context =
+ sandbox_file_system_.NewOperationContext();
+ // Grant enough quota for all test cases.
+ context->set_allowed_bytes_growth(1000000);
+ return make_scoped_ptr(context);
+ }
+
+ FileSystemURL URLForPath(const std::string& path) const {
+ return sandbox_file_system_.CreateURLFromUTF8(path);
+ }
+
+ base::FilePath PlatformPath(const std::string& path) {
+ return sandbox_file_system_.GetLocalPath(
+ base::FilePath::FromUTF8Unsafe(path));
+ }
+
+ bool FileExists(const std::string& path) {
+ return AsyncFileTestHelper::FileExists(
+ sandbox_file_system_.file_system_context(), URLForPath(path),
+ AsyncFileTestHelper::kDontCheckSize);
+ }
+
+ bool DirectoryExists(const std::string& path) {
+ return AsyncFileTestHelper::DirectoryExists(
+ sandbox_file_system_.file_system_context(), URLForPath(path));
+ }
+
+ FileSystemURL CreateFile(const std::string& path) {
+ FileSystemURL url = URLForPath(path);
+ bool created = false;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->EnsureFileExists(NewContext().get(),
+ url, &created));
+ EXPECT_TRUE(created);
+ return url;
+ }
+
+ FileSystemURL CreateDirectory(const std::string& path) {
+ FileSystemURL url = URLForPath(path);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->CreateDirectory(NewContext().get(), url,
+ false /* exclusive */, true));
+ return url;
+ }
+
+ int64 GetFileSize(const std::string& path) {
+ base::PlatformFileInfo info;
+ EXPECT_TRUE(base::GetFileInfo(PlatformPath(path), &info));
+ return info.size;
+ }
+
+ // Callbacks for recording test results.
+ FileSystemOperation::StatusCallback RecordStatusCallback() {
+ return base::Bind(&FileSystemOperationImplTest::DidFinish,
+ weak_factory_.GetWeakPtr());
+ }
+
+ FileSystemOperation::ReadDirectoryCallback
+ RecordReadDirectoryCallback() {
+ return base::Bind(&FileSystemOperationImplTest::DidReadDirectory,
+ weak_factory_.GetWeakPtr());
+ }
+
+ FileSystemOperation::GetMetadataCallback RecordMetadataCallback() {
+ return base::Bind(&FileSystemOperationImplTest::DidGetMetadata,
+ weak_factory_.GetWeakPtr());
+ }
+
+ FileSystemOperation::SnapshotFileCallback RecordSnapshotFileCallback() {
+ return base::Bind(&FileSystemOperationImplTest::DidCreateSnapshotFile,
+ weak_factory_.GetWeakPtr());
+ }
+
+ void DidFinish(base::PlatformFileError status) {
+ status_ = status;
+ }
+
+ void DidReadDirectory(
+ base::PlatformFileError status,
+ const std::vector<DirectoryEntry>& entries,
+ bool /* has_more */) {
+ entries_ = entries;
+ status_ = status;
+ }
+
+ void DidGetMetadata(base::PlatformFileError status,
+ const base::PlatformFileInfo& info) {
+ info_ = info;
+ status_ = status;
+ }
+
+ void DidCreateSnapshotFile(
+ base::PlatformFileError status,
+ const base::PlatformFileInfo& info,
+ const base::FilePath& platform_path,
+ const scoped_refptr<ShareableFileReference>& shareable_file_ref) {
+ info_ = info;
+ path_ = platform_path;
+ status_ = status;
+ shareable_file_ref_ = shareable_file_ref;
+ }
+
+ int64 GetDataSizeOnDisk() {
+ return sandbox_file_system_.ComputeCurrentOriginUsage() -
+ sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage();
+ }
+
+ void GetUsageAndQuota(int64* usage, int64* quota) {
+ quota::QuotaStatusCode status =
+ AsyncFileTestHelper::GetUsageAndQuota(quota_manager_.get(),
+ sandbox_file_system_.origin(),
+ sandbox_file_system_.type(),
+ usage,
+ quota);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(quota::kQuotaStatusOk, status);
+ }
+
+ int64 ComputePathCost(const FileSystemURL& url) {
+ int64 base_usage;
+ GetUsageAndQuota(&base_usage, NULL);
+
+ AsyncFileTestHelper::CreateFile(
+ sandbox_file_system_.file_system_context(), url);
+ operation_runner()->Remove(url, false /* recursive */,
+ base::Bind(&AssertFileErrorEq, FROM_HERE,
+ base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+ change_observer()->ResetCount();
+
+ int64 total_usage;
+ GetUsageAndQuota(&total_usage, NULL);
+ return total_usage - base_usage;
+ }
+
+ void GrantQuotaForCurrentUsage() {
+ int64 usage;
+ GetUsageAndQuota(&usage, NULL);
+ quota_manager()->SetQuota(sandbox_file_system_.origin(),
+ sandbox_file_system_.storage_type(),
+ usage);
+ }
+
+ int64 GetUsage() {
+ int64 usage = 0;
+ GetUsageAndQuota(&usage, NULL);
+ return usage;
+ }
+
+ void AddQuota(int64 quota_delta) {
+ int64 quota;
+ GetUsageAndQuota(NULL, &quota);
+ quota_manager()->SetQuota(sandbox_file_system_.origin(),
+ sandbox_file_system_.storage_type(),
+ quota + quota_delta);
+ }
+
+ base::MessageLoop message_loop_;
+ scoped_refptr<QuotaManager> quota_manager_;
+ scoped_refptr<QuotaManagerProxy> quota_manager_proxy_;
+
+ // Common temp base for nondestructive uses.
+ base::ScopedTempDir base_;
+
+ SandboxFileSystemTestHelper sandbox_file_system_;
+
+ // For post-operation status.
+ int status_;
+ base::PlatformFileInfo info_;
+ base::FilePath path_;
+ std::vector<DirectoryEntry> entries_;
+ scoped_refptr<ShareableFileReference> shareable_file_ref_;
+
+ MockFileChangeObserver change_observer_;
+ ChangeObserverList change_observers_;
+
+ base::WeakPtrFactory<FileSystemOperationImplTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplTest);
+};
+
+TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDoesntExist) {
+ change_observer()->ResetCount();
+ operation_runner()->Move(URLForPath("a"), URLForPath("b"),
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveFailureContainsPath) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("src/dest"));
+
+ operation_runner()->Move(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDirExistsDestFile) {
+ // Src exists and is dir. Dest is a file.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+ FileSystemURL dest_file(CreateFile("dest/file"));
+
+ operation_runner()->Move(src_dir, dest_file,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest,
+ TestMoveFailureSrcFileExistsDestNonEmptyDir) {
+ // Src exists and is a directory. Dest is a non-empty directory.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+ FileSystemURL dest_file(CreateFile("dest/file"));
+
+ operation_runner()->Move(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcFileExistsDestDir) {
+ // Src exists and is a file. Dest is a directory.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL src_file(CreateFile("src/file"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Move(src_file, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveFailureDestParentDoesntExist) {
+ // Dest. parent path does not exist.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ operation_runner()->Move(src_dir, URLForPath("nonexistent/deset"),
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndOverwrite) {
+ FileSystemURL src_file(CreateFile("src"));
+ FileSystemURL dest_file(CreateFile("dest"));
+
+ operation_runner()->Move(src_file, dest_file,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("dest"));
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndNew) {
+ FileSystemURL src_file(CreateFile("src"));
+
+ operation_runner()->Move(src_file, URLForPath("new"),
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("new"));
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndOverwrite) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Move(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(DirectoryExists("src"));
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_EQ(2, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Make sure we've overwritten but not moved the source under the |dest_dir|.
+ EXPECT_TRUE(DirectoryExists("dest"));
+ EXPECT_FALSE(DirectoryExists("dest/src"));
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndNew) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Move(src_dir, URLForPath("dest/new"),
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(DirectoryExists("src"));
+ EXPECT_TRUE(DirectoryExists("dest/new"));
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirRecursive) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ CreateDirectory("src/dir");
+ CreateFile("src/dir/sub");
+
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Move(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("dest/dir"));
+ EXPECT_TRUE(FileExists("dest/dir/sub"));
+
+ EXPECT_EQ(3, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDoesntExist) {
+ operation_runner()->Copy(URLForPath("a"), URLForPath("b"),
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureContainsPath) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("src/dir"));
+
+ operation_runner()->Copy(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDirExistsDestFile) {
+ // Src exists and is dir. Dest is a file.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+ FileSystemURL dest_file(CreateFile("dest/file"));
+
+ operation_runner()->Copy(src_dir, dest_file,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest,
+ TestCopyFailureSrcFileExistsDestNonEmptyDir) {
+ // Src exists and is a directory. Dest is a non-empty directory.
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+ FileSystemURL dest_file(CreateFile("dest/file"));
+
+ operation_runner()->Copy(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcFileExistsDestDir) {
+ // Src exists and is a file. Dest is a directory.
+ FileSystemURL src_file(CreateFile("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Copy(src_file, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureDestParentDoesntExist) {
+ // Dest. parent path does not exist.
+ FileSystemURL src_dir(CreateDirectory("src"));
+
+ operation_runner()->Copy(src_dir, URLForPath("nonexistent/dest"),
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyFailureByQuota) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL src_file(CreateFile("src/file"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+ operation_runner()->Truncate(src_file, 6, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(6, GetFileSize("src/file"));
+
+ FileSystemURL dest_file(URLForPath("dest/file"));
+ int64 dest_path_cost = ComputePathCost(dest_file);
+ GrantQuotaForCurrentUsage();
+ AddQuota(6 + dest_path_cost - 1);
+
+ operation_runner()->Copy(src_file, dest_file,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_FALSE(FileExists("dest/file"));
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndOverwrite) {
+ FileSystemURL src_file(CreateFile("src"));
+ FileSystemURL dest_file(CreateFile("dest"));
+
+ operation_runner()->Copy(src_file, dest_file,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("dest"));
+ EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndNew) {
+ FileSystemURL src_file(CreateFile("src"));
+
+ operation_runner()->Copy(src_file, URLForPath("new"),
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("new"));
+ EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndOverwrite) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Copy(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+
+ // Make sure we've overwritten but not copied the source under the |dest_dir|.
+ EXPECT_TRUE(DirectoryExists("dest"));
+ EXPECT_FALSE(DirectoryExists("dest/src"));
+ EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 3);
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndNew) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ FileSystemURL dest_dir_new(URLForPath("dest"));
+
+ operation_runner()->Copy(src_dir, dest_dir_new,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("dest"));
+ EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 2);
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirRecursive) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ CreateDirectory("src/dir");
+ CreateFile("src/dir/sub");
+
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ operation_runner()->Copy(src_dir, dest_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("dest/dir"));
+ EXPECT_TRUE(FileExists("dest/dir/sub"));
+
+ // For recursive copy we may record multiple read access.
+ EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 1);
+
+ EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileSuccess) {
+ base::FilePath src_local_disk_file_path;
+ base::CreateTemporaryFile(&src_local_disk_file_path);
+ const char test_data[] = "foo";
+ int data_size = ARRAYSIZE_UNSAFE(test_data);
+ file_util::WriteFile(src_local_disk_file_path, test_data, data_size);
+
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ int64 before_usage;
+ GetUsageAndQuota(&before_usage, NULL);
+
+ // Check that the file copied and corresponding usage increased.
+ operation_runner()->CopyInForeignFile(src_local_disk_file_path,
+ URLForPath("dest/file"),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, change_observer()->create_file_count());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("dest/file"));
+ int64 after_usage;
+ GetUsageAndQuota(&after_usage, NULL);
+ EXPECT_GT(after_usage, before_usage);
+
+ // Compare contents of src and copied file.
+ char buffer[100];
+ EXPECT_EQ(data_size, base::ReadFile(PlatformPath("dest/file"),
+ buffer, data_size));
+ for (int i = 0; i < data_size; ++i)
+ EXPECT_EQ(test_data[i], buffer[i]);
+}
+
+TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileFailureByQuota) {
+ base::FilePath src_local_disk_file_path;
+ base::CreateTemporaryFile(&src_local_disk_file_path);
+ const char test_data[] = "foo";
+ file_util::WriteFile(src_local_disk_file_path, test_data,
+ ARRAYSIZE_UNSAFE(test_data));
+
+ FileSystemURL dest_dir(CreateDirectory("dest"));
+
+ GrantQuotaForCurrentUsage();
+ operation_runner()->CopyInForeignFile(src_local_disk_file_path,
+ URLForPath("dest/file"),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(FileExists("dest/file"));
+ EXPECT_EQ(0, change_observer()->create_file_count());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateFileFailure) {
+ // Already existing file and exclusive true.
+ FileSystemURL file(CreateFile("file"));
+ operation_runner()->CreateFile(file, true, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileExists) {
+ // Already existing file and exclusive false.
+ FileSystemURL file(CreateFile("file"));
+ operation_runner()->CreateFile(file, false, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("file"));
+
+ // The file was already there; did nothing.
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessExclusive) {
+ // File doesn't exist but exclusive is true.
+ operation_runner()->CreateFile(URLForPath("new"), true,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(FileExists("new"));
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileDoesntExist) {
+ // Non existing file.
+ operation_runner()->CreateFile(URLForPath("nonexistent"), false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+}
+
+TEST_F(FileSystemOperationImplTest,
+ TestCreateDirFailureDestParentDoesntExist) {
+ // Dest. parent path does not exist.
+ operation_runner()->CreateDirectory(
+ URLForPath("nonexistent/dir"), false, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateDirFailureDirExists) {
+ // Exclusive and dir existing at path.
+ FileSystemURL dir(CreateDirectory("dir"));
+ operation_runner()->CreateDirectory(dir, true, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateDirFailureFileExists) {
+ // Exclusive true and file existing at path.
+ FileSystemURL file(CreateFile("file"));
+ operation_runner()->CreateDirectory(file, true, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateDirSuccess) {
+ // Dir exists and exclusive is false.
+ FileSystemURL dir(CreateDirectory("dir"));
+ operation_runner()->CreateDirectory(dir, false, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Dir doesn't exist.
+ operation_runner()->CreateDirectory(URLForPath("new"), false, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("new"));
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateDirSuccessExclusive) {
+ // Dir doesn't exist.
+ operation_runner()->CreateDirectory(URLForPath("new"), true, false,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("new"));
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataFailure) {
+ operation_runner()->GetMetadata(URLForPath("nonexistent"),
+ RecordMetadataCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+
+ operation_runner()->FileExists(URLForPath("nonexistent"),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+
+ operation_runner()->DirectoryExists(URLForPath("nonexistent"),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataSuccess) {
+ FileSystemURL dir(CreateDirectory("dir"));
+ FileSystemURL file(CreateFile("dir/file"));
+ int read_access = 0;
+
+ operation_runner()->DirectoryExists(dir, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ ++read_access;
+
+ operation_runner()->GetMetadata(dir, RecordMetadataCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(info().is_directory);
+ ++read_access;
+
+ operation_runner()->FileExists(file, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ ++read_access;
+
+ operation_runner()->GetMetadata(file, RecordMetadataCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(info().is_directory);
+ ++read_access;
+
+ EXPECT_EQ(read_access,
+ quota_manager_proxy()->notify_storage_accessed_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestTypeMismatchErrors) {
+ FileSystemURL dir(CreateDirectory("dir"));
+ operation_runner()->FileExists(dir, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, status());
+
+ FileSystemURL file(CreateFile("file"));
+ operation_runner()->DirectoryExists(file, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status());
+}
+
+TEST_F(FileSystemOperationImplTest, TestReadDirFailure) {
+ // Path doesn't exist
+ operation_runner()->ReadDirectory(URLForPath("nonexistent"),
+ RecordReadDirectoryCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+
+ // File exists.
+ FileSystemURL file(CreateFile("file"));
+ operation_runner()->ReadDirectory(file, RecordReadDirectoryCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestReadDirSuccess) {
+ // parent_dir
+ // | |
+ // child_dir child_file
+ // Verify reading parent_dir.
+ FileSystemURL parent_dir(CreateDirectory("dir"));
+ FileSystemURL child_dir(CreateDirectory("dir/child_dir"));
+ FileSystemURL child_file(CreateFile("dir/child_file"));
+
+ operation_runner()->ReadDirectory(parent_dir, RecordReadDirectoryCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(2u, entries().size());
+
+ for (size_t i = 0; i < entries().size(); ++i) {
+ if (entries()[i].is_directory)
+ EXPECT_EQ(FILE_PATH_LITERAL("child_dir"), entries()[i].name);
+ else
+ EXPECT_EQ(FILE_PATH_LITERAL("child_file"), entries()[i].name);
+ }
+ EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestRemoveFailure) {
+ // Path doesn't exist.
+ operation_runner()->Remove(URLForPath("nonexistent"), false /* recursive */,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+
+ // It's an error to try to remove a non-empty directory if recursive flag
+ // is false.
+ // parent_dir
+ // | |
+ // child_dir child_file
+ // Verify deleting parent_dir.
+ FileSystemURL parent_dir(CreateDirectory("dir"));
+ FileSystemURL child_dir(CreateDirectory("dir/child_dir"));
+ FileSystemURL child_file(CreateFile("dir/child_file"));
+
+ operation_runner()->Remove(parent_dir, false /* recursive */,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestRemoveSuccess) {
+ FileSystemURL empty_dir(CreateDirectory("empty_dir"));
+ EXPECT_TRUE(DirectoryExists("empty_dir"));
+ operation_runner()->Remove(empty_dir, false /* recursive */,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(DirectoryExists("empty_dir"));
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestRemoveSuccessRecursive) {
+ // Removing a non-empty directory with recursive flag == true should be ok.
+ // parent_dir
+ // | |
+ // child_dir child_files
+ // |
+ // child_files
+ //
+ // Verify deleting parent_dir.
+ FileSystemURL parent_dir(CreateDirectory("dir"));
+ for (int i = 0; i < 8; ++i)
+ CreateFile(base::StringPrintf("dir/file-%d", i));
+ FileSystemURL child_dir(CreateDirectory("dir/child_dir"));
+ for (int i = 0; i < 8; ++i)
+ CreateFile(base::StringPrintf("dir/child_dir/file-%d", i));
+
+ operation_runner()->Remove(parent_dir, true /* recursive */,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(DirectoryExists("parent_dir"));
+
+ EXPECT_EQ(2, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(16, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(FileSystemOperationImplTest, TestTruncate) {
+ FileSystemURL file(CreateFile("file"));
+ base::FilePath platform_path = PlatformPath("file");
+
+ char test_data[] = "test data";
+ int data_size = static_cast<int>(sizeof(test_data));
+ EXPECT_EQ(data_size,
+ file_util::WriteFile(platform_path, test_data, data_size));
+
+ // Check that its length is the size of the data written.
+ operation_runner()->GetMetadata(file, RecordMetadataCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(info().is_directory);
+ EXPECT_EQ(data_size, info().size);
+
+ // Extend the file by truncating it.
+ int length = 17;
+ operation_runner()->Truncate(file, length, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Check that its length is now 17 and that it's all zeroes after the test
+ // data.
+ EXPECT_EQ(length, GetFileSize("file"));
+ char data[100];
+ EXPECT_EQ(length, base::ReadFile(platform_path, data, length));
+ for (int i = 0; i < length; ++i) {
+ if (i < static_cast<int>(sizeof(test_data)))
+ EXPECT_EQ(test_data[i], data[i]);
+ else
+ EXPECT_EQ(0, data[i]);
+ }
+
+ // Shorten the file by truncating it.
+ length = 3;
+ operation_runner()->Truncate(file, length, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Check that its length is now 3 and that it contains only bits of test data.
+ EXPECT_EQ(length, GetFileSize("file"));
+ EXPECT_EQ(length, base::ReadFile(platform_path, data, length));
+ for (int i = 0; i < length; ++i)
+ EXPECT_EQ(test_data[i], data[i]);
+
+ // Truncate is not a 'read' access. (Here expected access count is 1
+ // since we made 1 read access for GetMetadata.)
+ EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count());
+}
+
+TEST_F(FileSystemOperationImplTest, TestTruncateFailureByQuota) {
+ FileSystemURL dir(CreateDirectory("dir"));
+ FileSystemURL file(CreateFile("dir/file"));
+
+ GrantQuotaForCurrentUsage();
+ AddQuota(10);
+
+ operation_runner()->Truncate(file, 10, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ EXPECT_EQ(10, GetFileSize("dir/file"));
+
+ operation_runner()->Truncate(file, 11, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ EXPECT_EQ(10, GetFileSize("dir/file"));
+}
+
+TEST_F(FileSystemOperationImplTest, TestTouchFile) {
+ FileSystemURL file(CreateFile("file"));
+ base::FilePath platform_path = PlatformPath("file");
+
+ base::PlatformFileInfo info;
+ EXPECT_TRUE(base::GetFileInfo(platform_path, &info));
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_EQ(0, info.size);
+ const base::Time last_modified = info.last_modified;
+ const base::Time last_accessed = info.last_accessed;
+
+ const base::Time new_modified_time = base::Time::UnixEpoch();
+ const base::Time new_accessed_time = new_modified_time +
+ base::TimeDelta::FromHours(77);
+ ASSERT_NE(last_modified, new_modified_time);
+ ASSERT_NE(last_accessed, new_accessed_time);
+
+ operation_runner()->TouchFile(file, new_accessed_time, new_modified_time,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ EXPECT_TRUE(base::GetFileInfo(platform_path, &info));
+ // We compare as time_t here to lower our resolution, to avoid false
+ // negatives caused by conversion to the local filesystem's native
+ // representation and back.
+ EXPECT_EQ(new_modified_time.ToTimeT(), info.last_modified.ToTimeT());
+ EXPECT_EQ(new_accessed_time.ToTimeT(), info.last_accessed.ToTimeT());
+}
+
+TEST_F(FileSystemOperationImplTest, TestCreateSnapshotFile) {
+ FileSystemURL dir(CreateDirectory("dir"));
+
+ // Create a file for the testing.
+ operation_runner()->DirectoryExists(dir, RecordStatusCallback());
+ FileSystemURL file(CreateFile("dir/file"));
+ operation_runner()->FileExists(file, RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+
+ // See if we can get a 'snapshot' file info for the file.
+ // Since FileSystemOperationImpl assumes the file exists in the local
+ // directory it should just returns the same metadata and platform_path
+ // as the file itself.
+ operation_runner()->CreateSnapshotFile(file, RecordSnapshotFileCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_FALSE(info().is_directory);
+ EXPECT_EQ(PlatformPath("dir/file"), path());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // The FileSystemOpration implementation does not create a
+ // shareable file reference.
+ EXPECT_EQ(NULL, shareable_file_ref());
+}
+
+TEST_F(FileSystemOperationImplTest,
+ TestMoveSuccessSrcDirRecursiveWithQuota) {
+ FileSystemURL src(CreateDirectory("src"));
+ int src_path_cost = GetUsage();
+
+ FileSystemURL dest(CreateDirectory("dest"));
+ FileSystemURL child_file1(CreateFile("src/file1"));
+ FileSystemURL child_file2(CreateFile("src/file2"));
+ FileSystemURL child_dir(CreateDirectory("src/dir"));
+ FileSystemURL grandchild_file1(CreateFile("src/dir/file1"));
+ FileSystemURL grandchild_file2(CreateFile("src/dir/file2"));
+
+ int total_path_cost = GetUsage();
+ EXPECT_EQ(0, GetDataSizeOnDisk());
+
+ operation_runner()->Truncate(
+ child_file1, 5000,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ child_file2, 400,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ grandchild_file1, 30,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ grandchild_file2, 2,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+
+ const int64 all_file_size = 5000 + 400 + 30 + 2;
+ EXPECT_EQ(all_file_size, GetDataSizeOnDisk());
+ EXPECT_EQ(all_file_size + total_path_cost, GetUsage());
+
+ operation_runner()->Move(
+ src, dest, FileSystemOperation::OPTION_NONE,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(DirectoryExists("src/dir"));
+ EXPECT_FALSE(FileExists("src/dir/file2"));
+ EXPECT_TRUE(DirectoryExists("dest/dir"));
+ EXPECT_TRUE(FileExists("dest/dir/file2"));
+
+ EXPECT_EQ(all_file_size, GetDataSizeOnDisk());
+ EXPECT_EQ(all_file_size + total_path_cost - src_path_cost,
+ GetUsage());
+}
+
+TEST_F(FileSystemOperationImplTest,
+ TestCopySuccessSrcDirRecursiveWithQuota) {
+ FileSystemURL src(CreateDirectory("src"));
+ FileSystemURL dest1(CreateDirectory("dest1"));
+ FileSystemURL dest2(CreateDirectory("dest2"));
+
+ int64 usage = GetUsage();
+ FileSystemURL child_file1(CreateFile("src/file1"));
+ FileSystemURL child_file2(CreateFile("src/file2"));
+ FileSystemURL child_dir(CreateDirectory("src/dir"));
+ int64 child_path_cost = GetUsage() - usage;
+ usage += child_path_cost;
+
+ FileSystemURL grandchild_file1(CreateFile("src/dir/file1"));
+ FileSystemURL grandchild_file2(CreateFile("src/dir/file2"));
+ int64 total_path_cost = GetUsage();
+ int64 grandchild_path_cost = total_path_cost - usage;
+
+ EXPECT_EQ(0, GetDataSizeOnDisk());
+
+ operation_runner()->Truncate(
+ child_file1, 8000,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ child_file2, 700,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ grandchild_file1, 60,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ operation_runner()->Truncate(
+ grandchild_file2, 5,
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+
+ const int64 child_file_size = 8000 + 700;
+ const int64 grandchild_file_size = 60 + 5;
+ const int64 all_file_size = child_file_size + grandchild_file_size;
+ int64 expected_usage = all_file_size + total_path_cost;
+
+ usage = GetUsage();
+ EXPECT_EQ(all_file_size, GetDataSizeOnDisk());
+ EXPECT_EQ(expected_usage, usage);
+
+ // Copy src to dest1.
+ operation_runner()->Copy(
+ src, dest1, FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+
+ expected_usage += all_file_size + child_path_cost + grandchild_path_cost;
+ EXPECT_TRUE(DirectoryExists("src/dir"));
+ EXPECT_TRUE(FileExists("src/dir/file2"));
+ EXPECT_TRUE(DirectoryExists("dest1/dir"));
+ EXPECT_TRUE(FileExists("dest1/dir/file2"));
+
+ EXPECT_EQ(2 * all_file_size, GetDataSizeOnDisk());
+ EXPECT_EQ(expected_usage, GetUsage());
+
+ // Copy src/dir to dest2.
+ operation_runner()->Copy(
+ child_dir, dest2, FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::RunLoop().RunUntilIdle();
+
+ expected_usage += grandchild_file_size + grandchild_path_cost;
+ usage = GetUsage();
+ EXPECT_EQ(2 * child_file_size + 3 * grandchild_file_size,
+ GetDataSizeOnDisk());
+ EXPECT_EQ(expected_usage, usage);
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc
new file mode 100644
index 00000000000..a92288af0fd
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc
@@ -0,0 +1,328 @@
+// Copyright 2013 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 <vector>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_backend.h"
+#include "content/public/test/test_file_system_context.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_job_factory_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+#include "webkit/browser/blob/blob_url_request_job.h"
+#include "webkit/browser/blob/mock_blob_url_request_context.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_operation_runner.h"
+#include "webkit/browser/fileapi/local_file_util.h"
+#include "webkit/browser/fileapi/mock_file_change_observer.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/common/blob/blob_data.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using webkit_blob::MockBlobURLRequestContext;
+using webkit_blob::ScopedTextBlob;
+
+namespace fileapi {
+
+namespace {
+
+const GURL kOrigin("http://example.com");
+const FileSystemType kFileSystemType = kFileSystemTypeTest;
+
+void AssertStatusEq(base::PlatformFileError expected,
+ base::PlatformFileError actual) {
+ ASSERT_EQ(expected, actual);
+}
+
+} // namespace
+
+class FileSystemOperationImplWriteTest
+ : public testing::Test {
+ public:
+ FileSystemOperationImplWriteTest()
+ : status_(base::PLATFORM_FILE_OK),
+ cancel_status_(base::PLATFORM_FILE_ERROR_FAILED),
+ bytes_written_(0),
+ complete_(false),
+ weak_factory_(this) {
+ change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+
+ quota_manager_ =
+ new quota::MockQuotaManager(false /* is_incognito */,
+ dir_.path(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ NULL /* special storage policy */);
+ virtual_path_ = base::FilePath(FILE_PATH_LITERAL("temporary file"));
+
+ file_system_context_ = CreateFileSystemContextForTesting(
+ quota_manager_->proxy(), dir_.path());
+ url_request_context_.reset(
+ new MockBlobURLRequestContext(file_system_context_.get()));
+
+ file_system_context_->operation_runner()->CreateFile(
+ URLForPath(virtual_path_), true /* exclusive */,
+ base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK));
+
+ static_cast<TestFileSystemBackend*>(
+ file_system_context_->GetFileSystemBackend(kFileSystemType))
+ ->AddFileChangeObserver(change_observer());
+ }
+
+ virtual void TearDown() {
+ quota_manager_ = NULL;
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ base::PlatformFileError status() const { return status_; }
+ base::PlatformFileError cancel_status() const { return cancel_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_; }
+
+ protected:
+ const ChangeObserverList& change_observers() const {
+ return change_observers_;
+ }
+
+ MockFileChangeObserver* change_observer() {
+ return &change_observer_;
+ }
+
+ FileSystemURL URLForPath(const base::FilePath& path) const {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ kOrigin, kFileSystemType, path);
+ }
+
+ // Callback function for recording test results.
+ FileSystemOperation::WriteCallback RecordWriteCallback() {
+ return base::Bind(&FileSystemOperationImplWriteTest::DidWrite,
+ weak_factory_.GetWeakPtr());
+ }
+
+ FileSystemOperation::StatusCallback RecordCancelCallback() {
+ return base::Bind(&FileSystemOperationImplWriteTest::DidCancel,
+ weak_factory_.GetWeakPtr());
+ }
+
+ void DidWrite(base::PlatformFileError status, int64 bytes, bool complete) {
+ if (status == base::PLATFORM_FILE_OK) {
+ add_bytes_written(bytes, complete);
+ if (complete)
+ base::MessageLoop::current()->Quit();
+ } else {
+ EXPECT_FALSE(complete_);
+ EXPECT_EQ(status_, base::PLATFORM_FILE_OK);
+ complete_ = true;
+ status_ = status;
+ if (base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+ }
+
+ void DidCancel(base::PlatformFileError status) {
+ cancel_status_ = status;
+ }
+
+ const MockBlobURLRequestContext& url_request_context() const {
+ return *url_request_context_;
+ }
+
+ scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<quota::MockQuotaManager> quota_manager_;
+
+ base::MessageLoopForIO loop_;
+
+ base::ScopedTempDir dir_;
+ base::FilePath virtual_path_;
+
+ // For post-operation status.
+ base::PlatformFileError status_;
+ base::PlatformFileError cancel_status_;
+ int64 bytes_written_;
+ bool complete_;
+
+ scoped_ptr<MockBlobURLRequestContext> url_request_context_;
+
+ MockFileChangeObserver change_observer_;
+ ChangeObserverList change_observers_;
+
+ base::WeakPtrFactory<FileSystemOperationImplWriteTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplWriteTest);
+};
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) {
+ ScopedTextBlob blob(url_request_context(),
+ "blob-id:success",
+ "Hello, world!\n");
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_path_),
+ blob.GetBlobDataHandle(),
+ 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(14, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) {
+ ScopedTextBlob blob(url_request_context(), "blob_id:zero", "");
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_path_),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+}
+
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidBlobUrl) {
+ scoped_ptr<webkit_blob::BlobDataHandle> null_handle;
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_path_),
+ null_handle.Pass(), 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_FAILED, status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) {
+ ScopedTextBlob blob(url_request_context(), "blob_id:writeinvalidfile",
+ "It\'ll not be written.");
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(),
+ URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) {
+ base::FilePath virtual_dir_path(FILE_PATH_LITERAL("d"));
+ file_system_context_->operation_runner()->CreateDirectory(
+ URLForPath(virtual_dir_path),
+ true /* exclusive */, false /* recursive */,
+ base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK));
+
+ ScopedTextBlob blob(url_request_context(), "blob:writedir",
+ "It\'ll not be written, too.");
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_dir_path),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(0, bytes_written());
+ // TODO(kinuko): This error code is platform- or fileutil- dependent
+ // right now. Make it return PLATFORM_FILE_ERROR_NOT_A_FILE in every case.
+ EXPECT_TRUE(status() == base::PLATFORM_FILE_ERROR_NOT_A_FILE ||
+ status() == base::PLATFORM_FILE_ERROR_ACCESS_DENIED ||
+ status() == base::PLATFORM_FILE_ERROR_FAILED);
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) {
+ ScopedTextBlob blob(url_request_context(), "blob:success",
+ "Hello, world!\n");
+ quota_manager_->SetQuota(
+ kOrigin, FileSystemTypeToQuotaStorageType(kFileSystemType), 10);
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_path_),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(10, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) {
+ ScopedTextBlob blob(url_request_context(), "blob:success",
+ "Hello, world!\n");
+ FileSystemOperationRunner::OperationID id =
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(), URLForPath(virtual_path_),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback());
+ // We use RunAllPendings() instead of Run() here, because we won't dispatch
+ // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
+ // to run another write cycle.
+ base::RunLoop().RunUntilIdle();
+
+ // Issued Cancel() before receiving any response from Write(),
+ // so nothing should have happen.
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
+}
+
+TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) {
+ ScopedTextBlob blob(url_request_context(), "blob:writeinvalidfile",
+ "It\'ll not be written.");
+ FileSystemOperationRunner::OperationID id =
+ file_system_context_->operation_runner()->Write(
+ &url_request_context(),
+ URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
+ blob.GetBlobDataHandle(), 0, RecordWriteCallback());
+ file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback());
+ // We use RunAllPendings() instead of Run() here, because we won't dispatch
+ // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
+ // to run another write cycle.
+ base::RunLoop().RunUntilIdle();
+
+ // Issued Cancel() before receiving any response from Write(),
+ // so nothing should have happen.
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status());
+ EXPECT_TRUE(complete());
+
+ EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
+}
+
+// TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases.
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc
new file mode 100644
index 00000000000..1fa711b8672
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc
@@ -0,0 +1,162 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_runner.h"
+
+namespace fileapi {
+
+void GetStatus(bool* done,
+ base::PlatformFileError *status_out,
+ base::PlatformFileError status) {
+ ASSERT_FALSE(*done);
+ *done = true;
+ *status_out = status;
+}
+
+void GetCancelStatus(bool* operation_done,
+ bool* cancel_done,
+ base::PlatformFileError *status_out,
+ base::PlatformFileError status) {
+ // Cancel callback must be always called after the operation's callback.
+ ASSERT_TRUE(*operation_done);
+ ASSERT_FALSE(*cancel_done);
+ *cancel_done = true;
+ *status_out = status;
+}
+
+class FileSystemOperationRunnerTest : public testing::Test {
+ protected:
+ FileSystemOperationRunnerTest() {}
+ virtual ~FileSystemOperationRunnerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(base_.CreateUniqueTempDir());
+ base::FilePath base_dir = base_.path();
+ file_system_context_ =
+ CreateFileSystemContextForTesting(NULL, base_dir);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ FileSystemURL URL(const std::string& path) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://example.com"), kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe(path));
+ }
+
+ FileSystemOperationRunner* operation_runner() {
+ return file_system_context_->operation_runner();
+ }
+
+ private:
+ base::ScopedTempDir base_;
+ base::MessageLoop message_loop_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemOperationRunnerTest);
+};
+
+TEST_F(FileSystemOperationRunnerTest, NotFoundError) {
+ bool done = false;
+ base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+
+ // Regular NOT_FOUND error, which is called asynchronously.
+ operation_runner()->Truncate(URL("foo"), 0,
+ base::Bind(&GetStatus, &done, &status));
+ ASSERT_FALSE(done);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(done);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status);
+}
+
+TEST_F(FileSystemOperationRunnerTest, InvalidURLError) {
+ bool done = false;
+ base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+
+ // Invalid URL error, which calls DidFinish synchronously.
+ operation_runner()->Truncate(FileSystemURL(), 0,
+ base::Bind(&GetStatus, &done, &status));
+ // The error call back shouldn't be fired synchronously.
+ ASSERT_FALSE(done);
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(done);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status);
+}
+
+TEST_F(FileSystemOperationRunnerTest, NotFoundErrorAndCancel) {
+ bool done = false;
+ bool cancel_done = false;
+ base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+
+ // Call Truncate with non-existent URL, and try to cancel it immediately
+ // after that (before its callback is fired).
+ FileSystemOperationRunner::OperationID id =
+ operation_runner()->Truncate(URL("foo"), 0,
+ base::Bind(&GetStatus, &done, &status));
+ operation_runner()->Cancel(id, base::Bind(&GetCancelStatus,
+ &done, &cancel_done,
+ &cancel_status));
+
+ ASSERT_FALSE(done);
+ ASSERT_FALSE(cancel_done);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(done);
+ ASSERT_TRUE(cancel_done);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+}
+
+TEST_F(FileSystemOperationRunnerTest, InvalidURLErrorAndCancel) {
+ bool done = false;
+ bool cancel_done = false;
+ base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+
+ // Call Truncate with invalid URL, and try to cancel it immediately
+ // after that (before its callback is fired).
+ FileSystemOperationRunner::OperationID id =
+ operation_runner()->Truncate(FileSystemURL(), 0,
+ base::Bind(&GetStatus, &done, &status));
+ operation_runner()->Cancel(id, base::Bind(&GetCancelStatus,
+ &done, &cancel_done,
+ &cancel_status));
+
+ ASSERT_FALSE(done);
+ ASSERT_FALSE(cancel_done);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(done);
+ ASSERT_TRUE(cancel_done);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+}
+
+TEST_F(FileSystemOperationRunnerTest, CancelWithInvalidId) {
+ const FileSystemOperationRunner::OperationID kInvalidId = -1;
+ bool done = true; // The operation is not running.
+ bool cancel_done = false;
+ base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+ operation_runner()->Cancel(kInvalidId, base::Bind(&GetCancelStatus,
+ &done, &cancel_done,
+ &cancel_status));
+
+ ASSERT_TRUE(cancel_done);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc b/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc
new file mode 100644
index 00000000000..c4baa4f8aa6
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc
@@ -0,0 +1,561 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_quota_client.h"
+#include "webkit/browser/fileapi/file_system_usage_cache.h"
+#include "webkit/browser/fileapi/obfuscated_file_util.h"
+#include "webkit/common/fileapi/file_system_types.h"
+#include "webkit/common/fileapi/file_system_util.h"
+#include "webkit/common/quota/quota_types.h"
+
+namespace fileapi {
+namespace {
+
+const char kDummyURL1[] = "http://www.dummy.org";
+const char kDummyURL2[] = "http://www.example.com";
+const char kDummyURL3[] = "http://www.bleh";
+
+// Declared to shorten the variable names.
+const quota::StorageType kTemporary = quota::kStorageTypeTemporary;
+const quota::StorageType kPersistent = quota::kStorageTypePersistent;
+
+} // namespace
+
+class FileSystemQuotaClientTest : public testing::Test {
+ public:
+ FileSystemQuotaClientTest()
+ : weak_factory_(this),
+ additional_callback_count_(0),
+ deletion_status_(quota::kQuotaStatusUnknown) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, data_dir_.path());
+ }
+
+ struct TestFile {
+ bool isDirectory;
+ const char* name;
+ int64 size;
+ const char* origin_url;
+ quota::StorageType type;
+ };
+
+ protected:
+ FileSystemQuotaClient* NewQuotaClient(bool is_incognito) {
+ return new FileSystemQuotaClient(file_system_context_.get(), is_incognito);
+ }
+
+ void GetOriginUsageAsync(FileSystemQuotaClient* quota_client,
+ const std::string& origin_url,
+ quota::StorageType type) {
+ quota_client->GetOriginUsage(
+ GURL(origin_url), type,
+ base::Bind(&FileSystemQuotaClientTest::OnGetUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ int64 GetOriginUsage(FileSystemQuotaClient* quota_client,
+ const std::string& origin_url,
+ quota::StorageType type) {
+ GetOriginUsageAsync(quota_client, origin_url, type);
+ base::RunLoop().RunUntilIdle();
+ return usage_;
+ }
+
+ const std::set<GURL>& GetOriginsForType(FileSystemQuotaClient* quota_client,
+ quota::StorageType type) {
+ origins_.clear();
+ quota_client->GetOriginsForType(
+ type,
+ base::Bind(&FileSystemQuotaClientTest::OnGetOrigins,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ const std::set<GURL>& GetOriginsForHost(FileSystemQuotaClient* quota_client,
+ quota::StorageType type,
+ const std::string& host) {
+ origins_.clear();
+ quota_client->GetOriginsForHost(
+ type, host,
+ base::Bind(&FileSystemQuotaClientTest::OnGetOrigins,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ void RunAdditionalOriginUsageTask(FileSystemQuotaClient* quota_client,
+ const std::string& origin_url,
+ quota::StorageType type) {
+ quota_client->GetOriginUsage(
+ GURL(origin_url), type,
+ base::Bind(&FileSystemQuotaClientTest::OnGetAdditionalUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ bool CreateFileSystemDirectory(const base::FilePath& file_path,
+ const std::string& origin_url,
+ quota::StorageType storage_type) {
+ FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ GURL(origin_url), type, file_path);
+
+ base::PlatformFileError result =
+ AsyncFileTestHelper::CreateDirectory(file_system_context_, url);
+ return result == base::PLATFORM_FILE_OK;
+ }
+
+ bool CreateFileSystemFile(const base::FilePath& file_path,
+ int64 file_size,
+ const std::string& origin_url,
+ quota::StorageType storage_type) {
+ if (file_path.empty())
+ return false;
+
+ FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ GURL(origin_url), type, file_path);
+
+ base::PlatformFileError result =
+ AsyncFileTestHelper::CreateFile(file_system_context_, url);
+ if (result != base::PLATFORM_FILE_OK)
+ return false;
+
+ result = AsyncFileTestHelper::TruncateFile(
+ file_system_context_, url, file_size);
+ return result == base::PLATFORM_FILE_OK;
+ }
+
+ void InitializeOriginFiles(FileSystemQuotaClient* quota_client,
+ const TestFile* files,
+ int num_files) {
+ for (int i = 0; i < num_files; i++) {
+ base::FilePath path = base::FilePath().AppendASCII(files[i].name);
+ if (files[i].isDirectory) {
+ ASSERT_TRUE(CreateFileSystemDirectory(
+ path, files[i].origin_url, files[i].type));
+ if (path.empty()) {
+ // Create the usage cache.
+ // HACK--we always create the root [an empty path] first. If we
+ // create it later, this will fail due to a quota mismatch. If we
+ // call this before we create the root, it succeeds, but hasn't
+ // actually created the cache.
+ ASSERT_EQ(0, GetOriginUsage(
+ quota_client, files[i].origin_url, files[i].type));
+ }
+ } else {
+ ASSERT_TRUE(CreateFileSystemFile(
+ path, files[i].size, files[i].origin_url, files[i].type));
+ }
+ }
+ }
+
+ // This is a bit fragile--it depends on the test data always creating a
+ // directory before adding a file or directory to it, so that we can just
+ // count the basename of each addition. A recursive creation of a path, which
+ // created more than one directory in a single shot, would break this.
+ int64 ComputeFilePathsCostForOriginAndType(const TestFile* files,
+ int num_files,
+ const std::string& origin_url,
+ quota::StorageType type) {
+ int64 file_paths_cost = 0;
+ for (int i = 0; i < num_files; i++) {
+ if (files[i].type == type &&
+ GURL(files[i].origin_url) == GURL(origin_url)) {
+ base::FilePath path = base::FilePath().AppendASCII(files[i].name);
+ if (!path.empty()) {
+ file_paths_cost += ObfuscatedFileUtil::ComputeFilePathCost(path);
+ }
+ }
+ }
+ return file_paths_cost;
+ }
+
+ void DeleteOriginData(FileSystemQuotaClient* quota_client,
+ const std::string& origin,
+ quota::StorageType type) {
+ deletion_status_ = quota::kQuotaStatusUnknown;
+ quota_client->DeleteOriginData(
+ GURL(origin), type,
+ base::Bind(&FileSystemQuotaClientTest::OnDeleteOrigin,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ int64 usage() const { return usage_; }
+ quota::QuotaStatusCode status() { return deletion_status_; }
+ int additional_callback_count() const { return additional_callback_count_; }
+ void set_additional_callback_count(int count) {
+ additional_callback_count_ = count;
+ }
+
+ private:
+ void OnGetUsage(int64 usage) {
+ usage_ = usage;
+ }
+
+ void OnGetOrigins(const std::set<GURL>& origins) {
+ origins_ = origins;
+ }
+
+ void OnGetAdditionalUsage(int64 usage_unused) {
+ ++additional_callback_count_;
+ }
+
+ void OnDeleteOrigin(quota::QuotaStatusCode status) {
+ deletion_status_ = status;
+ }
+
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ base::WeakPtrFactory<FileSystemQuotaClientTest> weak_factory_;
+ int64 usage_;
+ int additional_callback_count_;
+ std::set<GURL> origins_;
+ quota::QuotaStatusCode deletion_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemQuotaClientTest);
+};
+
+TEST_F(FileSystemQuotaClientTest, NoFileSystemTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+
+ EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+}
+
+TEST_F(FileSystemQuotaClientTest, NoFileTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, OneFileTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {false, "foo", 4921, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(4921 + file_paths_cost,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, TwoFilesTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {false, "foo", 10310, kDummyURL1, kTemporary},
+ {false, "bar", 41, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(10310 + 41 + file_paths_cost,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, EmptyFilesTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {false, "foo", 0, kDummyURL1, kTemporary},
+ {false, "bar", 0, kDummyURL1, kTemporary},
+ {false, "baz", 0, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(file_paths_cost,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, SubDirectoryTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {true, "dirtest", 0, kDummyURL1, kTemporary},
+ {false, "dirtest/foo", 11921, kDummyURL1, kTemporary},
+ {false, "bar", 4814, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(11921 + 4814 + file_paths_cost,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, MultiTypeTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {true, "dirtest", 0, kDummyURL1, kTemporary},
+ {false, "dirtest/foo", 133, kDummyURL1, kTemporary},
+ {false, "bar", 14, kDummyURL1, kTemporary},
+ {true, NULL, 0, kDummyURL1, kPersistent},
+ {true, "dirtest", 0, kDummyURL1, kPersistent},
+ {false, "dirtest/foo", 193, kDummyURL1, kPersistent},
+ {false, "bar", 9, kDummyURL1, kPersistent},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost_temporary = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+ const int64 file_paths_cost_persistent = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(133 + 14 + file_paths_cost_temporary,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ EXPECT_EQ(193 + 9 + file_paths_cost_persistent,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, MultiDomainTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {true, "dir1", 0, kDummyURL1, kTemporary},
+ {false, "dir1/foo", 1331, kDummyURL1, kTemporary},
+ {false, "bar", 134, kDummyURL1, kTemporary},
+ {true, NULL, 0, kDummyURL1, kPersistent},
+ {true, "dir2", 0, kDummyURL1, kPersistent},
+ {false, "dir2/foo", 1903, kDummyURL1, kPersistent},
+ {false, "bar", 19, kDummyURL1, kPersistent},
+ {true, NULL, 0, kDummyURL2, kTemporary},
+ {true, "dom", 0, kDummyURL2, kTemporary},
+ {false, "dom/fan", 1319, kDummyURL2, kTemporary},
+ {false, "bar", 113, kDummyURL2, kTemporary},
+ {true, NULL, 0, kDummyURL2, kPersistent},
+ {true, "dom", 0, kDummyURL2, kPersistent},
+ {false, "dom/fan", 2013, kDummyURL2, kPersistent},
+ {false, "baz", 18, kDummyURL2, kPersistent},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost_temporary1 = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+ const int64 file_paths_cost_persistent1 =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ kDummyURL1, kPersistent);
+ const int64 file_paths_cost_temporary2 = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL2, kTemporary);
+ const int64 file_paths_cost_persistent2 =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ kDummyURL2, kPersistent);
+
+ for (int i = 0; i < 2; i++) {
+ EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ EXPECT_EQ(1903 + 19 + file_paths_cost_persistent1,
+ GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent));
+ EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2,
+ GetOriginUsage(quota_client.get(), kDummyURL2, kTemporary));
+ EXPECT_EQ(2013 + 18 + file_paths_cost_persistent2,
+ GetOriginUsage(quota_client.get(), kDummyURL2, kPersistent));
+ }
+}
+
+TEST_F(FileSystemQuotaClientTest, GetUsage_MultipleTasks) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {false, "foo", 11, kDummyURL1, kTemporary},
+ {false, "bar", 22, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType(
+ kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary);
+
+ // Dispatching three GetUsage tasks.
+ set_additional_callback_count(0);
+ GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary);
+ RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary);
+ RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(11 + 22 + file_paths_cost, usage());
+ EXPECT_EQ(2, additional_callback_count());
+
+ // Once more, in a different order.
+ set_additional_callback_count(0);
+ RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary);
+ GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary);
+ RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(11 + 22 + file_paths_cost, usage());
+ EXPECT_EQ(2, additional_callback_count());
+}
+
+TEST_F(FileSystemQuotaClientTest, GetOriginsForType) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {true, NULL, 0, kDummyURL2, kTemporary},
+ {true, NULL, 0, kDummyURL3, kPersistent},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+
+ std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary);
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_TRUE(origins.find(GURL(kDummyURL1)) != origins.end());
+ EXPECT_TRUE(origins.find(GURL(kDummyURL2)) != origins.end());
+ EXPECT_TRUE(origins.find(GURL(kDummyURL3)) == origins.end());
+}
+
+TEST_F(FileSystemQuotaClientTest, GetOriginsForHost) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const char* kURL1 = "http://foo.com/";
+ const char* kURL2 = "https://foo.com/";
+ const char* kURL3 = "http://foo.com:1/";
+ const char* kURL4 = "http://foo2.com/";
+ const char* kURL5 = "http://foo.com:2/";
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kURL1, kTemporary},
+ {true, NULL, 0, kURL2, kTemporary},
+ {true, NULL, 0, kURL3, kTemporary},
+ {true, NULL, 0, kURL4, kTemporary},
+ {true, NULL, 0, kURL5, kPersistent},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+
+ std::set<GURL> origins = GetOriginsForHost(
+ quota_client.get(), kTemporary, "foo.com");
+ EXPECT_EQ(3U, origins.size());
+ EXPECT_TRUE(origins.find(GURL(kURL1)) != origins.end());
+ EXPECT_TRUE(origins.find(GURL(kURL2)) != origins.end());
+ EXPECT_TRUE(origins.find(GURL(kURL3)) != origins.end());
+ EXPECT_TRUE(origins.find(GURL(kURL4)) == origins.end()); // Different host.
+ EXPECT_TRUE(origins.find(GURL(kURL5)) == origins.end()); // Different type.
+}
+
+TEST_F(FileSystemQuotaClientTest, IncognitoTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(true));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, kDummyURL1, kTemporary},
+ {false, "foo", 10, kDummyURL1, kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+
+ // Having files in the usual directory wouldn't affect the result
+ // queried in incognito mode.
+ EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary));
+ EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent));
+
+ std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary);
+ EXPECT_EQ(0U, origins.size());
+ origins = GetOriginsForHost(quota_client.get(), kTemporary, "www.dummy.org");
+ EXPECT_EQ(0U, origins.size());
+}
+
+TEST_F(FileSystemQuotaClientTest, DeleteOriginTest) {
+ scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false));
+ const TestFile kFiles[] = {
+ {true, NULL, 0, "http://foo.com/", kTemporary},
+ {false, "a", 1, "http://foo.com/", kTemporary},
+ {true, NULL, 0, "https://foo.com/", kTemporary},
+ {false, "b", 2, "https://foo.com/", kTemporary},
+ {true, NULL, 0, "http://foo.com/", kPersistent},
+ {false, "c", 4, "http://foo.com/", kPersistent},
+ {true, NULL, 0, "http://bar.com/", kTemporary},
+ {false, "d", 8, "http://bar.com/", kTemporary},
+ {true, NULL, 0, "http://bar.com/", kPersistent},
+ {false, "e", 16, "http://bar.com/", kPersistent},
+ {true, NULL, 0, "https://bar.com/", kPersistent},
+ {false, "f", 32, "https://bar.com/", kPersistent},
+ {true, NULL, 0, "https://bar.com/", kTemporary},
+ {false, "g", 64, "https://bar.com/", kTemporary},
+ };
+ InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles));
+ const int64 file_paths_cost_temporary_foo_https =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ "https://foo.com/", kTemporary);
+ const int64 file_paths_cost_persistent_foo =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ "http://foo.com/", kPersistent);
+ const int64 file_paths_cost_temporary_bar =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ "http://bar.com/", kTemporary);
+ const int64 file_paths_cost_temporary_bar_https =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ "https://bar.com/", kTemporary);
+ const int64 file_paths_cost_persistent_bar_https =
+ ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles),
+ "https://bar.com/", kPersistent);
+
+ DeleteOriginData(quota_client.get(), "http://foo.com/", kTemporary);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(quota::kQuotaStatusOk, status());
+
+ DeleteOriginData(quota_client.get(), "http://bar.com/", kPersistent);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(quota::kQuotaStatusOk, status());
+
+ DeleteOriginData(quota_client.get(), "http://buz.com/", kTemporary);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(quota::kQuotaStatusOk, status());
+
+ EXPECT_EQ(0, GetOriginUsage(
+ quota_client.get(), "http://foo.com/", kTemporary));
+ EXPECT_EQ(0, GetOriginUsage(
+ quota_client.get(), "http://bar.com/", kPersistent));
+ EXPECT_EQ(0, GetOriginUsage(
+ quota_client.get(), "http://buz.com/", kTemporary));
+
+ EXPECT_EQ(2 + file_paths_cost_temporary_foo_https,
+ GetOriginUsage(quota_client.get(),
+ "https://foo.com/",
+ kTemporary));
+ EXPECT_EQ(4 + file_paths_cost_persistent_foo,
+ GetOriginUsage(quota_client.get(),
+ "http://foo.com/",
+ kPersistent));
+ EXPECT_EQ(8 + file_paths_cost_temporary_bar,
+ GetOriginUsage(quota_client.get(),
+ "http://bar.com/",
+ kTemporary));
+ EXPECT_EQ(32 + file_paths_cost_persistent_bar_https,
+ GetOriginUsage(quota_client.get(),
+ "https://bar.com/",
+ kPersistent));
+ EXPECT_EQ(64 + file_paths_cost_temporary_bar_https,
+ GetOriginUsage(quota_client.get(),
+ "https://bar.com/",
+ kTemporary));
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc b/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc
new file mode 100644
index 00000000000..7edd26aa656
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc
@@ -0,0 +1,368 @@
+// Copyright 2013 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 "webkit/browser/fileapi/file_system_url_request_job.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/format_macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/rand_util.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/load_flags.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_byte_range.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+
+namespace fileapi {
+namespace {
+
+// We always use the TEMPORARY FileSystem in this test.
+const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
+const char kTestFileData[] = "0123456789";
+
+void FillBuffer(char* buffer, size_t len) {
+ base::RandBytes(buffer, len);
+}
+
+} // namespace
+
+class FileSystemURLRequestJobTest : public testing::Test {
+ protected:
+ FileSystemURLRequestJobTest() : weak_factory_(this) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ // We use the main thread so that we can get the root path synchronously.
+ // TODO(adamk): Run this on the FILE thread we've created as well.
+ file_system_context_ =
+ CreateFileSystemContextForTesting(NULL, temp_dir_.path());
+
+ file_system_context_->OpenFileSystem(
+ GURL("http://remote/"), kFileSystemTypeTemporary,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&FileSystemURLRequestJobTest::OnOpenFileSystem,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+
+ net::URLRequest::Deprecated::RegisterProtocolFactory(
+ "filesystem", &FileSystemURLRequestJobFactory);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
+ ClearUnusedJob();
+ if (pending_job_.get()) {
+ pending_job_->Kill();
+ pending_job_ = NULL;
+ }
+ // FileReader posts a task to close the file in destructor.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void OnOpenFileSystem(const GURL& root_url,
+ const std::string& name,
+ base::PlatformFileError result) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ }
+
+ void TestRequestHelper(const GURL& url,
+ const net::HttpRequestHeaders* headers,
+ bool run_to_completion,
+ FileSystemContext* file_system_context) {
+ delegate_.reset(new net::TestDelegate());
+ // Make delegate_ exit the MessageLoop when the request is done.
+ delegate_->set_quit_on_complete(true);
+ delegate_->set_quit_on_redirect(true);
+ request_ = empty_context_.CreateRequest(
+ url, net::DEFAULT_PRIORITY, delegate_.get());
+ if (headers)
+ request_->SetExtraRequestHeaders(*headers);
+ ASSERT_TRUE(!job_);
+ job_ = new FileSystemURLRequestJob(
+ request_.get(), NULL, file_system_context);
+ pending_job_ = job_;
+
+ request_->Start();
+ ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
+ if (run_to_completion)
+ base::MessageLoop::current()->Run();
+ }
+
+ void TestRequest(const GURL& url) {
+ TestRequestHelper(url, NULL, true, file_system_context_.get());
+ }
+
+ void TestRequestWithContext(const GURL& url,
+ FileSystemContext* file_system_context) {
+ TestRequestHelper(url, NULL, true, file_system_context);
+ }
+
+ void TestRequestWithHeaders(const GURL& url,
+ const net::HttpRequestHeaders* headers) {
+ TestRequestHelper(url, headers, true, file_system_context_.get());
+ }
+
+ void TestRequestNoRun(const GURL& url) {
+ TestRequestHelper(url, NULL, false, file_system_context_.get());
+ }
+
+ void CreateDirectory(const base::StringPiece& dir_name) {
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://remote"),
+ kFileSystemTypeTemporary,
+ base::FilePath().AppendASCII(dir_name));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, AsyncFileTestHelper::CreateDirectory(
+ file_system_context_, url));
+ }
+
+ void WriteFile(const base::StringPiece& file_name,
+ const char* buf, int buf_size) {
+ FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://remote"),
+ kFileSystemTypeTemporary,
+ base::FilePath().AppendASCII(file_name));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFileWithData(
+ file_system_context_, url, buf, buf_size));
+ }
+
+ GURL CreateFileSystemURL(const std::string& path) {
+ return GURL(kFileSystemURLPrefix + path);
+ }
+
+ static net::URLRequestJob* FileSystemURLRequestJobFactory(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const std::string& scheme) {
+ DCHECK(job_);
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ }
+
+ static void ClearUnusedJob() {
+ if (job_) {
+ scoped_refptr<net::URLRequestJob> deleter = job_;
+ job_ = NULL;
+ }
+ }
+
+ // Put the message loop at the top, so that it's the last thing deleted.
+ base::MessageLoopForIO message_loop_;
+
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_;
+
+ net::URLRequestContext empty_context_;
+
+ // NOTE: order matters, request must die before delegate
+ scoped_ptr<net::TestDelegate> delegate_;
+ scoped_ptr<net::URLRequest> request_;
+
+ scoped_refptr<net::URLRequestJob> pending_job_;
+ static net::URLRequestJob* job_;
+};
+
+// static
+net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;
+
+namespace {
+
+TEST_F(FileSystemURLRequestJobTest, FileTest) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ TestRequest(CreateFileSystemURL("file1.dat"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_EQ(kTestFileData, delegate_->data_received());
+ EXPECT_EQ(200, request_->GetResponseCode());
+ std::string cache_control;
+ request_->GetResponseHeaderByName("cache-control", &cache_control);
+ EXPECT_EQ("no-cache", cache_control);
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) {
+ const size_t buffer_size = 4000;
+ scoped_ptr<char[]> buffer(new char[buffer_size]);
+ FillBuffer(buffer.get(), buffer_size);
+ WriteFile("bigfile", buffer.get(), buffer_size);
+
+ const size_t first_byte_position = 500;
+ const size_t last_byte_position = buffer_size - first_byte_position;
+ std::string partial_buffer_string(buffer.get() + first_byte_position,
+ buffer.get() + last_byte_position + 1);
+
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(
+ net::HttpRequestHeaders::kRange,
+ net::HttpByteRange::Bounded(
+ first_byte_position, last_byte_position).GetHeaderValue());
+ TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
+ const size_t buffer_size = 4000;
+ scoped_ptr<char[]> buffer(new char[buffer_size]);
+ FillBuffer(buffer.get(), buffer_size);
+ WriteFile("bigfile", buffer.get(), buffer_size);
+
+ const size_t first_byte_position = 500;
+ std::string partial_buffer_string(buffer.get() + first_byte_position,
+ buffer.get() + buffer_size);
+
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(
+ net::HttpRequestHeaders::kRange,
+ net::HttpByteRange::RightUnbounded(first_byte_position).GetHeaderValue());
+ TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
+ EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
+}
+
+
+TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kRange,
+ "bytes=0-5,10-200,200-300");
+ TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
+ request_->status().error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(
+ net::HttpRequestHeaders::kRange,
+ net::HttpByteRange::Bounded(500, 1000).GetHeaderValue());
+ TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
+ request_->status().error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) {
+ CreateDirectory("dir");
+ TestRequest(CreateFileSystemURL("dir"));
+
+ EXPECT_EQ(1, delegate_->received_redirect_count());
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_FALSE(delegate_->request_failed());
+
+ // We've deferred the redirect; now cancel the request to avoid following it.
+ request_->Cancel();
+ base::MessageLoop::current()->Run();
+}
+
+TEST_F(FileSystemURLRequestJobTest, InvalidURL) {
+ TestRequest(GURL("filesystem:/foo/bar/baz"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) {
+ TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, NoSuchFile) {
+ TestRequest(CreateFileSystemURL("somefile"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, Cancel) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ TestRequestNoRun(CreateFileSystemURL("file1.dat"));
+
+ // Run StartAsync() and only StartAsync().
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release());
+ base::RunLoop().RunUntilIdle();
+ // If we get here, success! we didn't crash!
+}
+
+TEST_F(FileSystemURLRequestJobTest, GetMimeType) {
+ const char kFilename[] = "hoge.html";
+
+ std::string mime_type_direct;
+ base::FilePath::StringType extension =
+ base::FilePath().AppendASCII(kFilename).Extension();
+ if (!extension.empty())
+ extension = extension.substr(1);
+ EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension(
+ extension, &mime_type_direct));
+
+ TestRequest(CreateFileSystemURL(kFilename));
+
+ std::string mime_type_from_job;
+ request_->GetMimeType(&mime_type_from_job);
+ EXPECT_EQ(mime_type_direct, mime_type_from_job);
+}
+
+TEST_F(FileSystemURLRequestJobTest, Incognito) {
+ WriteFile("file", kTestFileData, arraysize(kTestFileData) - 1);
+
+ // Creates a new filesystem context for incognito mode.
+ scoped_refptr<FileSystemContext> file_system_context =
+ CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path());
+
+ // The request should return NOT_FOUND error if it's in incognito mode.
+ TestRequestWithContext(CreateFileSystemURL("file"),
+ file_system_context.get());
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+
+ // Make sure it returns success with regular (non-incognito) context.
+ TestRequest(CreateFileSystemURL("file"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(kTestFileData, delegate_->data_received());
+ EXPECT_EQ(200, request_->GetResponseCode());
+}
+
+} // namespace
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc b/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc
new file mode 100644
index 00000000000..c18877f7727
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc
@@ -0,0 +1,455 @@
+// Copyright 2013 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/bind_helpers.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/request_priority.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 "url/gurl.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_quota_util.h"
+#include "webkit/browser/fileapi/file_writer_delegate.h"
+#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
+
+namespace fileapi {
+
+namespace {
+
+const GURL kOrigin("http://example.com");
+const FileSystemType kFileSystemType = kFileSystemTypeTest;
+
+const char kData[] = "The quick brown fox jumps over the lazy dog.\n";
+const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1;
+
+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)
+ base::MessageLoop::current()->Quit();
+ } else {
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status_);
+ status_ = status;
+ base::MessageLoop::current()->Quit();
+ }
+ }
+
+ private:
+ // For post-operation status.
+ base::PlatformFileError status_;
+ int64 bytes_written_;
+ FileWriterDelegate::WriteProgressStatus write_status_;
+};
+
+} // namespace (anonymous)
+
+class FileWriterDelegateTest : public PlatformTest {
+ public:
+ FileWriterDelegateTest() {}
+
+ protected:
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ int64 usage() {
+ return file_system_context_->GetQuotaUtil(kFileSystemType)
+ ->GetOriginUsageOnFileThread(
+ file_system_context_.get(), kOrigin, kFileSystemType);
+ }
+
+ int64 GetFileSizeOnDisk(const char* test_file_path) {
+ // There might be in-flight flush/write.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&base::DoNothing));
+ base::RunLoop().RunUntilIdle();
+
+ FileSystemURL url = GetFileSystemURL(test_file_path);
+ base::PlatformFileInfo file_info;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetMetadata(
+ file_system_context_, url, &file_info));
+ return file_info.size;
+ }
+
+ FileSystemURL GetFileSystemURL(const char* file_name) const {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
+ }
+
+ FileWriterDelegate* CreateWriterDelegate(
+ const char* test_file_path,
+ int64 offset,
+ int64 allowed_growth) {
+ SandboxFileStreamWriter* writer = new SandboxFileStreamWriter(
+ file_system_context_.get(),
+ GetFileSystemURL(test_file_path),
+ offset,
+ *file_system_context_->GetUpdateObservers(kFileSystemType));
+ writer->set_default_quota(allowed_growth);
+ return new FileWriterDelegate(scoped_ptr<FileStreamWriter>(writer));
+ }
+
+ FileWriterDelegate::DelegateWriteCallback GetWriteCallback(Result* result) {
+ return base::Bind(&Result::DidWrite, base::Unretained(result));
+ }
+
+ // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
+ // and creates a new FileWriterDelegate for the file.
+ void PrepareForWrite(const char* test_file_path,
+ const GURL& blob_url,
+ int64 offset,
+ int64 allowed_growth) {
+ file_writer_delegate_.reset(
+ CreateWriterDelegate(test_file_path, offset, allowed_growth));
+ request_ = empty_context_.CreateRequest(
+ blob_url, net::DEFAULT_PRIORITY, file_writer_delegate_.get());
+ }
+
+ static net::URLRequest::ProtocolFactory Factory;
+
+ // This should be alive until the very end of this instance.
+ base::MessageLoopForIO loop_;
+
+ scoped_refptr<FileSystemContext> file_system_context_;
+
+ net::URLRequestContext empty_context_;
+ scoped_ptr<FileWriterDelegate> file_writer_delegate_;
+ scoped_ptr<net::URLRequest> request_;
+
+ base::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 {
+ base::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());
+
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, dir_.path());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFile(
+ file_system_context_, GetFileSystemURL("test")));
+ net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory);
+}
+
+void FileWriterDelegateTest::TearDown() {
+ net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL);
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
+ const GURL kBlobURL("blob:nolimit");
+ content_ = kData;
+
+ PrepareForWrite("test", kBlobURL, 0, kint64max);
+
+ Result result;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ ASSERT_EQ(kDataSize, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ EXPECT_EQ(kDataSize, result.bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+}
+
+TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
+ const GURL kBlobURL("blob:just");
+ content_ = kData;
+ const int64 kAllowedGrowth = kDataSize;
+ PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth);
+
+ Result result;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ ASSERT_EQ(kAllowedGrowth, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+
+ 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("test", kBlobURL, 0, kAllowedGrowth);
+
+ Result result;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
+ file_writer_delegate_.reset();
+
+ ASSERT_EQ(kAllowedGrowth, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+
+ 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("test", kBlobURL, 0, kAllowedGrowth);
+
+ Result result;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ ASSERT_EQ(kAllowedGrowth, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+
+ EXPECT_EQ(kAllowedGrowth, result.bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+}
+
+TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
+ scoped_ptr<FileWriterDelegate> file_writer_delegate2;
+ scoped_ptr<net::URLRequest> request2;
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFile(
+ file_system_context_, GetFileSystemURL("test2")));
+
+ const GURL kBlobURL("blob:nolimitconcurrent");
+ const GURL kBlobURL2("blob:nolimitconcurrent2");
+ content_ = kData;
+
+ PrepareForWrite("test", kBlobURL, 0, kint64max);
+
+ // Credate another FileWriterDelegate for concurrent write.
+ file_writer_delegate2.reset(CreateWriterDelegate("test2", 0, kint64max));
+ request2 = empty_context_.CreateRequest(
+ kBlobURL2, net::DEFAULT_PRIORITY, file_writer_delegate2.get());
+
+ Result result, result2;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ file_writer_delegate2->Start(request2.Pass(), GetWriteCallback(&result2));
+ base::MessageLoop::current()->Run();
+ if (result.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING ||
+ result2.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING)
+ base::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, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
+
+ 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());
+}
+
+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("test", kBlobURL, offset, allowed_growth);
+
+ {
+ Result result;
+ ASSERT_EQ(0, usage());
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ ASSERT_EQ(kDataSize, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ 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("test", kBlobURL, offset, allowed_growth);
+
+ {
+ Result result;
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(kDataSize, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ 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("test", kBlobURL, offset, allowed_growth);
+
+ {
+ Result result;
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ EXPECT_EQ(offset + kDataSize, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ 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("test", kBlobURL, offset, allowed_growth);
+ int64 pre_write_usage = GetFileSizeOnDisk("test");
+
+ {
+ Result result;
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
+ file_writer_delegate_.reset();
+
+ EXPECT_EQ(pre_write_usage, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ 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("test", kBlobURL, offset, allowed_growth);
+
+ {
+ Result result;
+ file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result));
+ base::MessageLoop::current()->Run();
+ ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
+ file_writer_delegate_.reset();
+
+ EXPECT_EQ(pre_write_usage + allowed_growth, usage());
+ EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
+ EXPECT_EQ(kOverlap + allowed_growth, result.bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result.status());
+ }
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.cc b/chromium/content/browser/fileapi/fileapi_message_filter.cc
index 1a79ea2d349..b6b89fb50a6 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter.cc
+++ b/chromium/content/browser/fileapi/fileapi_message_filter.cc
@@ -34,7 +34,6 @@
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/isolated_context.h"
-#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/directory_entry.h"
@@ -42,17 +41,10 @@
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#if defined(ENABLE_PLUGINS)
-#include "content/browser/renderer_host/pepper/pepper_security_helper.h"
-#include "ppapi/shared_impl/file_type_conversion.h"
-#endif // defined(ENABLE_PLUGINS)
-
using fileapi::FileSystemFileUtil;
using fileapi::FileSystemBackend;
using fileapi::FileSystemOperation;
using fileapi::FileSystemURL;
-using fileapi::FileUpdateObserver;
-using fileapi::UpdateObserverList;
using webkit_blob::BlobData;
using webkit_blob::BlobStorageContext;
using webkit_blob::BlobStorageHost;
@@ -107,7 +99,6 @@ FileAPIMessageFilter::FileAPIMessageFilter(
void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- BrowserMessageFilter::OnChannelConnected(peer_pid);
if (request_context_getter_.get()) {
DCHECK(!request_context_);
@@ -124,7 +115,6 @@ void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) {
void FileAPIMessageFilter::OnChannelClosing() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- BrowserMessageFilter::OnChannelClosing();
// Unregister all the blob and stream URLs that are previously registered in
// this process.
@@ -136,22 +126,6 @@ void FileAPIMessageFilter::OnChannelClosing() {
in_transit_snapshot_files_.clear();
- // Close all files that are previously OpenFile()'ed in this process.
- if (!on_close_callbacks_.IsEmpty()) {
- DLOG(INFO)
- << "File API: Renderer process shut down before NotifyCloseFile"
- << " for " << on_close_callbacks_.size() << " files opened in PPAPI";
- }
-
- for (OnCloseCallbackMap::iterator itr(&on_close_callbacks_);
- !itr.IsAtEnd(); itr.Advance()) {
- const base::Closure* callback = itr.GetCurrentValue();
- DCHECK(callback);
- if (!callback->is_null())
- callback->Run();
- }
-
- on_close_callbacks_.Clear();
operation_runner_.reset();
operations_.clear();
}
@@ -168,31 +142,24 @@ bool FileAPIMessageFilter::OnMessageReceived(
*message_was_ok = true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(FileAPIMessageFilter, message, *message_was_ok)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_Open, OnOpen)
+ IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenFileSystem, OnOpenFileSystem)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ResolveURL, OnResolveURL)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DeleteFileSystem, OnDeleteFileSystem)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Move, OnMove)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Copy, OnCopy)
- IPC_MESSAGE_HANDLER(FileSystemMsg_Remove, OnRemove)
+ IPC_MESSAGE_HANDLER(FileSystemHostMsg_Remove, OnRemove)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadMetadata, OnReadMetadata)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Create, OnCreate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Exists, OnExists)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadDirectory, OnReadDirectory)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Write, OnWrite)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_WriteDeprecated, OnWriteDeprecated)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Truncate, OnTruncate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_TouchFile, OnTouchFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_CancelWrite, OnCancel)
-#if defined(ENABLE_PLUGINS)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenPepperFile, OnOpenPepperFile)
-#endif // defined(ENABLE_PLUGINS)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile,
OnCreateSnapshotFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidReceiveSnapshotFile,
OnDidReceiveSnapshotFile)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_WillUpdate, OnWillUpdate)
- IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidUpdate, OnDidUpdate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_SyncGetPlatformPath,
OnSyncGetPlatformPath)
IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuilding, OnStartBuildingBlob)
@@ -208,12 +175,6 @@ bool FileAPIMessageFilter::OnMessageReceived(
IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL,
OnRegisterPublicBlobURL)
IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL)
- IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRegisterBlobURL,
- OnDeprecatedRegisterBlobURL)
- IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRevokeBlobURL,
- OnDeprecatedRevokeBlobURL)
- IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedCloneBlobURL,
- OnDeprecatedCloneBlobURL)
IPC_MESSAGE_HANDLER(StreamHostMsg_StartBuilding, OnStartBuildingStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_AppendBlobDataItem,
OnAppendBlobDataItemToStream)
@@ -235,19 +196,17 @@ void FileAPIMessageFilter::BadMessageReceived() {
BrowserMessageFilter::BadMessageReceived();
}
-void FileAPIMessageFilter::OnOpen(
- int request_id, const GURL& origin_url, fileapi::FileSystemType type,
- int64 requested_size, bool create) {
+void FileAPIMessageFilter::OnOpenFileSystem(int request_id,
+ const GURL& origin_url,
+ fileapi::FileSystemType type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (type == fileapi::kFileSystemTypeTemporary) {
RecordAction(UserMetricsAction("OpenFileSystemTemporary"));
} else if (type == fileapi::kFileSystemTypePersistent) {
RecordAction(UserMetricsAction("OpenFileSystemPersistent"));
}
- // TODO(kinuko): Use this mode for IPC too.
fileapi::OpenFileSystemMode mode =
- create ? fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
- : fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT;
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT;
context_->OpenFileSystem(origin_url, type, mode, base::Bind(
&FileAPIMessageFilter::DidOpenFileSystem, this, request_id));
}
@@ -288,7 +247,7 @@ void FileAPIMessageFilter::OnMove(
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
- !security_policy_->CanWriteFileSystemFile(process_id_, src_url) ||
+ !security_policy_->CanDeleteFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
@@ -297,6 +256,7 @@ void FileAPIMessageFilter::OnMove(
operations_[request_id] = operation_runner()->Move(
src_url, dest_url,
+ fileapi::FileSystemOperation::OPTION_NONE,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
@@ -310,7 +270,7 @@ void FileAPIMessageFilter::OnCopy(
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
- !security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) {
+ !security_policy_->CanCopyIntoFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
@@ -318,6 +278,7 @@ void FileAPIMessageFilter::OnCopy(
operations_[request_id] = operation_runner()->Copy(
src_url, dest_url,
+ fileapi::FileSystemOperation::OPTION_NONE,
fileapi::FileSystemOperationRunner::CopyProgressCallback(),
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
@@ -328,7 +289,7 @@ void FileAPIMessageFilter::OnRemove(
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
- if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
+ if (!security_policy_->CanDeleteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
@@ -419,16 +380,6 @@ void FileAPIMessageFilter::OnReadDirectory(
this, request_id));
}
-void FileAPIMessageFilter::OnWriteDeprecated(
- int request_id,
- const GURL& path,
- const GURL& blob_url,
- int64 offset) {
- std::string uuid =
- blob_storage_context_->context()->LookupUuidFromDeprecatedURL(blob_url);
- OnWrite(request_id, path, uuid, offset);
-}
-
void FileAPIMessageFilter::OnWrite(
int request_id,
const GURL& path,
@@ -515,84 +466,6 @@ void FileAPIMessageFilter::OnCancel(
}
}
-#if defined(ENABLE_PLUGINS)
-void FileAPIMessageFilter::OnOpenPepperFile(
- int request_id, const GURL& path, int pp_open_flags) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-FileSystemURL url(context_->CrackURL(path));
- if (!ValidateFileSystemURL(request_id, url))
- return;
- if (!CanOpenFileSystemURLWithPepperFlags(pp_open_flags, process_id_, url)) {
- Send(new FileSystemMsg_DidFail(
- request_id, base::PLATFORM_FILE_ERROR_SECURITY));
- return;
- }
-
- quota::QuotaLimitType quota_policy = quota::kQuotaLimitTypeUnknown;
- quota::QuotaManagerProxy* quota_manager_proxy =
- context_->quota_manager_proxy();
- CHECK(quota_manager_proxy);
- CHECK(quota_manager_proxy->quota_manager());
-
- if (quota_manager_proxy->quota_manager()->IsStorageUnlimited(
- url.origin(), FileSystemTypeToQuotaStorageType(url.type()))) {
- quota_policy = quota::kQuotaLimitTypeUnlimited;
- } else {
- quota_policy = quota::kQuotaLimitTypeLimited;
- }
-
- int platform_file_flags = 0;
- if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags,
- &platform_file_flags)) {
- // |pp_open_flags| should have already been checked in PepperFileIOHost.
- NOTREACHED() << "Open file request with invalid pp_open_flags ignored.";
- }
-
- operations_[request_id] = operation_runner()->OpenFile(
- url, platform_file_flags, PeerHandle(),
- base::Bind(&FileAPIMessageFilter::DidOpenFile, this, request_id,
- quota_policy));
-}
-#endif // defined(ENABLE_PLUGINS)
-
-void FileAPIMessageFilter::OnNotifyCloseFile(int file_open_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- // Remove |file_open_id| from the map of |on_close_callback|s.
- // It must only be called for a ID that is successfully opened and enrolled in
- // DidOpenFile.
- base::Closure* on_close_callback = on_close_callbacks_.Lookup(file_open_id);
- if (on_close_callback && !on_close_callback->is_null()) {
- on_close_callback->Run();
- on_close_callbacks_.Remove(file_open_id);
- }
-}
-
-void FileAPIMessageFilter::OnWillUpdate(const GURL& path) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- FileSystemURL url(context_->CrackURL(path));
- if (!url.is_valid())
- return;
- const UpdateObserverList* observers =
- context_->GetUpdateObservers(url.type());
- if (!observers)
- return;
- observers->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
-}
-
-void FileAPIMessageFilter::OnDidUpdate(const GURL& path, int64 delta) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- FileSystemURL url(context_->CrackURL(path));
- if (!url.is_valid())
- return;
- const UpdateObserverList* observers =
- context_->GetUpdateObservers(url.type());
- if (!observers)
- return;
- observers->Notify(&FileUpdateObserver::OnUpdate, MakeTuple(url, delta));
- observers->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
-}
-
void FileAPIMessageFilter::OnSyncGetPlatformPath(
const GURL& path, base::FilePath* platform_path) {
SyncGetPlatformPath(context_, process_id_, path, platform_path);
@@ -706,23 +579,6 @@ void FileAPIMessageFilter::OnRevokePublicBlobURL(const GURL& public_url) {
ignore_result(blob_storage_host_->RevokePublicBlobURL(public_url));
}
-void FileAPIMessageFilter::OnDeprecatedRegisterBlobURL(
- const GURL& url, const std::string& uuid) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- blob_storage_host_->DeprecatedRegisterBlobURL(url, uuid);
-}
-
-void FileAPIMessageFilter::OnDeprecatedCloneBlobURL(
- const GURL& url, const GURL& src_url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- blob_storage_host_->DeprecatedCloneBlobURL(url, src_url);
-}
-
-void FileAPIMessageFilter::OnDeprecatedRevokeBlobURL(const GURL& url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- blob_storage_host_->DeprecatedRevokeBlobURL(url);
-}
-
void FileAPIMessageFilter::OnStartBuildingStream(
const GURL& url, const std::string& content_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -851,31 +707,6 @@ void FileAPIMessageFilter::DidReadDirectory(
operations_.erase(request_id);
}
-void FileAPIMessageFilter::DidOpenFile(int request_id,
- quota::QuotaLimitType quota_policy,
- base::PlatformFileError result,
- base::PlatformFile file,
- const base::Closure& on_close_callback,
- base::ProcessHandle peer_handle) {
- if (result == base::PLATFORM_FILE_OK) {
- IPC::PlatformFileForTransit file_for_transit =
- file != base::kInvalidPlatformFileValue ?
- IPC::GetFileHandleForProcess(file, peer_handle, true) :
- IPC::InvalidPlatformFileForTransit();
- int file_open_id = on_close_callbacks_.Add(
- new base::Closure(on_close_callback));
-
- Send(new FileSystemMsg_DidOpenFile(request_id,
- file_for_transit,
- file_open_id,
- quota_policy));
- } else {
- Send(new FileSystemMsg_DidFail(request_id,
- result));
- }
- operations_.erase(request_id);
-}
-
void FileAPIMessageFilter::DidWrite(int request_id,
base::PlatformFileError result,
int64 bytes,
@@ -891,9 +722,9 @@ void FileAPIMessageFilter::DidWrite(int request_id,
}
void FileAPIMessageFilter::DidOpenFileSystem(int request_id,
- base::PlatformFileError result,
+ const GURL& root,
const std::string& filesystem_name,
- const GURL& root) {
+ base::PlatformFileError result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (result == base::PLATFORM_FILE_OK) {
DCHECK(root.is_valid());
@@ -983,11 +814,22 @@ void FileAPIMessageFilter::DidCreateSnapshot(
bool FileAPIMessageFilter::ValidateFileSystemURL(
int request_id, const fileapi::FileSystemURL& url) {
- if (FileSystemURLIsValid(context_, url))
- return true;
- Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_INVALID_URL));
- return false;
+ if (!FileSystemURLIsValid(context_, url)) {
+ Send(new FileSystemMsg_DidFail(request_id,
+ base::PLATFORM_FILE_ERROR_INVALID_URL));
+ return false;
+ }
+
+ // Deny access to files in PluginPrivate FileSystem from JavaScript.
+ // TODO(nhiroki): Move this filter somewhere else since this is not for
+ // validation.
+ if (url.type() == fileapi::kFileSystemTypePluginPrivate) {
+ Send(new FileSystemMsg_DidFail(request_id,
+ base::PLATFORM_FILE_ERROR_SECURITY));
+ return false;
+ }
+
+ return true;
}
scoped_refptr<Stream> FileAPIMessageFilter::GetStreamForURL(const GURL& url) {
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h
index e9a707f4ac4..d734e85d217 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter.h
+++ b/chromium/content/browser/fileapi/fileapi_message_filter.h
@@ -12,7 +12,6 @@
#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/files/file_util_proxy.h"
-#include "base/id_map.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
#include "base/platform_file.h"
@@ -89,11 +88,9 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
private:
typedef fileapi::FileSystemOperationRunner::OperationID OperationID;
- void OnOpen(int request_id,
- const GURL& origin_url,
- fileapi::FileSystemType type,
- int64 requested_size,
- bool create);
+ void OnOpenFileSystem(int request_id,
+ const GURL& origin_url,
+ fileapi::FileSystemType type);
void OnResolveURL(int request_id,
const GURL& filesystem_url);
void OnDeleteFileSystem(int request_id,
@@ -118,23 +115,12 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
const GURL& path,
const std::string& blob_uuid,
int64 offset);
- void OnWriteDeprecated(
- int request_id,
- const GURL& path,
- const GURL& blob_url,
- int64 offset);
void OnTruncate(int request_id, const GURL& path, int64 length);
void OnTouchFile(int request_id,
const GURL& path,
const base::Time& last_access_time,
const base::Time& last_modified_time);
void OnCancel(int request_id, int request_to_cancel);
-#if defined(ENABLE_PLUGINS)
- void OnOpenPepperFile(int request_id, const GURL& path, int pp_open_flags);
-#endif // defined(ENABLE_PLUGINS)
- void OnNotifyCloseFile(int file_open_id);
- void OnWillUpdate(const GURL& path);
- void OnDidUpdate(const GURL& path, int64 delta);
void OnSyncGetPlatformPath(const GURL& path,
base::FilePath* platform_path);
void OnCreateSnapshotFile(int request_id,
@@ -157,13 +143,6 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid);
void OnRevokePublicBlobURL(const GURL& public_url);
- // Extra methods to establish a mapping from old-style blobURLs to uuids,
- // and to clone them. These won't be here for long, just during a
- // transition period. See crbug/174200
- void OnDeprecatedRegisterBlobURL(const GURL& url, const std::string& uuid);
- void OnDeprecatedCloneBlobURL(const GURL& url, const GURL& existing_url);
- void OnDeprecatedRevokeBlobURL(const GURL& url);
-
// Handlers for StreamHostMsg_ family messages.
//
// TODO(tyoshino): Consider renaming BlobData to more generic one as it's now
@@ -192,20 +171,14 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
base::PlatformFileError result,
const std::vector<fileapi::DirectoryEntry>& entries,
bool has_more);
- void DidOpenFile(int request_id,
- quota::QuotaLimitType quota_policy,
- base::PlatformFileError result,
- base::PlatformFile file,
- const base::Closure& on_close_callback,
- base::ProcessHandle peer_handle);
void DidWrite(int request_id,
base::PlatformFileError result,
int64 bytes,
bool complete);
void DidOpenFileSystem(int request_id,
- base::PlatformFileError result,
+ const GURL& root,
const std::string& filesystem_name,
- const GURL& root);
+ base::PlatformFileError result);
void DidResolveURL(int request_id,
base::PlatformFileError result,
const fileapi::FileSystemInfo& info,
@@ -266,11 +239,6 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
std::map<int, scoped_refptr<webkit_blob::ShareableFileReference> >
in_transit_snapshot_files_;
- // Keep track of file system file opened by OpenFile() in this process.
- // Need to close all of them when the renderer process dies.
- typedef IDMap<base::Closure, IDMapOwnPointer> OnCloseCallbackMap;
- OnCloseCallbackMap on_close_callbacks_;
-
DISALLOW_COPY_AND_ASSIGN(FileAPIMessageFilter);
};
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
index 90e422c7da6..3dbdcc3d538 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
+++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
@@ -21,11 +21,11 @@
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_file_system_context.h"
#include "net/base/io_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/mock_file_system_context.h"
#include "webkit/common/blob/blob_data.h"
namespace content {
@@ -76,12 +76,10 @@ class FileAPIMessageFilterTest : public testing::Test {
}
// Tests via OnMessageReceived(const IPC::Message&). The channel proxy calls
- // this method. Since OnMessageReceived is hidden on FileAPIMessageFilter,
- // we need to cast it.
+ // this method.
bool InvokeOnMessageReceived(const IPC::Message& message) {
- IPC::ChannelProxy::MessageFilter* casted_filter =
- static_cast<IPC::ChannelProxy::MessageFilter*>(filter_.get());
- return casted_filter->OnMessageReceived(message);
+ bool message_was_ok;
+ return filter_->OnMessageReceived(message, &message_was_ok);
}
base::MessageLoop message_loop_;
@@ -108,13 +106,11 @@ TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) {
// Complete initialization.
message_loop_.RunUntilIdle();
- IPC::ChannelProxy::MessageFilter* casted_filter =
- static_cast<IPC::ChannelProxy::MessageFilter*>(filter.get());
-
int request_id = 0;
const GURL kUrl("filesystem:http://example.com/temporary/foo");
FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl);
- EXPECT_TRUE(casted_filter->OnMessageReceived(read_metadata));
+ bool message_was_ok;
+ EXPECT_TRUE(filter->OnMessageReceived(read_metadata, &message_was_ok));
// Close the filter while it has inflight request.
filter->OnChannelClosing();
@@ -144,13 +140,11 @@ TEST_F(FileAPIMessageFilterTest, MultipleFilters) {
// Complete initialization.
message_loop_.RunUntilIdle();
- IPC::ChannelProxy::MessageFilter* casted_filter =
- static_cast<IPC::ChannelProxy::MessageFilter*>(filter1.get());
-
int request_id = 0;
const GURL kUrl("filesystem:http://example.com/temporary/foo");
FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl);
- EXPECT_TRUE(casted_filter->OnMessageReceived(read_metadata));
+ bool message_was_ok;
+ EXPECT_TRUE(filter1->OnMessageReceived(read_metadata, &message_was_ok));
// Close the other filter before the request for filter1 is processed.
filter2->OnChannelClosing();
@@ -251,8 +245,7 @@ TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) {
// OnAppendSharedMemoryToStream passes the peer process's handle to
// SharedMemory's constructor. If it's incorrect, DuplicateHandle won't work
// correctly.
- static_cast<IPC::ChannelProxy::MessageFilter*>(
- filter_.get())->OnChannelConnected(base::Process::Current().pid());
+ filter_->set_peer_pid_for_testing(base::Process::Current().pid());
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
EXPECT_TRUE(InvokeOnMessageReceived(start_message));
diff --git a/chromium/content/browser/fileapi/local_file_util_unittest.cc b/chromium/content/browser/fileapi/local_file_util_unittest.cc
new file mode 100644
index 00000000000..40995d69310
--- /dev/null
+++ b/chromium/content/browser/fileapi/local_file_util_unittest.cc
@@ -0,0 +1,388 @@
+// Copyright 2013 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 "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/async_file_util_adapter.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/local_file_util.h"
+#include "webkit/browser/fileapi/native_file_util.h"
+#include "webkit/common/fileapi/file_system_types.h"
+
+namespace fileapi {
+
+namespace {
+
+const GURL kOrigin("http://foo/");
+const FileSystemType kFileSystemType = kFileSystemTypeTest;
+
+} // namespace
+
+class LocalFileUtilTest : public testing::Test {
+ public:
+ LocalFileUtilTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, data_dir_.path());
+ }
+
+ virtual void TearDown() {
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ FileSystemOperationContext* NewContext() {
+ FileSystemOperationContext* context =
+ new FileSystemOperationContext(file_system_context_.get());
+ context->set_update_observers(
+ *file_system_context_->GetUpdateObservers(kFileSystemType));
+ return context;
+ }
+
+ LocalFileUtil* file_util() {
+ AsyncFileUtilAdapter* adapter = static_cast<AsyncFileUtilAdapter*>(
+ file_system_context_->GetAsyncFileUtil(kFileSystemType));
+ return static_cast<LocalFileUtil*>(adapter->sync_file_util());
+ }
+
+ FileSystemURL CreateURL(const std::string& file_name) {
+ return file_system_context_->CreateCrackedFileSystemURL(
+ kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
+ }
+
+ base::FilePath LocalPath(const char *file_name) {
+ base::FilePath path;
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ file_util()->GetLocalFilePath(context.get(), CreateURL(file_name), &path);
+ return path;
+ }
+
+ bool FileExists(const char *file_name) {
+ return base::PathExists(LocalPath(file_name)) &&
+ !base::DirectoryExists(LocalPath(file_name));
+ }
+
+ bool DirectoryExists(const char *file_name) {
+ return base::DirectoryExists(LocalPath(file_name));
+ }
+
+ int64 GetSize(const char *file_name) {
+ base::PlatformFileInfo info;
+ base::GetFileInfo(LocalPath(file_name), &info);
+ return info.size;
+ }
+
+ base::PlatformFileError CreateFile(const char* file_name,
+ base::PlatformFile* file_handle,
+ bool* created) {
+ int file_flags = base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC;
+
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ return file_util()->CreateOrOpen(
+ context.get(),
+ CreateURL(file_name),
+ file_flags, file_handle, created);
+ }
+
+ base::PlatformFileError EnsureFileExists(const char* file_name,
+ bool* created) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ return file_util()->EnsureFileExists(
+ context.get(),
+ CreateURL(file_name), created);
+ }
+
+ FileSystemContext* file_system_context() {
+ return file_system_context_.get();
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ base::ScopedTempDir data_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalFileUtilTest);
+};
+
+TEST_F(LocalFileUtilTest, CreateAndClose) {
+ const char *file_name = "test_file";
+ base::PlatformFile file_handle;
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ CreateFile(file_name, &file_handle, &created));
+ ASSERT_TRUE(created);
+
+ EXPECT_TRUE(FileExists(file_name));
+ EXPECT_EQ(0, GetSize(file_name));
+
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Close(context.get(), file_handle));
+}
+
+// base::CreateSymbolicLink is only supported on POSIX.
+#if defined(OS_POSIX)
+TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
+ // Create symlink target file.
+ const char *target_name = "symlink_target";
+ base::PlatformFile target_handle;
+ bool symlink_target_created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ CreateFile(target_name, &target_handle, &symlink_target_created));
+ ASSERT_TRUE(symlink_target_created);
+ base::FilePath target_path = LocalPath(target_name);
+
+ // Create symlink where target must be real file.
+ const char *symlink_name = "symlink_file";
+ base::FilePath symlink_path = LocalPath(symlink_name);
+ ASSERT_TRUE(base::CreateSymbolicLink(target_path, symlink_path));
+ ASSERT_TRUE(FileExists(symlink_name));
+
+ // Try to open the symlink file which should fail.
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ FileSystemURL url = CreateURL(symlink_name);
+ int file_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
+ base::PlatformFile file_handle;
+ bool created = false;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_util()->CreateOrOpen(
+ context.get(), url, file_flags, &file_handle, &created));
+ EXPECT_FALSE(created);
+}
+#endif
+
+TEST_F(LocalFileUtilTest, EnsureFileExists) {
+ const char *file_name = "foobar";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ EXPECT_TRUE(FileExists(file_name));
+ EXPECT_EQ(0, GetSize(file_name));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ EXPECT_FALSE(created);
+}
+
+TEST_F(LocalFileUtilTest, TouchFile) {
+ const char *file_name = "test_file";
+ base::PlatformFile file_handle;
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ CreateFile(file_name, &file_handle, &created));
+ ASSERT_TRUE(created);
+
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+
+ base::PlatformFileInfo info;
+ ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
+ const base::Time new_accessed =
+ info.last_accessed + base::TimeDelta::FromHours(10);
+ const base::Time new_modified =
+ info.last_modified + base::TimeDelta::FromHours(5);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Touch(context.get(), CreateURL(file_name),
+ new_accessed, new_modified));
+
+ ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
+ EXPECT_EQ(new_accessed, info.last_accessed);
+ EXPECT_EQ(new_modified, info.last_modified);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Close(context.get(), file_handle));
+}
+
+TEST_F(LocalFileUtilTest, TouchDirectory) {
+ const char *dir_name = "test_dir";
+ scoped_ptr<FileSystemOperationContext> context(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->CreateDirectory(context.get(),
+ CreateURL(dir_name),
+ false /* exclusive */,
+ false /* recursive */));
+
+ base::PlatformFileInfo info;
+ ASSERT_TRUE(base::GetFileInfo(LocalPath(dir_name), &info));
+ const base::Time new_accessed =
+ info.last_accessed + base::TimeDelta::FromHours(10);
+ const base::Time new_modified =
+ info.last_modified + base::TimeDelta::FromHours(5);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Touch(context.get(), CreateURL(dir_name),
+ new_accessed, new_modified));
+
+ ASSERT_TRUE(base::GetFileInfo(LocalPath(dir_name), &info));
+ EXPECT_EQ(new_accessed, info.last_accessed);
+ EXPECT_EQ(new_modified, info.last_modified);
+}
+
+TEST_F(LocalFileUtilTest, Truncate) {
+ const char *file_name = "truncated";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ scoped_ptr<FileSystemOperationContext> context;
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
+
+ EXPECT_TRUE(FileExists(file_name));
+ EXPECT_EQ(1020, GetSize(file_name));
+}
+
+TEST_F(LocalFileUtilTest, CopyFile) {
+ const char *from_file = "fromfile";
+ const char *to_file1 = "tofile1";
+ const char *to_file2 = "tofile2";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ scoped_ptr<FileSystemOperationContext> context;
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(file_system_context(),
+ CreateURL(from_file),
+ CreateURL(to_file1)));
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(file_system_context(),
+ CreateURL(from_file),
+ CreateURL(to_file2)));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+ EXPECT_TRUE(FileExists(to_file1));
+ EXPECT_EQ(1020, GetSize(to_file1));
+ EXPECT_TRUE(FileExists(to_file2));
+ EXPECT_EQ(1020, GetSize(to_file2));
+}
+
+TEST_F(LocalFileUtilTest, CopyDirectory) {
+ const char *from_dir = "fromdir";
+ const char *from_file = "fromdir/fromfile";
+ const char *to_dir = "todir";
+ const char *to_file = "todir/fromfile";
+ bool created;
+ scoped_ptr<FileSystemOperationContext> context;
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
+ false, false));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+
+ EXPECT_TRUE(DirectoryExists(from_dir));
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+ EXPECT_FALSE(DirectoryExists(to_dir));
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(file_system_context(),
+ CreateURL(from_dir), CreateURL(to_dir)));
+
+ EXPECT_TRUE(DirectoryExists(from_dir));
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+ EXPECT_TRUE(DirectoryExists(to_dir));
+ EXPECT_TRUE(FileExists(to_file));
+ EXPECT_EQ(1020, GetSize(to_file));
+}
+
+TEST_F(LocalFileUtilTest, MoveFile) {
+ const char *from_file = "fromfile";
+ const char *to_file = "tofile";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+ scoped_ptr<FileSystemOperationContext> context;
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Move(file_system_context(),
+ CreateURL(from_file),
+ CreateURL(to_file)));
+
+ EXPECT_FALSE(FileExists(from_file));
+ EXPECT_TRUE(FileExists(to_file));
+ EXPECT_EQ(1020, GetSize(to_file));
+}
+
+TEST_F(LocalFileUtilTest, MoveDirectory) {
+ const char *from_dir = "fromdir";
+ const char *from_file = "fromdir/fromfile";
+ const char *to_dir = "todir";
+ const char *to_file = "todir/fromfile";
+ bool created;
+ scoped_ptr<FileSystemOperationContext> context;
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
+ false, false));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+
+ EXPECT_TRUE(DirectoryExists(from_dir));
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+ EXPECT_FALSE(DirectoryExists(to_dir));
+
+ context.reset(NewContext());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Move(file_system_context(),
+ CreateURL(from_dir),
+ CreateURL(to_dir)));
+
+ EXPECT_FALSE(DirectoryExists(from_dir));
+ EXPECT_TRUE(DirectoryExists(to_dir));
+ EXPECT_TRUE(FileExists(to_file));
+ EXPECT_EQ(1020, GetSize(to_file));
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc b/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc
new file mode 100644
index 00000000000..72dd36008dd
--- /dev/null
+++ b/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -0,0 +1,2490 @@
+// Copyright 2013 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 <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "content/public/test/sandbox_file_system_test_helper.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_usage_cache.h"
+#include "webkit/browser/fileapi/mock_file_change_observer.h"
+#include "webkit/browser/fileapi/obfuscated_file_util.h"
+#include "webkit/browser/fileapi/sandbox_directory_database.h"
+#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
+#include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
+#include "webkit/browser/fileapi/sandbox_origin_database.h"
+#include "webkit/browser/fileapi/test_file_set.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/common/database/database_identifier.h"
+#include "webkit/common/quota/quota_types.h"
+
+namespace fileapi {
+
+namespace {
+
+bool FileExists(const base::FilePath& path) {
+ return base::PathExists(path) && !base::DirectoryExists(path);
+}
+
+int64 GetSize(const base::FilePath& path) {
+ int64 size;
+ EXPECT_TRUE(base::GetFileSize(path, &size));
+ return size;
+}
+
+// After a move, the dest exists and the source doesn't.
+// After a copy, both source and dest exist.
+struct CopyMoveTestCaseRecord {
+ bool is_copy_not_move;
+ const char source_path[64];
+ const char dest_path[64];
+ bool cause_overwrite;
+};
+
+const CopyMoveTestCaseRecord kCopyMoveTestCases[] = {
+ // This is the combinatoric set of:
+ // rename vs. same-name
+ // different directory vs. same directory
+ // overwrite vs. no-overwrite
+ // copy vs. move
+ // We can never be called with source and destination paths identical, so
+ // those cases are omitted.
+ {true, "dir0/file0", "dir0/file1", false},
+ {false, "dir0/file0", "dir0/file1", false},
+ {true, "dir0/file0", "dir0/file1", true},
+ {false, "dir0/file0", "dir0/file1", true},
+
+ {true, "dir0/file0", "dir1/file0", false},
+ {false, "dir0/file0", "dir1/file0", false},
+ {true, "dir0/file0", "dir1/file0", true},
+ {false, "dir0/file0", "dir1/file0", true},
+ {true, "dir0/file0", "dir1/file1", false},
+ {false, "dir0/file0", "dir1/file1", false},
+ {true, "dir0/file0", "dir1/file1", true},
+ {false, "dir0/file0", "dir1/file1", true},
+};
+
+struct OriginEnumerationTestRecord {
+ std::string origin_url;
+ bool has_temporary;
+ bool has_persistent;
+};
+
+const OriginEnumerationTestRecord kOriginEnumerationTestRecords[] = {
+ {"http://example.com", false, true},
+ {"http://example1.com", true, false},
+ {"https://example1.com", true, true},
+ {"file://", false, true},
+ {"http://example.com:8000", false, true},
+};
+
+FileSystemURL FileSystemURLAppend(
+ const FileSystemURL& url, const base::FilePath::StringType& child) {
+ return FileSystemURL::CreateForTest(
+ url.origin(), url.mount_type(), url.virtual_path().Append(child));
+}
+
+FileSystemURL FileSystemURLAppendUTF8(
+ const FileSystemURL& url, const std::string& child) {
+ return FileSystemURL::CreateForTest(
+ url.origin(),
+ url.mount_type(),
+ url.virtual_path().Append(base::FilePath::FromUTF8Unsafe(child)));
+}
+
+FileSystemURL FileSystemURLDirName(const FileSystemURL& url) {
+ return FileSystemURL::CreateForTest(
+ url.origin(), url.mount_type(), VirtualPath::DirName(url.virtual_path()));
+}
+
+std::string GetTypeString(FileSystemType type) {
+ return SandboxFileSystemBackendDelegate::GetTypeString(type);
+}
+
+bool HasFileSystemType(
+ ObfuscatedFileUtil::AbstractOriginEnumerator* enumerator,
+ FileSystemType type) {
+ return enumerator->HasTypeDirectory(GetTypeString(type));
+}
+
+} // namespace
+
+// TODO(ericu): The vast majority of this and the other FSFU subclass tests
+// could theoretically be shared. It would basically be a FSFU interface
+// compliance test, and only the subclass-specific bits that look into the
+// implementation would need to be written per-subclass.
+class ObfuscatedFileUtilTest : public testing::Test {
+ public:
+ ObfuscatedFileUtilTest()
+ : origin_(GURL("http://www.example.com")),
+ type_(kFileSystemTypeTemporary),
+ weak_factory_(this),
+ sandbox_file_system_(origin_, type_),
+ quota_status_(quota::kQuotaStatusUnknown),
+ usage_(-1) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+
+ storage_policy_ = new quota::MockSpecialStoragePolicy();
+
+ quota_manager_ =
+ new quota::QuotaManager(false /* is_incognito */,
+ data_dir_.path(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ storage_policy_.get());
+
+ // Every time we create a new sandbox_file_system helper,
+ // it creates another context, which creates another path manager,
+ // another sandbox_backend, and another OFU.
+ // We need to pass in the context to skip all that.
+ file_system_context_ = CreateFileSystemContextForTesting(
+ quota_manager_->proxy(),
+ data_dir_.path());
+
+ sandbox_file_system_.SetUp(file_system_context_.get());
+
+ change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+ }
+
+ virtual void TearDown() {
+ quota_manager_ = NULL;
+ sandbox_file_system_.TearDown();
+ }
+
+ scoped_ptr<FileSystemOperationContext> LimitedContext(
+ int64 allowed_bytes_growth) {
+ scoped_ptr<FileSystemOperationContext> context(
+ sandbox_file_system_.NewOperationContext());
+ context->set_allowed_bytes_growth(allowed_bytes_growth);
+ return context.Pass();
+ }
+
+ scoped_ptr<FileSystemOperationContext> UnlimitedContext() {
+ return LimitedContext(kint64max);
+ }
+
+ FileSystemOperationContext* NewContext(
+ SandboxFileSystemTestHelper* file_system) {
+ change_observer()->ResetCount();
+ FileSystemOperationContext* context;
+ if (file_system)
+ context = file_system->NewOperationContext();
+ else
+ context = sandbox_file_system_.NewOperationContext();
+ // Setting allowed_bytes_growth big enough for all tests.
+ context->set_allowed_bytes_growth(1024 * 1024);
+ context->set_change_observers(change_observers());
+ return context;
+ }
+
+ const ChangeObserverList& change_observers() const {
+ return change_observers_;
+ }
+
+ MockFileChangeObserver* change_observer() {
+ return &change_observer_;
+ }
+
+ // This can only be used after SetUp has run and created file_system_context_
+ // and obfuscated_file_util_.
+ // Use this for tests which need to run in multiple origins; we need a test
+ // helper per origin.
+ SandboxFileSystemTestHelper* NewFileSystem(
+ const GURL& origin, fileapi::FileSystemType type) {
+ SandboxFileSystemTestHelper* file_system =
+ new SandboxFileSystemTestHelper(origin, type);
+
+ file_system->SetUp(file_system_context_.get());
+ return file_system;
+ }
+
+ ObfuscatedFileUtil* ofu() {
+ return static_cast<ObfuscatedFileUtil*>(sandbox_file_system_.file_util());
+ }
+
+ const base::FilePath& test_directory() const {
+ return data_dir_.path();
+ }
+
+ const GURL& origin() const {
+ return origin_;
+ }
+
+ fileapi::FileSystemType type() const {
+ return type_;
+ }
+
+ std::string type_string() const {
+ return GetTypeString(type_);
+ }
+
+ int64 ComputeTotalFileSize() {
+ return sandbox_file_system_.ComputeCurrentOriginUsage() -
+ sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage();
+ }
+
+ void GetUsageFromQuotaManager() {
+ int64 quota = -1;
+ quota_status_ =
+ AsyncFileTestHelper::GetUsageAndQuota(quota_manager_.get(),
+ origin(),
+ sandbox_file_system_.type(),
+ &usage_,
+ &quota);
+ EXPECT_EQ(quota::kQuotaStatusOk, quota_status_);
+ }
+
+ void RevokeUsageCache() {
+ quota_manager_->ResetUsageTracker(sandbox_file_system_.storage_type());
+ usage_cache()->Delete(sandbox_file_system_.GetUsageCachePath());
+ }
+
+ int64 SizeByQuotaUtil() {
+ return sandbox_file_system_.GetCachedOriginUsage();
+ }
+
+ int64 SizeInUsageFile() {
+ base::RunLoop().RunUntilIdle();
+ int64 usage = 0;
+ return usage_cache()->GetUsage(
+ sandbox_file_system_.GetUsageCachePath(), &usage) ? usage : -1;
+ }
+
+ bool PathExists(const FileSystemURL& url) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ base::PlatformFileInfo file_info;
+ base::FilePath platform_path;
+ base::PlatformFileError error = ofu()->GetFileInfo(
+ context.get(), url, &file_info, &platform_path);
+ return error == base::PLATFORM_FILE_OK;
+ }
+
+ bool DirectoryExists(const FileSystemURL& url) {
+ return AsyncFileTestHelper::DirectoryExists(file_system_context(), url);
+ }
+
+ int64 usage() const { return usage_; }
+ FileSystemUsageCache* usage_cache() {
+ return sandbox_file_system_.usage_cache();
+ }
+
+ FileSystemURL CreateURLFromUTF8(const std::string& path) {
+ return sandbox_file_system_.CreateURLFromUTF8(path);
+ }
+
+ int64 PathCost(const FileSystemURL& url) {
+ return ObfuscatedFileUtil::ComputeFilePathCost(url.path());
+ }
+
+ FileSystemURL CreateURL(const base::FilePath& path) {
+ return sandbox_file_system_.CreateURL(path);
+ }
+
+ void CheckFileAndCloseHandle(
+ const FileSystemURL& url, base::PlatformFile file_handle) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
+ context.get(), url, &local_path));
+
+ base::PlatformFileInfo file_info0;
+ base::FilePath data_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), url, &file_info0, &data_path));
+ EXPECT_EQ(data_path, local_path);
+ EXPECT_TRUE(FileExists(data_path));
+ EXPECT_EQ(0, GetSize(data_path));
+
+ const char data[] = "test data";
+ const int length = arraysize(data) - 1;
+
+ if (base::kInvalidPlatformFileValue == file_handle) {
+ bool created = true;
+ base::PlatformFileError error;
+ file_handle = base::CreatePlatformFile(
+ data_path,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
+ &created,
+ &error);
+ ASSERT_NE(base::kInvalidPlatformFileValue, file_handle);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ EXPECT_FALSE(created);
+ }
+ ASSERT_EQ(length, base::WritePlatformFile(file_handle, 0, data, length));
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+
+ base::PlatformFileInfo file_info1;
+ EXPECT_EQ(length, GetSize(data_path));
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), url, &file_info1, &data_path));
+ EXPECT_EQ(data_path, local_path);
+
+ EXPECT_FALSE(file_info0.is_directory);
+ EXPECT_FALSE(file_info1.is_directory);
+ EXPECT_FALSE(file_info0.is_symbolic_link);
+ EXPECT_FALSE(file_info1.is_symbolic_link);
+ EXPECT_EQ(0, file_info0.size);
+ EXPECT_EQ(length, file_info1.size);
+ EXPECT_LE(file_info0.last_modified, file_info1.last_modified);
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
+ context.get(), url, length * 2));
+ EXPECT_EQ(length * 2, GetSize(data_path));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
+ context.get(), url, 0));
+ EXPECT_EQ(0, GetSize(data_path));
+ }
+
+ void ValidateTestDirectory(
+ const FileSystemURL& root_url,
+ const std::set<base::FilePath::StringType>& files,
+ const std::set<base::FilePath::StringType>& directories) {
+ scoped_ptr<FileSystemOperationContext> context;
+ std::set<base::FilePath::StringType>::const_iterator iter;
+ for (iter = files.begin(); iter != files.end(); ++iter) {
+ bool created = true;
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ context.get(), FileSystemURLAppend(root_url, *iter),
+ &created));
+ ASSERT_FALSE(created);
+ }
+ for (iter = directories.begin(); iter != directories.end(); ++iter) {
+ context.reset(NewContext(NULL));
+ EXPECT_TRUE(DirectoryExists(
+ FileSystemURLAppend(root_url, *iter)));
+ }
+ }
+
+ class UsageVerifyHelper {
+ public:
+ UsageVerifyHelper(scoped_ptr<FileSystemOperationContext> context,
+ SandboxFileSystemTestHelper* file_system,
+ int64 expected_usage)
+ : context_(context.Pass()),
+ sandbox_file_system_(file_system),
+ expected_usage_(expected_usage) {}
+
+ ~UsageVerifyHelper() {
+ base::RunLoop().RunUntilIdle();
+ Check();
+ }
+
+ FileSystemOperationContext* context() {
+ return context_.get();
+ }
+
+ private:
+ void Check() {
+ ASSERT_EQ(expected_usage_,
+ sandbox_file_system_->GetCachedOriginUsage());
+ }
+
+ scoped_ptr<FileSystemOperationContext> context_;
+ SandboxFileSystemTestHelper* sandbox_file_system_;
+ int64 expected_usage_;
+ };
+
+ scoped_ptr<UsageVerifyHelper> AllowUsageIncrease(int64 requested_growth) {
+ int64 usage = sandbox_file_system_.GetCachedOriginUsage();
+ return scoped_ptr<UsageVerifyHelper>(new UsageVerifyHelper(
+ LimitedContext(requested_growth),
+ &sandbox_file_system_, usage + requested_growth));
+ }
+
+ scoped_ptr<UsageVerifyHelper> DisallowUsageIncrease(int64 requested_growth) {
+ int64 usage = sandbox_file_system_.GetCachedOriginUsage();
+ return scoped_ptr<UsageVerifyHelper>(new UsageVerifyHelper(
+ LimitedContext(requested_growth - 1), &sandbox_file_system_, usage));
+ }
+
+ void FillTestDirectory(
+ const FileSystemURL& root_url,
+ std::set<base::FilePath::StringType>* files,
+ std::set<base::FilePath::StringType>* directories) {
+ scoped_ptr<FileSystemOperationContext> context;
+ std::vector<DirectoryEntry> entries;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), root_url, &entries));
+ EXPECT_EQ(0UL, entries.size());
+
+ files->clear();
+ files->insert(FILE_PATH_LITERAL("first"));
+ files->insert(FILE_PATH_LITERAL("second"));
+ files->insert(FILE_PATH_LITERAL("third"));
+ directories->clear();
+ directories->insert(FILE_PATH_LITERAL("fourth"));
+ directories->insert(FILE_PATH_LITERAL("fifth"));
+ directories->insert(FILE_PATH_LITERAL("sixth"));
+ std::set<base::FilePath::StringType>::iterator iter;
+ for (iter = files->begin(); iter != files->end(); ++iter) {
+ bool created = false;
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ context.get(),
+ FileSystemURLAppend(root_url, *iter),
+ &created));
+ ASSERT_TRUE(created);
+ }
+ for (iter = directories->begin(); iter != directories->end(); ++iter) {
+ bool exclusive = true;
+ bool recursive = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(
+ context.get(),
+ FileSystemURLAppend(root_url, *iter),
+ exclusive, recursive));
+ }
+ ValidateTestDirectory(root_url, *files, *directories);
+ }
+
+ void TestReadDirectoryHelper(const FileSystemURL& root_url) {
+ std::set<base::FilePath::StringType> files;
+ std::set<base::FilePath::StringType> directories;
+ FillTestDirectory(root_url, &files, &directories);
+
+ scoped_ptr<FileSystemOperationContext> context;
+ std::vector<DirectoryEntry> entries;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), root_url, &entries));
+ std::vector<DirectoryEntry>::iterator entry_iter;
+ EXPECT_EQ(files.size() + directories.size(), entries.size());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ for (entry_iter = entries.begin(); entry_iter != entries.end();
+ ++entry_iter) {
+ const DirectoryEntry& entry = *entry_iter;
+ std::set<base::FilePath::StringType>::iterator iter =
+ files.find(entry.name);
+ if (iter != files.end()) {
+ EXPECT_FALSE(entry.is_directory);
+ files.erase(iter);
+ continue;
+ }
+ iter = directories.find(entry.name);
+ EXPECT_FALSE(directories.end() == iter);
+ EXPECT_TRUE(entry.is_directory);
+ directories.erase(iter);
+ }
+ }
+
+ void TestTouchHelper(const FileSystemURL& url, bool is_file) {
+ base::Time last_access_time = base::Time::Now();
+ base::Time last_modified_time = base::Time::Now();
+
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Touch(
+ context.get(), url, last_access_time, last_modified_time));
+ // Currently we fire no change notifications for Touch.
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ base::FilePath local_path;
+ base::PlatformFileInfo file_info;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), url, &file_info, &local_path));
+ // We compare as time_t here to lower our resolution, to avoid false
+ // negatives caused by conversion to the local filesystem's native
+ // representation and back.
+ EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT());
+
+ context.reset(NewContext(NULL));
+ last_modified_time += base::TimeDelta::FromHours(1);
+ last_access_time += base::TimeDelta::FromHours(14);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Touch(
+ context.get(), url, last_access_time, last_modified_time));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), url, &file_info, &local_path));
+ EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT());
+ if (is_file) // Directories in OFU don't support atime.
+ EXPECT_EQ(file_info.last_accessed.ToTimeT(), last_access_time.ToTimeT());
+ }
+
+ void TestCopyInForeignFileHelper(bool overwrite) {
+ base::ScopedTempDir source_dir;
+ ASSERT_TRUE(source_dir.CreateUniqueTempDir());
+ base::FilePath root_file_path = source_dir.path();
+ base::FilePath src_file_path = root_file_path.AppendASCII("file_name");
+ FileSystemURL dest_url = CreateURLFromUTF8("new file");
+ int64 src_file_length = 87;
+
+ base::PlatformFileError error_code;
+ bool created = false;
+ int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
+ base::PlatformFile file_handle =
+ base::CreatePlatformFile(
+ src_file_path, file_flags, &created, &error_code);
+ EXPECT_TRUE(created);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error_code);
+ ASSERT_NE(base::kInvalidPlatformFileValue, file_handle);
+ ASSERT_TRUE(base::TruncatePlatformFile(file_handle, src_file_length));
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+
+ scoped_ptr<FileSystemOperationContext> context;
+
+ if (overwrite) {
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), dest_url, &created));
+ EXPECT_TRUE(created);
+
+ // We must have observed one (and only one) create_file_count.
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ }
+
+ const int64 path_cost =
+ ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path());
+ if (!overwrite) {
+ // Verify that file creation requires sufficient quota for the path.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(path_cost + src_file_length - 1);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->CopyInForeignFile(context.get(),
+ src_file_path, dest_url));
+ }
+
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(path_cost + src_file_length);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyInForeignFile(context.get(),
+ src_file_path, dest_url));
+
+ EXPECT_TRUE(PathExists(dest_url));
+ EXPECT_FALSE(DirectoryExists(dest_url));
+
+ context.reset(NewContext(NULL));
+ base::PlatformFileInfo file_info;
+ base::FilePath data_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), dest_url, &file_info, &data_path));
+ EXPECT_NE(data_path, src_file_path);
+ EXPECT_TRUE(FileExists(data_path));
+ EXPECT_EQ(src_file_length, GetSize(data_path));
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), dest_url));
+ }
+
+ void ClearTimestamp(const FileSystemURL& url) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Touch(context.get(), url, base::Time(), base::Time()));
+ EXPECT_EQ(base::Time(), GetModifiedTime(url));
+ }
+
+ base::Time GetModifiedTime(const FileSystemURL& url) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ base::FilePath data_path;
+ base::PlatformFileInfo file_info;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetFileInfo(context.get(), url, &file_info, &data_path));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ return file_info.last_modified;
+ }
+
+ void TestDirectoryTimestampHelper(const FileSystemURL& base_dir,
+ bool copy,
+ bool overwrite) {
+ scoped_ptr<FileSystemOperationContext> context;
+ const FileSystemURL src_dir_url(
+ FileSystemURLAppendUTF8(base_dir, "foo_dir"));
+ const FileSystemURL dest_dir_url(
+ FileSystemURLAppendUTF8(base_dir, "bar_dir"));
+
+ const FileSystemURL src_file_url(
+ FileSystemURLAppendUTF8(src_dir_url, "hoge"));
+ const FileSystemURL dest_file_url(
+ FileSystemURLAppendUTF8(dest_dir_url, "fuga"));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), src_dir_url, true, true));
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), dest_dir_url, true, true));
+
+ bool created = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_file_url, &created));
+ if (overwrite) {
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(),
+ dest_file_url, &created));
+ }
+
+ ClearTimestamp(src_dir_url);
+ ClearTimestamp(dest_dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(),
+ src_file_url, dest_file_url,
+ FileSystemOperation::OPTION_NONE,
+ copy));
+ if (copy)
+ EXPECT_EQ(base::Time(), GetModifiedTime(src_dir_url));
+ else
+ EXPECT_NE(base::Time(), GetModifiedTime(src_dir_url));
+ EXPECT_NE(base::Time(), GetModifiedTime(dest_dir_url));
+ }
+
+ int64 ComputeCurrentUsage() {
+ return sandbox_file_system_.ComputeCurrentOriginUsage() -
+ sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage();
+ }
+
+ FileSystemContext* file_system_context() {
+ return sandbox_file_system_.file_system_context();
+ }
+
+ const base::FilePath& data_dir_path() const {
+ return data_dir_.path();
+ }
+
+ protected:
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_refptr<quota::MockSpecialStoragePolicy> storage_policy_;
+ scoped_refptr<quota::QuotaManager> quota_manager_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ GURL origin_;
+ fileapi::FileSystemType type_;
+ base::WeakPtrFactory<ObfuscatedFileUtilTest> weak_factory_;
+ SandboxFileSystemTestHelper sandbox_file_system_;
+ quota::QuotaStatusCode quota_status_;
+ int64 usage_;
+ MockFileChangeObserver change_observer_;
+ ChangeObserverList change_observers_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtilTest);
+};
+
+TEST_F(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) {
+ base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
+ bool created;
+ FileSystemURL url = CreateURLFromUTF8("fake/file");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->CreateOrOpen(
+ context.get(), url, file_flags, &file_handle,
+ &created));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->DeleteFile(context.get(), url));
+
+ url = CreateURLFromUTF8("test file");
+
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Verify that file creation requires sufficient quota for the path.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->CreateOrOpen(
+ context.get(), url, file_flags, &file_handle, &created));
+
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ context.get(), url, file_flags, &file_handle, &created));
+ ASSERT_TRUE(created);
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+ EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
+
+ CheckFileAndCloseHandle(url, file_handle);
+
+ context.reset(NewContext(NULL));
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
+ context.get(), url, &local_path));
+ EXPECT_TRUE(base::PathExists(local_path));
+
+ // Verify that deleting a file isn't stopped by zero quota, and that it frees
+ // up quote from its path.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(0);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), url));
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_FALSE(base::PathExists(local_path));
+ EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()),
+ context->allowed_bytes_growth());
+
+ context.reset(NewContext(NULL));
+ bool exclusive = true;
+ bool recursive = true;
+ FileSystemURL directory_url = CreateURLFromUTF8(
+ "series/of/directories");
+ url = FileSystemURLAppendUTF8(directory_url, "file name");
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), directory_url, exclusive, recursive));
+ // The oepration created 3 directories recursively.
+ EXPECT_EQ(3, change_observer()->get_and_reset_create_directory_count());
+
+ context.reset(NewContext(NULL));
+ file_handle = base::kInvalidPlatformFileValue;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ context.get(), url, file_flags, &file_handle, &created));
+ ASSERT_TRUE(created);
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+ EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
+
+ CheckFileAndCloseHandle(url, file_handle);
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
+ context.get(), url, &local_path));
+ EXPECT_TRUE(base::PathExists(local_path));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), url));
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_FALSE(base::PathExists(local_path));
+
+ // Make sure we have no unexpected changes.
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestTruncate) {
+ bool created = false;
+ FileSystemURL url = CreateURLFromUTF8("file");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->Truncate(context.get(), url, 4));
+
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_TRUE(created);
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+
+ context.reset(NewContext(NULL));
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
+ context.get(), url, &local_path));
+ EXPECT_EQ(0, GetSize(local_path));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
+ context.get(), url, 10));
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+ EXPECT_EQ(10, GetSize(local_path));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
+ context.get(), url, 1));
+ EXPECT_EQ(1, GetSize(local_path));
+ EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
+
+ EXPECT_FALSE(DirectoryExists(url));
+ EXPECT_TRUE(PathExists(url));
+
+ // Make sure we have no unexpected changes.
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestQuotaOnTruncation) {
+ bool created = false;
+ FileSystemURL url = CreateURLFromUTF8("file");
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(url))->context(),
+ url, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(1020)->context(),
+ url, 1020));
+ ASSERT_EQ(1020, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(-1020)->context(),
+ url, 0));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->Truncate(
+ DisallowUsageIncrease(1021)->context(),
+ url, 1021));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(1020)->context(),
+ url, 1020));
+ ASSERT_EQ(1020, ComputeTotalFileSize());
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(0)->context(),
+ url, 1020));
+ ASSERT_EQ(1020, ComputeTotalFileSize());
+
+ // quota exceeded
+ {
+ scoped_ptr<UsageVerifyHelper> helper = AllowUsageIncrease(-1);
+ helper->context()->set_allowed_bytes_growth(
+ helper->context()->allowed_bytes_growth() - 1);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(helper->context(), url, 1019));
+ ASSERT_EQ(1019, ComputeTotalFileSize());
+ }
+
+ // Delete backing file to make following truncation fail.
+ base::FilePath local_path;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetLocalFilePath(
+ UnlimitedContext().get(),
+ url, &local_path));
+ ASSERT_FALSE(local_path.empty());
+ ASSERT_TRUE(base::DeleteFile(local_path, false));
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->Truncate(
+ LimitedContext(1234).get(),
+ url, 1234));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) {
+ FileSystemURL url = CreateURLFromUTF8("fake/file");
+ bool created = false;
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->EnsureFileExists(
+ context.get(), url, &created));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Verify that file creation requires sufficient quota for the path.
+ context.reset(NewContext(NULL));
+ url = CreateURLFromUTF8("test file");
+ created = false;
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_FALSE(created);
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_TRUE(created);
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
+
+ CheckFileAndCloseHandle(url, base::kInvalidPlatformFileValue);
+
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_FALSE(created);
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Also test in a subdirectory.
+ url = CreateURLFromUTF8("path/to/file.txt");
+ context.reset(NewContext(NULL));
+ bool exclusive = true;
+ bool recursive = true;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(),
+ FileSystemURLDirName(url),
+ exclusive, recursive));
+ // 2 directories: path/ and path/to.
+ EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
+
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_TRUE(created);
+ EXPECT_FALSE(DirectoryExists(url));
+ EXPECT_TRUE(PathExists(url));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ bool exclusive = false;
+ bool recursive = false;
+ FileSystemURL url = CreateURLFromUTF8("foo/bar");
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->DeleteDirectory(context.get(), url));
+
+ FileSystemURL root = CreateURLFromUTF8(std::string());
+ EXPECT_FALSE(DirectoryExists(url));
+ EXPECT_FALSE(PathExists(url));
+ context.reset(NewContext(NULL));
+ EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), root));
+
+ context.reset(NewContext(NULL));
+ exclusive = false;
+ recursive = true;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
+
+ EXPECT_TRUE(DirectoryExists(url));
+ EXPECT_TRUE(PathExists(url));
+
+ context.reset(NewContext(NULL));
+ EXPECT_FALSE(ofu()->IsDirectoryEmpty(context.get(), root));
+ EXPECT_TRUE(DirectoryExists(FileSystemURLDirName(url)));
+
+ context.reset(NewContext(NULL));
+ EXPECT_FALSE(ofu()->IsDirectoryEmpty(context.get(),
+ FileSystemURLDirName(url)));
+
+ // Can't remove a non-empty directory.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY,
+ ofu()->DeleteDirectory(context.get(),
+ FileSystemURLDirName(url)));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ base::PlatformFileInfo file_info;
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), url, &file_info, &local_path));
+ EXPECT_TRUE(local_path.empty());
+ EXPECT_TRUE(file_info.is_directory);
+ EXPECT_FALSE(file_info.is_symbolic_link);
+
+ // Same create again should succeed, since exclusive is false.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ exclusive = true;
+ recursive = true;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ // Verify that deleting a directory isn't stopped by zero quota, and that it
+ // frees up quota from its path.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(0);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()),
+ context->allowed_bytes_growth());
+
+ url = CreateURLFromUTF8("foo/bop");
+
+ EXPECT_FALSE(DirectoryExists(url));
+ EXPECT_FALSE(PathExists(url));
+
+ context.reset(NewContext(NULL));
+ EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(
+ context.get(), url, &file_info, &local_path));
+
+ // Verify that file creation requires sufficient quota for the path.
+ exclusive = true;
+ recursive = false;
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+
+ EXPECT_TRUE(DirectoryExists(url));
+ EXPECT_TRUE(PathExists(url));
+
+ exclusive = true;
+ recursive = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ exclusive = true;
+ recursive = false;
+ url = CreateURLFromUTF8("foo");
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+
+ url = CreateURLFromUTF8("blah");
+
+ EXPECT_FALSE(DirectoryExists(url));
+ EXPECT_FALSE(PathExists(url));
+
+ exclusive = true;
+ recursive = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
+
+ EXPECT_TRUE(DirectoryExists(url));
+ EXPECT_TRUE(PathExists(url));
+
+ exclusive = true;
+ recursive = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestReadDirectory) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ bool exclusive = true;
+ bool recursive = true;
+ FileSystemURL url = CreateURLFromUTF8("directory/to/use");
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ TestReadDirectoryHelper(url);
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestReadRootWithSlash) {
+ TestReadDirectoryHelper(CreateURLFromUTF8(std::string()));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestReadRootWithEmptyString) {
+ TestReadDirectoryHelper(CreateURLFromUTF8("/"));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestReadDirectoryOnFile) {
+ FileSystemURL url = CreateURLFromUTF8("file");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_TRUE(created);
+
+ std::vector<DirectoryEntry> entries;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), url, &entries));
+
+ EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestTouch) {
+ FileSystemURL url = CreateURLFromUTF8("file");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ base::Time last_access_time = base::Time::Now();
+ base::Time last_modified_time = base::Time::Now();
+
+ // It's not there yet.
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->Touch(
+ context.get(), url, last_access_time, last_modified_time));
+
+ // OK, now create it.
+ context.reset(NewContext(NULL));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_TRUE(created);
+ TestTouchHelper(url, true);
+
+ // Now test a directory:
+ context.reset(NewContext(NULL));
+ bool exclusive = true;
+ bool recursive = false;
+ url = CreateURLFromUTF8("dir");
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(context.get(),
+ url, exclusive, recursive));
+ TestTouchHelper(url, false);
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestPathQuotas) {
+ FileSystemURL url = CreateURLFromUTF8("fake/file");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ url = CreateURLFromUTF8("file name");
+ context->set_allowed_bytes_growth(5);
+ bool created = false;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_FALSE(created);
+ context->set_allowed_bytes_growth(1024);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_TRUE(created);
+ int64 path_cost = ObfuscatedFileUtil::ComputeFilePathCost(url.path());
+ EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth());
+
+ context->set_allowed_bytes_growth(1024);
+ bool exclusive = true;
+ bool recursive = true;
+ url = CreateURLFromUTF8("directory/to/use");
+ std::vector<base::FilePath::StringType> components;
+ url.path().GetComponents(&components);
+ path_cost = 0;
+ typedef std::vector<base::FilePath::StringType>::iterator iterator;
+ for (iterator iter = components.begin();
+ iter != components.end(); ++iter) {
+ path_cost += ObfuscatedFileUtil::ComputeFilePathCost(
+ base::FilePath(*iter));
+ }
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(1024);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), url, exclusive, recursive));
+ EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) {
+ FileSystemURL source_url = CreateURLFromUTF8("path0.txt");
+ FileSystemURL dest_url = CreateURLFromUTF8("path1.txt");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ bool is_copy_not_move = false;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ context.reset(NewContext(NULL));
+ is_copy_not_move = true;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ source_url = CreateURLFromUTF8("dir/dir/file");
+ bool exclusive = true;
+ bool recursive = true;
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(),
+ FileSystemURLDirName(source_url),
+ exclusive, recursive));
+ EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
+ is_copy_not_move = false;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+ context.reset(NewContext(NULL));
+ is_copy_not_move = true;
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) {
+ const int64 kSourceLength = 5;
+ const int64 kDestLength = 50;
+
+ for (size_t i = 0; i < arraysize(kCopyMoveTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "kCopyMoveTestCase " << i);
+ const CopyMoveTestCaseRecord& test_case = kCopyMoveTestCases[i];
+ SCOPED_TRACE(testing::Message() << "\t is_copy_not_move " <<
+ test_case.is_copy_not_move);
+ SCOPED_TRACE(testing::Message() << "\t source_path " <<
+ test_case.source_path);
+ SCOPED_TRACE(testing::Message() << "\t dest_path " <<
+ test_case.dest_path);
+ SCOPED_TRACE(testing::Message() << "\t cause_overwrite " <<
+ test_case.cause_overwrite);
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ bool exclusive = false;
+ bool recursive = true;
+ FileSystemURL source_url = CreateURLFromUTF8(test_case.source_path);
+ FileSystemURL dest_url = CreateURLFromUTF8(test_case.dest_path);
+
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(),
+ FileSystemURLDirName(source_url),
+ exclusive, recursive));
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(),
+ FileSystemURLDirName(dest_url),
+ exclusive, recursive));
+
+ bool created = false;
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), source_url, &created));
+ ASSERT_TRUE(created);
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(context.get(), source_url, kSourceLength));
+
+ if (test_case.cause_overwrite) {
+ context.reset(NewContext(NULL));
+ created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), dest_url, &created));
+ ASSERT_TRUE(created);
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(context.get(), dest_url, kDestLength));
+ }
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CopyOrMoveFile(
+ context.get(), source_url, dest_url, FileSystemOperation::OPTION_NONE,
+ test_case.is_copy_not_move));
+
+ if (test_case.is_copy_not_move) {
+ base::PlatformFileInfo file_info;
+ base::FilePath local_path;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), source_url, &file_info, &local_path));
+ EXPECT_EQ(kSourceLength, file_info.size);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), source_url));
+ } else {
+ base::PlatformFileInfo file_info;
+ base::FilePath local_path;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(
+ context.get(), source_url, &file_info, &local_path));
+ }
+ base::PlatformFileInfo file_info;
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ context.get(), dest_url, &file_info, &local_path));
+ EXPECT_EQ(kSourceLength, file_info.size);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), dest_url));
+ }
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestCopyPathQuotas) {
+ FileSystemURL src_url = CreateURLFromUTF8("src path");
+ FileSystemURL dest_url = CreateURLFromUTF8("destination path");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
+ context.get(), src_url, &created));
+
+ bool is_copy = true;
+ // Copy, no overwrite.
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - 1);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+
+ // Copy, with overwrite.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(0);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) {
+ FileSystemURL src_url = CreateURLFromUTF8("src path");
+ FileSystemURL dest_url = CreateURLFromUTF8("destination path");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
+ context.get(), src_url, &created));
+
+ bool is_copy = false;
+ // Move, rename, no overwrite.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) -
+ ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()) - 1);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(
+ ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) -
+ ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
+ context.get(), src_url, &created));
+
+ // Move, rename, with overwrite.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(0);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) {
+ FileSystemURL src_url = CreateURLFromUTF8("src path");
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
+ context.get(), src_url, &created));
+
+ bool exclusive = true;
+ bool recursive = false;
+ FileSystemURL dir_url = CreateURLFromUTF8("directory path");
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), dir_url, exclusive, recursive));
+
+ FileSystemURL dest_url = FileSystemURLAppend(
+ dir_url, src_url.path().value());
+
+ bool is_copy = false;
+ int64 allowed_bytes_growth = -1000; // Over quota, this should still work.
+ // Move, no rename, no overwrite.
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(allowed_bytes_growth);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(allowed_bytes_growth, context->allowed_bytes_growth());
+
+ // Move, no rename, with overwrite.
+ context.reset(NewContext(NULL));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
+ context.get(), src_url, &created));
+ context.reset(NewContext(NULL));
+ context->set_allowed_bytes_growth(allowed_bytes_growth);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(),
+ src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(
+ allowed_bytes_growth +
+ ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()),
+ context->allowed_bytes_growth());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestCopyInForeignFile) {
+ TestCopyInForeignFileHelper(false /* overwrite */);
+ TestCopyInForeignFileHelper(true /* overwrite */);
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestEnumerator) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ FileSystemURL src_url = CreateURLFromUTF8("source dir");
+ bool exclusive = true;
+ bool recursive = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
+ context.get(), src_url, exclusive, recursive));
+
+ std::set<base::FilePath::StringType> files;
+ std::set<base::FilePath::StringType> directories;
+ FillTestDirectory(src_url, &files, &directories);
+
+ FileSystemURL dest_url = CreateURLFromUTF8("destination dir");
+
+ EXPECT_FALSE(DirectoryExists(dest_url));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Copy(
+ file_system_context(), src_url, dest_url));
+
+ ValidateTestDirectory(dest_url, files, directories);
+ EXPECT_TRUE(DirectoryExists(src_url));
+ EXPECT_TRUE(DirectoryExists(dest_url));
+ recursive = true;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Remove(
+ file_system_context(), dest_url, recursive));
+ EXPECT_FALSE(DirectoryExists(dest_url));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestOriginEnumerator) {
+ scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator>
+ enumerator(ofu()->CreateOriginEnumerator());
+ // The test helper starts out with a single filesystem.
+ EXPECT_TRUE(enumerator.get());
+ EXPECT_EQ(origin(), enumerator->Next());
+ ASSERT_TRUE(type() == kFileSystemTypeTemporary);
+ EXPECT_TRUE(HasFileSystemType(enumerator.get(), kFileSystemTypeTemporary));
+ EXPECT_FALSE(HasFileSystemType(enumerator.get(), kFileSystemTypePersistent));
+ EXPECT_EQ(GURL(), enumerator->Next());
+ EXPECT_FALSE(HasFileSystemType(enumerator.get(), kFileSystemTypeTemporary));
+ EXPECT_FALSE(HasFileSystemType(enumerator.get(), kFileSystemTypePersistent));
+
+ std::set<GURL> origins_expected;
+ origins_expected.insert(origin());
+
+ for (size_t i = 0; i < arraysize(kOriginEnumerationTestRecords); ++i) {
+ SCOPED_TRACE(testing::Message() <<
+ "Validating kOriginEnumerationTestRecords " << i);
+ const OriginEnumerationTestRecord& record =
+ kOriginEnumerationTestRecords[i];
+ GURL origin_url(record.origin_url);
+ origins_expected.insert(origin_url);
+ if (record.has_temporary) {
+ scoped_ptr<SandboxFileSystemTestHelper> file_system(
+ NewFileSystem(origin_url, kFileSystemTypeTemporary));
+ scoped_ptr<FileSystemOperationContext> context(
+ NewContext(file_system.get()));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ context.get(),
+ file_system->CreateURLFromUTF8("file"),
+ &created));
+ EXPECT_TRUE(created);
+ }
+ if (record.has_persistent) {
+ scoped_ptr<SandboxFileSystemTestHelper> file_system(
+ NewFileSystem(origin_url, kFileSystemTypePersistent));
+ scoped_ptr<FileSystemOperationContext> context(
+ NewContext(file_system.get()));
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ context.get(),
+ file_system->CreateURLFromUTF8("file"),
+ &created));
+ EXPECT_TRUE(created);
+ }
+ }
+ enumerator.reset(ofu()->CreateOriginEnumerator());
+ EXPECT_TRUE(enumerator.get());
+ std::set<GURL> origins_found;
+ GURL origin_url;
+ while (!(origin_url = enumerator->Next()).is_empty()) {
+ origins_found.insert(origin_url);
+ SCOPED_TRACE(testing::Message() << "Handling " << origin_url.spec());
+ bool found = false;
+ for (size_t i = 0; !found && i < arraysize(kOriginEnumerationTestRecords);
+ ++i) {
+ const OriginEnumerationTestRecord& record =
+ kOriginEnumerationTestRecords[i];
+ if (GURL(record.origin_url) != origin_url)
+ continue;
+ found = true;
+ EXPECT_EQ(record.has_temporary,
+ HasFileSystemType(enumerator.get(), kFileSystemTypeTemporary));
+ EXPECT_EQ(record.has_persistent,
+ HasFileSystemType(enumerator.get(), kFileSystemTypePersistent));
+ }
+ // Deal with the default filesystem created by the test helper.
+ if (!found && origin_url == origin()) {
+ ASSERT_TRUE(type() == kFileSystemTypeTemporary);
+ EXPECT_TRUE(HasFileSystemType(enumerator.get(),
+ kFileSystemTypeTemporary));
+ EXPECT_FALSE(HasFileSystemType(enumerator.get(),
+ kFileSystemTypePersistent));
+ found = true;
+ }
+ EXPECT_TRUE(found);
+ }
+
+ std::set<GURL> diff;
+ std::set_symmetric_difference(origins_expected.begin(),
+ origins_expected.end(), origins_found.begin(), origins_found.end(),
+ inserter(diff, diff.begin()));
+ EXPECT_TRUE(diff.empty());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestRevokeUsageCache) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+
+ int64 expected_quota = 0;
+
+ for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ SCOPED_TRACE(testing::Message() << "Creating kRegularTestCase " << i);
+ const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ base::FilePath file_path(test_case.path);
+ expected_quota += ObfuscatedFileUtil::ComputeFilePathCost(file_path);
+ if (test_case.is_directory) {
+ bool exclusive = true;
+ bool recursive = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), CreateURL(file_path),
+ exclusive, recursive));
+ } else {
+ bool created = false;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), CreateURL(file_path),
+ &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(context.get(),
+ CreateURL(file_path),
+ test_case.data_file_size));
+ expected_quota += test_case.data_file_size;
+ }
+ }
+
+ // Usually raw size in usage cache and the usage returned by QuotaUtil
+ // should be same.
+ EXPECT_EQ(expected_quota, SizeInUsageFile());
+ EXPECT_EQ(expected_quota, SizeByQuotaUtil());
+
+ RevokeUsageCache();
+ EXPECT_EQ(-1, SizeInUsageFile());
+ EXPECT_EQ(expected_quota, SizeByQuotaUtil());
+
+ // This should reconstruct the cache.
+ GetUsageFromQuotaManager();
+ EXPECT_EQ(expected_quota, SizeInUsageFile());
+ EXPECT_EQ(expected_quota, SizeByQuotaUtil());
+ EXPECT_EQ(expected_quota, usage());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestInconsistency) {
+ const FileSystemURL kPath1 = CreateURLFromUTF8("hoge");
+ const FileSystemURL kPath2 = CreateURLFromUTF8("fuga");
+
+ scoped_ptr<FileSystemOperationContext> context;
+ base::PlatformFile file;
+ base::PlatformFileInfo file_info;
+ base::FilePath data_path;
+ bool created = false;
+
+ // Create a non-empty file.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), kPath1, &created));
+ EXPECT_TRUE(created);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(context.get(), kPath1, 10));
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetFileInfo(
+ context.get(), kPath1, &file_info, &data_path));
+ EXPECT_EQ(10, file_info.size);
+
+ // Destroy database to make inconsistency between database and filesystem.
+ ofu()->DestroyDirectoryDatabase(origin(), type_string());
+
+ // Try to get file info of broken file.
+ EXPECT_FALSE(PathExists(kPath1));
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), kPath1, &created));
+ EXPECT_TRUE(created);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetFileInfo(
+ context.get(), kPath1, &file_info, &data_path));
+ EXPECT_EQ(0, file_info.size);
+
+ // Make another broken file to |kPath2|.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), kPath2, &created));
+ EXPECT_TRUE(created);
+
+ // Destroy again.
+ ofu()->DestroyDirectoryDatabase(origin(), type_string());
+
+ // Repair broken |kPath1|.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->Touch(context.get(), kPath1, base::Time::Now(),
+ base::Time::Now()));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), kPath1, &created));
+ EXPECT_TRUE(created);
+
+ // Copy from sound |kPath1| to broken |kPath2|.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), kPath1, kPath2,
+ FileSystemOperation::OPTION_NONE,
+ true /* copy */));
+
+ ofu()->DestroyDirectoryDatabase(origin(), type_string());
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ context.get(), kPath1,
+ base::PLATFORM_FILE_READ | base::PLATFORM_FILE_CREATE,
+ &file, &created));
+ EXPECT_TRUE(created);
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &file_info));
+ EXPECT_EQ(0, file_info.size);
+ EXPECT_TRUE(base::ClosePlatformFile(file));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestIncompleteDirectoryReading) {
+ const FileSystemURL kPath[] = {
+ CreateURLFromUTF8("foo"),
+ CreateURLFromUTF8("bar"),
+ CreateURLFromUTF8("baz")
+ };
+ const FileSystemURL empty_path = CreateURL(base::FilePath());
+ scoped_ptr<FileSystemOperationContext> context;
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kPath); ++i) {
+ bool created = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), kPath[i], &created));
+ EXPECT_TRUE(created);
+ }
+
+ std::vector<DirectoryEntry> entries;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), empty_path, &entries));
+ EXPECT_EQ(3u, entries.size());
+
+ base::FilePath local_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), kPath[0], &local_path));
+ EXPECT_TRUE(base::DeleteFile(local_path, false));
+
+ entries.clear();
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(
+ file_system_context(), empty_path, &entries));
+ EXPECT_EQ(ARRAYSIZE_UNSAFE(kPath) - 1, entries.size());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir");
+
+ // Create working directory.
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), dir_url, false, false));
+
+ // EnsureFileExists, create case.
+ FileSystemURL url(FileSystemURLAppendUTF8(
+ dir_url, "EnsureFileExists_file"));
+ bool created = false;
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_TRUE(created);
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+
+ // non create case.
+ created = true;
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // fail case.
+ url = FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_dir");
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, false, false));
+
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // CreateOrOpen, create case.
+ url = FileSystemURLAppendUTF8(dir_url, "CreateOrOpen_file");
+ base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
+ created = false;
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ context.get(), url,
+ base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
+ EXPECT_TRUE(created);
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+
+ // open case.
+ file_handle = base::kInvalidPlatformFileValue;
+ created = true;
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ context.get(), url,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
+ EXPECT_FALSE(created);
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // fail case
+ file_handle = base::kInvalidPlatformFileValue;
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS,
+ ofu()->CreateOrOpen(
+ context.get(), url,
+ base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ EXPECT_EQ(base::kInvalidPlatformFileValue, file_handle);
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // CreateDirectory, create case.
+ // Creating CreateDirectory_dir and CreateDirectory_dir/subdir.
+ url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir");
+ FileSystemURL subdir_url(FileSystemURLAppendUTF8(url, "subdir"));
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), subdir_url,
+ true /* exclusive */, true /* recursive */));
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+
+ // create subdir case.
+ // Creating CreateDirectory_dir/subdir2.
+ subdir_url = FileSystemURLAppendUTF8(url, "subdir2");
+ ClearTimestamp(dir_url);
+ ClearTimestamp(url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), subdir_url,
+ true /* exclusive */, true /* recursive */));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+ EXPECT_NE(base::Time(), GetModifiedTime(url));
+
+ // fail case.
+ url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir");
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS,
+ ofu()->CreateDirectory(context.get(), url,
+ true /* exclusive */, true /* recursive */));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // CopyInForeignFile, create case.
+ url = FileSystemURLAppendUTF8(dir_url, "CopyInForeignFile_file");
+ FileSystemURL src_path = FileSystemURLAppendUTF8(
+ dir_url, "CopyInForeignFile_src_file");
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_path, &created));
+ EXPECT_TRUE(created);
+ base::FilePath src_local_path;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), src_path, &src_local_path));
+
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyInForeignFile(context.get(),
+ src_local_path,
+ url));
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) {
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir");
+
+ // Create working directory.
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), dir_url, false, false));
+
+ // DeleteFile, delete case.
+ FileSystemURL url = FileSystemURLAppendUTF8(
+ dir_url, "DeleteFile_file");
+ bool created = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_TRUE(created);
+
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), url));
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+
+ // fail case.
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ofu()->DeleteFile(context.get(), url));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // DeleteDirectory, fail case.
+ url = FileSystemURLAppendUTF8(dir_url, "DeleteDirectory_dir");
+ FileSystemURL file_path(FileSystemURLAppendUTF8(url, "pakeratta"));
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, true, true));
+ created = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), file_path, &created));
+ EXPECT_TRUE(created);
+
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY,
+ ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
+
+ // delete case.
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(context.get(), file_path));
+
+ ClearTimestamp(dir_url);
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCopyAndMove) {
+ TestDirectoryTimestampHelper(
+ CreateURLFromUTF8("copy overwrite"), true, true);
+ TestDirectoryTimestampHelper(
+ CreateURLFromUTF8("copy non-overwrite"), true, false);
+ TestDirectoryTimestampHelper(
+ CreateURLFromUTF8("move overwrite"), false, true);
+ TestDirectoryTimestampHelper(
+ CreateURLFromUTF8("move non-overwrite"), false, false);
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestFileEnumeratorTimestamp) {
+ FileSystemURL dir = CreateURLFromUTF8("foo");
+ FileSystemURL url1 = FileSystemURLAppendUTF8(dir, "bar");
+ FileSystemURL url2 = FileSystemURLAppendUTF8(dir, "baz");
+
+ scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), dir, false, false));
+
+ bool created = false;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url1, &created));
+ EXPECT_TRUE(created);
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(context.get(), url2, false, false));
+
+ base::FilePath file_path;
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), url1, &file_path));
+ EXPECT_FALSE(file_path.empty());
+
+ context.reset(NewContext(NULL));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Touch(context.get(), url1,
+ base::Time::Now() + base::TimeDelta::FromHours(1),
+ base::Time()));
+
+ context.reset(NewContext(NULL));
+ scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum(
+ ofu()->CreateFileEnumerator(context.get(), dir, false));
+
+ int count = 0;
+ base::FilePath file_path_each;
+ while (!(file_path_each = file_enum->Next()).empty()) {
+ context.reset(NewContext(NULL));
+ base::PlatformFileInfo file_info;
+ base::FilePath file_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->GetFileInfo(context.get(),
+ FileSystemURL::CreateForTest(
+ dir.origin(),
+ dir.mount_type(),
+ file_path_each),
+ &file_info, &file_path));
+ EXPECT_EQ(file_info.is_directory, file_enum->IsDirectory());
+ EXPECT_EQ(file_info.last_modified, file_enum->LastModifiedTime());
+ EXPECT_EQ(file_info.size, file_enum->Size());
+ ++count;
+ }
+ EXPECT_EQ(2, count);
+}
+
+// crbug.com/176470
+#if defined(OS_WIN) || defined(OS_ANDROID)
+#define MAYBE_TestQuotaOnCopyFile DISABLED_TestQuotaOnCopyFile
+#else
+#define MAYBE_TestQuotaOnCopyFile TestQuotaOnCopyFile
+#endif
+TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
+ FileSystemURL from_file(CreateURLFromUTF8("fromfile"));
+ FileSystemURL obstacle_file(CreateURLFromUTF8("obstaclefile"));
+ FileSystemURL to_file1(CreateURLFromUTF8("tofile1"));
+ FileSystemURL to_file2(CreateURLFromUTF8("tofile2"));
+ bool created;
+
+ int64 expected_total_file_size = 0;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(from_file))->context(),
+ from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(obstacle_file))->context(),
+ obstacle_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 from_file_size = 1020;
+ expected_total_file_size += from_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(from_file_size)->context(),
+ from_file, from_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 obstacle_file_size = 1;
+ expected_total_file_size += obstacle_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(obstacle_file_size)->context(),
+ obstacle_file, obstacle_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 to_file1_size = from_file_size;
+ expected_total_file_size += to_file1_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ AllowUsageIncrease(
+ PathCost(to_file1) + to_file1_size)->context(),
+ from_file, to_file1,
+ FileSystemOperation::OPTION_NONE,
+ true /* copy */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ofu()->CopyOrMoveFile(
+ DisallowUsageIncrease(
+ PathCost(to_file2) + from_file_size)->context(),
+ from_file, to_file2, FileSystemOperation::OPTION_NONE,
+ true /* copy */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 old_obstacle_file_size = obstacle_file_size;
+ obstacle_file_size = from_file_size;
+ expected_total_file_size += obstacle_file_size - old_obstacle_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ AllowUsageIncrease(
+ obstacle_file_size - old_obstacle_file_size)->context(),
+ from_file, obstacle_file,
+ FileSystemOperation::OPTION_NONE,
+ true /* copy */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 old_from_file_size = from_file_size;
+ from_file_size = old_from_file_size - 1;
+ expected_total_file_size += from_file_size - old_from_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(
+ from_file_size - old_from_file_size)->context(),
+ from_file, from_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ // quota exceeded
+ {
+ old_obstacle_file_size = obstacle_file_size;
+ obstacle_file_size = from_file_size;
+ expected_total_file_size += obstacle_file_size - old_obstacle_file_size;
+ scoped_ptr<UsageVerifyHelper> helper = AllowUsageIncrease(
+ obstacle_file_size - old_obstacle_file_size);
+ helper->context()->set_allowed_bytes_growth(
+ helper->context()->allowed_bytes_growth() - 1);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ helper->context(),
+ from_file, obstacle_file,
+ FileSystemOperation::OPTION_NONE,
+ true /* copy */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+ }
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
+ FileSystemURL from_file(CreateURLFromUTF8("fromfile"));
+ FileSystemURL obstacle_file(CreateURLFromUTF8("obstaclefile"));
+ FileSystemURL to_file(CreateURLFromUTF8("tofile"));
+ bool created;
+
+ int64 expected_total_file_size = 0;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(from_file))->context(),
+ from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 from_file_size = 1020;
+ expected_total_file_size += from_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(from_file_size)->context(),
+ from_file, from_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 to_file_size ALLOW_UNUSED = from_file_size;
+ from_file_size = 0;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ AllowUsageIncrease(-PathCost(from_file) +
+ PathCost(to_file))->context(),
+ from_file, to_file,
+ FileSystemOperation::OPTION_NONE,
+ false /* move */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(from_file))->context(),
+ from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(obstacle_file))->context(),
+ obstacle_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ from_file_size = 1020;
+ expected_total_file_size += from_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(from_file_size)->context(),
+ from_file, from_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 obstacle_file_size = 1;
+ expected_total_file_size += obstacle_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(1)->context(),
+ obstacle_file, obstacle_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ int64 old_obstacle_file_size = obstacle_file_size;
+ obstacle_file_size = from_file_size;
+ from_file_size = 0;
+ expected_total_file_size -= old_obstacle_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ AllowUsageIncrease(
+ -old_obstacle_file_size - PathCost(from_file))->context(),
+ from_file, obstacle_file,
+ FileSystemOperation::OPTION_NONE,
+ false /* move */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(from_file))->context(),
+ from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ from_file_size = 10;
+ expected_total_file_size += from_file_size;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(from_file_size)->context(),
+ from_file, from_file_size));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+
+ // quota exceeded even after operation
+ old_obstacle_file_size = obstacle_file_size;
+ obstacle_file_size = from_file_size;
+ from_file_size = 0;
+ expected_total_file_size -= old_obstacle_file_size;
+ scoped_ptr<FileSystemOperationContext> context =
+ LimitedContext(-old_obstacle_file_size - PathCost(from_file) - 1);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CopyOrMoveFile(
+ context.get(), from_file, obstacle_file,
+ FileSystemOperation::OPTION_NONE,
+ false /* move */));
+ ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
+ context.reset();
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestQuotaOnRemove) {
+ FileSystemURL dir(CreateURLFromUTF8("dir"));
+ FileSystemURL file(CreateURLFromUTF8("file"));
+ FileSystemURL dfile1(CreateURLFromUTF8("dir/dfile1"));
+ FileSystemURL dfile2(CreateURLFromUTF8("dir/dfile2"));
+ bool created;
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(file))->context(),
+ file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateDirectory(
+ AllowUsageIncrease(PathCost(dir))->context(),
+ dir, false, false));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(dfile1))->context(),
+ dfile1, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(dfile2))->context(),
+ dfile2, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(340)->context(),
+ file, 340));
+ ASSERT_EQ(340, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(1020)->context(),
+ dfile1, 1020));
+ ASSERT_EQ(1360, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(120)->context(),
+ dfile2, 120));
+ ASSERT_EQ(1480, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->DeleteFile(
+ AllowUsageIncrease(-PathCost(file) - 340)->context(),
+ file));
+ ASSERT_EQ(1140, ComputeTotalFileSize());
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::Remove(
+ file_system_context(), dir, true /* recursive */));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+}
+
+TEST_F(ObfuscatedFileUtilTest, TestQuotaOnOpen) {
+ FileSystemURL file(CreateURLFromUTF8("file"));
+ base::PlatformFile file_handle;
+ bool created;
+
+ // Creating a file.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(
+ AllowUsageIncrease(PathCost(file))->context(),
+ file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(0, ComputeTotalFileSize());
+
+ // Opening it, which shouldn't change the usage.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ AllowUsageIncrease(0)->context(), file,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+
+ const int length = 33;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(length)->context(), file, length));
+ ASSERT_EQ(length, ComputeTotalFileSize());
+
+ // Opening it with CREATE_ALWAYS flag, which should truncate the file size.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ AllowUsageIncrease(-length)->context(), file,
+ base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+
+ // Extending the file again.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->Truncate(
+ AllowUsageIncrease(length)->context(), file, length));
+ ASSERT_EQ(length, ComputeTotalFileSize());
+
+ // Opening it with TRUNCATED flag, which should truncate the file size.
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->CreateOrOpen(
+ AllowUsageIncrease(-length)->context(), file,
+ base::PLATFORM_FILE_OPEN_TRUNCATED | base::PLATFORM_FILE_WRITE,
+ &file_handle, &created));
+ ASSERT_EQ(0, ComputeTotalFileSize());
+ EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+}
+
+TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAliveCase) {
+ scoped_ptr<ObfuscatedFileUtil> file_util(
+ ObfuscatedFileUtil::CreateForTesting(
+ NULL, data_dir_path(),
+ base::MessageLoopProxy::current().get()));
+ file_util->InitOriginDatabase(GURL(), true /*create*/);
+ ASSERT_TRUE(file_util->origin_database_ != NULL);
+
+ // Callback to Drop DB is called while ObfuscatedFileUtilTest is still alive.
+ file_util->db_flush_delay_seconds_ = 0;
+ file_util->MarkUsed();
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(file_util->origin_database_ == NULL);
+}
+
+TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAlreadyDeletedCase) {
+ // Run message loop after OFU is already deleted to make sure callback doesn't
+ // cause a crash for use after free.
+ {
+ scoped_ptr<ObfuscatedFileUtil> file_util(
+ ObfuscatedFileUtil::CreateForTesting(
+ NULL, data_dir_path(),
+ base::MessageLoopProxy::current().get()));
+ file_util->InitOriginDatabase(GURL(), true /*create*/);
+ file_util->db_flush_delay_seconds_ = 0;
+ file_util->MarkUsed();
+ }
+
+ // At this point the callback is still in the message queue but OFU is gone.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(ObfuscatedFileUtilTest, DestroyDirectoryDatabase_Isolated) {
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util(
+ ObfuscatedFileUtil::CreateForTesting(
+ storage_policy_.get(), data_dir_path(),
+ base::MessageLoopProxy::current().get()));
+ const FileSystemURL url = FileSystemURL::CreateForTest(
+ origin_, kFileSystemTypePersistent, base::FilePath());
+
+ // Create DirectoryDatabase for isolated origin.
+ SandboxDirectoryDatabase* db =
+ file_util->GetDirectoryDatabase(url, true /* create */);
+ ASSERT_TRUE(db != NULL);
+
+ // Destory it.
+ ASSERT_TRUE(file_util->DestroyDirectoryDatabase(
+ url.origin(), GetTypeString(url.type())));
+ ASSERT_TRUE(file_util->directories_.empty());
+}
+
+TEST_F(ObfuscatedFileUtilTest, GetDirectoryDatabase_Isolated) {
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util(
+ ObfuscatedFileUtil::CreateForTesting(
+ storage_policy_.get(), data_dir_path(),
+ base::MessageLoopProxy::current().get()));
+ const FileSystemURL url = FileSystemURL::CreateForTest(
+ origin_, kFileSystemTypePersistent, base::FilePath());
+
+ // Create DirectoryDatabase for isolated origin.
+ SandboxDirectoryDatabase* db =
+ file_util->GetDirectoryDatabase(url, true /* create */);
+ ASSERT_TRUE(db != NULL);
+ ASSERT_EQ(1U, file_util->directories_.size());
+
+ // Remove isolated.
+ storage_policy_->RemoveIsolated(url.origin());
+
+ // This should still get the same database.
+ SandboxDirectoryDatabase* db2 =
+ file_util->GetDirectoryDatabase(url, false /* create */);
+ ASSERT_EQ(db, db2);
+}
+
+TEST_F(ObfuscatedFileUtilTest, MigrationBackFromIsolated) {
+ std::string kFakeDirectoryData("0123456789");
+ base::FilePath old_directory_db_path;
+
+ // Initialize the directory with one origin using
+ // SandboxIsolatedOriginDatabase.
+ {
+ std::string origin_string =
+ webkit_database::GetIdentifierFromOrigin(origin_);
+ SandboxIsolatedOriginDatabase database_old(
+ origin_string, data_dir_path(),
+ base::FilePath(
+ SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory));
+ base::FilePath path;
+ EXPECT_TRUE(database_old.GetPathForOrigin(origin_string, &path));
+ EXPECT_FALSE(path.empty());
+
+ // Populate the origin directory with some fake data.
+ old_directory_db_path = data_dir_path().Append(path);
+ ASSERT_TRUE(base::CreateDirectory(old_directory_db_path));
+ EXPECT_EQ(static_cast<int>(kFakeDirectoryData.size()),
+ file_util::WriteFile(old_directory_db_path.AppendASCII("dummy"),
+ kFakeDirectoryData.data(),
+ kFakeDirectoryData.size()));
+ }
+
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util(
+ ObfuscatedFileUtil::CreateForTesting(
+ storage_policy_.get(), data_dir_path(),
+ base::MessageLoopProxy::current().get()));
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::FilePath origin_directory = file_util->GetDirectoryForOrigin(
+ origin_, true /* create */, &error);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+
+ // The database is migrated from the old one.
+ EXPECT_TRUE(base::DirectoryExists(origin_directory));
+ EXPECT_FALSE(base::DirectoryExists(old_directory_db_path));
+
+ // Check we see the same contents in the new origin directory.
+ std::string origin_db_data;
+ EXPECT_TRUE(base::PathExists(origin_directory.AppendASCII("dummy")));
+ EXPECT_TRUE(base::ReadFileToString(
+ origin_directory.AppendASCII("dummy"), &origin_db_data));
+ EXPECT_EQ(kFakeDirectoryData, origin_db_data);
+}
+
+TEST_F(ObfuscatedFileUtilTest, OpenPathInNonDirectory) {
+ FileSystemURL file(CreateURLFromUTF8("file"));
+ FileSystemURL path_in_file(CreateURLFromUTF8("file/file"));
+ bool created;
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(UnlimitedContext().get(), file, &created));
+ ASSERT_TRUE(created);
+
+ created = false;
+ base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
+ int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ofu()->CreateOrOpen(UnlimitedContext().get(),
+ path_in_file,
+ file_flags,
+ &file_handle,
+ &created));
+ ASSERT_FALSE(created);
+ ASSERT_EQ(base::kInvalidPlatformFileValue, file_handle);
+
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ofu()->CreateDirectory(UnlimitedContext().get(),
+ path_in_file,
+ false /* exclusive */,
+ false /* recursive */));
+}
+
+TEST_F(ObfuscatedFileUtilTest, CreateDirectory_NotADirectoryInRecursive) {
+ FileSystemURL file(CreateURLFromUTF8("file"));
+ FileSystemURL path_in_file(CreateURLFromUTF8("file/child"));
+ FileSystemURL path_in_file_in_file(
+ CreateURLFromUTF8("file/child/grandchild"));
+ bool created;
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ofu()->EnsureFileExists(UnlimitedContext().get(), file, &created));
+ ASSERT_TRUE(created);
+
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ofu()->CreateDirectory(UnlimitedContext().get(),
+ path_in_file,
+ false /* exclusive */,
+ true /* recursive */));
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ofu()->CreateDirectory(UnlimitedContext().get(),
+ path_in_file_in_file,
+ false /* exclusive */,
+ true /* recursive */));
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc b/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc
new file mode 100644
index 00000000000..0cc2f48bb00
--- /dev/null
+++ b/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "content/public/test/test_file_system_options.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+#include "webkit/browser/fileapi/obfuscated_file_util.h"
+#include "webkit/browser/fileapi/plugin_private_file_system_backend.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+namespace fileapi {
+
+namespace {
+
+const GURL kOrigin("http://www.example.com");
+const std::string kPlugin1("plugin1");
+const std::string kPlugin2("plugin2");
+const FileSystemType kType = kFileSystemTypePluginPrivate;
+const std::string kRootName = "pluginprivate";
+
+void DidOpenFileSystem(base::PlatformFileError* error_out,
+ base::PlatformFileError error) {
+ *error_out = error;
+}
+
+std::string RegisterFileSystem() {
+ return IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
+ kType, kRootName, base::FilePath());
+}
+
+} // namespace
+
+class PluginPrivateFileSystemBackendTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ context_ = CreateFileSystemContextForTesting(
+ NULL /* quota_manager_proxy */,
+ data_dir_.path());
+ }
+
+ FileSystemURL CreateURL(const GURL& root_url, const std::string& relative) {
+ FileSystemURL root = context_->CrackURL(root_url);
+ return context_->CreateCrackedFileSystemURL(
+ root.origin(),
+ root.mount_type(),
+ root.virtual_path().AppendASCII(relative));
+ }
+
+ PluginPrivateFileSystemBackend* backend() const {
+ return context_->plugin_private_backend();
+ }
+
+ const base::FilePath& base_path() const { return backend()->base_path(); }
+
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_refptr<FileSystemContext> context_;
+ std::string filesystem_id_;
+};
+
+TEST_F(PluginPrivateFileSystemBackendTest, OpenFileSystemBasic) {
+ const std::string filesystem_id1 = RegisterFileSystem();
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ backend()->OpenPrivateFileSystem(
+ kOrigin, kType, filesystem_id1, kPlugin1,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&DidOpenFileSystem, &error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ // Run this again with FAIL_IF_NONEXISTENT to see if it succeeds.
+ const std::string filesystem_id2 = RegisterFileSystem();
+ error = base::PLATFORM_FILE_ERROR_FAILED;
+ backend()->OpenPrivateFileSystem(
+ kOrigin, kType, filesystem_id2, kPlugin1,
+ OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ base::Bind(&DidOpenFileSystem, &error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ const GURL root_url(
+ GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id1, kRootName));
+ FileSystemURL file = CreateURL(root_url, "foo");
+ base::FilePath platform_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFile(context_.get(), file));
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetPlatformPath(context_.get(), file,
+ &platform_path));
+ EXPECT_TRUE(base_path().AppendASCII("000").AppendASCII(kPlugin1).IsParent(
+ platform_path));
+}
+
+TEST_F(PluginPrivateFileSystemBackendTest, PluginIsolation) {
+ // Open filesystem for kPlugin1 and kPlugin2.
+ const std::string filesystem_id1 = RegisterFileSystem();
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ backend()->OpenPrivateFileSystem(
+ kOrigin, kType, filesystem_id1, kPlugin1,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&DidOpenFileSystem, &error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ const std::string filesystem_id2 = RegisterFileSystem();
+ error = base::PLATFORM_FILE_ERROR_FAILED;
+ backend()->OpenPrivateFileSystem(
+ kOrigin, kType, filesystem_id2, kPlugin2,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&DidOpenFileSystem, &error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ // Create 'foo' in kPlugin1.
+ const GURL root_url1(
+ GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id1, kRootName));
+ FileSystemURL file1 = CreateURL(root_url1, "foo");
+ base::FilePath platform_path;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFile(context_.get(), file1));
+ EXPECT_TRUE(AsyncFileTestHelper::FileExists(
+ context_.get(), file1, AsyncFileTestHelper::kDontCheckSize));
+
+ // See the same path is not available in kPlugin2.
+ const GURL root_url2(
+ GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id2, kRootName));
+ FileSystemURL file2 = CreateURL(root_url2, "foo");
+ EXPECT_FALSE(AsyncFileTestHelper::FileExists(
+ context_.get(), file2, AsyncFileTestHelper::kDontCheckSize));
+}
+
+// TODO(kinuko,nhiroki): also test if DeleteOriginDataOnFileThread
+// works fine when there's multiple plugin partitions.
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc
new file mode 100644
index 00000000000..d3671fe89f8
--- /dev/null
+++ b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc
@@ -0,0 +1,280 @@
+// Copyright 2013 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 "webkit/browser/fileapi/recursive_operation_delegate.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/sandbox_file_system_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation.h"
+#include "webkit/browser/fileapi/file_system_operation_runner.h"
+
+namespace fileapi {
+namespace {
+
+class LoggingRecursiveOperation : public RecursiveOperationDelegate {
+ public:
+ struct LogEntry {
+ enum Type {
+ PROCESS_FILE,
+ PROCESS_DIRECTORY,
+ POST_PROCESS_DIRECTORY
+ };
+ Type type;
+ FileSystemURL url;
+ };
+
+ LoggingRecursiveOperation(FileSystemContext* file_system_context,
+ const FileSystemURL& root,
+ const StatusCallback& callback)
+ : RecursiveOperationDelegate(file_system_context),
+ root_(root),
+ callback_(callback),
+ weak_factory_(this) {
+ }
+ virtual ~LoggingRecursiveOperation() {}
+
+ const std::vector<LogEntry>& log_entries() const { return log_entries_; }
+
+ // RecursiveOperationDelegate overrides.
+ virtual void Run() OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void RunRecursively() OVERRIDE {
+ StartRecursiveOperation(root_, callback_);
+ }
+
+ virtual void ProcessFile(const FileSystemURL& url,
+ const StatusCallback& callback) OVERRIDE {
+ RecordLogEntry(LogEntry::PROCESS_FILE, url);
+ operation_runner()->GetMetadata(
+ url,
+ base::Bind(&LoggingRecursiveOperation::DidGetMetadata,
+ weak_factory_.GetWeakPtr(), callback));
+ }
+
+ virtual void ProcessDirectory(const FileSystemURL& url,
+ const StatusCallback& callback) OVERRIDE {
+ RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url);
+ callback.Run(base::PLATFORM_FILE_OK);
+ }
+
+ virtual void PostProcessDirectory(const FileSystemURL& url,
+ const StatusCallback& callback) OVERRIDE {
+ RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url);
+ callback.Run(base::PLATFORM_FILE_OK);
+ }
+
+ private:
+ void RecordLogEntry(LogEntry::Type type, const FileSystemURL& url) {
+ LogEntry entry;
+ entry.type = type;
+ entry.url = url;
+ log_entries_.push_back(entry);
+ }
+
+ void DidGetMetadata(const StatusCallback& callback,
+ base::PlatformFileError result,
+ const base::PlatformFileInfo& file_info) {
+ if (result != base::PLATFORM_FILE_OK) {
+ callback.Run(result);
+ return;
+ }
+
+ callback.Run(file_info.is_directory ?
+ base::PLATFORM_FILE_ERROR_NOT_A_FILE :
+ base::PLATFORM_FILE_OK);
+ }
+
+ FileSystemURL root_;
+ StatusCallback callback_;
+ std::vector<LogEntry> log_entries_;
+
+ base::WeakPtrFactory<LoggingRecursiveOperation> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation);
+};
+
+void ReportStatus(base::PlatformFileError* out_error,
+ base::PlatformFileError error) {
+ DCHECK(out_error);
+ *out_error = error;
+}
+
+// To test the Cancel() during operation, calls Cancel() of |operation|
+// after |counter| times message posting.
+void CallCancelLater(RecursiveOperationDelegate* operation, int counter) {
+ if (counter > 0) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CallCancelLater, base::Unretained(operation), counter - 1));
+ return;
+ }
+
+ operation->Cancel();
+}
+
+} // namespace
+
+class RecursiveOperationDelegateTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ EXPECT_TRUE(base_.CreateUniqueTempDir());
+ sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem"));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ sandbox_file_system_.TearDown();
+ }
+
+ scoped_ptr<FileSystemOperationContext> NewContext() {
+ FileSystemOperationContext* context =
+ sandbox_file_system_.NewOperationContext();
+ // Grant enough quota for all test cases.
+ context->set_allowed_bytes_growth(1000000);
+ return make_scoped_ptr(context);
+ }
+
+ FileSystemFileUtil* file_util() {
+ return sandbox_file_system_.file_util();
+ }
+
+ FileSystemURL URLForPath(const std::string& path) const {
+ return sandbox_file_system_.CreateURLFromUTF8(path);
+ }
+
+ FileSystemURL CreateFile(const std::string& path) {
+ FileSystemURL url = URLForPath(path);
+ bool created = false;
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->EnsureFileExists(NewContext().get(),
+ url, &created));
+ EXPECT_TRUE(created);
+ return url;
+ }
+
+ FileSystemURL CreateDirectory(const std::string& path) {
+ FileSystemURL url = URLForPath(path);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->CreateDirectory(NewContext().get(), url,
+ false /* exclusive */, true));
+ return url;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+
+ // Common temp base for nondestructive uses.
+ base::ScopedTempDir base_;
+ SandboxFileSystemTestHelper sandbox_file_system_;
+};
+
+TEST_F(RecursiveOperationDelegateTest, RootIsFile) {
+ FileSystemURL src_file(CreateFile("src"));
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ scoped_ptr<FileSystemOperationContext> context = NewContext();
+ scoped_ptr<LoggingRecursiveOperation> operation(
+ new LoggingRecursiveOperation(
+ context->file_system_context(), src_file,
+ base::Bind(&ReportStatus, &error)));
+ operation->RunRecursively();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
+ operation->log_entries();
+ ASSERT_EQ(1U, log_entries.size());
+ const LoggingRecursiveOperation::LogEntry& entry = log_entries[0];
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, entry.type);
+ EXPECT_EQ(src_file, entry.url);
+}
+
+TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) {
+ FileSystemURL src_root(CreateDirectory("src"));
+ FileSystemURL src_dir1(CreateDirectory("src/dir1"));
+ FileSystemURL src_file1(CreateFile("src/file1"));
+ FileSystemURL src_file2(CreateFile("src/dir1/file2"));
+ FileSystemURL src_file3(CreateFile("src/dir1/file3"));
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ scoped_ptr<FileSystemOperationContext> context = NewContext();
+ scoped_ptr<LoggingRecursiveOperation> operation(
+ new LoggingRecursiveOperation(
+ context->file_system_context(), src_root,
+ base::Bind(&ReportStatus, &error)));
+ operation->RunRecursively();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+
+ const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
+ operation->log_entries();
+ ASSERT_EQ(8U, log_entries.size());
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
+ log_entries[0].type);
+ EXPECT_EQ(src_root, log_entries[0].url);
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY,
+ log_entries[1].type);
+ EXPECT_EQ(src_root, log_entries[1].url);
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
+ log_entries[2].type);
+ EXPECT_EQ(src_file1, log_entries[2].url);
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY,
+ log_entries[3].type);
+ EXPECT_EQ(src_dir1, log_entries[3].url);
+
+ // The order of src/dir1/file2 and src/dir1/file3 depends on the file system
+ // implementation (can be swapped).
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
+ log_entries[4].type);
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
+ log_entries[5].type);
+ EXPECT_TRUE((src_file2 == log_entries[4].url &&
+ src_file3 == log_entries[5].url) ||
+ (src_file3 == log_entries[4].url &&
+ src_file2 == log_entries[5].url));
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY,
+ log_entries[6].type);
+ EXPECT_EQ(src_dir1, log_entries[6].url);
+
+ EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY,
+ log_entries[7].type);
+ EXPECT_EQ(src_root, log_entries[7].url);
+}
+
+TEST_F(RecursiveOperationDelegateTest, Cancel) {
+ FileSystemURL src_root(CreateDirectory("src"));
+ FileSystemURL src_dir1(CreateDirectory("src/dir1"));
+ FileSystemURL src_file1(CreateFile("src/file1"));
+ FileSystemURL src_file2(CreateFile("src/dir1/file2"));
+
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ scoped_ptr<FileSystemOperationContext> context = NewContext();
+ scoped_ptr<LoggingRecursiveOperation> operation(
+ new LoggingRecursiveOperation(
+ context->file_system_context(), src_root,
+ base::Bind(&ReportStatus, &error)));
+ operation->RunRecursively();
+
+ // Invoke Cancel(), after 5 times message posting.
+ CallCancelLater(operation.get(), 5);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error);
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc b/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
new file mode 100644
index 00000000000..c87043b74ca
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 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 "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/public/test/test_file_system_options.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+namespace fileapi {
+
+namespace {
+
+FileSystemURL CreateFileSystemURL(const char* path) {
+ const GURL kOrigin("http://foo/");
+ return FileSystemURL::CreateForTest(
+ kOrigin, kFileSystemTypeTemporary, base::FilePath::FromUTF8Unsafe(path));
+}
+
+} // namespace
+
+class SandboxFileSystemBackendDelegateTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ delegate_.reset(new SandboxFileSystemBackendDelegate(
+ NULL /* quota_manager_proxy */,
+ base::MessageLoopProxy::current().get(),
+ data_dir_.path(),
+ NULL /* special_storage_policy */,
+ CreateAllowFileAccessOptions()));
+ }
+
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_ptr<SandboxFileSystemBackendDelegate> delegate_;
+};
+
+TEST_F(SandboxFileSystemBackendDelegateTest, IsAccessValid) {
+ // Normal case.
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("a")));
+
+ // Access to a path with parent references ('..') should be disallowed.
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("a/../b")));
+
+ // Access from non-allowed scheme should be disallowed.
+ EXPECT_FALSE(delegate_->IsAccessValid(
+ FileSystemURL::CreateForTest(
+ GURL("unknown://bar"), kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe("foo"))));
+
+ // Access with restricted name should be disallowed.
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".")));
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("..")));
+
+ // This is also disallowed due to Windows XP parent path handling.
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("...")));
+
+ // These are identified as unsafe cases due to weird path handling
+ // on Windows.
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(" ..")));
+ EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".. ")));
+
+ // Similar but safe cases.
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(" .")));
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(". ")));
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("b.")));
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(".b")));
+
+ // A path that looks like a drive letter.
+ EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("c:")));
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc b/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc
new file mode 100644
index 00000000000..4bb12261a97
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc
@@ -0,0 +1,319 @@
+// Copyright 2013 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 "webkit/browser/fileapi/sandbox_file_system_backend.h"
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_options.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+// PS stands for path separator.
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+#define PS "\\"
+#else
+#define PS "/"
+#endif
+
+namespace fileapi {
+
+namespace {
+
+const struct RootPathTest {
+ fileapi::FileSystemType type;
+ const char* origin_url;
+ const char* expected_path;
+} kRootPathTestCases[] = {
+ { fileapi::kFileSystemTypeTemporary, "http://foo:1/",
+ "000" PS "t" },
+ { fileapi::kFileSystemTypePersistent, "http://foo:1/",
+ "000" PS "p" },
+ { fileapi::kFileSystemTypeTemporary, "http://bar.com/",
+ "001" PS "t" },
+ { fileapi::kFileSystemTypePersistent, "http://bar.com/",
+ "001" PS "p" },
+ { fileapi::kFileSystemTypeTemporary, "https://foo:2/",
+ "002" PS "t" },
+ { fileapi::kFileSystemTypePersistent, "https://foo:2/",
+ "002" PS "p" },
+ { fileapi::kFileSystemTypeTemporary, "https://bar.com/",
+ "003" PS "t" },
+ { fileapi::kFileSystemTypePersistent, "https://bar.com/",
+ "003" PS "p" },
+};
+
+const struct RootPathFileURITest {
+ fileapi::FileSystemType type;
+ const char* origin_url;
+ const char* expected_path;
+ const char* virtual_path;
+} kRootPathFileURITestCases[] = {
+ { fileapi::kFileSystemTypeTemporary, "file:///",
+ "000" PS "t", NULL },
+ { fileapi::kFileSystemTypePersistent, "file:///",
+ "000" PS "p", NULL },
+};
+
+void DidOpenFileSystem(base::PlatformFileError* error_out,
+ const GURL& origin_url,
+ const std::string& name,
+ base::PlatformFileError error) {
+ *error_out = error;
+}
+
+} // namespace
+
+class SandboxFileSystemBackendTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ SetUpNewDelegate(CreateAllowFileAccessOptions());
+ }
+
+ void SetUpNewDelegate(const FileSystemOptions& options) {
+ delegate_.reset(new SandboxFileSystemBackendDelegate(
+ NULL /* quota_manager_proxy */,
+ base::MessageLoopProxy::current().get(),
+ data_dir_.path(),
+ NULL /* special_storage_policy */,
+ options));
+ }
+
+ void SetUpNewBackend(const FileSystemOptions& options) {
+ SetUpNewDelegate(options);
+ backend_.reset(new SandboxFileSystemBackend(delegate_.get()));
+ }
+
+ SandboxFileSystemBackendDelegate::OriginEnumerator*
+ CreateOriginEnumerator() const {
+ return backend_->CreateOriginEnumerator();
+ }
+
+ void CreateOriginTypeDirectory(const GURL& origin,
+ fileapi::FileSystemType type) {
+ base::FilePath target = delegate_->
+ GetBaseDirectoryForOriginAndType(origin, type, true);
+ ASSERT_TRUE(!target.empty());
+ ASSERT_TRUE(base::DirectoryExists(target));
+ }
+
+ bool GetRootPath(const GURL& origin_url,
+ fileapi::FileSystemType type,
+ OpenFileSystemMode mode,
+ base::FilePath* root_path) {
+ base::PlatformFileError error = base::PLATFORM_FILE_OK;
+ backend_->OpenFileSystem(
+ origin_url, type, mode,
+ base::Bind(&DidOpenFileSystem, &error));
+ base::RunLoop().RunUntilIdle();
+ if (error != base::PLATFORM_FILE_OK)
+ return false;
+ base::FilePath returned_root_path =
+ delegate_->GetBaseDirectoryForOriginAndType(
+ origin_url, type, false /* create */);
+ if (root_path)
+ *root_path = returned_root_path;
+ return !returned_root_path.empty();
+ }
+
+ base::FilePath file_system_path() const {
+ return data_dir_.path().Append(
+ SandboxFileSystemBackendDelegate::kFileSystemDirectory);
+ }
+
+ base::ScopedTempDir data_dir_;
+ base::MessageLoop message_loop_;
+ scoped_ptr<SandboxFileSystemBackendDelegate> delegate_;
+ scoped_ptr<SandboxFileSystemBackend> backend_;
+};
+
+TEST_F(SandboxFileSystemBackendTest, Empty) {
+ SetUpNewBackend(CreateAllowFileAccessOptions());
+ scoped_ptr<SandboxFileSystemBackendDelegate::OriginEnumerator> enumerator(
+ CreateOriginEnumerator());
+ ASSERT_TRUE(enumerator->Next().is_empty());
+}
+
+TEST_F(SandboxFileSystemBackendTest, EnumerateOrigins) {
+ SetUpNewBackend(CreateAllowFileAccessOptions());
+ const char* temporary_origins[] = {
+ "http://www.bar.com/",
+ "http://www.foo.com/",
+ "http://www.foo.com:1/",
+ "http://www.example.com:8080/",
+ "http://www.google.com:80/",
+ };
+ const char* persistent_origins[] = {
+ "http://www.bar.com/",
+ "http://www.foo.com:8080/",
+ "http://www.foo.com:80/",
+ };
+ size_t temporary_size = ARRAYSIZE_UNSAFE(temporary_origins);
+ size_t persistent_size = ARRAYSIZE_UNSAFE(persistent_origins);
+ std::set<GURL> temporary_set, persistent_set;
+ for (size_t i = 0; i < temporary_size; ++i) {
+ CreateOriginTypeDirectory(GURL(temporary_origins[i]),
+ fileapi::kFileSystemTypeTemporary);
+ temporary_set.insert(GURL(temporary_origins[i]));
+ }
+ for (size_t i = 0; i < persistent_size; ++i) {
+ CreateOriginTypeDirectory(GURL(persistent_origins[i]),
+ kFileSystemTypePersistent);
+ persistent_set.insert(GURL(persistent_origins[i]));
+ }
+
+ scoped_ptr<SandboxFileSystemBackendDelegate::OriginEnumerator> enumerator(
+ CreateOriginEnumerator());
+ size_t temporary_actual_size = 0;
+ size_t persistent_actual_size = 0;
+ GURL current;
+ while (!(current = enumerator->Next()).is_empty()) {
+ SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current.spec());
+ if (enumerator->HasFileSystemType(kFileSystemTypeTemporary)) {
+ ASSERT_TRUE(temporary_set.find(current) != temporary_set.end());
+ ++temporary_actual_size;
+ }
+ if (enumerator->HasFileSystemType(kFileSystemTypePersistent)) {
+ ASSERT_TRUE(persistent_set.find(current) != persistent_set.end());
+ ++persistent_actual_size;
+ }
+ }
+
+ EXPECT_EQ(temporary_size, temporary_actual_size);
+ EXPECT_EQ(persistent_size, persistent_actual_size);
+}
+
+TEST_F(SandboxFileSystemBackendTest, GetRootPathCreateAndExamine) {
+ std::vector<base::FilePath> returned_root_path(
+ ARRAYSIZE_UNSAFE(kRootPathTestCases));
+ SetUpNewBackend(CreateAllowFileAccessOptions());
+
+ // Create a new root directory.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPath (create) #" << i << " "
+ << kRootPathTestCases[i].expected_path);
+
+ base::FilePath root_path;
+ EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
+ kRootPathTestCases[i].type,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ &root_path));
+
+ base::FilePath expected = file_system_path().AppendASCII(
+ kRootPathTestCases[i].expected_path);
+ EXPECT_EQ(expected.value(), root_path.value());
+ EXPECT_TRUE(base::DirectoryExists(root_path));
+ ASSERT_TRUE(returned_root_path.size() > i);
+ returned_root_path[i] = root_path;
+ }
+
+ // Get the root directory with create=false and see if we get the
+ // same directory.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPath (get) #" << i << " "
+ << kRootPathTestCases[i].expected_path);
+
+ base::FilePath root_path;
+ EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
+ kRootPathTestCases[i].type,
+ OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ &root_path));
+ ASSERT_TRUE(returned_root_path.size() > i);
+ EXPECT_EQ(returned_root_path[i].value(), root_path.value());
+ }
+}
+
+TEST_F(SandboxFileSystemBackendTest,
+ GetRootPathCreateAndExamineWithNewBackend) {
+ std::vector<base::FilePath> returned_root_path(
+ ARRAYSIZE_UNSAFE(kRootPathTestCases));
+ SetUpNewBackend(CreateAllowFileAccessOptions());
+
+ GURL origin_url("http://foo.com:1/");
+
+ base::FilePath root_path1;
+ EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ &root_path1));
+
+ SetUpNewBackend(CreateDisallowFileAccessOptions());
+ base::FilePath root_path2;
+ EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary,
+ OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ &root_path2));
+
+ EXPECT_EQ(root_path1.value(), root_path2.value());
+}
+
+TEST_F(SandboxFileSystemBackendTest, GetRootPathGetWithoutCreate) {
+ SetUpNewBackend(CreateDisallowFileAccessOptions());
+
+ // Try to get a root directory without creating.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPath (create=false) #" << i << " "
+ << kRootPathTestCases[i].expected_path);
+ EXPECT_FALSE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
+ kRootPathTestCases[i].type,
+ OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ NULL));
+ }
+}
+
+TEST_F(SandboxFileSystemBackendTest, GetRootPathInIncognito) {
+ SetUpNewBackend(CreateIncognitoFileSystemOptions());
+
+ // Try to get a root directory.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPath (incognito) #" << i << " "
+ << kRootPathTestCases[i].expected_path);
+ EXPECT_FALSE(
+ GetRootPath(GURL(kRootPathTestCases[i].origin_url),
+ kRootPathTestCases[i].type,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ NULL));
+ }
+}
+
+TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURI) {
+ SetUpNewBackend(CreateDisallowFileAccessOptions());
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPathFileURI (disallow) #"
+ << i << " " << kRootPathFileURITestCases[i].expected_path);
+ EXPECT_FALSE(
+ GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url),
+ kRootPathFileURITestCases[i].type,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ NULL));
+ }
+}
+
+TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURIWithAllowFlag) {
+ SetUpNewBackend(CreateAllowFileAccessOptions());
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "RootPathFileURI (allow) #"
+ << i << " " << kRootPathFileURITestCases[i].expected_path);
+ base::FilePath root_path;
+ EXPECT_TRUE(GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url),
+ kRootPathFileURITestCases[i].type,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ &root_path));
+ base::FilePath expected = file_system_path().AppendASCII(
+ kRootPathFileURITestCases[i].expected_path);
+ EXPECT_EQ(expected.value(), root_path.value());
+ EXPECT_TRUE(base::DirectoryExists(root_path));
+ }
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/transient_file_util_unittest.cc b/chromium/content/browser/fileapi/transient_file_util_unittest.cc
new file mode 100644
index 00000000000..2ddb315c55f
--- /dev/null
+++ b/chromium/content/browser/fileapi/transient_file_util_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+#include "webkit/browser/fileapi/transient_file_util.h"
+#include "webkit/common/blob/scoped_file.h"
+
+namespace fileapi {
+
+class TransientFileUtilTest : public testing::Test {
+ public:
+ TransientFileUtilTest() {}
+ virtual ~TransientFileUtilTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ file_system_context_ = CreateFileSystemContextForTesting(
+ NULL, base::FilePath(FILE_PATH_LITERAL("dummy")));
+ transient_file_util_.reset(new TransientFileUtil);
+
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ file_system_context_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void CreateAndRegisterTemporaryFile(
+ FileSystemURL* file_url,
+ base::FilePath* file_path) {
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(data_dir_.path(), file_path));
+ IsolatedContext* isolated_context = IsolatedContext::GetInstance();
+ std::string name = "tmp";
+ std::string fsid = isolated_context->RegisterFileSystemForPath(
+ kFileSystemTypeForTransientFile,
+ *file_path,
+ &name);
+ ASSERT_TRUE(!fsid.empty());
+ base::FilePath virtual_path = isolated_context->CreateVirtualRootPath(
+ fsid).AppendASCII(name);
+ *file_url = file_system_context_->CreateCrackedFileSystemURL(
+ GURL("http://foo"),
+ kFileSystemTypeIsolated,
+ virtual_path);
+ }
+
+ scoped_ptr<FileSystemOperationContext> NewOperationContext() {
+ return make_scoped_ptr(
+ new FileSystemOperationContext(file_system_context_.get()));
+ }
+
+ FileSystemFileUtil* file_util() { return transient_file_util_.get(); }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_ptr<TransientFileUtil> transient_file_util_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientFileUtilTest);
+};
+
+TEST_F(TransientFileUtilTest, TransientFile) {
+ FileSystemURL temp_url;
+ base::FilePath temp_path;
+
+ CreateAndRegisterTemporaryFile(&temp_url, &temp_path);
+
+ base::PlatformFileError error;
+ base::PlatformFileInfo file_info;
+ base::FilePath path;
+
+ // Make sure the file is there.
+ ASSERT_TRUE(temp_url.is_valid());
+ ASSERT_TRUE(base::PathExists(temp_path));
+ ASSERT_FALSE(base::DirectoryExists(temp_path));
+
+ // Create a snapshot file.
+ {
+ webkit_blob::ScopedFile scoped_file =
+ file_util()->CreateSnapshotFile(NewOperationContext().get(),
+ temp_url,
+ &error,
+ &file_info,
+ &path);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(temp_path, path);
+ ASSERT_FALSE(file_info.is_directory);
+
+ // The file should be still there.
+ ASSERT_TRUE(base::PathExists(temp_path));
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_util()->GetFileInfo(NewOperationContext().get(),
+ temp_url, &file_info, &path));
+ ASSERT_EQ(temp_path, path);
+ ASSERT_FALSE(file_info.is_directory);
+ }
+
+ // The file's now scoped out.
+ base::RunLoop().RunUntilIdle();
+
+ // Now the temporary file and the transient filesystem must be gone too.
+ ASSERT_FALSE(base::PathExists(temp_path));
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ file_util()->GetFileInfo(NewOperationContext().get(),
+ temp_url, &file_info, &path));
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc b/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc
new file mode 100644
index 00000000000..4ae03b9b344
--- /dev/null
+++ b/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright 2013 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 "webkit/browser/fileapi/upload_file_system_file_element_reader.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+namespace fileapi {
+
+namespace {
+
+const char kFileSystemURLOrigin[] = "http://remote";
+const fileapi::FileSystemType kFileSystemType =
+ fileapi::kFileSystemTypeTemporary;
+
+} // namespace
+
+class UploadFileSystemFileElementReaderTest : public testing::Test {
+ public:
+ UploadFileSystemFileElementReaderTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ file_system_context_ = fileapi::CreateFileSystemContextForTesting(
+ NULL, temp_dir_.path());
+
+ file_system_context_->OpenFileSystem(
+ GURL(kFileSystemURLOrigin),
+ kFileSystemType,
+ OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&UploadFileSystemFileElementReaderTest::OnOpenFileSystem,
+ base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(file_system_root_url_.is_valid());
+
+ // Prepare a file on file system.
+ const char kTestData[] = "abcdefghijklmnop0123456789";
+ file_data_.assign(kTestData, kTestData + arraysize(kTestData) - 1);
+ const char kFilename[] = "File.dat";
+ file_url_ = GetFileSystemURL(kFilename);
+ WriteFileSystemFile(kFilename, &file_data_[0], file_data_.size(),
+ &file_modification_time_);
+
+ // Create and initialize a reader.
+ reader_.reset(
+ new UploadFileSystemFileElementReader(file_system_context_.get(),
+ file_url_,
+ 0,
+ kuint64max,
+ file_modification_time_));
+ net::TestCompletionCallback callback;
+ ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(callback.callback()));
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
+ EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
+ EXPECT_FALSE(reader_->IsInMemory());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ reader_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ GURL GetFileSystemURL(const std::string& filename) {
+ return GURL(file_system_root_url_.spec() + filename);
+ }
+
+ void WriteFileSystemFile(const std::string& filename,
+ const char* buf,
+ int buf_size,
+ base::Time* modification_time) {
+ fileapi::FileSystemURL url =
+ file_system_context_->CreateCrackedFileSystemURL(
+ GURL(kFileSystemURLOrigin),
+ kFileSystemType,
+ base::FilePath().AppendASCII(filename));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::CreateFileWithData(
+ file_system_context_, url, buf, buf_size));
+
+ base::PlatformFileInfo file_info;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ AsyncFileTestHelper::GetMetadata(
+ file_system_context_, url, &file_info));
+ *modification_time = file_info.last_modified;
+ }
+
+ void OnOpenFileSystem(const GURL& root,
+ const std::string& name,
+ base::PlatformFileError result) {
+ ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ ASSERT_TRUE(root.is_valid());
+ file_system_root_url_ = root;
+ }
+
+ base::MessageLoopForIO message_loop_;
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<FileSystemContext> file_system_context_;
+ GURL file_system_root_url_;
+ std::vector<char> file_data_;
+ GURL file_url_;
+ base::Time file_modification_time_;
+ scoped_ptr<UploadFileSystemFileElementReader> reader_;
+};
+
+TEST_F(UploadFileSystemFileElementReaderTest, ReadAll) {
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(file_data_.size());
+ net::TestCompletionCallback read_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback.callback()));
+ EXPECT_EQ(buf->size(), read_callback.WaitForResult());
+ EXPECT_EQ(0U, reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
+ // Try to read again.
+ EXPECT_EQ(0, reader_->Read(buf.get(), buf->size(), read_callback.callback()));
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, ReadPartially) {
+ const size_t kHalfSize = file_data_.size() / 2;
+ ASSERT_EQ(file_data_.size(), kHalfSize * 2);
+
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(kHalfSize);
+
+ net::TestCompletionCallback read_callback1;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
+ EXPECT_EQ(buf->size(), read_callback1.WaitForResult());
+ EXPECT_EQ(file_data_.size() - buf->size(), reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + kHalfSize,
+ buf->data()));
+
+ net::TestCompletionCallback read_callback2;
+ EXPECT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback2.callback()));
+ EXPECT_EQ(buf->size(), read_callback2.WaitForResult());
+ EXPECT_EQ(0U, reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin() + kHalfSize, file_data_.end(),
+ buf->data()));
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, ReadTooMuch) {
+ const size_t kTooLargeSize = file_data_.size() * 2;
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(kTooLargeSize);
+ net::TestCompletionCallback read_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback.callback()));
+ EXPECT_EQ(static_cast<int>(file_data_.size()), read_callback.WaitForResult());
+ EXPECT_EQ(0U, reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, MultipleInit) {
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(file_data_.size());
+
+ // Read all.
+ net::TestCompletionCallback read_callback1;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
+ EXPECT_EQ(buf->size(), read_callback1.WaitForResult());
+ EXPECT_EQ(0U, reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
+
+ // Call Init() again to reset the state.
+ net::TestCompletionCallback init_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
+ EXPECT_EQ(net::OK, init_callback.WaitForResult());
+ EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
+ EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
+
+ // Read again.
+ net::TestCompletionCallback read_callback2;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback2.callback()));
+ EXPECT_EQ(buf->size(), read_callback2.WaitForResult());
+ EXPECT_EQ(0U, reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, InitDuringAsyncOperation) {
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(file_data_.size());
+
+ // Start reading all.
+ net::TestCompletionCallback read_callback1;
+ EXPECT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
+
+ // Call Init to cancel the previous read.
+ net::TestCompletionCallback init_callback1;
+ EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback1.callback()));
+
+ // Call Init again to cancel the previous init.
+ net::TestCompletionCallback init_callback2;
+ EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback2.callback()));
+ EXPECT_EQ(net::OK, init_callback2.WaitForResult());
+ EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
+ EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
+
+ // Read half.
+ scoped_refptr<net::IOBufferWithSize> buf2 =
+ new net::IOBufferWithSize(file_data_.size() / 2);
+ net::TestCompletionCallback read_callback2;
+ EXPECT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf2.get(), buf2->size(), read_callback2.callback()));
+ EXPECT_EQ(buf2->size(), read_callback2.WaitForResult());
+ EXPECT_EQ(file_data_.size() - buf2->size(), reader_->BytesRemaining());
+ EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + buf2->size(),
+ buf2->data()));
+
+ // Make sure callbacks are not called for cancelled operations.
+ EXPECT_FALSE(read_callback1.have_result());
+ EXPECT_FALSE(init_callback1.have_result());
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, Range) {
+ const int kOffset = 2;
+ const int kLength = file_data_.size() - kOffset * 3;
+ reader_.reset(new UploadFileSystemFileElementReader(
+ file_system_context_.get(), file_url_, kOffset, kLength, base::Time()));
+ net::TestCompletionCallback init_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
+ EXPECT_EQ(net::OK, init_callback.WaitForResult());
+ EXPECT_EQ(static_cast<uint64>(kLength), reader_->GetContentLength());
+ EXPECT_EQ(static_cast<uint64>(kLength), reader_->BytesRemaining());
+ scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(kLength);
+ net::TestCompletionCallback read_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING,
+ reader_->Read(buf.get(), buf->size(), read_callback.callback()));
+ EXPECT_EQ(kLength, read_callback.WaitForResult());
+ EXPECT_TRUE(std::equal(file_data_.begin() + kOffset,
+ file_data_.begin() + kOffset + kLength,
+ buf->data()));
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, FileChanged) {
+ // Expect one second before the actual modification time to simulate change.
+ const base::Time expected_modification_time =
+ file_modification_time_ - base::TimeDelta::FromSeconds(1);
+ reader_.reset(
+ new UploadFileSystemFileElementReader(file_system_context_.get(),
+ file_url_,
+ 0,
+ kuint64max,
+ expected_modification_time));
+ net::TestCompletionCallback init_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
+ EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, init_callback.WaitForResult());
+}
+
+TEST_F(UploadFileSystemFileElementReaderTest, WrongURL) {
+ const GURL wrong_url = GetFileSystemURL("wrong_file_name.dat");
+ reader_.reset(new UploadFileSystemFileElementReader(
+ file_system_context_.get(), wrong_url, 0, kuint64max, base::Time()));
+ net::TestCompletionCallback init_callback;
+ ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, init_callback.WaitForResult());
+}
+
+} // namespace fileapi