root/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ExpectEquivalent
  2. ExpectEquivalent
  3. ExpectEquivalent
  4. ExpectEquivalent
  5. ExpectEquivalent
  6. ExpectEquivalent
  7. ExpectEquivalent
  8. ExpectEquivalent
  9. ExpectEquivalent
  10. ExpectEquivalent
  11. ExpectEquivalentMaps
  12. ExpectEquivalentSets
  13. CreateNormalizedPath
  14. next_md5_sequence_number_
  15. SetUp
  16. TearDown
  17. GenerateFileID
  18. GetTrackerIDByFileID
  19. InitializeMetadataDatabase
  20. DropDatabase
  21. SetUpDatabaseByTrackedFiles
  22. VerifyTrackedFile
  23. VerifyTrackedFiles
  24. metadata_database
  25. InitializeLevelDB
  26. SetUpServiceMetadata
  27. CreateSyncRootMetadata
  28. CreateFileMetadata
  29. CreateFolderMetadata
  30. CreateSyncRootTracker
  31. CreateTracker
  32. CreateTrackedSyncRoot
  33. CreateTrackedAppRoot
  34. CreateTrackedFile
  35. CreateTrackedFolder
  36. CreateFileResourceFromMetadata
  37. CreateChangeResourceFromMetadata
  38. ApplyRenameChangeToMetadata
  39. ApplyReorganizeChangeToMetadata
  40. ApplyContentChangeToMetadata
  41. ApplyNoopChangeToMetadata
  42. PushToChangeList
  43. PutFileToDB
  44. PutTrackerToDB
  45. VerifyReloadConsistency
  46. VerifyFile
  47. VerifyTracker
  48. RegisterApp
  49. DisableApp
  50. EnableApp
  51. UnregisterApp
  52. UpdateByChangeList
  53. PopulateFolder
  54. UpdateTracker
  55. PopulateInitialData
  56. ResetTrackerID
  57. current_change_id
  58. TEST_F
  59. TEST_F
  60. TEST_F
  61. TEST_F
  62. TEST_F
  63. TEST_F
  64. TEST_F
  65. TEST_F
  66. TEST_F
  67. TEST_F
  68. TEST_F
  69. 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 "chrome/browser/sync_file_system/drive_backend/metadata_database.h"

#include "base/bind.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/strings/string_number_conversions.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
#include "google_apis/drive/drive_api_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"

#define FPL(a) FILE_PATH_LITERAL(a)

namespace sync_file_system {
namespace drive_backend {

namespace {

typedef MetadataDatabase::FileIDList FileIDList;

const int64 kInitialChangeID = 1234;
const int64 kSyncRootTrackerID = 100;
const char kSyncRootFolderID[] = "sync_root_folder_id";

// This struct is used to setup initial state of the database in the test and
// also used to match to the modified content of the database as the
// expectation.
struct TrackedFile {
  // Holds the latest remote metadata which may be not-yet-synced to |tracker|.
  FileMetadata metadata;
  FileTracker tracker;

  // Implies the file should not in the database.
  bool should_be_absent;

  // Implies the file should have a tracker in the database but should have no
  // metadata.
  bool tracker_only;

  TrackedFile() : should_be_absent(false), tracker_only(false) {}
};

void ExpectEquivalent(const ServiceMetadata* left,
                      const ServiceMetadata* right) {
  test_util::ExpectEquivalentServiceMetadata(*left, *right);
}

void ExpectEquivalent(const FileMetadata* left, const FileMetadata* right) {
  if (!left) {
    ASSERT_FALSE(right);
    return;
  }
  ASSERT_TRUE(right);
  test_util::ExpectEquivalentMetadata(*left, *right);
}

void ExpectEquivalent(const FileTracker* left, const FileTracker* right) {
  if (!left) {
    ASSERT_FALSE(right);
    return;
  }
  ASSERT_TRUE(right);
  test_util::ExpectEquivalentTrackers(*left, *right);
}

void ExpectEquivalent(int64 left, int64 right) {
  EXPECT_EQ(left, right);
}

template <typename Container>
void ExpectEquivalentMaps(const Container& left, const Container& right);

template <typename Key, typename Value>
void ExpectEquivalent(const std::map<Key, Value>& left,
                      const std::map<Key, Value>& right) {
  ExpectEquivalentMaps(left, right);
}

template <typename Key, typename Value>
void ExpectEquivalent(const base::hash_map<Key, Value>& left,
                      const base::hash_map<Key, Value>& right) {
  ExpectEquivalentMaps(std::map<Key, Value>(left.begin(), left.end()),
                       std::map<Key, Value>(right.begin(), right.end()));
}

template <typename Key, typename Value>
void ExpectEquivalent(const base::ScopedPtrHashMap<Key, Value>& left,
                      const base::ScopedPtrHashMap<Key, Value>& right) {
  ExpectEquivalentMaps(std::map<Key, Value*>(left.begin(), left.end()),
                       std::map<Key, Value*>(right.begin(), right.end()));
}

template <typename Container>
void ExpectEquivalentSets(const Container& left, const Container& right);

template <typename Value, typename Comparator>
void ExpectEquivalent(const std::set<Value, Comparator>& left,
                      const std::set<Value, Comparator>& right) {
  return ExpectEquivalentSets(left, right);
}

template <typename Value>
void ExpectEquivalent(const base::hash_set<Value>& left,
                      const base::hash_set<Value>& right) {
  return ExpectEquivalentSets(std::set<Value>(left.begin(), left.end()),
                              std::set<Value>(right.begin(), right.end()));
}

void ExpectEquivalent(const TrackerIDSet& left,
                      const TrackerIDSet& right) {
  {
    SCOPED_TRACE("Expect equivalent active_tracker");
    EXPECT_EQ(left.active_tracker(), right.active_tracker());
  }
  ExpectEquivalent(left.tracker_set(), right.tracker_set());
}

template <typename Container>
void ExpectEquivalentMaps(const Container& left, const Container& right) {
  ASSERT_EQ(left.size(), right.size());

  typedef typename Container::const_iterator const_iterator;
  const_iterator left_itr = left.begin();
  const_iterator right_itr = right.begin();
  while (left_itr != left.end()) {
    EXPECT_EQ(left_itr->first, right_itr->first);
    ExpectEquivalent(left_itr->second, right_itr->second);
    ++left_itr;
    ++right_itr;
  }
}

template <typename Container>
void ExpectEquivalentSets(const Container& left, const Container& right) {
  ASSERT_EQ(left.size(), right.size());

  typedef typename Container::const_iterator const_iterator;
  const_iterator left_itr = left.begin();
  const_iterator right_itr = right.begin();
  while (left_itr != left.end()) {
    ExpectEquivalent(*left_itr, *right_itr);
    ++left_itr;
    ++right_itr;
  }
}

base::FilePath CreateNormalizedPath(const base::FilePath::StringType& path) {
  return base::FilePath(path).NormalizePathSeparators();
}

}  // namespace

class MetadataDatabaseTest : public testing::Test {
 public:
  MetadataDatabaseTest()
      : current_change_id_(kInitialChangeID),
        next_tracker_id_(kSyncRootTrackerID + 1),
        next_file_id_number_(1),
        next_md5_sequence_number_(1) {}

