resource_metadata_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/resource_metadata.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
11#include "base/files/scoped_temp_dir.h"
12#include "base/sequenced_task_runner.h"
13#include "base/threading/sequenced_worker_pool.h"
14#include "base/threading/thread_restrictions.h"
15#include "chrome/browser/chromeos/drive/drive.pb.h"
16#include "chrome/browser/chromeos/drive/file_system_util.h"
17#include "chrome/browser/chromeos/drive/test_util.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/test/test_browser_thread_bundle.h"
20#include "google_apis/drive/test_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace drive {
24namespace internal {
25namespace {
26
27const char kTestRootResourceId[] = "test_root";
28
29// The changestamp of the resource metadata used in
30// ResourceMetadataTest.
31const int64 kTestChangestamp = 100;
32
33// Returns the sorted base names from |entries|.
34std::vector<std::string> GetSortedBaseNames(
35    const ResourceEntryVector& entries) {
36  std::vector<std::string> base_names;
37  for (size_t i = 0; i < entries.size(); ++i)
38    base_names.push_back(entries[i].base_name());
39  std::sort(base_names.begin(), base_names.end());
40
41  return base_names;
42}
43
44// Creates a ResourceEntry for a directory with explicitly set resource_id.
45ResourceEntry CreateDirectoryEntryWithResourceId(
46    const std::string& title,
47    const std::string& resource_id,
48    const std::string& parent_local_id) {
49  ResourceEntry entry;
50  entry.set_title(title);
51  entry.set_resource_id(resource_id);
52  entry.set_parent_local_id(parent_local_id);
53  entry.mutable_file_info()->set_is_directory(true);
54  entry.mutable_directory_specific_info()->set_changestamp(kTestChangestamp);
55  return entry;
56}
57
58// Creates a ResourceEntry for a directory.
59ResourceEntry CreateDirectoryEntry(const std::string& title,
60                                   const std::string& parent_local_id) {
61  return CreateDirectoryEntryWithResourceId(
62      title, "id:" + title, parent_local_id);
63}
64
65// Creates a ResourceEntry for a file with explicitly set resource_id.
66ResourceEntry CreateFileEntryWithResourceId(
67    const std::string& title,
68    const std::string& resource_id,
69    const std::string& parent_local_id) {
70  ResourceEntry entry;
71  entry.set_title(title);
72  entry.set_resource_id(resource_id);
73  entry.set_parent_local_id(parent_local_id);
74  entry.mutable_file_info()->set_is_directory(false);
75  entry.mutable_file_info()->set_size(1024);
76  entry.mutable_file_specific_info()->set_md5("md5:" + title);
77  return entry;
78}
79
80// Creates a ResourceEntry for a file.
81ResourceEntry CreateFileEntry(const std::string& title,
82                              const std::string& parent_local_id) {
83  return CreateFileEntryWithResourceId(title, "id:" + title, parent_local_id);
84}
85
86// Creates the following files/directories
87// drive/root/dir1/
88// drive/root/dir2/
89// drive/root/dir1/dir3/
90// drive/root/dir1/file4
91// drive/root/dir1/file5
92// drive/root/dir2/file6
93// drive/root/dir2/file7
94// drive/root/dir2/file8
95// drive/root/dir1/dir3/file9
96// drive/root/dir1/dir3/file10
97void SetUpEntries(ResourceMetadata* resource_metadata) {
98  // Create mydrive root directory.
99  std::string local_id;
100  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
101      util::CreateMyDriveRootEntry(kTestRootResourceId), &local_id));
102  const std::string root_local_id = local_id;
103
104  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
105      CreateDirectoryEntry("dir1", root_local_id), &local_id));
106  const std::string local_id_dir1 = local_id;
107
108  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
109      CreateDirectoryEntry("dir2", root_local_id), &local_id));
110  const std::string local_id_dir2 = local_id;
111
112  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
113      CreateDirectoryEntry("dir3", local_id_dir1), &local_id));
114  const std::string local_id_dir3 = local_id;
115
116  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
117      CreateFileEntry("file4", local_id_dir1), &local_id));
118  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
119      CreateFileEntry("file5", local_id_dir1), &local_id));
120
121  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
122      CreateFileEntry("file6", local_id_dir2), &local_id));
123  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
124      CreateFileEntry("file7", local_id_dir2), &local_id));
125  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
126      CreateFileEntry("file8", local_id_dir2), &local_id));
127
128  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
129      CreateFileEntry("file9", local_id_dir3), &local_id));
130  ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
131      CreateFileEntry("file10", local_id_dir3), &local_id));
132
133  ASSERT_EQ(FILE_ERROR_OK,
134            resource_metadata->SetLargestChangestamp(kTestChangestamp));
135}
136
137}  // namespace
138
139// Tests for methods invoked from the UI thread.
140class ResourceMetadataTestOnUIThread : public testing::Test {
141 protected:
142  virtual void SetUp() OVERRIDE {
143    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
144
145    base::ThreadRestrictions::SetIOAllowed(false);  // For strict thread check.
146    scoped_refptr<base::SequencedWorkerPool> pool =
147        content::BrowserThread::GetBlockingPool();
148    blocking_task_runner_ =
149        pool->GetSequencedTaskRunner(pool->GetSequenceToken());
150
151    metadata_storage_.reset(new ResourceMetadataStorage(
152        temp_dir_.path(), blocking_task_runner_.get()));
153    bool success = false;
154    base::PostTaskAndReplyWithResult(
155        blocking_task_runner_.get(),
156        FROM_HERE,
157        base::Bind(&ResourceMetadataStorage::Initialize,
158                   base::Unretained(metadata_storage_.get())),
159        google_apis::test_util::CreateCopyResultCallback(&success));
160    test_util::RunBlockingPoolTask();
161    ASSERT_TRUE(success);
162
163    resource_metadata_.reset(new ResourceMetadata(metadata_storage_.get(),
164                                                  blocking_task_runner_));
165
166    FileError error = FILE_ERROR_FAILED;
167    base::PostTaskAndReplyWithResult(
168        blocking_task_runner_.get(),
169        FROM_HERE,
170        base::Bind(&ResourceMetadata::Initialize,
171                   base::Unretained(resource_metadata_.get())),
172        google_apis::test_util::CreateCopyResultCallback(&error));
173    test_util::RunBlockingPoolTask();
174    ASSERT_EQ(FILE_ERROR_OK, error);
175
176    blocking_task_runner_->PostTask(
177        FROM_HERE,
178        base::Bind(&SetUpEntries,
179                   base::Unretained(resource_metadata_.get())));
180    test_util::RunBlockingPoolTask();
181  }
182
183  virtual void TearDown() OVERRIDE {
184    metadata_storage_.reset();
185    resource_metadata_.reset();
186    base::ThreadRestrictions::SetIOAllowed(true);
187  }
188
189  content::TestBrowserThreadBundle thread_bundle_;
190  base::ScopedTempDir temp_dir_;
191  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
192  scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
193      metadata_storage_;
194  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests>
195      resource_metadata_;
196};
197
198TEST_F(ResourceMetadataTestOnUIThread, GetResourceEntryByPath) {
199  // Confirm that an existing file is found.
200  FileError error = FILE_ERROR_FAILED;
201  scoped_ptr<ResourceEntry> entry;
202  resource_metadata_->GetResourceEntryByPathOnUIThread(
203      base::FilePath::FromUTF8Unsafe("drive/root/dir1/file4"),
204      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
205  test_util::RunBlockingPoolTask();
206  EXPECT_EQ(FILE_ERROR_OK, error);
207  ASSERT_TRUE(entry.get());
208  EXPECT_EQ("file4", entry->base_name());
209
210  // Confirm that a non existing file is not found.
211  error = FILE_ERROR_FAILED;
212  entry.reset();
213  resource_metadata_->GetResourceEntryByPathOnUIThread(
214      base::FilePath::FromUTF8Unsafe("drive/root/dir1/non_existing"),
215      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
216  test_util::RunBlockingPoolTask();
217  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
218  EXPECT_FALSE(entry.get());
219
220  // Confirm that the root is found.
221  error = FILE_ERROR_FAILED;
222  entry.reset();
223  resource_metadata_->GetResourceEntryByPathOnUIThread(
224      base::FilePath::FromUTF8Unsafe("drive"),
225      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
226  test_util::RunBlockingPoolTask();
227  EXPECT_EQ(FILE_ERROR_OK, error);
228  EXPECT_TRUE(entry.get());
229
230  // Confirm that a non existing file is not found at the root level.
231  error = FILE_ERROR_FAILED;
232  entry.reset();
233  resource_metadata_->GetResourceEntryByPathOnUIThread(
234      base::FilePath::FromUTF8Unsafe("non_existing"),
235      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
236  test_util::RunBlockingPoolTask();
237  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
238  EXPECT_FALSE(entry.get());
239
240  // Confirm that an entry is not found with a wrong root.
241  error = FILE_ERROR_FAILED;
242  entry.reset();
243  resource_metadata_->GetResourceEntryByPathOnUIThread(
244      base::FilePath::FromUTF8Unsafe("non_existing/root"),
245      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
246  test_util::RunBlockingPoolTask();
247  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
248  EXPECT_FALSE(entry.get());
249}
250
251TEST_F(ResourceMetadataTestOnUIThread, ReadDirectoryByPath) {
252  // Confirm that an existing directory is found.
253  FileError error = FILE_ERROR_FAILED;
254  scoped_ptr<ResourceEntryVector> entries;
255  resource_metadata_->ReadDirectoryByPathOnUIThread(
256      base::FilePath::FromUTF8Unsafe("drive/root/dir1"),
257      google_apis::test_util::CreateCopyResultCallback(&error, &entries));
258  test_util::RunBlockingPoolTask();
259  EXPECT_EQ(FILE_ERROR_OK, error);
260  ASSERT_TRUE(entries.get());
261  ASSERT_EQ(3U, entries->size());
262  // The order is not guaranteed so we should sort the base names.
263  std::vector<std::string> base_names = GetSortedBaseNames(*entries);
264  EXPECT_EQ("dir3", base_names[0]);
265  EXPECT_EQ("file4", base_names[1]);
266  EXPECT_EQ("file5", base_names[2]);
267
268  // Confirm that a non existing directory is not found.
269  error = FILE_ERROR_FAILED;
270  entries.reset();
271  resource_metadata_->ReadDirectoryByPathOnUIThread(
272      base::FilePath::FromUTF8Unsafe("drive/root/non_existing"),
273      google_apis::test_util::CreateCopyResultCallback(&error, &entries));
274  test_util::RunBlockingPoolTask();
275  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
276  EXPECT_FALSE(entries.get());
277
278  // Confirm that reading a file results in FILE_ERROR_NOT_A_DIRECTORY.
279  error = FILE_ERROR_FAILED;
280  entries.reset();
281  resource_metadata_->ReadDirectoryByPathOnUIThread(
282      base::FilePath::FromUTF8Unsafe("drive/root/dir1/file4"),
283      google_apis::test_util::CreateCopyResultCallback(&error, &entries));
284  test_util::RunBlockingPoolTask();
285  EXPECT_EQ(FILE_ERROR_NOT_A_DIRECTORY, error);
286  EXPECT_FALSE(entries.get());
287}
288
289// Tests for methods running on the blocking task runner.
290class ResourceMetadataTest : public testing::Test {
291 protected:
292  virtual void SetUp() OVERRIDE {
293    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
294
295    metadata_storage_.reset(new ResourceMetadataStorage(
296        temp_dir_.path(), base::MessageLoopProxy::current().get()));
297    ASSERT_TRUE(metadata_storage_->Initialize());
298
299    resource_metadata_.reset(new ResourceMetadata(
300        metadata_storage_.get(), base::MessageLoopProxy::current()));
301
302    ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
303
304    SetUpEntries(resource_metadata_.get());
305  }
306
307  base::ScopedTempDir temp_dir_;
308  content::TestBrowserThreadBundle thread_bundle_;
309  scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
310      metadata_storage_;
311  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests>
312      resource_metadata_;
313};
314
315TEST_F(ResourceMetadataTest, LargestChangestamp) {
316  const int64 kChangestamp = 123456;
317  EXPECT_EQ(FILE_ERROR_OK,
318            resource_metadata_->SetLargestChangestamp(kChangestamp));
319  EXPECT_EQ(kChangestamp, resource_metadata_->GetLargestChangestamp());
320}
321
322TEST_F(ResourceMetadataTest, RefreshEntry) {
323  base::FilePath drive_file_path;
324  ResourceEntry entry;
325
326  // Get file9.
327  std::string file_id;
328  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
329      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/file9"), &file_id));
330  EXPECT_EQ(FILE_ERROR_OK,
331            resource_metadata_->GetResourceEntryById(file_id, &entry));
332  EXPECT_EQ("file9", entry.base_name());
333  EXPECT_TRUE(!entry.file_info().is_directory());
334  EXPECT_EQ("md5:file9", entry.file_specific_info().md5());
335
336  // Rename it.
337  ResourceEntry file_entry(entry);
338  file_entry.set_title("file100");
339  EXPECT_EQ(FILE_ERROR_OK,
340            resource_metadata_->RefreshEntry(file_entry));
341
342  EXPECT_EQ("drive/root/dir1/dir3/file100",
343            resource_metadata_->GetFilePath(file_id).AsUTF8Unsafe());
344  entry.Clear();
345  EXPECT_EQ(FILE_ERROR_OK,
346            resource_metadata_->GetResourceEntryById(file_id, &entry));
347  EXPECT_EQ("file100", entry.base_name());
348  EXPECT_TRUE(!entry.file_info().is_directory());
349  EXPECT_EQ("md5:file9", entry.file_specific_info().md5());
350
351  // Update the file md5.
352  const std::string updated_md5("md5:updated");
353  file_entry = entry;
354  file_entry.mutable_file_specific_info()->set_md5(updated_md5);
355  EXPECT_EQ(FILE_ERROR_OK,
356            resource_metadata_->RefreshEntry(file_entry));
357
358  EXPECT_EQ("drive/root/dir1/dir3/file100",
359            resource_metadata_->GetFilePath(file_id).AsUTF8Unsafe());
360  entry.Clear();
361  EXPECT_EQ(FILE_ERROR_OK,
362            resource_metadata_->GetResourceEntryById(file_id, &entry));
363  EXPECT_EQ("file100", entry.base_name());
364  EXPECT_TRUE(!entry.file_info().is_directory());
365  EXPECT_EQ(updated_md5, entry.file_specific_info().md5());
366
367  // Make sure we get the same thing from GetResourceEntryByPath.
368  entry.Clear();
369  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryByPath(
370      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/file100"), &entry));
371  EXPECT_EQ("file100", entry.base_name());
372  ASSERT_TRUE(!entry.file_info().is_directory());
373  EXPECT_EQ(updated_md5, entry.file_specific_info().md5());
374
375  // Get dir2.
376  entry.Clear();
377  std::string dir_id;
378  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
379      base::FilePath::FromUTF8Unsafe("drive/root/dir2"), &dir_id));
380  EXPECT_EQ(FILE_ERROR_OK,
381            resource_metadata_->GetResourceEntryById(dir_id, &entry));
382  EXPECT_EQ("dir2", entry.base_name());
383  ASSERT_TRUE(entry.file_info().is_directory());
384
385  // Get dir3's ID.
386  std::string dir3_id;
387  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
388      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3"), &dir3_id));
389
390  // Change the name to dir100 and change the parent to drive/dir1/dir3.
391  ResourceEntry dir_entry(entry);
392  dir_entry.set_title("dir100");
393  dir_entry.set_parent_local_id(dir3_id);
394  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->RefreshEntry(dir_entry));
395
396  EXPECT_EQ("drive/root/dir1/dir3/dir100",
397            resource_metadata_->GetFilePath(dir_id).AsUTF8Unsafe());
398  entry.Clear();
399  EXPECT_EQ(FILE_ERROR_OK,
400            resource_metadata_->GetResourceEntryById(dir_id, &entry));
401  EXPECT_EQ("dir100", entry.base_name());
402  EXPECT_TRUE(entry.file_info().is_directory());
403  EXPECT_EQ("id:dir2", entry.resource_id());
404
405  // Make sure the children have moved over. Test file6.
406  entry.Clear();
407  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryByPath(
408      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/dir100/file6"),
409      &entry));
410  EXPECT_EQ("file6", entry.base_name());
411
412  // Make sure dir2 no longer exists.
413  EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->GetResourceEntryByPath(
414      base::FilePath::FromUTF8Unsafe("drive/root/dir2"), &entry));
415
416  // Make sure that directory cannot move under a file.
417  dir_entry.set_parent_local_id(file_id);
418  EXPECT_EQ(FILE_ERROR_NOT_A_DIRECTORY,
419            resource_metadata_->RefreshEntry(dir_entry));
420
421  // Cannot refresh root.
422  dir_entry.Clear();
423  dir_entry.set_resource_id(util::kDriveGrandRootLocalId);
424  dir_entry.set_local_id(util::kDriveGrandRootLocalId);
425  dir_entry.set_title("new-root-name");
426  dir_entry.set_parent_local_id(dir3_id);
427  EXPECT_EQ(FILE_ERROR_INVALID_OPERATION,
428            resource_metadata_->RefreshEntry(dir_entry));
429}
430
431TEST_F(ResourceMetadataTest, GetSubDirectoriesRecursively) {
432  std::set<base::FilePath> sub_directories;
433
434  // file9: not a directory, so no children.
435  std::string local_id;
436  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
437      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/file9"), &local_id));
438  resource_metadata_->GetSubDirectoriesRecursively(local_id, &sub_directories);
439  EXPECT_TRUE(sub_directories.empty());
440
441  // dir2: no child directories.
442  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
443      base::FilePath::FromUTF8Unsafe("drive/root/dir2"), &local_id));
444  resource_metadata_->GetSubDirectoriesRecursively(local_id, &sub_directories);
445  EXPECT_TRUE(sub_directories.empty());
446  const std::string dir2_id = local_id;
447
448  // dir1: dir3 is the only child
449  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
450      base::FilePath::FromUTF8Unsafe("drive/root/dir1"), &local_id));
451  resource_metadata_->GetSubDirectoriesRecursively(local_id, &sub_directories);
452  EXPECT_EQ(1u, sub_directories.size());
453  EXPECT_EQ(1u, sub_directories.count(
454      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3")));
455  sub_directories.clear();
456
457  // Add a few more directories to make sure deeper nesting works.
458  // dir2/dir100
459  // dir2/dir101
460  // dir2/dir101/dir102
461  // dir2/dir101/dir103
462  // dir2/dir101/dir104
463  // dir2/dir101/dir104/dir105
464  // dir2/dir101/dir104/dir105/dir106
465  // dir2/dir101/dir104/dir105/dir106/dir107
466  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
467      CreateDirectoryEntry("dir100", dir2_id), &local_id));
468  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
469      CreateDirectoryEntry("dir101", dir2_id), &local_id));
470  const std::string dir101_id = local_id;
471  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
472      CreateDirectoryEntry("dir102", dir101_id), &local_id));
473  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
474      CreateDirectoryEntry("dir103", dir101_id), &local_id));
475  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
476      CreateDirectoryEntry("dir104", dir101_id), &local_id));
477  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
478      CreateDirectoryEntry("dir105", local_id), &local_id));
479  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
480      CreateDirectoryEntry("dir106", local_id), &local_id));
481  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
482      CreateDirectoryEntry("dir107", local_id), &local_id));
483
484  resource_metadata_->GetSubDirectoriesRecursively(dir2_id, &sub_directories);
485  EXPECT_EQ(8u, sub_directories.size());
486  EXPECT_EQ(1u, sub_directories.count(base::FilePath::FromUTF8Unsafe(
487      "drive/root/dir2/dir101")));
488  EXPECT_EQ(1u, sub_directories.count(base::FilePath::FromUTF8Unsafe(
489      "drive/root/dir2/dir101/dir104")));
490  EXPECT_EQ(1u, sub_directories.count(base::FilePath::FromUTF8Unsafe(
491      "drive/root/dir2/dir101/dir104/dir105/dir106/dir107")));
492}
493
494TEST_F(ResourceMetadataTest, AddEntry) {
495  base::FilePath drive_file_path;
496
497  // Add a file to dir3.
498  std::string local_id;
499  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
500      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3"), &local_id));
501  ResourceEntry file_entry = CreateFileEntry("file100", local_id);
502  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(file_entry, &local_id));
503  EXPECT_EQ("drive/root/dir1/dir3/file100",
504            resource_metadata_->GetFilePath(local_id).AsUTF8Unsafe());
505
506  // Add a directory.
507  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
508      base::FilePath::FromUTF8Unsafe("drive/root/dir1"), &local_id));
509  ResourceEntry dir_entry = CreateDirectoryEntry("dir101", local_id);
510  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(dir_entry, &local_id));
511  EXPECT_EQ("drive/root/dir1/dir101",
512            resource_metadata_->GetFilePath(local_id).AsUTF8Unsafe());
513
514  // Add to an invalid parent.
515  ResourceEntry file_entry3 = CreateFileEntry("file103", "id:invalid");
516  EXPECT_EQ(FILE_ERROR_NOT_FOUND,
517            resource_metadata_->AddEntry(file_entry3, &local_id));
518
519  // Add an existing file.
520  EXPECT_EQ(FILE_ERROR_EXISTS,
521            resource_metadata_->AddEntry(file_entry, &local_id));
522}
523
524TEST_F(ResourceMetadataTest, RemoveEntry) {
525  // Make sure file9 is found.
526  std::string file9_local_id;
527  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
528      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/file9"),
529      &file9_local_id));
530  ResourceEntry entry;
531  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
532      file9_local_id, &entry));
533  EXPECT_EQ("file9", entry.base_name());
534
535  // Remove file9.
536  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->RemoveEntry(file9_local_id));
537
538  // file9 should no longer exist.
539  EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->GetResourceEntryById(
540      file9_local_id, &entry));
541
542  // Look for dir3.
543  std::string dir3_local_id;
544  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
545      base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3"), &dir3_local_id));
546  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
547      dir3_local_id, &entry));
548  EXPECT_EQ("dir3", entry.base_name());
549
550  // Remove dir3.
551  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->RemoveEntry(dir3_local_id));
552
553  // dir3 should no longer exist.
554  EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->GetResourceEntryById(
555      dir3_local_id, &entry));
556
557  // Remove unknown local_id using RemoveEntry.
558  EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->RemoveEntry("foo"));
559
560  // Try removing root. This should fail.
561  EXPECT_EQ(FILE_ERROR_ACCESS_DENIED, resource_metadata_->RemoveEntry(
562      util::kDriveGrandRootLocalId));
563}
564
565TEST_F(ResourceMetadataTest, GetResourceEntryById_RootDirectory) {
566  // Look up the root directory by its ID.
567  ResourceEntry entry;
568  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
569      util::kDriveGrandRootLocalId, &entry));
570  EXPECT_EQ("drive", entry.base_name());
571}
572
573TEST_F(ResourceMetadataTest, GetResourceEntryById) {
574  // Get file4 by path.
575  std::string local_id;
576  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
577      base::FilePath::FromUTF8Unsafe("drive/root/dir1/file4"), &local_id));
578
579  // Confirm that an existing file is found.
580  ResourceEntry entry;
581  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
582      local_id, &entry));
583  EXPECT_EQ("file4", entry.base_name());
584
585  // Confirm that a non existing file is not found.
586  EXPECT_EQ(FILE_ERROR_NOT_FOUND, resource_metadata_->GetResourceEntryById(
587      "file:non_existing", &entry));
588}
589
590TEST_F(ResourceMetadataTest, Iterate) {
591  scoped_ptr<ResourceMetadata::Iterator> it = resource_metadata_->GetIterator();
592  ASSERT_TRUE(it);
593
594  int file_count = 0, directory_count = 0;
595  for (; !it->IsAtEnd(); it->Advance()) {
596    if (!it->GetValue().file_info().is_directory())
597      ++file_count;
598    else
599      ++directory_count;
600  }
601
602  EXPECT_EQ(7, file_count);
603  EXPECT_EQ(7, directory_count);
604}
605
606TEST_F(ResourceMetadataTest, DuplicatedNames) {
607  std::string root_local_id;
608  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
609      base::FilePath::FromUTF8Unsafe("drive/root"), &root_local_id));
610
611  ResourceEntry entry;
612
613  // When multiple entries with the same title are added in a single directory,
614  // their base_names are de-duped.
615  // - drive/root/foo
616  // - drive/root/foo (1)
617  std::string dir_id_0;
618  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
619      CreateDirectoryEntryWithResourceId(
620          "foo", "foo0", root_local_id), &dir_id_0));
621  std::string dir_id_1;
622  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
623      CreateDirectoryEntryWithResourceId(
624          "foo", "foo1", root_local_id), &dir_id_1));
625
626  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
627      dir_id_0, &entry));
628  EXPECT_EQ("foo", entry.base_name());
629  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
630      dir_id_1, &entry));
631  EXPECT_EQ("foo (1)", entry.base_name());
632
633  // - drive/root/foo/bar.txt
634  // - drive/root/foo/bar (1).txt
635  // - drive/root/foo/bar (2).txt
636  std::string file_id_0;
637  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
638      CreateFileEntryWithResourceId(
639          "bar.txt", "bar0", dir_id_0), &file_id_0));
640  std::string file_id_1;
641  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
642      CreateFileEntryWithResourceId(
643          "bar.txt", "bar1", dir_id_0), &file_id_1));
644  std::string file_id_2;
645  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
646      CreateFileEntryWithResourceId(
647          "bar.txt", "bar2", dir_id_0), &file_id_2));
648
649  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
650      file_id_0, &entry));
651  EXPECT_EQ("bar.txt", entry.base_name());
652  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
653      file_id_1, &entry));
654  EXPECT_EQ("bar (1).txt", entry.base_name());
655  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
656      file_id_2, &entry));
657  EXPECT_EQ("bar (2).txt", entry.base_name());
658
659  // Same name but different parent. No renaming.
660  // - drive/root/foo (1)/bar.txt
661  std::string file_id_3;
662  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
663      CreateFileEntryWithResourceId(
664          "bar.txt", "bar3", dir_id_1), &file_id_3));
665
666  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
667      file_id_3, &entry));
668  EXPECT_EQ("bar.txt", entry.base_name());
669
670  // Checks that the entries can be looked up by the de-duped paths.
671  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryByPath(
672      base::FilePath::FromUTF8Unsafe("drive/root/foo/bar (2).txt"), &entry));
673  EXPECT_EQ("bar2", entry.resource_id());
674  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryByPath(
675      base::FilePath::FromUTF8Unsafe("drive/root/foo (1)/bar.txt"), &entry));
676  EXPECT_EQ("bar3", entry.resource_id());
677}
678
679TEST_F(ResourceMetadataTest, EncodedNames) {
680  std::string root_local_id;
681  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
682      base::FilePath::FromUTF8Unsafe("drive/root"), &root_local_id));
683
684  ResourceEntry entry;
685
686  std::string dir_id;
687  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
688      CreateDirectoryEntry("\\(^o^)/", root_local_id), &dir_id));
689  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
690      dir_id, &entry));
691  EXPECT_EQ("\\(^o^)_", entry.base_name());
692
693  std::string file_id;
694  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
695      CreateFileEntryWithResourceId("Slash /.txt", "myfile", dir_id),
696      &file_id));
697  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
698      file_id, &entry));
699  EXPECT_EQ("Slash _.txt", entry.base_name());
700
701  ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryByPath(
702      base::FilePath::FromUTF8Unsafe(
703          "drive/root/\\(^o^)_/Slash _.txt"),
704      &entry));
705  EXPECT_EQ("myfile", entry.resource_id());
706}
707
708TEST_F(ResourceMetadataTest, Reset) {
709  // The grand root has "root" which is not empty.
710  std::vector<ResourceEntry> entries;
711  ASSERT_EQ(FILE_ERROR_OK,
712            resource_metadata_->ReadDirectoryByPath(
713                base::FilePath::FromUTF8Unsafe("drive/root"), &entries));
714  ASSERT_FALSE(entries.empty());
715
716  // Reset.
717  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->Reset());
718
719  // change stamp should be reset.
720  EXPECT_EQ(0, resource_metadata_->GetLargestChangestamp());
721
722  // root should continue to exist.
723  ResourceEntry entry;
724  ASSERT_EQ(FILE_ERROR_OK,
725            resource_metadata_->GetResourceEntryByPath(
726                base::FilePath::FromUTF8Unsafe("drive"), &entry));
727  EXPECT_EQ("drive", entry.base_name());
728  ASSERT_TRUE(entry.file_info().is_directory());
729  EXPECT_EQ(util::kDriveGrandRootLocalId, entry.resource_id());
730
731  // There are "other" and "trash" under "drive".
732  ASSERT_EQ(FILE_ERROR_OK,
733            resource_metadata_->ReadDirectoryByPath(
734                base::FilePath::FromUTF8Unsafe("drive"), &entries));
735  EXPECT_EQ(2U, entries.size());
736
737  // The "other" directory should be empty.
738  ASSERT_EQ(FILE_ERROR_OK,
739            resource_metadata_->ReadDirectoryByPath(
740                base::FilePath::FromUTF8Unsafe("drive/other"), &entries));
741  EXPECT_TRUE(entries.empty());
742
743  // The "trash" directory should be empty.
744  ASSERT_EQ(FILE_ERROR_OK,
745            resource_metadata_->ReadDirectoryByPath(
746                base::FilePath::FromUTF8Unsafe("drive/trash"), &entries));
747  EXPECT_TRUE(entries.empty());
748}
749
750}  // namespace internal
751}  // namespace drive
752