root/chrome/browser/chromeos/drive/change_list_processor_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ConvertToMap
  2. SetUp
  3. ParseChangeList
  4. ApplyFullResourceList
  5. ApplyChangeList
  6. GetResourceEntry
  7. TEST_F
  8. TEST_F
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. TEST_F
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. 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/chromeos/drive/change_list_processor.h"

#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/values.h"
#include "chrome/browser/chromeos/drive/drive.pb.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/drive/resource_metadata.h"
#include "chrome/browser/chromeos/drive/test_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "google_apis/drive/drive_api_parser.h"
#include "google_apis/drive/gdata_wapi_parser.h"
#include "google_apis/drive/test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace drive {
namespace internal {

namespace {

const int64 kBaseResourceListChangestamp = 123;
const char kBaseResourceListFile[] = "gdata/root_feed.json";
const char kRootId[] = "fake_root";

enum FileOrDirectory {
  FILE,
  DIRECTORY,
};

struct EntryExpectation {
  std::string path;
  std::string id;
  std::string parent_id;
  FileOrDirectory type;
};

typedef std::map<std::string, ResourceEntry> ResourceEntryMap;
typedef std::map<std::string, std::string> ParentResourceIdMap;
void ConvertToMap(ScopedVector<ChangeList> change_lists,
                  ResourceEntryMap* entry_map,
                  ParentResourceIdMap* parent_resource_id_map) {
  for (size_t i = 0; i < change_lists.size(); ++i) {
    const std::vector<ResourceEntry>& entries = change_lists[i]->entries();
    const std::vector<std::string>& parent_resource_ids =
        change_lists[i]->parent_resource_ids();
    EXPECT_EQ(entries.size(), parent_resource_ids.size());

    for (size_t i = 0; i < entries.size(); ++i) {
      const std::string& resource_id = entries[i].resource_id();
      (*parent_resource_id_map)[resource_id] = parent_resource_ids[i];
      (*entry_map)[resource_id] = entries[i];
    }
  }
}

class ChangeListProcessorTest : public testing::Test {
 protected:
  virtual void SetUp() OVERRIDE {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    metadata_storage_.reset(new ResourceMetadataStorage(
        temp_dir_.path(), base::MessageLoopProxy::current().get()));
    ASSERT_TRUE(metadata_storage_->Initialize());

    metadata_.reset(new internal::ResourceMetadata(
        metadata_storage_.get(), base::MessageLoopProxy::current()));
    ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
  }

  // Parses a json file at |test_data_path| relative to Chrome test directory
  // into a ScopedVector<drive::internal::ChangeList>.
  ScopedVector<ChangeList> ParseChangeList(const std::string& test_data_path) {
    ScopedVector<ChangeList> changes;
    changes.push_back(new ChangeList(
        *google_apis::ResourceList::ExtractAndParse(
            *google_apis::test_util::LoadJSONFile(
                test_data_path))));
    return changes.Pass();
  }

  // Applies the |changes| to |metadata_| as a full resource list of changestamp
  // |kBaseResourceListChangestamp|.
  FileError ApplyFullResourceList(ScopedVector<ChangeList> changes) {
    scoped_ptr<google_apis::AboutResource> about_resource(
        new google_apis::AboutResource);
    about_resource->set_largest_change_id(kBaseResourceListChangestamp);
    about_resource->set_root_folder_id(kRootId);

    ChangeListProcessor processor(metadata_.get());
    return processor.Apply(about_resource.Pass(),
                           changes.Pass(),
                           false /* is_delta_update */);
  }

  // Applies the |changes| to |metadata_| as a delta update. Delta changelists
  // should contain their changestamp in themselves.
  FileError ApplyChangeList(ScopedVector<ChangeList> changes,
                            std::set<base::FilePath>* changed_dirs) {
    scoped_ptr<google_apis::AboutResource> about_resource(
        new google_apis::AboutResource);
    about_resource->set_largest_change_id(kBaseResourceListChangestamp);
    about_resource->set_root_folder_id(kRootId);

    ChangeListProcessor processor(metadata_.get());
    FileError error = processor.Apply(about_resource.Pass(),
                                      changes.Pass(),
                                      true /* is_delta_update */);
    *changed_dirs = processor.changed_dirs();
    return error;
  }