  virtual ~MetadataDatabaseTest() {}

  virtual void SetUp() OVERRIDE {
    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
  }

  virtual void TearDown() OVERRIDE { DropDatabase(); }

 protected:
  std::string GenerateFileID() {
    return "file_id_" + base::Int64ToString(next_file_id_number_++);
  }

  int64 GetTrackerIDByFileID(const std::string& file_id) {
    TrackerIDSet trackers;
    if (metadata_database_->FindTrackersByFileID(file_id, &trackers)) {
      EXPECT_FALSE(trackers.empty());
      return *trackers.begin();
    }
    return 0;
  }

  SyncStatusCode InitializeMetadataDatabase() {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    MetadataDatabase::Create(base::MessageLoopProxy::current(),
                             database_dir_.path(),
                             in_memory_env_.get(),
                             CreateResultReceiver(&status,
                                                  &metadata_database_));
    message_loop_.RunUntilIdle();
    return status;
  }

  void DropDatabase() {
    metadata_database_.reset();
    message_loop_.RunUntilIdle();
  }

  void SetUpDatabaseByTrackedFiles(const TrackedFile** tracked_files,
                                   int size) {
    scoped_ptr<leveldb::DB> db = InitializeLevelDB();
    ASSERT_TRUE(db);

    for (int i = 0; i < size; ++i) {
      const TrackedFile* file = tracked_files[i];
      if (file->should_be_absent)
        continue;
      if (!file->tracker_only)
        EXPECT_TRUE(PutFileToDB(db.get(), file->metadata).ok());
      EXPECT_TRUE(PutTrackerToDB(db.get(), file->tracker).ok());
    }
  }

  void VerifyTrackedFile(const TrackedFile& file) {
    if (!file.should_be_absent) {
      if (file.tracker_only) {
        EXPECT_FALSE(metadata_database()->FindFileByFileID(
            file.metadata.file_id(), NULL));
      } else {
        VerifyFile(file.metadata);
      }
      VerifyTracker(file.tracker);
      return;
    }

    EXPECT_FALSE(metadata_database()->FindFileByFileID(
        file.metadata.file_id(), NULL));
    EXPECT_FALSE(metadata_database()->FindTrackerByTrackerID(
        file.tracker.tracker_id(), NULL));
  }

  void VerifyTrackedFiles(const TrackedFile** tracked_files, int size) {
    for (int i = 0; i < size; ++i)
      VerifyTrackedFile(*tracked_files[i]);
  }

  MetadataDatabase* metadata_database() { return metadata_database_.get(); }

  scoped_ptr<leveldb::DB> InitializeLevelDB() {
    leveldb::DB* db = NULL;
    leveldb::Options options;
    options.create_if_missing = true;
    options.max_open_files = 0;  // Use minimum.
    options.env = in_memory_env_.get();
    leveldb::Status status =
        leveldb::DB::Open(options, database_dir_.path().AsUTF8Unsafe(), &db);
    EXPECT_TRUE(status.ok());

    db->Put(leveldb::WriteOptions(),
            kDatabaseVersionKey,
            base::Int64ToString(3));
    SetUpServiceMetadata(db);

    return make_scoped_ptr(db);
  }

