change_list_processor_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/drive/change_list_processor.h"
6
7#include "base/files/scoped_temp_dir.h"
8#include "base/message_loop/message_loop_proxy.h"
9#include "base/values.h"
10#include "chrome/browser/chromeos/drive/drive.pb.h"
11#include "chrome/browser/chromeos/drive/file_system_util.h"
12#include "chrome/browser/chromeos/drive/resource_metadata.h"
13#include "chrome/browser/chromeos/drive/test_util.h"
14#include "content/public/test/test_browser_thread_bundle.h"
15#include "google_apis/drive/drive_api_parser.h"
16#include "google_apis/drive/gdata_wapi_parser.h"
17#include "google_apis/drive/test_util.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace drive {
21namespace internal {
22
23namespace {
24
25const int64 kBaseResourceListChangestamp = 123;
26const char kRootId[] = "fake_root";
27
28enum FileOrDirectory {
29  FILE,
30  DIRECTORY,
31};
32
33struct EntryExpectation {
34  std::string path;
35  std::string id;
36  std::string parent_id;
37  FileOrDirectory type;
38};
39
40// Returns a basic change list which contains some files and directories.
41ScopedVector<ChangeList> CreateBaseChangeList() {
42  ScopedVector<ChangeList> change_lists;
43  change_lists.push_back(new ChangeList);
44
45  // Add directories to the change list.
46  ResourceEntry directory;
47  directory.mutable_file_info()->set_is_directory(true);
48
49  directory.set_title("Directory 1");
50  directory.set_resource_id("folder:1_folder_resource_id");
51  change_lists[0]->mutable_entries()->push_back(directory);
52  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
53
54  directory.set_title("Sub Directory Folder");
55  directory.set_resource_id("folder:sub_dir_folder_resource_id");
56  change_lists[0]->mutable_entries()->push_back(directory);
57  change_lists[0]->mutable_parent_resource_ids()->push_back(
58      "folder:1_folder_resource_id");
59
60  directory.set_title("Sub Sub Directory Folder");
61  directory.set_resource_id("folder:sub_sub_directory_folder_id");
62  change_lists[0]->mutable_entries()->push_back(directory);
63  change_lists[0]->mutable_parent_resource_ids()->push_back(
64      "folder:sub_dir_folder_resource_id");
65
66  directory.set_title("Directory 2 excludeDir-test");
67  directory.set_resource_id("folder:sub_dir_folder_2_self_link");
68  change_lists[0]->mutable_entries()->push_back(directory);
69  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
70
71  // Add files to the change list.
72  ResourceEntry file;
73
74  file.set_title("File 1.txt");
75  file.set_resource_id("file:2_file_resource_id");
76  change_lists[0]->mutable_entries()->push_back(file);
77  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
78
79  file.set_title("SubDirectory File 1.txt");
80  file.set_resource_id("file:subdirectory_file_1_id");
81  change_lists[0]->mutable_entries()->push_back(file);
82  change_lists[0]->mutable_parent_resource_ids()->push_back(
83      "folder:1_folder_resource_id");
84
85  file.set_title("Orphan File 1.txt");
86  file.set_resource_id("file:1_orphanfile_resource_id");
87  change_lists[0]->mutable_entries()->push_back(file);
88  change_lists[0]->mutable_parent_resource_ids()->push_back("");
89
90  change_lists[0]->set_largest_changestamp(kBaseResourceListChangestamp);
91  return change_lists.Pass();
92}
93
94class ChangeListProcessorTest : public testing::Test {
95 protected:
96  virtual void SetUp() OVERRIDE {
97    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
98
99    metadata_storage_.reset(new ResourceMetadataStorage(
100        temp_dir_.path(), base::MessageLoopProxy::current().get()));
101    ASSERT_TRUE(metadata_storage_->Initialize());
102
103    metadata_.reset(new internal::ResourceMetadata(
104        metadata_storage_.get(), base::MessageLoopProxy::current()));
105    ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
106  }
107
108  // Applies the |changes| to |metadata_| as a full resource list of changestamp
109  // |kBaseResourceListChangestamp|.
110  FileError ApplyFullResourceList(ScopedVector<ChangeList> changes) {
111    scoped_ptr<google_apis::AboutResource> about_resource(
112        new google_apis::AboutResource);
113    about_resource->set_largest_change_id(kBaseResourceListChangestamp);
114    about_resource->set_root_folder_id(kRootId);
115
116    ChangeListProcessor processor(metadata_.get());
117    return processor.Apply(about_resource.Pass(),
118                           changes.Pass(),
119                           false /* is_delta_update */);
120  }
121
122  // Applies the |changes| to |metadata_| as a delta update. Delta changelists
123  // should contain their changestamp in themselves.
124  FileError ApplyChangeList(ScopedVector<ChangeList> changes,
125                            std::set<base::FilePath>* changed_dirs) {
126    scoped_ptr<google_apis::AboutResource> about_resource(
127        new google_apis::AboutResource);
128    about_resource->set_largest_change_id(kBaseResourceListChangestamp);
129    about_resource->set_root_folder_id(kRootId);
130
131    ChangeListProcessor processor(metadata_.get());
132    FileError error = processor.Apply(about_resource.Pass(),
133                                      changes.Pass(),
134                                      true /* is_delta_update */);
135    *changed_dirs = processor.changed_dirs();
136    return error;
137  }
138
139  // Gets the resource entry for the path from |metadata_| synchronously.
140  // Returns null if the entry does not exist.
141  scoped_ptr<ResourceEntry> GetResourceEntry(const std::string& path) {
142    scoped_ptr<ResourceEntry> entry(new ResourceEntry);
143    FileError error = metadata_->GetResourceEntryByPath(
144        base::FilePath::FromUTF8Unsafe(path), entry.get());
145    if (error != FILE_ERROR_OK)
146      entry.reset();
147    return entry.Pass();
148  }
149
150  content::TestBrowserThreadBundle thread_bundle_;
151  base::ScopedTempDir temp_dir_;
152  scoped_ptr<ResourceMetadataStorage,
153             test_util::DestroyHelperForTests> metadata_storage_;
154  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
155};
156
157}  // namespace
158
159TEST_F(ChangeListProcessorTest, ApplyFullResourceList) {
160  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
161
162  const EntryExpectation kExpected[] = {
163      // Root files
164      {"drive/root", kRootId, "", DIRECTORY},
165      {"drive/root/File 1.txt",
166          "file:2_file_resource_id", kRootId, FILE},
167      // Subdirectory files
168      {"drive/root/Directory 1",
169          "folder:1_folder_resource_id", kRootId, DIRECTORY},
170      {"drive/root/Directory 1/SubDirectory File 1.txt",
171          "file:subdirectory_file_1_id", "folder:1_folder_resource_id", FILE},
172      {"drive/root/Directory 2 excludeDir-test",
173          "folder:sub_dir_folder_2_self_link", kRootId, DIRECTORY},
174      // Deeper
175      {"drive/root/Directory 1/Sub Directory Folder",
176          "folder:sub_dir_folder_resource_id",
177          "folder:1_folder_resource_id", DIRECTORY},
178      {"drive/root/Directory 1/Sub Directory Folder/Sub Sub Directory Folder",
179          "folder:sub_sub_directory_folder_id",
180          "folder:sub_dir_folder_resource_id", DIRECTORY},
181      // Orphan
182      {"drive/other/Orphan File 1.txt", "file:1_orphanfile_resource_id",
183           "", FILE},
184  };
185
186  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpected); ++i) {
187    scoped_ptr<ResourceEntry> entry = GetResourceEntry(kExpected[i].path);
188    ASSERT_TRUE(entry) << "for path: " << kExpected[i].path;
189    EXPECT_EQ(kExpected[i].id, entry->resource_id());
190
191    ResourceEntry parent_entry;
192    EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(
193        entry->parent_local_id(), &parent_entry));
194    EXPECT_EQ(kExpected[i].parent_id, parent_entry.resource_id());
195    EXPECT_EQ(kExpected[i].type,
196              entry->file_info().is_directory() ? DIRECTORY : FILE);
197  }
198
199  EXPECT_EQ(kBaseResourceListChangestamp, metadata_->GetLargestChangestamp());
200}
201
202TEST_F(ChangeListProcessorTest, DeltaFileAddedInNewDirectory) {
203  ScopedVector<ChangeList> change_lists;
204  change_lists.push_back(new ChangeList);
205
206  ResourceEntry new_folder;
207  new_folder.set_resource_id("folder:new_folder_resource_id");
208  new_folder.set_title("New Directory");
209  new_folder.mutable_file_info()->set_is_directory(true);
210  change_lists[0]->mutable_entries()->push_back(new_folder);
211  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
212
213  ResourceEntry new_file;
214  new_file.set_resource_id("document:file_added_in_new_dir_id");
215  new_file.set_title("File in new dir.txt");
216  change_lists[0]->mutable_entries()->push_back(new_file);
217  change_lists[0]->mutable_parent_resource_ids()->push_back(
218      new_folder.resource_id());
219
220  change_lists[0]->set_largest_changestamp(16730);
221
222  // Apply the changelist and check the effect.
223  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
224  std::set<base::FilePath> changed_dirs;
225  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
226
227  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
228  EXPECT_TRUE(GetResourceEntry("drive/root/New Directory"));
229  EXPECT_TRUE(GetResourceEntry(
230      "drive/root/New Directory/File in new dir.txt"));
231
232  EXPECT_EQ(2U, changed_dirs.size());
233  EXPECT_TRUE(changed_dirs.count(
234      base::FilePath::FromUTF8Unsafe("drive/root")));
235  EXPECT_TRUE(changed_dirs.count(
236      base::FilePath::FromUTF8Unsafe("drive/root/New Directory")));
237}
238
239TEST_F(ChangeListProcessorTest, DeltaDirMovedFromRootToDirectory) {
240  ScopedVector<ChangeList> change_lists;
241  change_lists.push_back(new ChangeList);
242
243  ResourceEntry entry;
244  entry.set_resource_id("folder:1_folder_resource_id");
245  entry.set_title("Directory 1");
246  entry.mutable_file_info()->set_is_directory(true);
247  change_lists[0]->mutable_entries()->push_back(entry);
248  change_lists[0]->mutable_parent_resource_ids()->push_back(
249      "folder:sub_dir_folder_2_self_link");
250
251  change_lists[0]->set_largest_changestamp(16809);
252
253  // Apply the changelist and check the effect.
254  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
255  std::set<base::FilePath> changed_dirs;
256  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
257
258  EXPECT_EQ(16809, metadata_->GetLargestChangestamp());
259  EXPECT_FALSE(GetResourceEntry("drive/root/Directory 1"));
260  EXPECT_TRUE(GetResourceEntry(
261      "drive/root/Directory 2 excludeDir-test/Directory 1"));
262
263  EXPECT_EQ(4U, changed_dirs.size());
264  EXPECT_TRUE(changed_dirs.count(
265      base::FilePath::FromUTF8Unsafe("drive/root")));
266  EXPECT_TRUE(changed_dirs.count(
267      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
268  EXPECT_TRUE(changed_dirs.count(
269      base::FilePath::FromUTF8Unsafe(
270          "drive/root/Directory 2 excludeDir-test")));
271  EXPECT_TRUE(changed_dirs.count(
272      base::FilePath::FromUTF8Unsafe(
273          "drive/root/Directory 2 excludeDir-test/Directory 1")));
274}
275
276TEST_F(ChangeListProcessorTest, DeltaFileMovedFromDirectoryToRoot) {
277  ScopedVector<ChangeList> change_lists;
278  change_lists.push_back(new ChangeList);
279
280  ResourceEntry entry;
281  entry.set_resource_id("file:subdirectory_file_1_id");
282  entry.set_title("SubDirectory File 1.txt");
283  change_lists[0]->mutable_entries()->push_back(entry);
284  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
285
286  change_lists[0]->set_largest_changestamp(16815);
287
288  // Apply the changelist and check the effect.
289  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
290  std::set<base::FilePath> changed_dirs;
291  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
292
293  EXPECT_EQ(16815, metadata_->GetLargestChangestamp());
294  EXPECT_FALSE(GetResourceEntry(
295      "drive/root/Directory 1/SubDirectory File 1.txt"));
296  EXPECT_TRUE(GetResourceEntry("drive/root/SubDirectory File 1.txt"));
297
298  EXPECT_EQ(2U, changed_dirs.size());
299  EXPECT_TRUE(changed_dirs.count(
300      base::FilePath::FromUTF8Unsafe("drive/root")));
301  EXPECT_TRUE(changed_dirs.count(
302      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
303}
304
305TEST_F(ChangeListProcessorTest, DeltaFileRenamedInDirectory) {
306  ScopedVector<ChangeList> change_lists;
307  change_lists.push_back(new ChangeList);
308
309  ResourceEntry entry;
310  entry.set_resource_id("file:subdirectory_file_1_id");
311  entry.set_title("New SubDirectory File 1.txt");
312  change_lists[0]->mutable_entries()->push_back(entry);
313  change_lists[0]->mutable_parent_resource_ids()->push_back(
314      "folder:1_folder_resource_id");
315
316  change_lists[0]->set_largest_changestamp(16767);
317
318  // Apply the changelist and check the effect.
319  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
320  std::set<base::FilePath> changed_dirs;
321  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
322
323  EXPECT_EQ(16767, metadata_->GetLargestChangestamp());
324  EXPECT_FALSE(GetResourceEntry(
325      "drive/root/Directory 1/SubDirectory File 1.txt"));
326  EXPECT_TRUE(GetResourceEntry(
327      "drive/root/Directory 1/New SubDirectory File 1.txt"));
328
329  EXPECT_EQ(1U, changed_dirs.size());
330  EXPECT_TRUE(changed_dirs.count(
331      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
332}
333
334TEST_F(ChangeListProcessorTest, DeltaAddAndDeleteFileInRoot) {
335  // Create ChangeList to add a file.
336  ScopedVector<ChangeList> change_lists;
337  change_lists.push_back(new ChangeList);
338
339  ResourceEntry entry;
340  entry.set_resource_id("document:added_in_root_id");
341  entry.set_title("Added file.txt");
342  change_lists[0]->mutable_entries()->push_back(entry);
343  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
344
345  change_lists[0]->set_largest_changestamp(16683);
346
347  // Apply.
348  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
349  std::set<base::FilePath> changed_dirs;
350  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
351  EXPECT_EQ(16683, metadata_->GetLargestChangestamp());
352  EXPECT_TRUE(GetResourceEntry("drive/root/Added file.txt"));
353  EXPECT_EQ(1U, changed_dirs.size());
354  EXPECT_TRUE(changed_dirs.count(
355      base::FilePath::FromUTF8Unsafe("drive/root")));
356
357  // Create ChangeList to delete the file.
358  change_lists.push_back(new ChangeList);
359
360  entry.set_deleted(true);
361  change_lists[0]->mutable_entries()->push_back(entry);
362  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
363
364  change_lists[0]->set_largest_changestamp(16687);
365
366  // Apply.
367  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
368  EXPECT_EQ(16687, metadata_->GetLargestChangestamp());
369  EXPECT_FALSE(GetResourceEntry("drive/root/Added file.txt"));
370  EXPECT_EQ(1U, changed_dirs.size());
371  EXPECT_TRUE(changed_dirs.count(
372      base::FilePath::FromUTF8Unsafe("drive/root")));
373}
374
375
376TEST_F(ChangeListProcessorTest, DeltaAddAndDeleteFileFromExistingDirectory) {
377  // Create ChangeList to add a file.
378  ScopedVector<ChangeList> change_lists;
379  change_lists.push_back(new ChangeList);
380
381  ResourceEntry entry;
382  entry.set_resource_id("document:added_in_root_id");
383  entry.set_title("Added file.txt");
384  change_lists[0]->mutable_entries()->push_back(entry);
385  change_lists[0]->mutable_parent_resource_ids()->push_back(
386      "folder:1_folder_resource_id");
387
388  change_lists[0]->set_largest_changestamp(16730);
389
390  // Apply.
391  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
392  std::set<base::FilePath> changed_dirs;
393  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
394  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
395  EXPECT_TRUE(GetResourceEntry("drive/root/Directory 1/Added file.txt"));
396
397  EXPECT_EQ(1U, changed_dirs.size());
398  EXPECT_TRUE(changed_dirs.count(
399      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
400
401  // Create ChangeList to delete the file.
402  change_lists.push_back(new ChangeList);
403
404  entry.set_deleted(true);
405  change_lists[0]->mutable_entries()->push_back(entry);
406  change_lists[0]->mutable_parent_resource_ids()->push_back(
407      "folder:1_folder_resource_id");
408
409  change_lists[0]->set_largest_changestamp(16770);
410
411  // Apply.
412  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
413  EXPECT_EQ(16770, metadata_->GetLargestChangestamp());
414  EXPECT_FALSE(GetResourceEntry("drive/root/Directory 1/Added file.txt"));
415
416  EXPECT_EQ(1U, changed_dirs.size());
417  EXPECT_TRUE(changed_dirs.count(
418      base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
419}
420
421TEST_F(ChangeListProcessorTest, DeltaAddFileToNewButDeletedDirectory) {
422  // Create a change which contains the following updates:
423  // 1) A new PDF file is added to a new directory
424  // 2) but the new directory is marked "deleted" (i.e. moved to Trash)
425  // Hence, the PDF file should be just ignored.
426  ScopedVector<ChangeList> change_lists;
427  change_lists.push_back(new ChangeList);
428
429  ResourceEntry file;
430  file.set_resource_id("pdf:file_added_in_deleted_id");
431  file.set_title("new_pdf_file.pdf");
432  file.set_deleted(true);
433  change_lists[0]->mutable_entries()->push_back(file);
434  change_lists[0]->mutable_parent_resource_ids()->push_back(
435      "folder:new_folder_resource_id");
436
437  ResourceEntry directory;
438  directory.set_resource_id("folder:new_folder_resource_id");
439  directory.set_title("New Directory");
440  directory.mutable_file_info()->set_is_directory(true);
441  directory.set_deleted(true);
442  change_lists[0]->mutable_entries()->push_back(directory);
443  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
444
445  change_lists[0]->set_largest_changestamp(16730);
446
447  // Apply the changelist and check the effect.
448  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
449  std::set<base::FilePath> changed_dirs;
450  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
451
452  EXPECT_EQ(16730, metadata_->GetLargestChangestamp());
453  EXPECT_FALSE(GetResourceEntry("drive/root/New Directory/new_pdf_file.pdf"));
454
455  EXPECT_TRUE(changed_dirs.empty());
456}
457
458TEST_F(ChangeListProcessorTest, RefreshDirectory) {
459  // Prepare metadata.
460  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
461
462  // Create change list.
463  scoped_ptr<ChangeList> change_list(new ChangeList);
464
465  // Add a new file to the change list.
466  ResourceEntry new_file;
467  new_file.set_title("new_file");
468  new_file.set_resource_id("new_file_id");
469  change_list->mutable_entries()->push_back(new_file);
470  change_list->mutable_parent_resource_ids()->push_back(kRootId);
471
472  // Add "Directory 1" to the map with a new name.
473  ResourceEntry dir1;
474  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
475      util::GetDriveMyDriveRootPath().AppendASCII("Directory 1"), &dir1));
476  dir1.set_title(dir1.title() + " (renamed)");
477  change_list->mutable_entries()->push_back(dir1);
478  change_list->mutable_parent_resource_ids()->push_back(kRootId);
479
480  // Update the directory with the map.
481  ResourceEntry root;
482  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
483      util::GetDriveMyDriveRootPath(), &root));
484  const int64 kNewChangestamp = 12345;
485  ResourceEntryVector refreshed_entries;
486  EXPECT_EQ(FILE_ERROR_OK, ChangeListProcessor::RefreshDirectory(
487      metadata_.get(),
488      DirectoryFetchInfo(root.local_id(), kRootId, kNewChangestamp),
489      change_list.Pass(),
490      &refreshed_entries));
491
492  // "new_file" should be added.
493  ResourceEntry entry;
494  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
495      util::GetDriveMyDriveRootPath().AppendASCII(new_file.title()), &entry));
496
497  // "Directory 1" should be renamed.
498  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
499      util::GetDriveMyDriveRootPath().AppendASCII(dir1.title()), &entry));
500}
501
502TEST_F(ChangeListProcessorTest, RefreshDirectory_WrongParentId) {
503  // Prepare metadata.
504  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
505
506  // Create change list and add a new file to it.
507  scoped_ptr<ChangeList> change_list(new ChangeList);
508  ResourceEntry new_file;
509  new_file.set_title("new_file");
510  new_file.set_resource_id("new_file_id");
511  // This entry should not be added because the parent ID does not match.
512  change_list->mutable_parent_resource_ids()->push_back(
513      "some-random-resource-id");
514  change_list->mutable_entries()->push_back(new_file);
515
516
517  // Update the directory.
518  ResourceEntry root;
519  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
520      util::GetDriveMyDriveRootPath(), &root));
521  const int64 kNewChangestamp = 12345;
522  ResourceEntryVector refreshed_entries;
523  EXPECT_EQ(FILE_ERROR_OK, ChangeListProcessor::RefreshDirectory(
524      metadata_.get(),
525      DirectoryFetchInfo(root.local_id(), kRootId, kNewChangestamp),
526      change_list.Pass(),
527      &refreshed_entries));
528
529  // "new_file" should not be added.
530  ResourceEntry entry;
531  EXPECT_EQ(FILE_ERROR_NOT_FOUND, metadata_->GetResourceEntryByPath(
532      util::GetDriveMyDriveRootPath().AppendASCII(new_file.title()), &entry));
533}
534
535TEST_F(ChangeListProcessorTest, SharedFilesWithNoParentInFeed) {
536  // Prepare metadata.
537  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
538
539  // Create change lists.
540  ScopedVector<ChangeList> change_lists;
541  change_lists.push_back(new ChangeList);
542
543  // Add a new file with non-existing parent resource id to the change lists.
544  ResourceEntry new_file;
545  new_file.set_title("new_file");
546  new_file.set_resource_id("new_file_id");
547  change_lists[0]->mutable_entries()->push_back(new_file);
548  change_lists[0]->mutable_parent_resource_ids()->push_back("nonexisting");
549  change_lists[0]->set_largest_changestamp(kBaseResourceListChangestamp + 1);
550
551  std::set<base::FilePath> changed_dirs;
552  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
553
554  // "new_file" should be added under drive/other.
555  ResourceEntry entry;
556  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
557      util::GetDriveGrandRootPath().AppendASCII("other/new_file"), &entry));
558}
559
560TEST_F(ChangeListProcessorTest, ModificationDate) {
561  // Prepare metadata.
562  EXPECT_EQ(FILE_ERROR_OK, ApplyFullResourceList(CreateBaseChangeList()));
563
564  // Create change lists with a new file.
565  ScopedVector<ChangeList> change_lists;
566  change_lists.push_back(new ChangeList);
567
568  const base::Time now = base::Time::Now();
569  ResourceEntry new_file_remote;
570  new_file_remote.set_title("new_file_remote");
571  new_file_remote.set_resource_id("new_file_id");
572  new_file_remote.set_modification_date(now.ToInternalValue());
573
574  change_lists[0]->mutable_entries()->push_back(new_file_remote);
575  change_lists[0]->mutable_parent_resource_ids()->push_back(kRootId);
576  change_lists[0]->set_largest_changestamp(kBaseResourceListChangestamp + 1);
577
578  // Add the same file locally, but with a different name, a dirty metadata
579  // state, and a newer modification date.
580  ResourceEntry root;
581  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryByPath(
582      util::GetDriveMyDriveRootPath(), &root));
583
584  ResourceEntry new_file_local;
585  new_file_local.set_resource_id(new_file_remote.resource_id());
586  new_file_local.set_parent_local_id(root.local_id());
587  new_file_local.set_title("new_file_local");
588  new_file_local.set_metadata_edit_state(ResourceEntry::DIRTY);
589  new_file_local.set_modification_date(
590      (now + base::TimeDelta::FromSeconds(1)).ToInternalValue());
591  std::string local_id;
592  EXPECT_EQ(FILE_ERROR_OK, metadata_->AddEntry(new_file_local, &local_id));
593
594  // Apply the change.
595  std::set<base::FilePath> changed_dirs;
596  EXPECT_EQ(FILE_ERROR_OK, ApplyChangeList(change_lists.Pass(), &changed_dirs));
597
598  // The change is rejected due to the old modification date.
599  ResourceEntry entry;
600  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(local_id, &entry));
601  EXPECT_EQ(new_file_local.title(), entry.title());
602}
603
604}  // namespace internal
605}  // namespace drive
606