  // Gets the resource entry for the path from |metadata_| synchronously.
  // Returns null if the entry does not exist.
  scoped_ptr<ResourceEntry> GetResourceEntry(const std::string& path) {
    scoped_ptr<ResourceEntry> entry(new ResourceEntry);
    FileError error = metadata_->GetResourceEntryByPath(
        base::FilePath::FromUTF8Unsafe(path), entry.get());
    if (error != FILE_ERROR_OK)
      entry.reset();
    return entry.Pass();
  }

  content::TestBrowserThreadBundle thread_bundle_;
  base::ScopedTempDir temp_dir_;
  scoped_ptr<ResourceMetadataStorage,
             test_util::DestroyHelperForTests> metadata_storage_;
  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
};

}  // namespace

TEST_F(ChangeListProcessorTest, ApplyFullResourceList) {
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));

  const EntryExpectation kExpected[] = {
      // Root files
      {"drive/root", kRootId, "", DIRECTORY},
      {"drive/root/File 1.txt",
          "file:2_file_resource_id", kRootId, FILE},
      {"drive/root/Slash _ in file 1.txt",
          "file:slash_file_resource_id", kRootId, FILE},
      {"drive/root/Document 1 excludeDir-test.gdoc",
          "document:5_document_resource_id", kRootId, FILE},
      // Subdirectory files
      {"drive/root/Directory 1",
          "folder:1_folder_resource_id", kRootId, DIRECTORY},
      {"drive/root/Directory 1/SubDirectory File 1.txt",
          "file:subdirectory_file_1_id", "folder:1_folder_resource_id", FILE},
      {"drive/root/Directory 1/Shared To The Account Owner.txt",
          "file:subdirectory_unowned_file_1_id",
          "folder:1_folder_resource_id", FILE},
      {"drive/root/Directory 2 excludeDir-test",
          "folder:sub_dir_folder_2_self_link", kRootId, DIRECTORY},
      {"drive/root/Slash _ in directory",
          "folder:slash_dir_folder_resource_id", kRootId, DIRECTORY},
      {"drive/root/Slash _ in directory/Slash SubDir File.txt",
          "file:slash_subdir_file",
          "folder:slash_dir_folder_resource_id", FILE},
      // Deeper
      {"drive/root/Directory 1/Sub Directory Folder",
          "folder:sub_dir_folder_resource_id",
          "folder:1_folder_resource_id", DIRECTORY},
      {"drive/root/Directory 1/Sub Directory Folder/Sub Sub Directory Folder",
          "folder:sub_sub_directory_folder_id",
          "folder:sub_dir_folder_resource_id", DIRECTORY},
      // Orphan
      {"drive/other/Orphan File 1.txt", "file:1_orphanfile_resource_id",
           "", FILE},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpected); ++i) {
    scoped_ptr<ResourceEntry> entry = GetResourceEntry(kExpected[i].path);
    ASSERT_TRUE(entry) << "for path: " << kExpected[i].path;
    EXPECT_EQ(kExpected[i].id, entry->resource_id());

    ResourceEntry parent_entry;
    EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(
        entry->parent_local_id(), &parent_entry));
    EXPECT_EQ(kExpected[i].parent_id, parent_entry.resource_id());
    EXPECT_EQ(kExpected[i].type,
              entry->file_info().is_directory() ? DIRECTORY : FILE);
  }

  EXPECT_EQ(kBaseResourceListChangestamp, metadata_->GetLargestChangestamp());
}

TEST_F(ChangeListProcessorTest, DeltaFileAddedInNewDirectory) {
  const char kTestJson[] =
      "gdata/delta_file_added_in_new_directory.json";

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;
  ConvertToMap(ParseChangeList(kTestJson), &entry_map, &parent_resource_id_map);

  const std::string kNewFolderId("folder:new_folder_resource_id");
  const std::string kNewFileId("document:file_added_in_new_dir_id");

  // Check the content of parsed ResourceEntryMap.
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kNewFolderId));
  EXPECT_TRUE(entry_map.count(kNewFileId));
  EXPECT_EQ(kRootId, parent_resource_id_map[kNewFolderId]);
  EXPECT_EQ(kNewFolderId, parent_resource_id_map[kNewFileId]);
  EXPECT_TRUE(entry_map[kNewFolderId].file_info().is_directory());
  EXPECT_FALSE(entry_map[kNewFileId].file_info().is_directory());
  EXPECT_EQ("New Directory", entry_map[kNewFolderId].title());
  EXPECT_EQ("File in new dir", entry_map[kNewFileId].title());

  // Apply the changelist and check the effect.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJson), &changed_dirs));

  // The value is written in kTestJson.
  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
  EXPECT_TRUE(GetResourceEntry("drive/root/New Directory"));
  EXPECT_TRUE(GetResourceEntry(
      "drive/root/New Directory/File in new dir.gdoc"));

  EXPECT_EQ(2U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/New Directory")));
}