  void SetUpServiceMetadata(leveldb::DB* db) {
    ServiceMetadata service_metadata;
    service_metadata.set_largest_change_id(kInitialChangeID);
    service_metadata.set_sync_root_tracker_id(kSyncRootTrackerID);
    service_metadata.set_next_tracker_id(next_tracker_id_);
    leveldb::WriteBatch batch;
    PutServiceMetadataToBatch(service_metadata, &batch);
    EXPECT_TRUE(db->Write(leveldb::WriteOptions(), &batch).ok());
  }

  FileMetadata CreateSyncRootMetadata() {
    FileMetadata sync_root;
    sync_root.set_file_id(kSyncRootFolderID);
    FileDetails* details = sync_root.mutable_details();
    details->set_title(kSyncRootFolderTitle);
    details->set_file_kind(FILE_KIND_FOLDER);
    details->set_change_id(current_change_id_);
    return sync_root;
  }

  FileMetadata CreateFileMetadata(const FileMetadata& parent,
                                  const std::string& title) {
    FileMetadata file;
    file.set_file_id(GenerateFileID());
    FileDetails* details = file.mutable_details();
    details->add_parent_folder_ids(parent.file_id());
    details->set_title(title);
    details->set_file_kind(FILE_KIND_FILE);
    details->set_md5(
        "md5_value_" + base::Int64ToString(next_md5_sequence_number_++));
    details->set_change_id(current_change_id_);
    return file;
  }

  FileMetadata CreateFolderMetadata(const FileMetadata& parent,
                                    const std::string& title) {
    FileMetadata folder;
    folder.set_file_id(GenerateFileID());
    FileDetails* details = folder.mutable_details();
    details->add_parent_folder_ids(parent.file_id());
    details->set_title(title);
    details->set_file_kind(FILE_KIND_FOLDER);
    details->set_change_id(current_change_id_);
    return folder;
  }

  FileTracker CreateSyncRootTracker(const FileMetadata& sync_root) {
    FileTracker sync_root_tracker;
    sync_root_tracker.set_tracker_id(kSyncRootTrackerID);
    sync_root_tracker.set_parent_tracker_id(0);
    sync_root_tracker.set_file_id(sync_root.file_id());
    sync_root_tracker.set_dirty(false);
    sync_root_tracker.set_active(true);
    sync_root_tracker.set_needs_folder_listing(false);
    *sync_root_tracker.mutable_synced_details() = sync_root.details();
    return sync_root_tracker;
  }

  FileTracker CreateTracker(const FileTracker& parent_tracker,
                            const FileMetadata& file) {
    FileTracker tracker;
    tracker.set_tracker_id(next_tracker_id_++);
    tracker.set_parent_tracker_id(parent_tracker.tracker_id());
    tracker.set_file_id(file.file_id());
    tracker.set_app_id(parent_tracker.app_id());
    tracker.set_tracker_kind(TRACKER_KIND_REGULAR);
    tracker.set_dirty(false);
    tracker.set_active(true);
    tracker.set_needs_folder_listing(false);
    *tracker.mutable_synced_details() = file.details();
    return tracker;
  }

  TrackedFile CreateTrackedSyncRoot() {
    TrackedFile sync_root;
    sync_root.metadata = CreateSyncRootMetadata();
    sync_root.tracker = CreateSyncRootTracker(sync_root.metadata);
    return sync_root;
  }

  TrackedFile CreateTrackedAppRoot(const TrackedFile& sync_root,
                                   const std::string& app_id) {
    TrackedFile app_root(CreateTrackedFolder(sync_root, app_id));
    app_root.tracker.set_app_id(app_id);
    app_root.tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);
    return app_root;
  }

  TrackedFile CreateTrackedFile(const TrackedFile& parent,
                                const std::string& title) {
    TrackedFile file;
    file.metadata = CreateFileMetadata(parent.metadata, title);
    file.tracker = CreateTracker(parent.tracker, file.metadata);
    return file;
  }

  TrackedFile CreateTrackedFolder(const TrackedFile& parent,
                                  const std::string& title) {
    TrackedFile folder;
    folder.metadata = CreateFolderMetadata(parent.metadata, title);
    folder.tracker = CreateTracker(parent.tracker, folder.metadata);
    return folder;
  }

  scoped_ptr<google_apis::FileResource> CreateFileResourceFromMetadata(
      const FileMetadata& file) {
    scoped_ptr<google_apis::FileResource> file_resource(
        new google_apis::FileResource);
    for (int i = 0; i < file.details().parent_folder_ids_size(); ++i) {
      google_apis::ParentReference parent;
      parent.set_file_id(file.details().parent_folder_ids(i));
      file_resource->mutable_parents()->push_back(parent);
    }

    file_resource->set_file_id(file.file_id());
    file_resource->set_title(file.details().title());
    if (file.details().file_kind() == FILE_KIND_FOLDER)
      file_resource->set_mime_type("application/vnd.google-apps.folder");
    else if (file.details().file_kind() == FILE_KIND_FILE)
      file_resource->set_mime_type("text/plain");
    else
      file_resource->set_mime_type("application/vnd.google-apps.document");
    file_resource->set_md5_checksum(file.details().md5());
    file_resource->set_etag(file.details().etag());
    file_resource->set_created_date(base::Time::FromInternalValue(
        file.details().creation_time()));
    file_resource->set_modified_date(base::Time::FromInternalValue(
        file.details().modification_time()));

    return file_resource.Pass();
  }

