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