TEST_F(ChangeListProcessorTest, DeltaDirMovedFromRootToDirectory) {
  const char kTestJson[] =
      "gdata/delta_dir_moved_from_root_to_directory.json";

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;
  ConvertToMap(ParseChangeList(kTestJson), &entry_map, &parent_resource_id_map);

  const std::string kMovedId("folder:1_folder_resource_id");
  const std::string kDestId("folder:sub_dir_folder_2_self_link");

  // Check the content of parsed ResourceEntryMap.
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kMovedId));
  EXPECT_TRUE(entry_map.count(kDestId));
  EXPECT_EQ(kDestId, parent_resource_id_map[kMovedId]);

  // Apply the changelist and check the effect.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJson), &changed_dirs));

  // The value is written in kTestJson.
  EXPECT_EQ(16809, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry("drive/root/Directory 1"));
  EXPECT_TRUE(GetResourceEntry(
      "drive/root/Directory 2 excludeDir-test/Directory 1"));

  EXPECT_EQ(4U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe(
          "drive/root/Directory 2 excludeDir-test")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe(
          "drive/root/Directory 2 excludeDir-test/Directory 1")));
}

TEST_F(ChangeListProcessorTest, DeltaFileMovedFromDirectoryToRoot) {
  const char kTestJson[] =
      "gdata/delta_file_moved_from_directory_to_root.json";

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;
  ConvertToMap(ParseChangeList(kTestJson), &entry_map, &parent_resource_id_map);

  const std::string kMovedId("file:subdirectory_file_1_id");
  const std::string kSrcId("folder:1_folder_resource_id");

  // Check the content of parsed ResourceEntryMap.
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kMovedId));
  EXPECT_TRUE(entry_map.count(kSrcId));
  EXPECT_EQ(kRootId, parent_resource_id_map[kMovedId]);

  // Apply the changelist and check the effect.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJson), &changed_dirs));

  // The value is written in kTestJson.
  EXPECT_EQ(16815, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry(
      "drive/root/Directory 1/SubDirectory File 1.txt"));
  EXPECT_TRUE(GetResourceEntry("drive/root/SubDirectory File 1.txt"));

  EXPECT_EQ(2U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
}

TEST_F(ChangeListProcessorTest, DeltaFileRenamedInDirectory) {
  const char kTestJson[] =
      "gdata/delta_file_renamed_in_directory.json";

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;
  ConvertToMap(ParseChangeList(kTestJson), &entry_map, &parent_resource_id_map);

  const std::string kRenamedId("file:subdirectory_file_1_id");
  const std::string kParentId("folder:1_folder_resource_id");

  // Check the content of parsed ResourceEntryMap.
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kRenamedId));
  EXPECT_TRUE(entry_map.count(kParentId));
  EXPECT_EQ(kParentId, parent_resource_id_map[kRenamedId]);
  EXPECT_EQ("New SubDirectory File 1.txt", entry_map[kRenamedId].title());

  // Apply the changelist and check the effect.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJson), &changed_dirs));

  // The value is written in kTestJson.
  EXPECT_EQ(16767, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry(
      "drive/root/Directory 1/SubDirectory File 1.txt"));
  EXPECT_TRUE(GetResourceEntry(
      "drive/root/Directory 1/New SubDirectory File 1.txt"));

  EXPECT_EQ(2U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
}