  scoped_ptr<google_apis::ChangeResource> CreateChangeResourceFromMetadata(
      const FileMetadata& file) {
    scoped_ptr<google_apis::ChangeResource> change(
        new google_apis::ChangeResource);
    change->set_change_id(file.details().change_id());
    change->set_file_id(file.file_id());
    change->set_deleted(file.details().missing());
    if (change->is_deleted())
      return change.Pass();

    change->set_file(CreateFileResourceFromMetadata(file));
    return change.Pass();
  }

  void ApplyRenameChangeToMetadata(const std::string& new_title,
                                   FileMetadata* file) {
    FileDetails* details = file->mutable_details();
    details->set_title(new_title);
    details->set_change_id(++current_change_id_);
  }

  void ApplyReorganizeChangeToMetadata(const std::string& new_parent,
                                       FileMetadata* file) {
    FileDetails* details = file->mutable_details();
    details->clear_parent_folder_ids();
    details->add_parent_folder_ids(new_parent);
    details->set_change_id(++current_change_id_);
  }

  void ApplyContentChangeToMetadata(FileMetadata* file) {
    FileDetails* details = file->mutable_details();
    details->set_md5(
        "md5_value_" + base::Int64ToString(next_md5_sequence_number_++));
    details->set_change_id(++current_change_id_);
  }

  void ApplyNoopChangeToMetadata(FileMetadata* file) {
    file->mutable_details()->set_change_id(++current_change_id_);
  }

  void PushToChangeList(scoped_ptr<google_apis::ChangeResource> change,
                        ScopedVector<google_apis::ChangeResource>* changes) {
    changes->push_back(change.release());
  }

  leveldb::Status PutFileToDB(leveldb::DB* db, const FileMetadata& file) {
    leveldb::WriteBatch batch;
    PutFileMetadataToBatch(file, &batch);
    return db->Write(leveldb::WriteOptions(), &batch);
  }

  leveldb::Status PutTrackerToDB(leveldb::DB* db,
                                 const FileTracker& tracker) {
    leveldb::WriteBatch batch;
    PutFileTrackerToBatch(tracker, &batch);
    return db->Write(leveldb::WriteOptions(), &batch);
  }

  void VerifyReloadConsistency() {
    scoped_ptr<MetadataDatabase> metadata_database_2;
    ASSERT_EQ(SYNC_STATUS_OK,
              MetadataDatabase::CreateForTesting(
                  metadata_database_->db_.Pass(),
                  &metadata_database_2));
    metadata_database_->db_ = metadata_database_2->db_.Pass();

    const MetadataDatabaseIndex* on_memory =
        metadata_database_->index_.get();
    const MetadataDatabaseIndex* reloaded =
        metadata_database_2->index_.get();

    {
      SCOPED_TRACE("Expect equivalent service_metadata");
      ExpectEquivalent(metadata_database_->service_metadata_.get(),
                       metadata_database_2->service_metadata_.get());
    }

    {
      SCOPED_TRACE("Expect equivalent metadata_by_id_ contents.");
      ExpectEquivalent(on_memory->metadata_by_id_,
                       reloaded->metadata_by_id_);
    }

    {
      SCOPED_TRACE("Expect equivalent tracker_by_id_ contents.");
      ExpectEquivalent(on_memory->tracker_by_id_,
                       reloaded->tracker_by_id_);
    }

    {
      SCOPED_TRACE("Expect equivalent trackers_by_file_id_ contents.");
      ExpectEquivalent(on_memory->trackers_by_file_id_,
                       reloaded->trackers_by_file_id_);
    }

    {
      SCOPED_TRACE("Expect equivalent app_root_by_app_id_ contents.");
      ExpectEquivalent(on_memory->app_root_by_app_id_,
                       reloaded->app_root_by_app_id_);
    }

    {
      SCOPED_TRACE("Expect equivalent trackers_by_parent_and_title_ contents.");
      ExpectEquivalent(on_memory->trackers_by_parent_and_title_,
                       reloaded->trackers_by_parent_and_title_);
    }

    {
      SCOPED_TRACE("Expect equivalent dirty_trackers_ contents.");
      ExpectEquivalent(on_memory->dirty_trackers_,
                       reloaded->dirty_trackers_);
    }
  }

  void VerifyFile(const FileMetadata& file) {
    FileMetadata file_in_metadata_database;
    ASSERT_TRUE(metadata_database()->FindFileByFileID(
        file.file_id(), &file_in_metadata_database));

    SCOPED_TRACE("Expect equivalent " + file.file_id());
    ExpectEquivalent(&file, &file_in_metadata_database);
  }

  void VerifyTracker(const FileTracker& tracker) {
    FileTracker tracker_in_metadata_database;
    ASSERT_TRUE(metadata_database()->FindTrackerByTrackerID(
        tracker.tracker_id(), &tracker_in_metadata_database));

    SCOPED_TRACE("Expect equivalent tracker[" +
                 base::Int64ToString(tracker.tracker_id()) + "]");
    ExpectEquivalent(&tracker, &tracker_in_metadata_database);
  }

