blob: b921319ddfcf0e9170f98e9235478438c363f007 [file] [log] [blame]
// 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, &quota));
// 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, &quota));
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, &quota));
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, &quota));
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