TEST_F(ChangeListProcessorTest, DeltaAddAndDeleteFileInRoot) {
  const char kTestJsonAdd[] =
      "gdata/delta_file_added_in_root.json";
  const char kTestJsonDelete[] =
      "gdata/delta_file_deleted_in_root.json";

  const std::string kParentId(kRootId);
  const std::string kFileId("document:added_in_root_id");

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;

  // Check the content of kTestJsonAdd.
  ConvertToMap(ParseChangeList(kTestJsonAdd),
               &entry_map, &parent_resource_id_map);
  EXPECT_EQ(1U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kFileId));
  EXPECT_EQ(kParentId, parent_resource_id_map[kFileId]);
  EXPECT_EQ("Added file", entry_map[kFileId].title());
  EXPECT_FALSE(entry_map[kFileId].deleted());

  // Apply.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJsonAdd), &changed_dirs));
  EXPECT_EQ(16683, metadata_->GetLargestChangestamp());
  EXPECT_TRUE(GetResourceEntry("drive/root/Added file.gdoc"));
  EXPECT_EQ(1U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));

  // Check the content of kTestJsonDelete.
  entry_map.clear();
  parent_resource_id_map.clear();
  ConvertToMap(ParseChangeList(kTestJsonDelete),
               &entry_map, &parent_resource_id_map);
  EXPECT_EQ(1U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kFileId));
  EXPECT_EQ(kParentId, parent_resource_id_map[kFileId]);
  EXPECT_EQ("Added file", entry_map[kFileId].title());
  EXPECT_TRUE(entry_map[kFileId].deleted());

  // Apply.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJsonDelete), &changed_dirs));
  EXPECT_EQ(16687, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry("drive/root/Added file.gdoc"));
  EXPECT_EQ(1U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
}


TEST_F(ChangeListProcessorTest, DeltaAddAndDeleteFileFromExistingDirectory) {
  const char kTestJsonAdd[] =
      "gdata/delta_file_added_in_directory.json";
  const char kTestJsonDelete[] =
      "gdata/delta_file_deleted_in_directory.json";

  const std::string kParentId("folder:1_folder_resource_id");
  const std::string kFileId("document:added_in_root_id");

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;

  // Check the content of kTestJsonAdd.
  ConvertToMap(ParseChangeList(kTestJsonAdd),
               &entry_map, &parent_resource_id_map);
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kFileId));
  EXPECT_TRUE(entry_map.count(kParentId));
  EXPECT_EQ(kParentId, parent_resource_id_map[kFileId]);
  EXPECT_EQ("Added file", entry_map[kFileId].title());
  EXPECT_FALSE(entry_map[kFileId].deleted());

  // Apply.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJsonAdd), &changed_dirs));
  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
  EXPECT_TRUE(GetResourceEntry("drive/root/Directory 1/Added file.gdoc"));

  EXPECT_EQ(2U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root")));
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));

  // Check the content of kTestJsonDelete.
  entry_map.clear();
  parent_resource_id_map.clear();
  ConvertToMap(ParseChangeList(kTestJsonDelete),
               &entry_map, &parent_resource_id_map);
  EXPECT_EQ(1U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kFileId));
  EXPECT_EQ(kParentId, parent_resource_id_map[kFileId]);
  EXPECT_EQ("Added file", entry_map[kFileId].title());
  EXPECT_TRUE(entry_map[kFileId].deleted());

  // Apply.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJsonDelete), &changed_dirs));
  EXPECT_EQ(16770, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry("drive/root/Directory 1/Added file.gdoc"));

  EXPECT_EQ(1U, changed_dirs.size());
  EXPECT_TRUE(changed_dirs.count(
      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
}

TEST_F(ChangeListProcessorTest, DeltaAddFileToNewButDeletedDirectory) {
  // This file contains the following updates:
  // 1) A new PDF file is added to a new directory
  // 2) but the new directory is marked "deleted" (i.e. moved to Trash)
  // Hence, the PDF file should be just ignored.
  const char kTestJson[] =
      "gdata/delta_file_added_in_new_but_deleted_directory.json";

  ResourceEntryMap entry_map;
  ParentResourceIdMap parent_resource_id_map;
  ConvertToMap(ParseChangeList(kTestJson), &entry_map, &parent_resource_id_map);

  const std::string kDirId("folder:new_folder_resource_id");
  const std::string kFileId("pdf:file_added_in_deleted_dir_id");

  // Check the content of parsed ResourceEntryMap.
  EXPECT_EQ(2U, entry_map.size());
  EXPECT_TRUE(entry_map.count(kDirId));
  EXPECT_TRUE(entry_map.count(kFileId));
  EXPECT_EQ(kDirId, parent_resource_id_map[kFileId]);
  EXPECT_TRUE(entry_map[kDirId].deleted());

  // Apply the changelist and check the effect.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));
  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyChangeList(ParseChangeList(kTestJson), &changed_dirs));

  // The value is written in kTestJson.
  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
  EXPECT_FALSE(GetResourceEntry("drive/root/New Directory/new_pdf_file.pdf"));

  EXPECT_TRUE(changed_dirs.empty());
}