  SyncStatusCode RegisterApp(const std::string& app_id,
                             const std::string& folder_id) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->RegisterApp(
        app_id, folder_id,
        CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode DisableApp(const std::string& app_id) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->DisableApp(
        app_id, CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode EnableApp(const std::string& app_id) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->EnableApp(
        app_id, CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode UnregisterApp(const std::string& app_id) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->UnregisterApp(
        app_id, CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode UpdateByChangeList(
      ScopedVector<google_apis::ChangeResource> changes) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->UpdateByChangeList(
        current_change_id_,
        changes.Pass(), CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode PopulateFolder(const std::string& folder_id,
                                const FileIDList& listed_children) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->PopulateFolderByChildList(
        folder_id, listed_children,
        CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode UpdateTracker(const FileTracker& tracker) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->UpdateTracker(
        tracker.tracker_id(), tracker.synced_details(),
        CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  SyncStatusCode PopulateInitialData(
      int64 largest_change_id,
      const google_apis::FileResource& sync_root_folder,
      const ScopedVector<google_apis::FileResource>& app_root_folders) {
    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    metadata_database_->PopulateInitialData(
        largest_change_id,
        sync_root_folder,
        app_root_folders,
        CreateResultReceiver(&status));
    message_loop_.RunUntilIdle();
    return status;
  }

  void ResetTrackerID(FileTracker* tracker) {
    tracker->set_tracker_id(GetTrackerIDByFileID(tracker->file_id()));
  }

  int64 current_change_id() const {
    return current_change_id_;
  }

 private:
  base::ScopedTempDir database_dir_;
  base::MessageLoop message_loop_;

  scoped_ptr<leveldb::Env> in_memory_env_;
  scoped_ptr<MetadataDatabase> metadata_database_;

  int64 current_change_id_;
  int64 next_tracker_id_;
  int64 next_file_id_number_;
  int64 next_md5_sequence_number_;

  DISALLOW_COPY_AND_ASSIGN(MetadataDatabaseTest);
};

TEST_F(MetadataDatabaseTest, InitializationTest_Empty) {
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  DropDatabase();
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
}

TEST_F(MetadataDatabaseTest, InitializationTest_SimpleTree) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedFolder(sync_root, "app_id"));
  app_root.tracker.set_app_id(app_root.metadata.details().title());
  app_root.tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);

  TrackedFile file(CreateTrackedFile(app_root, "file"));
  TrackedFile folder(CreateTrackedFolder(app_root, "folder"));
  TrackedFile file_in_folder(CreateTrackedFile(folder, "file_in_folder"));
  TrackedFile orphaned_file(CreateTrackedFile(sync_root, "orphaned_file"));
  orphaned_file.metadata.mutable_details()->clear_parent_folder_ids();
  orphaned_file.tracker.set_parent_tracker_id(0);

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &file, &folder, &file_in_folder, &orphaned_file
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());

  orphaned_file.should_be_absent = true;
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
}

TEST_F(MetadataDatabaseTest, AppManagementTest) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedFolder(sync_root, "app_id"));
  app_root.tracker.set_app_id(app_root.metadata.details().title());
  app_root.tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);

  TrackedFile file(CreateTrackedFile(app_root, "file"));
  TrackedFile folder(CreateTrackedFolder(sync_root, "folder"));
  folder.tracker.set_active(false);

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &file, &folder,
  };
  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));

  folder.tracker.set_app_id("foo");
  EXPECT_EQ(SYNC_STATUS_OK, RegisterApp(
      folder.tracker.app_id(), folder.metadata.file_id()));
  folder.tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);
  folder.tracker.set_active(true);
  folder.tracker.set_dirty(true);
  folder.tracker.set_needs_folder_listing(true);
  VerifyTrackedFile(folder);
  VerifyReloadConsistency();

  EXPECT_EQ(SYNC_STATUS_OK, DisableApp(folder.tracker.app_id()));
  folder.tracker.set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
  VerifyTrackedFile(folder);
  VerifyReloadConsistency();

  EXPECT_EQ(SYNC_STATUS_OK, EnableApp(folder.tracker.app_id()));
  folder.tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);
  VerifyTrackedFile(folder);
  VerifyReloadConsistency();

  EXPECT_EQ(SYNC_STATUS_OK, UnregisterApp(folder.tracker.app_id()));
  folder.tracker.set_app_id(std::string());
  folder.tracker.set_tracker_kind(TRACKER_KIND_REGULAR);
  folder.tracker.set_active(false);
  VerifyTrackedFile(folder);
  VerifyReloadConsistency();

  EXPECT_EQ(SYNC_STATUS_OK, UnregisterApp(app_root.tracker.app_id()));
  app_root.tracker.set_app_id(std::string());
  app_root.tracker.set_tracker_kind(TRACKER_KIND_REGULAR);
  app_root.tracker.set_active(false);
  app_root.tracker.set_dirty(true);
  file.should_be_absent = true;
  VerifyTrackedFile(app_root);
  VerifyTrackedFile(file);
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, BuildPathTest) {
  FileMetadata sync_root(CreateSyncRootMetadata());
  FileTracker sync_root_tracker(CreateSyncRootTracker(sync_root));

  FileMetadata app_root(CreateFolderMetadata(sync_root, "app_id"));
  FileTracker app_root_tracker(
      CreateTracker(sync_root_tracker, app_root));
  app_root_tracker.set_app_id(app_root.details().title());
  app_root_tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);

  FileMetadata folder(CreateFolderMetadata(app_root, "folder"));
  FileTracker folder_tracker(CreateTracker(app_root_tracker, folder));

  FileMetadata file(CreateFileMetadata(folder, "file"));
  FileTracker file_tracker(CreateTracker(folder_tracker, file));

  FileMetadata inactive_folder(CreateFolderMetadata(app_root, "folder"));
  FileTracker inactive_folder_tracker(CreateTracker(app_root_tracker,
                                                    inactive_folder));
  inactive_folder_tracker.set_active(false);

  {
    scoped_ptr<leveldb::DB> db = InitializeLevelDB();
    ASSERT_TRUE(db);

    EXPECT_TRUE(PutFileToDB(db.get(), sync_root).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), sync_root_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), app_root).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), app_root_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), folder).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), folder_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), file).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), file_tracker).ok());
  }

  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());

  base::FilePath path;
  EXPECT_FALSE(metadata_database()->BuildPathForTracker(
      sync_root_tracker.tracker_id(), &path));
  EXPECT_TRUE(metadata_database()->BuildPathForTracker(
      app_root_tracker.tracker_id(), &path));
  EXPECT_EQ(base::FilePath(FPL("/")).NormalizePathSeparators(), path);
  EXPECT_TRUE(metadata_database()->BuildPathForTracker(
      file_tracker.tracker_id(), &path));
  EXPECT_EQ(base::FilePath(FPL("/folder/file")).NormalizePathSeparators(),
            path);
}

