| // 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/stl_util.h" |
| #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" |
| #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" |
| #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" |
| #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" |
| #include "chrome/browser/sync_file_system/syncable_file_system_util.h" |
| #include "content/public/test/sandbox_file_system_test_helper.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.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/isolated_context.h" |
| #include "webkit/browser/quota/quota_manager.h" |
| #include "webkit/common/fileapi/file_system_types.h" |
| #include "webkit/common/quota/quota_types.h" |
| |
| using base::PlatformFileError; |
| using fileapi::FileSystemContext; |
| using fileapi::FileSystemOperationContext; |
| using fileapi::FileSystemURL; |
| using fileapi::FileSystemURLSet; |
| using fileapi::SandboxFileSystemTestHelper; |
| using quota::QuotaManager; |
| using quota::QuotaStatusCode; |
| |
| namespace sync_file_system { |
| |
| class SyncableFileSystemTest : public testing::Test { |
| public: |
| SyncableFileSystemTest() |
| : file_system_(GURL("http://example.com/"), |
| base::MessageLoopProxy::current().get(), |
| base::MessageLoopProxy::current().get()), |
| weak_factory_(this) {} |
| |
| virtual void SetUp() { |
| ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); |
| |
| file_system_.SetUp(); |
| |
| sync_context_ = |
| new LocalFileSyncContext(data_dir_.path(), |
| base::MessageLoopProxy::current().get(), |
| base::MessageLoopProxy::current().get()); |
| ASSERT_EQ( |
| sync_file_system::SYNC_STATUS_OK, |
| file_system_.MaybeInitializeFileSystemContext(sync_context_.get())); |
| } |
| |
| virtual void TearDown() { |
| if (sync_context_.get()) |
| sync_context_->ShutdownOnUIThread(); |
| sync_context_ = NULL; |
| |
| file_system_.TearDown(); |
| |
| // Make sure we don't leave the external filesystem. |
| // (CannedSyncableFileSystem::TearDown does not do this as there may be |
| // multiple syncable file systems registered for the name) |
| RevokeSyncableFileSystem(); |
| } |
| |
| protected: |
| void VerifyAndClearChange(const FileSystemURL& url, |
| const FileChange& expected_change) { |
| SCOPED_TRACE(testing::Message() << url.DebugString() << |
| " expecting:" << expected_change.DebugString()); |
| // Get the changes for URL and verify. |
| FileChangeList changes; |
| change_tracker()->GetChangesForURL(url, &changes); |
| ASSERT_EQ(1U, changes.size()); |
| SCOPED_TRACE(testing::Message() << url.DebugString() << |
| " actual:" << changes.DebugString()); |
| EXPECT_EQ(expected_change, changes.front()); |
| |
| // Clear the URL from the change tracker. |
| change_tracker()->ClearChangesForURL(url); |
| } |
| |
| FileSystemURL URL(const std::string& path) { |
| return file_system_.URL(path); |
| } |
| |
| FileSystemContext* file_system_context() { |
| return file_system_.file_system_context(); |
| } |
| |
| LocalFileChangeTracker* change_tracker() { |
| return file_system_.backend()->change_tracker(); |
| } |
| |
| ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; |
| |
| base::ScopedTempDir data_dir_; |
| content::TestBrowserThreadBundle thread_bundle_; |
| CannedSyncableFileSystem file_system_; |
| |
| private: |
| scoped_refptr<LocalFileSyncContext> sync_context_; |
| |
| base::WeakPtrFactory<SyncableFileSystemTest> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SyncableFileSystemTest); |
| }; |
| |
| // Brief combined testing. Just see if all the sandbox feature works. |
| TEST_F(SyncableFileSystemTest, SyncableLocalSandboxCombined) { |
| // Opens a syncable file system. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.OpenFileSystem()); |
| |
| // Do some operations. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateDirectory(URL("dir"))); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateFile(URL("dir/foo"))); |
| |
| const int64 kOriginalQuota = QuotaManager::kSyncableStorageDefaultHostQuota; |
| |
| const int64 kQuota = 12345 * 1024; |
| QuotaManager::kSyncableStorageDefaultHostQuota = kQuota; |
| int64 usage, quota; |
| EXPECT_EQ(quota::kQuotaStatusOk, |
| file_system_.GetUsageAndQuota(&usage, "a)); |
| |
| // Returned quota must be what we overrode. Usage must be greater than 0 |
| // as creating a file or directory consumes some space. |
| EXPECT_EQ(kQuota, quota); |
| EXPECT_GT(usage, 0); |
| |
| // Truncate to extend an existing file and see if the usage reflects it. |
| const int64 kFileSizeToExtend = 333; |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateFile(URL("dir/foo"))); |
| |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.TruncateFile(URL("dir/foo"), kFileSizeToExtend)); |
| |
| int64 new_usage; |
| EXPECT_EQ(quota::kQuotaStatusOk, |
| file_system_.GetUsageAndQuota(&new_usage, "a)); |
| EXPECT_EQ(kFileSizeToExtend, new_usage - usage); |
| |
| // Shrink the quota to the current usage, try to extend the file further |
| // and see if it fails. |
| QuotaManager::kSyncableStorageDefaultHostQuota = new_usage; |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, |
| file_system_.TruncateFile(URL("dir/foo"), kFileSizeToExtend + 1)); |
| |
| usage = new_usage; |
| EXPECT_EQ(quota::kQuotaStatusOk, |
| file_system_.GetUsageAndQuota(&new_usage, "a)); |
| EXPECT_EQ(usage, new_usage); |
| |
| // Deletes the file system. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.DeleteFileSystem()); |
| |
| // Now the usage must be zero. |
| EXPECT_EQ(quota::kQuotaStatusOk, |
| file_system_.GetUsageAndQuota(&usage, "a)); |
| EXPECT_EQ(0, usage); |
| |
| // Restore the system default quota. |
| QuotaManager::kSyncableStorageDefaultHostQuota = kOriginalQuota; |
| } |
| |
| // Combined testing with LocalFileChangeTracker. |
| TEST_F(SyncableFileSystemTest, ChangeTrackerSimple) { |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.OpenFileSystem()); |
| |
| const char kPath0[] = "dir a"; |
| const char kPath1[] = "dir a/dir"; // child of kPath0 |
| const char kPath2[] = "dir a/file"; // child of kPath0 |
| const char kPath3[] = "dir b"; |
| |
| // Do some operations. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateDirectory(URL(kPath0))); // Creates a dir. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateDirectory(URL(kPath1))); // Creates another. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateFile(URL(kPath2))); // Creates a file. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.TruncateFile(URL(kPath2), 1)); // Modifies the file. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.TruncateFile(URL(kPath2), 2)); // Modifies it again. |
| |
| FileSystemURLSet urls; |
| file_system_.GetChangedURLsInTracker(&urls); |
| |
| EXPECT_EQ(3U, urls.size()); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath0))); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath1))); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath2))); |
| |
| VerifyAndClearChange(URL(kPath0), |
| FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, |
| sync_file_system::SYNC_FILE_TYPE_DIRECTORY)); |
| VerifyAndClearChange(URL(kPath1), |
| FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, |
| sync_file_system::SYNC_FILE_TYPE_DIRECTORY)); |
| VerifyAndClearChange(URL(kPath2), |
| FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, |
| sync_file_system::SYNC_FILE_TYPE_FILE)); |
| |
| // Creates and removes a same directory. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.CreateDirectory(URL(kPath3))); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.Remove(URL(kPath3), false /* recursive */)); |
| |
| // The changes will be offset. |
| urls.clear(); |
| file_system_.GetChangedURLsInTracker(&urls); |
| EXPECT_TRUE(urls.empty()); |
| |
| // Recursively removes the kPath0 directory. |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.Remove(URL(kPath0), true /* recursive */)); |
| |
| urls.clear(); |
| file_system_.GetChangedURLsInTracker(&urls); |
| |
| // kPath0 and its all chidren (kPath1 and kPath2) must have been deleted. |
| EXPECT_EQ(3U, urls.size()); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath0))); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath1))); |
| EXPECT_TRUE(ContainsKey(urls, URL(kPath2))); |
| |
| VerifyAndClearChange(URL(kPath0), |
| FileChange(FileChange::FILE_CHANGE_DELETE, |
| sync_file_system::SYNC_FILE_TYPE_DIRECTORY)); |
| VerifyAndClearChange(URL(kPath1), |
| FileChange(FileChange::FILE_CHANGE_DELETE, |
| sync_file_system::SYNC_FILE_TYPE_DIRECTORY)); |
| VerifyAndClearChange(URL(kPath2), |
| FileChange(FileChange::FILE_CHANGE_DELETE, |
| sync_file_system::SYNC_FILE_TYPE_FILE)); |
| } |
| |
| // Make sure directory operation is disabled (when it's configured so). |
| TEST_F(SyncableFileSystemTest, DisableDirectoryOperations) { |
| bool was_enabled = IsSyncFSDirectoryOperationEnabled(); |
| SetEnableSyncFSDirectoryOperation(false); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| file_system_.OpenFileSystem()); |
| |
| // Try some directory operations (which should fail). |
| EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, |
| file_system_.CreateDirectory(URL("dir"))); |
| |
| // Set up another (non-syncable) local file system. |
| SandboxFileSystemTestHelper other_file_system_( |
| GURL("http://foo.com/"), fileapi::kFileSystemTypeTemporary); |
| other_file_system_.SetUp(file_system_.file_system_context()); |
| |
| // Create directory '/a' and file '/a/b' in the other file system. |
| const FileSystemURL kSrcDir = other_file_system_.CreateURLFromUTF8("/a"); |
| const FileSystemURL kSrcChild = other_file_system_.CreateURLFromUTF8("/a/b"); |
| |
| bool created = false; |
| scoped_ptr<FileSystemOperationContext> operation_context; |
| |
| operation_context.reset(other_file_system_.NewOperationContext()); |
| operation_context->set_allowed_bytes_growth(1024); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| other_file_system_.file_util()->CreateDirectory( |
| operation_context.get(), |
| kSrcDir, false /* exclusive */, false /* recursive */)); |
| |
| operation_context.reset(other_file_system_.NewOperationContext()); |
| operation_context->set_allowed_bytes_growth(1024); |
| EXPECT_EQ(base::PLATFORM_FILE_OK, |
| other_file_system_.file_util()->EnsureFileExists( |
| operation_context.get(), kSrcChild, &created)); |
| EXPECT_TRUE(created); |
| |
| // Now try copying the directory into the syncable file system, which should |
| // fail if directory operation is disabled. (http://crbug.com/161442) |
| EXPECT_NE(base::PLATFORM_FILE_OK, |
| file_system_.Copy(kSrcDir, URL("dest"))); |
| |
| other_file_system_.TearDown(); |
| SetEnableSyncFSDirectoryOperation(was_enabled); |
| } |
| |
| } // namespace sync_file_system |