TEST_F(ChangeListProcessorTest, RefreshDirectory) {
  // Prepare metadata.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));

  // Create change list.
  scoped_ptr<ChangeList> change_list(new ChangeList);

  // Add a new file to the change list.
  ResourceEntry new_file;
  new_file.set_title("new_file");
  new_file.set_resource_id("new_file_id");
  change_list->mutable_entries()->push_back(new_file);
  change_list->mutable_parent_resource_ids()->push_back(kRootId);

  // Add "Directory 1" to the map with a new name.
  ResourceEntry dir1;
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath().AppendASCII("Directory 1"), &dir1));
  dir1.set_title(dir1.title() + " (renamed)");
  change_list->mutable_entries()->push_back(dir1);
  change_list->mutable_parent_resource_ids()->push_back(kRootId);

  // Update the directory with the map.
  ResourceEntry root;
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath(), &root));
  const int64 kNewChangestamp = 12345;
  ResourceEntryVector refreshed_entries;
  EXPECT_EQ(FILE_ERROR_OK, ChangeListProcessor::RefreshDirectory(
      metadata_.get(),
      DirectoryFetchInfo(root.local_id(), kRootId, kNewChangestamp),
      change_list.Pass(),
      &refreshed_entries));

  // "new_file" should be added.
  ResourceEntry entry;
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath().AppendASCII(new_file.title()), &entry));

  // "Directory 1" should be renamed.
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath().AppendASCII(dir1.title()), &entry));
}

TEST_F(ChangeListProcessorTest, RefreshDirectory_WrongParentId) {
  // Prepare metadata.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));

  // Create change list and add a new file to it.
  scoped_ptr<ChangeList> change_list(new ChangeList);
  ResourceEntry new_file;
  new_file.set_title("new_file");
  new_file.set_resource_id("new_file_id");
  // This entry should not be added because the parent ID does not match.
  change_list->mutable_parent_resource_ids()->push_back(
      "some-random-resource-id");
  change_list->mutable_entries()->push_back(new_file);


  // Update the directory.
  ResourceEntry root;
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath(), &root));
  const int64 kNewChangestamp = 12345;
  ResourceEntryVector refreshed_entries;
  EXPECT_EQ(FILE_ERROR_OK, ChangeListProcessor::RefreshDirectory(
      metadata_.get(),
      DirectoryFetchInfo(root.local_id(), kRootId, kNewChangestamp),
      change_list.Pass(),
      &refreshed_entries));

  // "new_file" should not be added.
  ResourceEntry entry;
  EXPECT_EQ(FILE_ERROR_NOT_FOUND, metadata_->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath().AppendASCII(new_file.title()), &entry));
}

TEST_F(ChangeListProcessorTest, SharedFilesWithNoParentInFeed) {
  // Prepare metadata.
  EXPECT_EQ(FILE_ERROR_OK,
            ApplyFullResourceList(ParseChangeList(kBaseResourceListFile)));

  // Create change lists.
  ScopedVector<ChangeList> change_lists;
  change_lists.push_back(new ChangeList);

  // Add a new file with non-existing parent resource id to the change lists.
  ResourceEntry new_file;
  new_file.set_title("new_file");
  new_file.set_resource_id("new_file_id");
  change_lists[0]->mutable_entries()->push_back(new_file);
  change_lists[0]->mutable_parent_resource_ids()->push_back("nonexisting");
  change_lists[0]->mutable_modification_dates()->push_back(base::Time());
  change_lists[0]->set_largest_changestamp(kBaseResourceListChangestamp + 1);

  std::set<base::FilePath> changed_dirs;
  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));

  // "new_file" should be added under drive/other.
  ResourceEntry entry;
  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
      util::GetDriveGrandRootPath().AppendASCII("other/new_file"), &entry));
}

}  // namespace internal
}  // namespace drive

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