Home | History | Annotate | Download | only in drive
      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/file_system.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/scoped_temp_dir.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop/message_loop_proxy.h"
     16 #include "base/prefs/testing_pref_service.h"
     17 #include "base/run_loop.h"
     18 #include "chrome/browser/chromeos/drive/change_list_loader.h"
     19 #include "chrome/browser/chromeos/drive/drive.pb.h"
     20 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
     21 #include "chrome/browser/chromeos/drive/file_system_observer.h"
     22 #include "chrome/browser/chromeos/drive/file_system_util.h"
     23 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     24 #include "chrome/browser/chromeos/drive/sync_client.h"
     25 #include "chrome/browser/chromeos/drive/test_util.h"
     26 #include "chrome/browser/drive/fake_drive_service.h"
     27 #include "chrome/browser/google_apis/drive_api_parser.h"
     28 #include "chrome/browser/google_apis/test_util.h"
     29 #include "chrome/test/base/testing_profile.h"
     30 #include "content/public/test/test_browser_thread_bundle.h"
     31 #include "testing/gtest/include/gtest/gtest.h"
     32 
     33 namespace drive {
     34 namespace {
     35 
     36 const int64 kLotsOfSpace = internal::kMinFreeSpace * 10;
     37 
     38 // Counts the number of invocation, and if it increased up to |expected_counter|
     39 // quits the current message loop by calling |quit|.
     40 void AsyncInitializationCallback(
     41     int* counter, int expected_counter, const base::Closure& quit,
     42     FileError error, scoped_ptr<ResourceEntry> entry) {
     43   if (error != FILE_ERROR_OK || !entry) {
     44     // If we hit an error case, quit the message loop immediately.
     45     // Then the expectation in the test case can find it because the actual
     46     // value of |counter| is different from the expected one.
     47     quit.Run();
     48     return;
     49   }
     50 
     51   (*counter)++;
     52   if (*counter >= expected_counter)
     53     quit.Run();
     54 }
     55 
     56 // This class is used to record directory changes and examine them later.
     57 class MockDirectoryChangeObserver : public FileSystemObserver {
     58  public:
     59   MockDirectoryChangeObserver() {}
     60   virtual ~MockDirectoryChangeObserver() {}
     61 
     62   // FileSystemObserver overrides.
     63   virtual void OnDirectoryChanged(
     64       const base::FilePath& directory_path) OVERRIDE {
     65     changed_directories_.push_back(directory_path);
     66   }
     67 
     68   const std::vector<base::FilePath>& changed_directories() const {
     69     return changed_directories_;
     70   }
     71 
     72  private:
     73   std::vector<base::FilePath> changed_directories_;
     74   DISALLOW_COPY_AND_ASSIGN(MockDirectoryChangeObserver);
     75 };
     76 
     77 }  // namespace
     78 
     79 class FileSystemTest : public testing::Test {
     80  protected:
     81   virtual void SetUp() OVERRIDE {
     82     profile_.reset(new TestingProfile);
     83     pref_service_.reset(new TestingPrefServiceSimple);
     84     test_util::RegisterDrivePrefs(pref_service_->registry());
     85 
     86     fake_network_change_notifier_.reset(
     87         new test_util::FakeNetworkChangeNotifier);
     88 
     89     fake_drive_service_.reset(new FakeDriveService);
     90     fake_drive_service_->LoadResourceListForWapi(
     91         "gdata/root_feed.json");
     92     fake_drive_service_->LoadAccountMetadataForWapi(
     93         "gdata/account_metadata.json");
     94 
     95     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
     96 
     97     scheduler_.reset(new JobScheduler(pref_service_.get(),
     98                                       fake_drive_service_.get(),
     99                                       base::MessageLoopProxy::current().get()));
    100 
    101     ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
    102         profile_.get()).Append(util::kMetadataDirectory)));
    103     ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
    104         profile_.get()).Append(util::kCacheFileDirectory)));
    105     ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
    106         profile_.get()).Append(util::kTemporaryFileDirectory)));
    107 
    108     mock_directory_observer_.reset(new MockDirectoryChangeObserver);
    109 
    110     SetUpResourceMetadataAndFileSystem();
    111   }
    112 
    113   void SetUpResourceMetadataAndFileSystem() {
    114     metadata_storage_.reset(new internal::ResourceMetadataStorage(
    115         util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory),
    116         base::MessageLoopProxy::current().get()));
    117     ASSERT_TRUE(metadata_storage_->Initialize());
    118 
    119     cache_.reset(new internal::FileCache(
    120         metadata_storage_.get(),
    121         util::GetCacheRootPath(profile_.get()).Append(
    122             util::kCacheFileDirectory),
    123         base::MessageLoopProxy::current().get(),
    124         fake_free_disk_space_getter_.get()));
    125     ASSERT_TRUE(cache_->Initialize());
    126 
    127     resource_metadata_.reset(new internal::ResourceMetadata(
    128         metadata_storage_.get(), base::MessageLoopProxy::current()));
    129 
    130     file_system_.reset(new FileSystem(
    131         pref_service_.get(),
    132         cache_.get(),
    133         fake_drive_service_.get(),
    134         scheduler_.get(),
    135         resource_metadata_.get(),
    136         base::MessageLoopProxy::current().get(),
    137         util::GetCacheRootPath(profile_.get()).Append(
    138             util::kTemporaryFileDirectory)));
    139     file_system_->AddObserver(mock_directory_observer_.get());
    140     file_system_->Initialize();
    141 
    142     // Disable delaying so that the sync starts immediately.
    143     file_system_->sync_client_for_testing()->set_delay_for_testing(
    144         base::TimeDelta::FromSeconds(0));
    145 
    146     ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
    147   }
    148 
    149   // Loads the full resource list via FakeDriveService.
    150   bool LoadFullResourceList() {
    151     FileError error = FILE_ERROR_FAILED;
    152     file_system_->change_list_loader_for_testing()->LoadIfNeeded(
    153         DirectoryFetchInfo(),
    154         google_apis::test_util::CreateCopyResultCallback(&error));
    155     test_util::RunBlockingPoolTask();
    156     return error == FILE_ERROR_OK;
    157   }
    158 
    159   // Gets resource entry by path synchronously.
    160   scoped_ptr<ResourceEntry> GetResourceEntryByPathSync(
    161       const base::FilePath& file_path) {
    162     FileError error = FILE_ERROR_FAILED;
    163     scoped_ptr<ResourceEntry> entry;
    164     file_system_->GetResourceEntryByPath(
    165         file_path,
    166         google_apis::test_util::CreateCopyResultCallback(&error, &entry));
    167     test_util::RunBlockingPoolTask();
    168 
    169     return entry.Pass();
    170   }
    171 
    172   // Gets directory info by path synchronously.
    173   scoped_ptr<ResourceEntryVector> ReadDirectoryByPathSync(
    174       const base::FilePath& file_path) {
    175     FileError error = FILE_ERROR_FAILED;
    176     scoped_ptr<ResourceEntryVector> entries;
    177     file_system_->ReadDirectoryByPath(
    178         file_path,
    179         google_apis::test_util::CreateCopyResultCallback(
    180             &error, &entries));
    181     test_util::RunBlockingPoolTask();
    182 
    183     return entries.Pass();
    184   }
    185 
    186   // Returns true if an entry exists at |file_path|.
    187   bool EntryExists(const base::FilePath& file_path) {
    188     return GetResourceEntryByPathSync(file_path);
    189   }
    190 
    191   // Gets the resource ID of |file_path|. Returns an empty string if not found.
    192   std::string GetResourceIdByPath(const base::FilePath& file_path) {
    193     scoped_ptr<ResourceEntry> entry =
    194         GetResourceEntryByPathSync(file_path);
    195     if (entry)
    196       return entry->resource_id();
    197     else
    198       return "";
    199   }
    200 
    201   // Flag for specifying the timestamp of the test filesystem cache.
    202   enum SetUpTestFileSystemParam {
    203     USE_OLD_TIMESTAMP,
    204     USE_SERVER_TIMESTAMP,
    205   };
    206 
    207   // Sets up a filesystem with directories: drive/root, drive/root/Dir1,
    208   // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2,
    209   // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets
    210   // the changestamp to 654321, equal to that of "account_metadata.json" test
    211   // data, indicating the cache is holding the latest file system info.
    212   bool SetUpTestFileSystem(SetUpTestFileSystemParam param) {
    213     // Destroy the existing resource metadata to close DB.
    214     resource_metadata_.reset();
    215 
    216     base::FilePath metadata_directory =
    217         util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory);
    218     scoped_ptr<internal::ResourceMetadataStorage,
    219                test_util::DestroyHelperForTests> metadata_storage(
    220         new internal::ResourceMetadataStorage(
    221             metadata_directory, base::MessageLoopProxy::current().get()));
    222 
    223     scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
    224         resource_metadata(new internal::ResourceMetadata(
    225             metadata_storage_.get(), base::MessageLoopProxy::current()));
    226 
    227     if (resource_metadata->Initialize() != FILE_ERROR_OK)
    228       return false;
    229 
    230     const int64 changestamp = param == USE_SERVER_TIMESTAMP ? 654321 : 1;
    231     if (resource_metadata->SetLargestChangestamp(changestamp) != FILE_ERROR_OK)
    232       return false;
    233 
    234     // drive/root
    235     const std::string root_resource_id =
    236         fake_drive_service_->GetRootResourceId();
    237     if (resource_metadata->AddEntry(util::CreateMyDriveRootEntry(
    238             root_resource_id)) != FILE_ERROR_OK)
    239       return false;
    240 
    241     // drive/root/File1
    242     ResourceEntry file1;
    243     file1.set_title("File1");
    244     file1.set_resource_id("resource_id:File1");
    245     file1.set_parent_resource_id(root_resource_id);
    246     file1.mutable_file_specific_info()->set_md5("md5");
    247     file1.mutable_file_info()->set_is_directory(false);
    248     file1.mutable_file_info()->set_size(1048576);
    249     if (resource_metadata->AddEntry(file1) != FILE_ERROR_OK)
    250       return false;
    251 
    252     // drive/root/Dir1
    253     ResourceEntry dir1;
    254     dir1.set_title("Dir1");
    255     dir1.set_resource_id("resource_id:Dir1");
    256     dir1.set_parent_resource_id(root_resource_id);
    257     dir1.mutable_file_info()->set_is_directory(true);
    258     if (resource_metadata->AddEntry(dir1) != FILE_ERROR_OK)
    259       return false;
    260 
    261     // drive/root/Dir1/File2
    262     ResourceEntry file2;
    263     file2.set_title("File2");
    264     file2.set_resource_id("resource_id:File2");
    265     file2.set_parent_resource_id(dir1.resource_id());
    266     file2.mutable_file_specific_info()->set_md5("md5");
    267     file2.mutable_file_info()->set_is_directory(false);
    268     file2.mutable_file_info()->set_size(555);
    269     if (resource_metadata->AddEntry(file2) != FILE_ERROR_OK)
    270       return false;
    271 
    272     // drive/root/Dir1/SubDir2
    273     ResourceEntry dir2;
    274     dir2.set_title("SubDir2");
    275     dir2.set_resource_id("resource_id:SubDir2");
    276     dir2.set_parent_resource_id(dir1.resource_id());
    277     dir2.mutable_file_info()->set_is_directory(true);
    278     if (resource_metadata->AddEntry(dir2) != FILE_ERROR_OK)
    279       return false;
    280 
    281     // drive/root/Dir1/SubDir2/File3
    282     ResourceEntry file3;
    283     file3.set_title("File3");
    284     file3.set_resource_id("resource_id:File3");
    285     file3.set_parent_resource_id(dir2.resource_id());
    286     file3.mutable_file_specific_info()->set_md5("md5");
    287     file3.mutable_file_info()->set_is_directory(false);
    288     file3.mutable_file_info()->set_size(12345);
    289     if (resource_metadata->AddEntry(file3) != FILE_ERROR_OK)
    290       return false;
    291 
    292     // Recreate resource metadata.
    293     SetUpResourceMetadataAndFileSystem();
    294 
    295     return true;
    296   }
    297 
    298   content::TestBrowserThreadBundle thread_bundle_;
    299   scoped_ptr<TestingProfile> profile_;
    300   // We don't use TestingProfile::GetPrefs() in favor of having less
    301   // dependencies to Profile in general.
    302   scoped_ptr<TestingPrefServiceSimple> pref_service_;
    303   scoped_ptr<test_util::FakeNetworkChangeNotifier>
    304       fake_network_change_notifier_;
    305 
    306   scoped_ptr<FakeDriveService> fake_drive_service_;
    307   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
    308   scoped_ptr<JobScheduler> scheduler_;
    309   scoped_ptr<MockDirectoryChangeObserver> mock_directory_observer_;
    310 
    311   scoped_ptr<internal::ResourceMetadataStorage,
    312              test_util::DestroyHelperForTests> metadata_storage_;
    313   scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_;
    314   scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
    315       resource_metadata_;
    316   scoped_ptr<FileSystem> file_system_;
    317 };
    318 
    319 TEST_F(FileSystemTest, DuplicatedAsyncInitialization) {
    320   base::RunLoop loop;
    321 
    322   int counter = 0;
    323   const GetResourceEntryCallback& callback = base::Bind(
    324       &AsyncInitializationCallback, &counter, 2, loop.QuitClosure());
    325 
    326   file_system_->GetResourceEntryByPath(
    327       base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
    328   file_system_->GetResourceEntryByPath(
    329       base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
    330   loop.Run();  // Wait to get our result
    331   EXPECT_EQ(2, counter);
    332 
    333   // Although GetResourceEntryByPath() was called twice, the resource list
    334   // should only be loaded once. In the past, there was a bug that caused
    335   // it to be loaded twice.
    336   EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
    337   // See the comment in GetMyDriveRoot test case why this is 2.
    338   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
    339 
    340   // "Fast fetch" will fire an OnirectoryChanged event.
    341   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
    342   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
    343             mock_directory_observer_->changed_directories()[0]);
    344 }
    345 
    346 TEST_F(FileSystemTest, GetGrandRootEntry) {
    347   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive"));
    348   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    349   ASSERT_TRUE(entry);
    350   EXPECT_EQ(util::kDriveGrandRootSpecialResourceId, entry->resource_id());
    351 
    352   // Getting the grand root entry should not cause the resource load to happen.
    353   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
    354   EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
    355 }
    356 
    357 TEST_F(FileSystemTest, GetOtherDirEntry) {
    358   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/other"));
    359   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    360   ASSERT_TRUE(entry);
    361   EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, entry->resource_id());
    362 
    363   // Getting the "other" directory entry should not cause the resource load to
    364   // happen.
    365   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
    366   EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
    367 }
    368 
    369 TEST_F(FileSystemTest, GetMyDriveRoot) {
    370   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root"));
    371   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    372   ASSERT_TRUE(entry);
    373   EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id());
    374 
    375   // Absence of "drive/root" in the local metadata triggers the "fast fetch"
    376   // of "drive" directory. Fetch of "drive" grand root directory has a special
    377   // implementation. Instead of normal GetResourceListInDirectory(), it is
    378   // emulated by calling GetAboutResource() so that the resource_id of
    379   // "drive/root" is listed.
    380   // Together with the normal GetAboutResource() call to retrieve the largest
    381   // changestamp, the method is called twice.
    382   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
    383 
    384   // After "fast fetch" is done, full resource list is fetched.
    385   EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
    386 
    387   // "Fast fetch" will fire an OnirectoryChanged event.
    388   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
    389   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
    390             mock_directory_observer_->changed_directories()[0]);
    391 }
    392 
    393 TEST_F(FileSystemTest, GetExistingFile) {
    394   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    395   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    396   ASSERT_TRUE(entry);
    397   EXPECT_EQ("file:2_file_resource_id", entry->resource_id());
    398 
    399   EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
    400   EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
    401 }
    402 
    403 TEST_F(FileSystemTest, GetExistingDocument) {
    404   const base::FilePath kFilePath(
    405       FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc"));
    406   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    407   ASSERT_TRUE(entry);
    408   EXPECT_EQ("document:5_document_resource_id", entry->resource_id());
    409 }
    410 
    411 TEST_F(FileSystemTest, GetNonExistingFile) {
    412   const base::FilePath kFilePath(
    413       FILE_PATH_LITERAL("drive/root/nonexisting.file"));
    414   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    415   EXPECT_FALSE(entry);
    416 }
    417 
    418 TEST_F(FileSystemTest, GetEncodedFileNames) {
    419   const base::FilePath kFilePath1(
    420       FILE_PATH_LITERAL("drive/root/Slash / in file 1.txt"));
    421   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath1);
    422   ASSERT_FALSE(entry);
    423 
    424   const base::FilePath kFilePath2 = base::FilePath::FromUTF8Unsafe(
    425       "drive/root/Slash \xE2\x88\x95 in file 1.txt");
    426   entry = GetResourceEntryByPathSync(kFilePath2);
    427   ASSERT_TRUE(entry);
    428   EXPECT_EQ("file:slash_file_resource_id", entry->resource_id());
    429 
    430   const base::FilePath kFilePath3 = base::FilePath::FromUTF8Unsafe(
    431       "drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt");
    432   entry = GetResourceEntryByPathSync(kFilePath3);
    433   ASSERT_TRUE(entry);
    434   EXPECT_EQ("file:slash_subdir_file", entry->resource_id());
    435 }
    436 
    437 TEST_F(FileSystemTest, GetDuplicateNames) {
    438   const base::FilePath kFilePath1(
    439       FILE_PATH_LITERAL("drive/root/Duplicate Name.txt"));
    440   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath1);
    441   ASSERT_TRUE(entry);
    442   const std::string resource_id1 = entry->resource_id();
    443 
    444   const base::FilePath kFilePath2(
    445       FILE_PATH_LITERAL("drive/root/Duplicate Name (1).txt"));
    446   entry = GetResourceEntryByPathSync(kFilePath2);
    447   ASSERT_TRUE(entry);
    448   const std::string resource_id2 = entry->resource_id();
    449 
    450   // The entries are de-duped non-deterministically, so we shouldn't rely on the
    451   // names matching specific resource ids.
    452   const std::string file3_resource_id = "file:3_file_resource_id";
    453   const std::string file4_resource_id = "file:4_file_resource_id";
    454   EXPECT_TRUE(file3_resource_id == resource_id1 ||
    455               file3_resource_id == resource_id2);
    456   EXPECT_TRUE(file4_resource_id == resource_id1 ||
    457               file4_resource_id == resource_id2);
    458 }
    459 
    460 TEST_F(FileSystemTest, GetExistingDirectory) {
    461   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/Directory 1"));
    462   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    463   ASSERT_TRUE(entry);
    464   ASSERT_EQ("folder:1_folder_resource_id", entry->resource_id());
    465 
    466   // The changestamp should be propagated to the directory.
    467   EXPECT_EQ(fake_drive_service_->largest_changestamp(),
    468             entry->directory_specific_info().changestamp());
    469 }
    470 
    471 TEST_F(FileSystemTest, GetInSubSubdir) {
    472   const base::FilePath kFilePath(
    473       FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/"
    474                         "Sub Sub Directory Folder"));
    475   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    476   ASSERT_TRUE(entry);
    477   ASSERT_EQ("folder:sub_sub_directory_folder_id", entry->resource_id());
    478 }
    479 
    480 TEST_F(FileSystemTest, GetOrphanFile) {
    481   const base::FilePath kFilePath(
    482       FILE_PATH_LITERAL("drive/other/Orphan File 1.txt"));
    483   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
    484   ASSERT_TRUE(entry);
    485   EXPECT_EQ("file:1_orphanfile_resource_id", entry->resource_id());
    486 }
    487 
    488 TEST_F(FileSystemTest, ReadDirectoryByPath_Root) {
    489   // ReadDirectoryByPath() should kick off the resource list loading.
    490   scoped_ptr<ResourceEntryVector> entries(
    491       ReadDirectoryByPathSync(base::FilePath::FromUTF8Unsafe("drive")));
    492   // The root directory should be read correctly.
    493   ASSERT_TRUE(entries);
    494   ASSERT_EQ(2U, entries->size());
    495 
    496   // The found two directories should be /drive/root and /drive/other.
    497   bool found_other = false;
    498   bool found_my_drive = false;
    499   for (size_t i = 0; i < entries->size(); ++i) {
    500     const base::FilePath title =
    501         base::FilePath::FromUTF8Unsafe((*entries)[i].title());
    502     if (title == base::FilePath(util::kDriveOtherDirName)) {
    503       found_other = true;
    504     } else if (title == base::FilePath(util::kDriveMyDriveRootDirName)) {
    505       found_my_drive = true;
    506     }
    507   }
    508 
    509   EXPECT_TRUE(found_other);
    510   EXPECT_TRUE(found_my_drive);
    511 
    512   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
    513   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
    514             mock_directory_observer_->changed_directories()[0]);
    515 }
    516 
    517 TEST_F(FileSystemTest, ReadDirectoryByPath_NonRootDirectory) {
    518   // ReadDirectoryByPath() should kick off the resource list loading.
    519   scoped_ptr<ResourceEntryVector> entries(
    520       ReadDirectoryByPathSync(
    521           base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
    522   // The non root directory should also be read correctly.
    523   // There was a bug (crbug.com/181487), which broke this behavior.
    524   // Make sure this is fixed.
    525   ASSERT_TRUE(entries);
    526   EXPECT_EQ(3U, entries->size());
    527 }
    528 
    529 TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) {
    530   ASSERT_TRUE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
    531 
    532   // Kicks loading of cached file system and query for server update.
    533   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
    534 
    535   // SetUpTestFileSystem and "account_metadata.json" have the same
    536   // changestamp (i.e. the local metadata is up-to-date), so no request for
    537   // new resource list (i.e., call to GetResourceList) should happen.
    538   EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
    539   EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
    540 
    541   // Since the file system has verified that it holds the latest snapshot,
    542   // it should change its state to "loaded", which admits periodic refresh.
    543   // To test it, call CheckForUpdates and verify it does try to check updates.
    544   file_system_->CheckForUpdates();
    545   test_util::RunBlockingPoolTask();
    546   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
    547 }
    548 
    549 TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) {
    550   ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
    551 
    552   // Make GetResourceList fail for simulating offline situation. This will
    553   // leave the file system "loaded from cache, but not synced with server"
    554   // state.
    555   fake_network_change_notifier_->SetConnectionType(
    556       net::NetworkChangeNotifier::CONNECTION_NONE);
    557   fake_drive_service_->set_offline(true);
    558 
    559   // Load the root.
    560   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveGrandRootPath()));
    561   // Loading of about resource should not happen as it's offline.
    562   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
    563 
    564   // Load "My Drive".
    565   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
    566   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
    567 
    568   // Tests that cached data can be loaded even if the server is not reachable.
    569   EXPECT_TRUE(EntryExists(base::FilePath(
    570       FILE_PATH_LITERAL("drive/root/File1"))));
    571   EXPECT_TRUE(EntryExists(base::FilePath(
    572       FILE_PATH_LITERAL("drive/root/Dir1"))));
    573   EXPECT_TRUE(
    574       EntryExists(base::FilePath(FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
    575   EXPECT_TRUE(EntryExists(base::FilePath(
    576       FILE_PATH_LITERAL("drive/root/Dir1/SubDir2"))));
    577   EXPECT_TRUE(EntryExists(
    578       base::FilePath(FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"))));
    579 
    580   // Since the file system has at least succeeded to load cached snapshot,
    581   // the file system should be able to start periodic refresh.
    582   // To test it, call CheckForUpdates and verify it does try to check
    583   // updates, which will cause directory changes.
    584   fake_network_change_notifier_->SetConnectionType(
    585       net::NetworkChangeNotifier::CONNECTION_WIFI);
    586   fake_drive_service_->set_offline(false);
    587 
    588   file_system_->CheckForUpdates();
    589 
    590   test_util::RunBlockingPoolTask();
    591   EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
    592   EXPECT_EQ(1, fake_drive_service_->change_list_load_count());
    593 
    594   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
    595 }
    596 
    597 TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) {
    598   // Enter the "refreshing" state so the fast fetch will be performed.
    599   ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
    600   file_system_->CheckForUpdates();
    601 
    602   // The list of resources in "drive/root/Dir1" should be fetched.
    603   EXPECT_TRUE(ReadDirectoryByPathSync(base::FilePath(
    604       FILE_PATH_LITERAL("drive/root/Dir1"))));
    605   EXPECT_EQ(1, fake_drive_service_->directory_load_count());
    606 
    607   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
    608 }
    609 
    610 TEST_F(FileSystemTest, GetResourceEntryExistingWhileRefreshing) {
    611   // Enter the "refreshing" state.
    612   ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
    613   file_system_->CheckForUpdates();
    614 
    615   // If an entry is already found in local metadata, no directory fetch happens.
    616   EXPECT_TRUE(GetResourceEntryByPathSync(base::FilePath(
    617       FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
    618   EXPECT_EQ(0, fake_drive_service_->directory_load_count());
    619 }
    620 
    621 TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) {
    622   // Enter the "refreshing" state so the fast fetch will be performed.
    623   ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
    624   file_system_->CheckForUpdates();
    625 
    626   // If an entry is not found, parent directory's resource list is fetched.
    627   EXPECT_FALSE(GetResourceEntryByPathSync(base::FilePath(
    628       FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile"))));
    629   EXPECT_EQ(1, fake_drive_service_->directory_load_count());
    630 
    631   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
    632 }
    633 
    634 TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) {
    635   // Intentionally *not* calling LoadFullResourceList(), for testing that
    636   // CreateDirectory ensures the resource list is loaded before it runs.
    637 
    638   base::FilePath existing_directory(
    639       FILE_PATH_LITERAL("drive/root/Directory 1"));
    640   FileError error = FILE_ERROR_FAILED;
    641   file_system_->CreateDirectory(
    642       existing_directory,
    643       true,  // is_exclusive
    644       false,  // is_recursive
    645       google_apis::test_util::CreateCopyResultCallback(&error));
    646   test_util::RunBlockingPoolTask();
    647 
    648   // It should fail because is_exclusive is set to true.
    649   EXPECT_EQ(FILE_ERROR_EXISTS, error);
    650 }
    651 
    652 TEST_F(FileSystemTest, PinAndUnpin) {
    653   ASSERT_TRUE(LoadFullResourceList());
    654 
    655   base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    656 
    657   // Get the file info.
    658   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
    659   ASSERT_TRUE(entry);
    660 
    661   // Pin the file.
    662   FileError error = FILE_ERROR_FAILED;
    663   file_system_->Pin(file_path,
    664                     google_apis::test_util::CreateCopyResultCallback(&error));
    665   test_util::RunBlockingPoolTask();
    666   EXPECT_EQ(FILE_ERROR_OK, error);
    667 
    668   FileCacheEntry cache_entry;
    669   EXPECT_TRUE(cache_->GetCacheEntry(entry->resource_id(), &cache_entry));
    670   EXPECT_TRUE(cache_entry.is_pinned());
    671   EXPECT_TRUE(cache_entry.is_present());
    672 
    673   // Unpin the file.
    674   error = FILE_ERROR_FAILED;
    675   file_system_->Unpin(file_path,
    676                       google_apis::test_util::CreateCopyResultCallback(&error));
    677   test_util::RunBlockingPoolTask();
    678   EXPECT_EQ(FILE_ERROR_OK, error);
    679 
    680   EXPECT_TRUE(cache_->GetCacheEntry(entry->resource_id(), &cache_entry));
    681   EXPECT_FALSE(cache_entry.is_pinned());
    682 
    683   // Pinned file gets synced and it results in entry state changes.
    684   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
    685   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive/root")),
    686             mock_directory_observer_->changed_directories()[0]);
    687 }
    688 
    689 TEST_F(FileSystemTest, PinAndUnpin_NotSynced) {
    690   ASSERT_TRUE(LoadFullResourceList());
    691 
    692   base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    693 
    694   // Get the file info.
    695   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
    696   ASSERT_TRUE(entry);
    697 
    698   // Unpin the file just after pinning. File fetch should be cancelled.
    699   FileError error_pin = FILE_ERROR_FAILED;
    700   file_system_->Pin(
    701       file_path,
    702       google_apis::test_util::CreateCopyResultCallback(&error_pin));
    703 
    704   FileError error_unpin = FILE_ERROR_FAILED;
    705   file_system_->Unpin(
    706       file_path,
    707       google_apis::test_util::CreateCopyResultCallback(&error_unpin));
    708 
    709   test_util::RunBlockingPoolTask();
    710   EXPECT_EQ(FILE_ERROR_OK, error_pin);
    711   EXPECT_EQ(FILE_ERROR_OK, error_unpin);
    712 
    713   // No cache file available because the sync was cancelled by Unpin().
    714   FileCacheEntry cache_entry;
    715   EXPECT_FALSE(cache_->GetCacheEntry(entry->resource_id(), &cache_entry));
    716 }
    717 
    718 TEST_F(FileSystemTest, GetAvailableSpace) {
    719   FileError error = FILE_ERROR_OK;
    720   int64 bytes_total;
    721   int64 bytes_used;
    722   file_system_->GetAvailableSpace(
    723       google_apis::test_util::CreateCopyResultCallback(
    724           &error, &bytes_total, &bytes_used));
    725   test_util::RunBlockingPoolTask();
    726   EXPECT_EQ(GG_LONGLONG(6789012345), bytes_used);
    727   EXPECT_EQ(GG_LONGLONG(9876543210), bytes_total);
    728 }
    729 
    730 TEST_F(FileSystemTest, OpenAndCloseFile) {
    731   ASSERT_TRUE(LoadFullResourceList());
    732 
    733   const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    734   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(kFileInRoot));
    735   const std::string& file_resource_id = entry->resource_id();
    736 
    737   // Open kFileInRoot ("drive/root/File 1.txt").
    738   FileError error = FILE_ERROR_FAILED;
    739   base::FilePath file_path;
    740   base::Closure close_callback;
    741   file_system_->OpenFile(
    742       kFileInRoot,
    743       OPEN_FILE,
    744       google_apis::test_util::CreateCopyResultCallback(
    745           &error, &file_path, &close_callback));
    746   test_util::RunBlockingPoolTask();
    747   const base::FilePath opened_file_path = file_path;
    748 
    749   // Verify that the file was properly opened.
    750   EXPECT_EQ(FILE_ERROR_OK, error);
    751 
    752   // The opened file is downloaded, which means the file is available
    753   // offline. The directory change should be notified so Files.app can change
    754   // the offline availability status of the file.
    755   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
    756   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive/root")),
    757             mock_directory_observer_->changed_directories()[0]);
    758 
    759   // Verify that the file contents match the expected contents.
    760   const std::string kExpectedContent = "This is some test content.";
    761   std::string cache_file_data;
    762   EXPECT_TRUE(file_util::ReadFileToString(opened_file_path, &cache_file_data));
    763   EXPECT_EQ(kExpectedContent, cache_file_data);
    764 
    765   FileCacheEntry cache_entry;
    766   EXPECT_TRUE(cache_->GetCacheEntry(file_resource_id, &cache_entry));
    767   EXPECT_TRUE(cache_entry.is_present());
    768   EXPECT_TRUE(cache_entry.is_dirty());
    769 
    770   base::FilePath cache_file_path;
    771   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(file_resource_id, &cache_file_path));
    772   EXPECT_EQ(cache_file_path, opened_file_path);
    773 
    774   // Write a new content.
    775   const std::string kNewContent = kExpectedContent + kExpectedContent;
    776   EXPECT_TRUE(google_apis::test_util::WriteStringToFile(cache_file_path,
    777                                                         kNewContent));
    778 
    779   // Close kFileInRoot ("drive/root/File 1.txt").
    780   ASSERT_FALSE(close_callback.is_null());
    781   close_callback.Run();
    782   test_util::RunBlockingPoolTask();
    783 
    784   // Verify that the file was properly closed.
    785   EXPECT_EQ(FILE_ERROR_OK, error);
    786 
    787   // Verify that the file was synced as expected.
    788   google_apis::GDataErrorCode gdata_error = google_apis::GDATA_FILE_ERROR;
    789   scoped_ptr<google_apis::ResourceEntry> gdata_entry;
    790   fake_drive_service_->GetResourceEntry(
    791       file_resource_id,
    792       google_apis::test_util::CreateCopyResultCallback(
    793           &gdata_error, &gdata_entry));
    794   test_util::RunBlockingPoolTask();
    795   EXPECT_EQ(gdata_error, google_apis::HTTP_SUCCESS);
    796   ASSERT_TRUE(gdata_entry);
    797   EXPECT_EQ(static_cast<int>(kNewContent.size()), gdata_entry->file_size());
    798 
    799   // The modified file is uploaded. The directory change should be notified
    800   // so Files.app can show new metadata of the modified file.
    801   ASSERT_EQ(2u, mock_directory_observer_->changed_directories().size());
    802   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive/root")),
    803             mock_directory_observer_->changed_directories()[1]);
    804 }
    805 
    806 TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) {
    807   ASSERT_TRUE(LoadFullResourceList());
    808 
    809   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    810   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_in_root));
    811   ASSERT_TRUE(entry);
    812 
    813   // Write to cache.
    814   ASSERT_EQ(FILE_ERROR_OK, cache_->Store(
    815       entry->resource_id(),
    816       entry->file_specific_info().md5(),
    817       google_apis::test_util::GetTestFilePath("gdata/root_feed.json"),
    818       internal::FileCache::FILE_OPERATION_COPY));
    819 
    820   // Test for mounting.
    821   FileError error = FILE_ERROR_FAILED;
    822   base::FilePath file_path;
    823   file_system_->MarkCacheFileAsMounted(
    824       file_in_root,
    825       google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
    826   test_util::RunBlockingPoolTask();
    827   EXPECT_EQ(FILE_ERROR_OK, error);
    828 
    829   // Cannot remove a cache entry while it's being mounted.
    830   EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->resource_id()));
    831 
    832   // Test for unmounting.
    833   error = FILE_ERROR_FAILED;
    834   file_system_->MarkCacheFileAsUnmounted(
    835       file_path,
    836       google_apis::test_util::CreateCopyResultCallback(&error));
    837   test_util::RunBlockingPoolTask();
    838   EXPECT_EQ(FILE_ERROR_OK, error);
    839 
    840   // Now able to remove the cache entry.
    841   EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->resource_id()));
    842 }
    843 
    844 TEST_F(FileSystemTest, GetShareUrl) {
    845   ASSERT_TRUE(LoadFullResourceList());
    846 
    847   const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    848   const GURL kEmbedOrigin("chrome-extension://test-id");
    849 
    850   // Try to fetch the URL for the sharing dialog.
    851   FileError error = FILE_ERROR_FAILED;
    852   GURL share_url;
    853   file_system_->GetShareUrl(
    854       kFileInRoot,
    855       kEmbedOrigin,
    856       google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
    857   test_util::RunBlockingPoolTask();
    858 
    859   // Verify the share url to the sharing dialog.
    860   EXPECT_EQ(FILE_ERROR_OK, error);
    861   EXPECT_EQ(GURL("https://file_link_share/"), share_url);
    862 }
    863 
    864 TEST_F(FileSystemTest, GetShareUrlNotAvailable) {
    865   ASSERT_TRUE(LoadFullResourceList());
    866 
    867   const base::FilePath kFileInRoot(
    868       FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
    869   const GURL kEmbedOrigin("chrome-extension://test-id");
    870 
    871   // Try to fetch the URL for the sharing dialog.
    872   FileError error = FILE_ERROR_FAILED;
    873   GURL share_url;
    874 
    875   file_system_->GetShareUrl(
    876       kFileInRoot,
    877       kEmbedOrigin,
    878       google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
    879   test_util::RunBlockingPoolTask();
    880 
    881   // Verify the error and the share url, which should be empty.
    882   EXPECT_EQ(FILE_ERROR_FAILED, error);
    883   EXPECT_TRUE(share_url.is_empty());
    884 }
    885 
    886 }   // namespace drive
    887