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