root/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_factory_
  2. SetUp
  3. TearDown
  4. VerifyAndClearChange
  5. file_system_context
  6. change_tracker
  7. TEST_F
  8. TEST_F
  9. TEST_F

// 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/async_file_test_helper.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 "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/env.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/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/quota/quota_types.h"

using base::PlatformFileError;
using content::SandboxFileSystemTestHelper;
using fileapi::FileSystemContext;
using fileapi::FileSystemOperationContext;
using fileapi::FileSystemURL;
using fileapi::FileSystemURLSet;
using quota::QuotaManager;
using quota::QuotaStatusCode;

namespace sync_file_system {

class SyncableFileSystemTest : public testing::Test {
 public:
  SyncableFileSystemTest()
      : in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())),
        file_system_(GURL("http://example.com/"),
                     in_memory_env_.get(),
                     base::MessageLoopProxy::current().get(),
                     base::MessageLoopProxy::current().get()),
        weak_factory_(this) {}

  virtual void SetUp() {
    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
    file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);

    sync_context_ =
        new LocalFileSyncContext(data_dir_.path(),
                                 in_memory_env_.get(),
                                 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_;
  scoped_ptr<leveldb::Env> in_memory_env_;
  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::File::FILE_OK,
            file_system_.OpenFileSystem());

  // Do some operations.
  EXPECT_EQ(base::File::FILE_OK,
            file_system_.CreateDirectory(URL("dir")));
  EXPECT_EQ(base::File::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::File::FILE_OK,
            file_system_.CreateFile(URL("dir/foo")));

  EXPECT_EQ(base::File::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::File::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::File::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::File::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::File::FILE_OK,
            file_system_.CreateDirectory(URL(kPath0)));  // Creates a dir.
  EXPECT_EQ(base::File::FILE_OK,
            file_system_.CreateDirectory(URL(kPath1)));  // Creates another.
  EXPECT_EQ(base::File::FILE_OK,
            file_system_.CreateFile(URL(kPath2)));       // Creates a file.
  EXPECT_EQ(base::File::FILE_OK,
            file_system_.TruncateFile(URL(kPath2), 1));  // Modifies the file.
  EXPECT_EQ(base::File::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::File::FILE_OK,
            file_system_.CreateDirectory(URL(kPath3)));
  EXPECT_EQ(base::File::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::File::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::File::FILE_OK,
            file_system_.OpenFileSystem());

  // Try some directory operations (which should fail).
  EXPECT_EQ(base::File::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");

  EXPECT_EQ(base::File::FILE_OK,
            content::AsyncFileTestHelper::CreateDirectory(
                other_file_system_.file_system_context(), kSrcDir));
  EXPECT_EQ(base::File::FILE_OK,
            content::AsyncFileTestHelper::CreateFile(
                other_file_system_.file_system_context(), kSrcChild));

  // 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::File::FILE_OK,
            file_system_.Copy(kSrcDir, URL("dest")));

  other_file_system_.TearDown();
  SetEnableSyncFSDirectoryOperation(was_enabled);
}

}  // namespace sync_file_system

/* [<][>][^][v][top][bottom][index][help] */