TEST_F(MetadataDatabaseTest, FindNearestActiveAncestorTest) {
  const std::string kAppID = "app_id";

  FileMetadata sync_root(CreateSyncRootMetadata());
  FileTracker sync_root_tracker(CreateSyncRootTracker(sync_root));

  FileMetadata app_root(CreateFolderMetadata(sync_root, kAppID));
  FileTracker app_root_tracker(
      CreateTracker(sync_root_tracker, app_root));
  app_root_tracker.set_app_id(app_root.details().title());
  app_root_tracker.set_tracker_kind(TRACKER_KIND_APP_ROOT);

  // Create directory structure like this: "/folder1/folder2/file"
  FileMetadata folder1(CreateFolderMetadata(app_root, "folder1"));
  FileTracker folder_tracker1(CreateTracker(app_root_tracker, folder1));
  FileMetadata folder2(CreateFolderMetadata(folder1, "folder2"));
  FileTracker folder_tracker2(CreateTracker(folder_tracker1, folder2));
  FileMetadata file(CreateFileMetadata(folder2, "file"));
  FileTracker file_tracker(CreateTracker(folder_tracker2, file));

  FileMetadata inactive_folder(CreateFolderMetadata(app_root, "folder1"));
  FileTracker inactive_folder_tracker(CreateTracker(app_root_tracker,
                                                    inactive_folder));
  inactive_folder_tracker.set_active(false);

  {
    scoped_ptr<leveldb::DB> db = InitializeLevelDB();
    ASSERT_TRUE(db);

    EXPECT_TRUE(PutFileToDB(db.get(), sync_root).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), sync_root_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), app_root).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), app_root_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), folder1).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), folder_tracker1).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), folder2).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), folder_tracker2).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), file).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), file_tracker).ok());
    EXPECT_TRUE(PutFileToDB(db.get(), inactive_folder).ok());
    EXPECT_TRUE(PutTrackerToDB(db.get(), inactive_folder_tracker).ok());
  }

  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_FALSE(metadata_database()->FindNearestActiveAncestor(
        "non_registered_app_id",
        CreateNormalizedPath(FPL("folder1/folder2/file")),
        &tracker, &path));
  }

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_TRUE(metadata_database()->FindNearestActiveAncestor(
        kAppID, CreateNormalizedPath(FPL("")), &tracker, &path));
    EXPECT_EQ(app_root_tracker.tracker_id(), tracker.tracker_id());
    EXPECT_EQ(CreateNormalizedPath(FPL("")), path);
  }

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_TRUE(metadata_database()->FindNearestActiveAncestor(
        kAppID, CreateNormalizedPath(FPL("folder1/folder2")),
        &tracker, &path));
    EXPECT_EQ(folder_tracker2.tracker_id(), tracker.tracker_id());
    EXPECT_EQ(CreateNormalizedPath(FPL("folder1/folder2")), path);
  }

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_TRUE(metadata_database()->FindNearestActiveAncestor(
        kAppID, CreateNormalizedPath(FPL("folder1/folder2/file")),
        &tracker, &path));
    EXPECT_EQ(file_tracker.tracker_id(), tracker.tracker_id());
    EXPECT_EQ(CreateNormalizedPath(FPL("folder1/folder2/file")), path);
  }

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_TRUE(metadata_database()->FindNearestActiveAncestor(
        kAppID,
        CreateNormalizedPath(FPL("folder1/folder2/folder3/folder4/file")),
        &tracker, &path));
    EXPECT_EQ(folder_tracker2.tracker_id(), tracker.tracker_id());
    EXPECT_EQ(CreateNormalizedPath(FPL("folder1/folder2")), path);
  }

  {
    base::FilePath path;
    FileTracker tracker;
    EXPECT_TRUE(metadata_database()->FindNearestActiveAncestor(
        kAppID, CreateNormalizedPath(FPL("folder1/folder2/file/folder4/file")),
        &tracker, &path));
    EXPECT_EQ(folder_tracker2.tracker_id(), tracker.tracker_id());
    EXPECT_EQ(CreateNormalizedPath(FPL("folder1/folder2")), path);
  }
}

