// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This test checks the entire behavior of FileSystem usage and quota, such as: // 1) the actual size of files on disk, // 2) the described size in .usage, and // 3) the result of QuotaManager::GetUsageAndQuota. #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/platform_file.h" #include "base/scoped_temp_dir.h" #include "base/string_number_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/fileapi/file_system_operation.h" #include "webkit/fileapi/file_system_test_helper.h" #include "webkit/fileapi/file_system_usage_cache.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/quota/quota_manager.h" namespace fileapi { const int kFileOperationStatusNotSet = 1; namespace { void AssertFileErrorEq(base::PlatformFileError expected, base::PlatformFileError actual) { ASSERT_EQ(expected, actual); } } // namespace class FileSystemQuotaTest : public testing::Test, public base::SupportsWeakPtr { public: FileSystemQuotaTest() : weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), next_unique_path_suffix_(0), status_(kFileOperationStatusNotSet), quota_status_(quota::kQuotaStatusUnknown), usage_(-1), quota_(-1) {} FileSystemOperation* operation(); int status() const { return status_; } quota::QuotaStatusCode quota_status() const { return quota_status_; } int64 usage() { return usage_; } int64 quota() { return quota_; } virtual void SetUp() OVERRIDE; virtual void TearDown() OVERRIDE; void OnGetUsageAndQuota( quota::QuotaStatusCode status, int64 usage, int64 quota); protected: FileSystemFileUtil* file_util() { return test_helper_.file_util(); } FileSystemOperationContext* NewContext() { FileSystemOperationContext* context = test_helper_.NewOperationContext(); context->set_allowed_bytes_growth(10000000); return context; } void PrepareFileSet(const FilePath& virtual_path); GURL URLForPath(const FilePath& path) const { return test_helper_.GetURLForPath(path); } FilePath PlatformPath(const FilePath& virtual_path) { return test_helper_.GetLocalPath(virtual_path); } int64 ActualFileSize() { return test_helper_.ComputeCurrentOriginUsage() - test_helper_.ComputeCurrentDirectoryDatabaseUsage(); } int64 SizeInUsageFile() { return test_helper_.GetCachedOriginUsage(); } void GetUsageAndQuotaFromQuotaManager() { quota_manager_->GetUsageAndQuota( test_helper_.origin(), test_helper_.storage_type(), base::Bind(&FileSystemQuotaTest::OnGetUsageAndQuota, weak_factory_.GetWeakPtr())); MessageLoop::current()->RunAllPending(); } bool FileExists(const FilePath& virtual_path) { FileSystemPath path = test_helper_.CreatePath(virtual_path); scoped_ptr context(NewContext()); return file_util()->PathExists(context.get(), path); } bool DirectoryExists(const FilePath& virtual_path) { FileSystemPath path = test_helper_.CreatePath(virtual_path); scoped_ptr context(NewContext()); return file_util()->DirectoryExists(context.get(), path); } FilePath CreateUniqueFileInDir(const FilePath& virtual_dir_path) { FilePath file_name = FilePath::FromUTF8Unsafe( "tmpfile-" + base::IntToString(next_unique_path_suffix_++)); FileSystemPath path = test_helper_.CreatePath( virtual_dir_path.Append(file_name)); scoped_ptr context(NewContext()); bool created; EXPECT_EQ(base::PLATFORM_FILE_OK, file_util()->EnsureFileExists(context.get(), path, &created)); EXPECT_TRUE(created); return path.internal_path(); } FilePath CreateUniqueDirInDir(const FilePath& virtual_dir_path) { FilePath dir_name = FilePath::FromUTF8Unsafe( "tmpdir-" + base::IntToString(next_unique_path_suffix_++)); FileSystemPath path = test_helper_.CreatePath( virtual_dir_path.Append(dir_name)); scoped_ptr context(NewContext()); EXPECT_EQ(base::PLATFORM_FILE_OK, file_util()->CreateDirectory(context.get(), path, false, true)); return path.internal_path(); } FilePath CreateUniqueDir() { return CreateUniqueDirInDir(FilePath()); } FilePath child_dir_path_; FilePath child_file1_path_; FilePath child_file2_path_; FilePath grandchild_file1_path_; FilePath grandchild_file2_path_; int64 child_path_cost_; int64 grandchild_path_cost_; protected: // Callback for recording test results. FileSystemOperationInterface::StatusCallback RecordStatusCallback() { return base::Bind(&FileSystemQuotaTest::DidFinish, AsWeakPtr()); } void DidFinish(base::PlatformFileError status) { status_ = status; } FileSystemTestOriginHelper test_helper_; ScopedTempDir work_dir_; scoped_refptr quota_manager_; base::WeakPtrFactory weak_factory_; int next_unique_path_suffix_; // For post-operation status. int status_; quota::QuotaStatusCode quota_status_; int64 usage_; int64 quota_; DISALLOW_COPY_AND_ASSIGN(FileSystemQuotaTest); }; void FileSystemQuotaTest::SetUp() { ASSERT_TRUE(work_dir_.CreateUniqueTempDir()); FilePath filesystem_dir_path = work_dir_.path().AppendASCII("filesystem"); ASSERT_TRUE(file_util::CreateDirectory(filesystem_dir_path)); quota_manager_ = new quota::QuotaManager( false /* is_incognito */, filesystem_dir_path, base::MessageLoopProxy::current(), base::MessageLoopProxy::current(), NULL); test_helper_.SetUp(filesystem_dir_path, false /* unlimited quota */, quota_manager_->proxy(), NULL); } void FileSystemQuotaTest::TearDown() { quota_manager_ = NULL; test_helper_.TearDown(); } FileSystemOperation* FileSystemQuotaTest::operation() { return test_helper_.NewOperation(); } void FileSystemQuotaTest::OnGetUsageAndQuota( quota::QuotaStatusCode status, int64 usage, int64 quota) { quota_status_ = status; usage_ = usage; quota_ = quota; } void FileSystemQuotaTest::PrepareFileSet(const FilePath& virtual_path) { int64 usage = SizeInUsageFile(); child_dir_path_ = CreateUniqueDirInDir(virtual_path); child_file1_path_ = CreateUniqueFileInDir(virtual_path); child_file2_path_ = CreateUniqueFileInDir(virtual_path); child_path_cost_ = SizeInUsageFile() - usage; usage += child_path_cost_; grandchild_file1_path_ = CreateUniqueFileInDir(child_dir_path_); grandchild_file2_path_ = CreateUniqueFileInDir(child_dir_path_); grandchild_path_cost_ = SizeInUsageFile() - usage; } TEST_F(FileSystemQuotaTest, TestMoveSuccessSrcDirRecursive) { FilePath src_dir_path(CreateUniqueDir()); int src_path_cost = SizeInUsageFile(); PrepareFileSet(src_dir_path); FilePath dest_dir_path(CreateUniqueDir()); EXPECT_EQ(0, ActualFileSize()); int total_path_cost = SizeInUsageFile(); operation()->Truncate(URLForPath(child_file1_path_), 5000, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(child_file2_path_), 400, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(grandchild_file1_path_), 30, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(grandchild_file2_path_), 2, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); MessageLoop::current()->RunAllPending(); const int64 all_file_size = 5000 + 400 + 30 + 2; EXPECT_EQ(all_file_size, ActualFileSize()); EXPECT_EQ(all_file_size + total_path_cost, SizeInUsageFile()); GetUsageAndQuotaFromQuotaManager(); EXPECT_EQ(quota::kQuotaStatusOk, quota_status()); EXPECT_EQ(all_file_size + total_path_cost, usage()); ASSERT_LT(all_file_size + total_path_cost, quota()); operation()->Move(URLForPath(src_dir_path), URLForPath(dest_dir_path), RecordStatusCallback()); MessageLoop::current()->RunAllPending(); EXPECT_EQ(base::PLATFORM_FILE_OK, status()); EXPECT_TRUE(DirectoryExists(dest_dir_path.Append( VirtualPath::BaseName(child_dir_path_)))); EXPECT_TRUE(FileExists(dest_dir_path.Append( VirtualPath::BaseName(child_dir_path_)).Append( VirtualPath::BaseName(grandchild_file1_path_)))); EXPECT_EQ(all_file_size, ActualFileSize()); EXPECT_EQ(all_file_size + total_path_cost - src_path_cost, SizeInUsageFile()); GetUsageAndQuotaFromQuotaManager(); EXPECT_EQ(quota::kQuotaStatusOk, quota_status()); EXPECT_EQ(all_file_size + total_path_cost - src_path_cost, usage()); ASSERT_LT(all_file_size + total_path_cost - src_path_cost, quota()); } TEST_F(FileSystemQuotaTest, TestCopySuccessSrcDirRecursive) { FilePath src_dir_path(CreateUniqueDir()); PrepareFileSet(src_dir_path); FilePath dest_dir1_path(CreateUniqueDir()); FilePath dest_dir2_path(CreateUniqueDir()); EXPECT_EQ(0, ActualFileSize()); int total_path_cost = SizeInUsageFile(); operation()->Truncate(URLForPath(child_file1_path_), 8000, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(child_file2_path_), 700, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(grandchild_file1_path_), 60, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); operation()->Truncate(URLForPath(grandchild_file2_path_), 5, base::Bind(&AssertFileErrorEq, base::PLATFORM_FILE_OK)); MessageLoop::current()->RunAllPending(); 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; EXPECT_EQ(all_file_size, ActualFileSize()); EXPECT_EQ(expected_usage, SizeInUsageFile()); GetUsageAndQuotaFromQuotaManager(); EXPECT_EQ(quota::kQuotaStatusOk, quota_status()); EXPECT_EQ(expected_usage, usage()); ASSERT_LT(expected_usage, quota()); operation()->Copy(URLForPath(src_dir_path), URLForPath(dest_dir1_path), RecordStatusCallback()); MessageLoop::current()->RunAllPending(); expected_usage += all_file_size + child_path_cost_ + grandchild_path_cost_; EXPECT_EQ(base::PLATFORM_FILE_OK, status()); EXPECT_TRUE(DirectoryExists(src_dir_path.Append( VirtualPath::BaseName(child_dir_path_)))); EXPECT_TRUE(FileExists(src_dir_path.Append( VirtualPath::BaseName(child_dir_path_)).Append( VirtualPath::BaseName(grandchild_file1_path_)))); EXPECT_EQ(base::PLATFORM_FILE_OK, status()); EXPECT_TRUE(DirectoryExists(dest_dir1_path.Append( VirtualPath::BaseName(child_dir_path_)))); EXPECT_TRUE(FileExists(dest_dir1_path.Append( VirtualPath::BaseName(child_dir_path_)).Append( VirtualPath::BaseName(grandchild_file1_path_)))); EXPECT_EQ(2 * all_file_size, ActualFileSize()); EXPECT_EQ(expected_usage, SizeInUsageFile()); GetUsageAndQuotaFromQuotaManager(); EXPECT_EQ(quota::kQuotaStatusOk, quota_status()); EXPECT_EQ(expected_usage, usage()); ASSERT_LT(expected_usage, quota()); operation()->Copy(URLForPath(child_dir_path_), URLForPath(dest_dir2_path), RecordStatusCallback()); MessageLoop::current()->RunAllPending(); EXPECT_EQ(base::PLATFORM_FILE_OK, status()); expected_usage += grandchild_file_size + grandchild_path_cost_; EXPECT_EQ(2 * child_file_size + 3 * grandchild_file_size, ActualFileSize()); EXPECT_EQ(expected_usage, SizeInUsageFile()); GetUsageAndQuotaFromQuotaManager(); EXPECT_EQ(quota::kQuotaStatusOk, quota_status()); EXPECT_EQ(expected_usage, usage()); ASSERT_LT(expected_usage, quota()); } } // namespace fileapi