TEST_F(MetadataDatabaseTest, UpdateByChangeListTest) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedFolder(sync_root, "app_id"));
  TrackedFile disabled_app_root(CreateTrackedFolder(sync_root, "disabled_app"));
  TrackedFile file(CreateTrackedFile(app_root, "file"));
  TrackedFile renamed_file(CreateTrackedFile(app_root, "to be renamed"));
  TrackedFile folder(CreateTrackedFolder(app_root, "folder"));
  TrackedFile reorganized_file(
      CreateTrackedFile(app_root, "to be reorganized"));
  TrackedFile updated_file(
      CreateTrackedFile(app_root, "to be updated"));
  TrackedFile noop_file(CreateTrackedFile(app_root, "has noop change"));
  TrackedFile new_file(CreateTrackedFile(app_root, "to be added later"));
  new_file.should_be_absent = true;

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &disabled_app_root,
    &file, &renamed_file, &folder, &reorganized_file, &updated_file, &noop_file,
    &new_file,
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());

  ApplyRenameChangeToMetadata("renamed", &renamed_file.metadata);
  ApplyReorganizeChangeToMetadata(folder.metadata.file_id(),
                                  &reorganized_file.metadata);
  ApplyContentChangeToMetadata(&updated_file.metadata);

  // Update change ID.
  ApplyNoopChangeToMetadata(&noop_file.metadata);

  ScopedVector<google_apis::ChangeResource> changes;
  PushToChangeList(
      CreateChangeResourceFromMetadata(renamed_file.metadata), &changes);
  PushToChangeList(
      CreateChangeResourceFromMetadata(reorganized_file.metadata), &changes);
  PushToChangeList(
      CreateChangeResourceFromMetadata(updated_file.metadata), &changes);
  PushToChangeList(
      CreateChangeResourceFromMetadata(noop_file.metadata), &changes);
  PushToChangeList(
      CreateChangeResourceFromMetadata(new_file.metadata), &changes);
  EXPECT_EQ(SYNC_STATUS_OK, UpdateByChangeList(changes.Pass()));

  renamed_file.tracker.set_dirty(true);
  reorganized_file.tracker.set_dirty(true);
  updated_file.tracker.set_dirty(true);
  noop_file.tracker.set_dirty(true);
  new_file.tracker.mutable_synced_details()->set_missing(true);
  new_file.tracker.mutable_synced_details()->clear_md5();
  new_file.tracker.set_active(false);
  new_file.tracker.set_dirty(true);
  ResetTrackerID(&new_file.tracker);
  EXPECT_NE(0, new_file.tracker.tracker_id());

  new_file.should_be_absent = false;

  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, PopulateFolderTest_RegularFolder) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedAppRoot(sync_root, "app_id"));
  app_root.tracker.set_app_id(app_root.metadata.details().title());

  TrackedFile folder_to_populate(
      CreateTrackedFolder(app_root, "folder_to_populate"));
  folder_to_populate.tracker.set_needs_folder_listing(true);
  folder_to_populate.tracker.set_dirty(true);

  TrackedFile known_file(CreateTrackedFile(folder_to_populate, "known_file"));
  TrackedFile new_file(CreateTrackedFile(folder_to_populate, "new_file"));
  new_file.should_be_absent = true;

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &folder_to_populate, &known_file, &new_file
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));

  FileIDList listed_children;
  listed_children.push_back(known_file.metadata.file_id());
  listed_children.push_back(new_file.metadata.file_id());

  EXPECT_EQ(SYNC_STATUS_OK,
            PopulateFolder(folder_to_populate.metadata.file_id(),
                           listed_children));

  folder_to_populate.tracker.set_dirty(false);
  folder_to_populate.tracker.set_needs_folder_listing(false);
  ResetTrackerID(&new_file.tracker);
  new_file.tracker.set_dirty(true);
  new_file.tracker.set_active(false);
  new_file.tracker.clear_synced_details();
  new_file.should_be_absent = false;
  new_file.tracker_only = true;
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, PopulateFolderTest_InactiveFolder) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedAppRoot(sync_root, "app_id"));

  TrackedFile inactive_folder(CreateTrackedFolder(app_root, "inactive_folder"));
  inactive_folder.tracker.set_active(false);
  inactive_folder.tracker.set_dirty(true);

  TrackedFile new_file(
      CreateTrackedFile(inactive_folder, "file_in_inactive_folder"));
  new_file.should_be_absent = true;

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &inactive_folder, &new_file,
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));

  FileIDList listed_children;
  listed_children.push_back(new_file.metadata.file_id());

  EXPECT_EQ(SYNC_STATUS_OK,
            PopulateFolder(inactive_folder.metadata.file_id(),
                           listed_children));
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, PopulateFolderTest_DisabledAppRoot) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile disabled_app_root(
      CreateTrackedAppRoot(sync_root, "disabled_app"));
  disabled_app_root.tracker.set_dirty(true);
  disabled_app_root.tracker.set_needs_folder_listing(true);

  TrackedFile known_file(CreateTrackedFile(disabled_app_root, "known_file"));
  TrackedFile file(CreateTrackedFile(disabled_app_root, "file"));
  file.should_be_absent = true;

  const TrackedFile* tracked_files[] = {
    &sync_root, &disabled_app_root, &disabled_app_root, &known_file, &file,
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));

  FileIDList disabled_app_children;
  disabled_app_children.push_back(file.metadata.file_id());
  EXPECT_EQ(SYNC_STATUS_OK, PopulateFolder(
      disabled_app_root.metadata.file_id(), disabled_app_children));
  ResetTrackerID(&file.tracker);
  file.tracker.clear_synced_details();
  file.tracker.set_dirty(true);
  file.tracker.set_active(false);
  file.should_be_absent = false;
  file.tracker_only = true;

  disabled_app_root.tracker.set_dirty(false);
  disabled_app_root.tracker.set_needs_folder_listing(false);
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

// TODO(tzik): Fix expectation and re-enable this test.
TEST_F(MetadataDatabaseTest, DISABLED_UpdateTrackerTest) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedAppRoot(sync_root, "app_root"));
  TrackedFile file(CreateTrackedFile(app_root, "file"));
  file.tracker.set_dirty(true);
  file.metadata.mutable_details()->set_title("renamed file");

  TrackedFile inactive_file(CreateTrackedFile(app_root, "inactive_file"));
  inactive_file.tracker.set_active(false);
  inactive_file.tracker.set_dirty(true);
  inactive_file.metadata.mutable_details()->set_title("renamed inactive file");
  inactive_file.metadata.mutable_details()->set_md5("modified_md5");

  TrackedFile new_conflict(CreateTrackedFile(app_root, "new conflict file"));
  new_conflict.tracker.set_dirty(true);
  new_conflict.metadata.mutable_details()->set_title("renamed file");

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &file, &inactive_file, &new_conflict
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();

  *file.tracker.mutable_synced_details() = file.metadata.details();
  file.tracker.set_dirty(false);
  EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(file.tracker));
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();

  *inactive_file.tracker.mutable_synced_details() =
       inactive_file.metadata.details();
  inactive_file.tracker.set_dirty(false);
  inactive_file.tracker.set_active(true);
  EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(inactive_file.tracker));
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();

  *new_conflict.tracker.mutable_synced_details() =
       new_conflict.metadata.details();
  new_conflict.tracker.set_dirty(false);
  new_conflict.tracker.set_active(true);
  file.tracker.set_dirty(true);
  file.tracker.set_active(false);
  EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(new_conflict.tracker));
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, PopulateInitialDataTest) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedFolder(sync_root, "app_root"));
  app_root.tracker.set_active(false);

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root
  };

  scoped_ptr<google_apis::FileResource> sync_root_folder(
      CreateFileResourceFromMetadata(sync_root.metadata));
  scoped_ptr<google_apis::FileResource> app_root_folder(
      CreateFileResourceFromMetadata(app_root.metadata));

  ScopedVector<google_apis::FileResource> app_root_folders;
  app_root_folders.push_back(app_root_folder.release());

  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  EXPECT_EQ(SYNC_STATUS_OK, PopulateInitialData(
      current_change_id(),
      *sync_root_folder,
      app_root_folders));

  ResetTrackerID(&sync_root.tracker);
  ResetTrackerID(&app_root.tracker);
  app_root.tracker.set_parent_tracker_id(sync_root.tracker.tracker_id());

  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
  VerifyReloadConsistency();
}

TEST_F(MetadataDatabaseTest, DumpFiles) {
  TrackedFile sync_root(CreateTrackedSyncRoot());
  TrackedFile app_root(CreateTrackedAppRoot(sync_root, "app_id"));
  app_root.tracker.set_app_id(app_root.metadata.details().title());

  TrackedFile folder_0(CreateTrackedFolder(app_root, "folder_0"));
  TrackedFile file_0(CreateTrackedFile(folder_0, "file_0"));

  const TrackedFile* tracked_files[] = {
    &sync_root, &app_root, &folder_0, &file_0
  };

  SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
  EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
  VerifyTrackedFiles(tracked_files, arraysize(tracked_files));

  scoped_ptr<base::ListValue> files =
      metadata_database()->DumpFiles(app_root.tracker.app_id());
  ASSERT_EQ(2u, files->GetSize());

  base::DictionaryValue* file = NULL;
  std::string str;

  ASSERT_TRUE(files->GetDictionary(0, &file));
  EXPECT_TRUE(file->GetString("title", &str) && str == "folder_0");
  EXPECT_TRUE(file->GetString("type", &str) && str == "folder");
  EXPECT_TRUE(file->HasKey("details"));

  ASSERT_TRUE(files->GetDictionary(1, &file));
  EXPECT_TRUE(file->GetString("title", &str) && str == "file_0");
  EXPECT_TRUE(file->GetString("type", &str) && str == "file");
  EXPECT_TRUE(file->HasKey("details"));
}

}  // namespace drive_backend
}  // namespace sync_file_system

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