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/change_list_processor.h" 20 #include "chrome/browser/chromeos/drive/drive.pb.h" 21 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" 22 #include "chrome/browser/chromeos/drive/file_system_observer.h" 23 #include "chrome/browser/chromeos/drive/file_system_util.h" 24 #include "chrome/browser/chromeos/drive/job_scheduler.h" 25 #include "chrome/browser/chromeos/drive/sync_client.h" 26 #include "chrome/browser/chromeos/drive/test_util.h" 27 #include "chrome/browser/drive/fake_drive_service.h" 28 #include "content/public/test/test_browser_thread_bundle.h" 29 #include "google_apis/drive/drive_api_parser.h" 30 #include "google_apis/drive/test_util.h" 31 #include "testing/gtest/include/gtest/gtest.h" 32 33 namespace drive { 34 namespace { 35 36 // Counts the number of invocation, and if it increased up to |expected_counter| 37 // quits the current message loop by calling |quit|. 38 void AsyncInitializationCallback( 39 int* counter, int expected_counter, const base::Closure& quit, 40 FileError error, scoped_ptr<ResourceEntry> entry) { 41 if (error != FILE_ERROR_OK || !entry) { 42 // If we hit an error case, quit the message loop immediately. 43 // Then the expectation in the test case can find it because the actual 44 // value of |counter| is different from the expected one. 45 quit.Run(); 46 return; 47 } 48 49 (*counter)++; 50 if (*counter >= expected_counter) 51 quit.Run(); 52 } 53 54 // This class is used to record directory changes and examine them later. 55 class MockDirectoryChangeObserver : public FileSystemObserver { 56 public: 57 MockDirectoryChangeObserver() {} 58 virtual ~MockDirectoryChangeObserver() {} 59 60 // FileSystemObserver overrides. 61 virtual void OnDirectoryChanged( 62 const base::FilePath& directory_path) OVERRIDE { 63 changed_directories_.push_back(directory_path); 64 } 65 66 const std::vector<base::FilePath>& changed_directories() const { 67 return changed_directories_; 68 } 69 70 private: 71 std::vector<base::FilePath> changed_directories_; 72 DISALLOW_COPY_AND_ASSIGN(MockDirectoryChangeObserver); 73 }; 74 75 } // namespace 76 77 class FileSystemTest : public testing::Test { 78 protected: 79 virtual void SetUp() OVERRIDE { 80 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 81 pref_service_.reset(new TestingPrefServiceSimple); 82 test_util::RegisterDrivePrefs(pref_service_->registry()); 83 84 fake_drive_service_.reset(new FakeDriveService); 85 fake_drive_service_->LoadResourceListForWapi( 86 "gdata/root_feed.json"); 87 fake_drive_service_->LoadAccountMetadataForWapi( 88 "gdata/account_metadata.json"); 89 90 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); 91 92 scheduler_.reset(new JobScheduler(pref_service_.get(), 93 fake_drive_service_.get(), 94 base::MessageLoopProxy::current().get())); 95 96 mock_directory_observer_.reset(new MockDirectoryChangeObserver); 97 98 SetUpResourceMetadataAndFileSystem(); 99 } 100 101 void SetUpResourceMetadataAndFileSystem() { 102 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); 103 ASSERT_TRUE(base::CreateDirectory(metadata_dir)); 104 metadata_storage_.reset(new internal::ResourceMetadataStorage( 105 metadata_dir, base::MessageLoopProxy::current().get())); 106 ASSERT_TRUE(metadata_storage_->Initialize()); 107 108 const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files"); 109 ASSERT_TRUE(base::CreateDirectory(cache_dir)); 110 cache_.reset(new internal::FileCache( 111 metadata_storage_.get(), 112 cache_dir, 113 base::MessageLoopProxy::current().get(), 114 fake_free_disk_space_getter_.get())); 115 ASSERT_TRUE(cache_->Initialize()); 116 117 resource_metadata_.reset(new internal::ResourceMetadata( 118 metadata_storage_.get(), base::MessageLoopProxy::current())); 119 ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize()); 120 121 const base::FilePath temp_file_dir = temp_dir_.path().AppendASCII("tmp"); 122 ASSERT_TRUE(base::CreateDirectory(temp_file_dir)); 123 file_system_.reset(new FileSystem( 124 pref_service_.get(), 125 cache_.get(), 126 fake_drive_service_.get(), 127 scheduler_.get(), 128 resource_metadata_.get(), 129 base::MessageLoopProxy::current().get(), 130 temp_file_dir)); 131 file_system_->AddObserver(mock_directory_observer_.get()); 132 133 // Disable delaying so that the sync starts immediately. 134 file_system_->sync_client_for_testing()->set_delay_for_testing( 135 base::TimeDelta::FromSeconds(0)); 136 } 137 138 // Loads the full resource list via FakeDriveService. 139 bool LoadFullResourceList() { 140 FileError error = FILE_ERROR_FAILED; 141 file_system_->change_list_loader_for_testing()->LoadIfNeeded( 142 internal::DirectoryFetchInfo(), 143 google_apis::test_util::CreateCopyResultCallback(&error)); 144 test_util::RunBlockingPoolTask(); 145 return error == FILE_ERROR_OK; 146 } 147 148 // Gets resource entry by path synchronously. 149 scoped_ptr<ResourceEntry> GetResourceEntrySync( 150 const base::FilePath& file_path) { 151 FileError error = FILE_ERROR_FAILED; 152 scoped_ptr<ResourceEntry> entry; 153 file_system_->GetResourceEntry( 154 file_path, 155 google_apis::test_util::CreateCopyResultCallback(&error, &entry)); 156 test_util::RunBlockingPoolTask(); 157 158 return entry.Pass(); 159 } 160 161 // Gets directory info by path synchronously. 162 scoped_ptr<ResourceEntryVector> ReadDirectorySync( 163 const base::FilePath& file_path) { 164 FileError error = FILE_ERROR_FAILED; 165 scoped_ptr<ResourceEntryVector> entries; 166 file_system_->ReadDirectory( 167 file_path, 168 google_apis::test_util::CreateCopyResultCallback(&error, &entries)); 169 test_util::RunBlockingPoolTask(); 170 171 return entries.Pass(); 172 } 173 174 // Returns true if an entry exists at |file_path|. 175 bool EntryExists(const base::FilePath& file_path) { 176 return GetResourceEntrySync(file_path); 177 } 178 179 // Flag for specifying the timestamp of the test filesystem cache. 180 enum SetUpTestFileSystemParam { 181 USE_OLD_TIMESTAMP, 182 USE_SERVER_TIMESTAMP, 183 }; 184 185 // Sets up a filesystem with directories: drive/root, drive/root/Dir1, 186 // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2, 187 // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets 188 // the changestamp to 654321, equal to that of "account_metadata.json" test 189 // data, indicating the cache is holding the latest file system info. 190 void SetUpTestFileSystem(SetUpTestFileSystemParam param) { 191 // Destroy the existing resource metadata to close DB. 192 resource_metadata_.reset(); 193 194 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); 195 ASSERT_TRUE(base::CreateDirectory(metadata_dir)); 196 scoped_ptr<internal::ResourceMetadataStorage, 197 test_util::DestroyHelperForTests> metadata_storage( 198 new internal::ResourceMetadataStorage( 199 metadata_dir, base::MessageLoopProxy::current().get())); 200 201 scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests> 202 resource_metadata(new internal::ResourceMetadata( 203 metadata_storage_.get(), base::MessageLoopProxy::current())); 204 205 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->Initialize()); 206 207 const int64 changestamp = param == USE_SERVER_TIMESTAMP ? 654321 : 1; 208 ASSERT_EQ(FILE_ERROR_OK, 209 resource_metadata->SetLargestChangestamp(changestamp)); 210 211 // drive/root 212 const std::string root_resource_id = 213 fake_drive_service_->GetRootResourceId(); 214 std::string local_id; 215 ASSERT_EQ(FILE_ERROR_OK, 216 resource_metadata->AddEntry(util::CreateMyDriveRootEntry( 217 root_resource_id), &local_id)); 218 const std::string root_local_id = local_id; 219 220 // drive/root/File1 221 ResourceEntry file1; 222 file1.set_title("File1"); 223 file1.set_resource_id("resource_id:File1"); 224 file1.set_parent_local_id(root_local_id); 225 file1.mutable_file_specific_info()->set_md5("md5"); 226 file1.mutable_file_info()->set_is_directory(false); 227 file1.mutable_file_info()->set_size(1048576); 228 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file1, &local_id)); 229 230 // drive/root/Dir1 231 ResourceEntry dir1; 232 dir1.set_title("Dir1"); 233 dir1.set_resource_id("resource_id:Dir1"); 234 dir1.set_parent_local_id(root_local_id); 235 dir1.mutable_file_info()->set_is_directory(true); 236 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir1, &local_id)); 237 const std::string dir1_local_id = local_id; 238 239 // drive/root/Dir1/File2 240 ResourceEntry file2; 241 file2.set_title("File2"); 242 file2.set_resource_id("resource_id:File2"); 243 file2.set_parent_local_id(dir1_local_id); 244 file2.mutable_file_specific_info()->set_md5("md5"); 245 file2.mutable_file_info()->set_is_directory(false); 246 file2.mutable_file_info()->set_size(555); 247 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file2, &local_id)); 248 249 // drive/root/Dir1/SubDir2 250 ResourceEntry dir2; 251 dir2.set_title("SubDir2"); 252 dir2.set_resource_id("resource_id:SubDir2"); 253 dir2.set_parent_local_id(dir1_local_id); 254 dir2.mutable_file_info()->set_is_directory(true); 255 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir2, &local_id)); 256 const std::string dir2_local_id = local_id; 257 258 // drive/root/Dir1/SubDir2/File3 259 ResourceEntry file3; 260 file3.set_title("File3"); 261 file3.set_resource_id("resource_id:File3"); 262 file3.set_parent_local_id(dir2_local_id); 263 file3.mutable_file_specific_info()->set_md5("md5"); 264 file3.mutable_file_info()->set_is_directory(false); 265 file3.mutable_file_info()->set_size(12345); 266 ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file3, &local_id)); 267 268 // Recreate resource metadata. 269 SetUpResourceMetadataAndFileSystem(); 270 } 271 272 content::TestBrowserThreadBundle thread_bundle_; 273 base::ScopedTempDir temp_dir_; 274 // We don't use TestingProfile::GetPrefs() in favor of having less 275 // dependencies to Profile in general. 276 scoped_ptr<TestingPrefServiceSimple> pref_service_; 277 278 scoped_ptr<FakeDriveService> fake_drive_service_; 279 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; 280 scoped_ptr<JobScheduler> scheduler_; 281 scoped_ptr<MockDirectoryChangeObserver> mock_directory_observer_; 282 283 scoped_ptr<internal::ResourceMetadataStorage, 284 test_util::DestroyHelperForTests> metadata_storage_; 285 scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_; 286 scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests> 287 resource_metadata_; 288 scoped_ptr<FileSystem> file_system_; 289 }; 290 291 TEST_F(FileSystemTest, DuplicatedAsyncInitialization) { 292 base::RunLoop loop; 293 294 int counter = 0; 295 const GetResourceEntryCallback& callback = base::Bind( 296 &AsyncInitializationCallback, &counter, 2, loop.QuitClosure()); 297 298 file_system_->GetResourceEntry( 299 base::FilePath(FILE_PATH_LITERAL("drive/root")), callback); 300 file_system_->GetResourceEntry( 301 base::FilePath(FILE_PATH_LITERAL("drive/root")), callback); 302 loop.Run(); // Wait to get our result 303 EXPECT_EQ(2, counter); 304 305 EXPECT_EQ(1, fake_drive_service_->resource_list_load_count()); 306 307 // "Fast fetch" will fire an OnirectoryChanged event. 308 ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size()); 309 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")), 310 mock_directory_observer_->changed_directories()[0]); 311 } 312 313 TEST_F(FileSystemTest, GetGrandRootEntry) { 314 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive")); 315 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 316 ASSERT_TRUE(entry); 317 EXPECT_EQ(util::kDriveGrandRootLocalId, entry->resource_id()); 318 319 // Getting the grand root entry should not cause the resource load to happen. 320 EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); 321 EXPECT_EQ(0, fake_drive_service_->resource_list_load_count()); 322 } 323 324 TEST_F(FileSystemTest, GetOtherDirEntry) { 325 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/other")); 326 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 327 ASSERT_TRUE(entry); 328 EXPECT_EQ(util::kDriveOtherDirLocalId, entry->resource_id()); 329 330 // Getting the "other" directory entry should not cause the resource load to 331 // happen. 332 EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); 333 EXPECT_EQ(0, fake_drive_service_->resource_list_load_count()); 334 } 335 336 TEST_F(FileSystemTest, GetMyDriveRoot) { 337 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root")); 338 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 339 ASSERT_TRUE(entry); 340 EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id()); 341 342 EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); 343 344 // After "fast fetch" is done, full resource list is fetched. 345 EXPECT_EQ(1, fake_drive_service_->resource_list_load_count()); 346 347 // "Fast fetch" will fire an OnirectoryChanged event. 348 ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size()); 349 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")), 350 mock_directory_observer_->changed_directories()[0]); 351 } 352 353 TEST_F(FileSystemTest, GetExistingFile) { 354 // Simulate the situation that full feed fetching takes very long time, 355 // to test the recursive "fast fetch" feature is properly working. 356 fake_drive_service_->set_never_return_all_resource_list(true); 357 358 const base::FilePath kFilePath( 359 FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); 360 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 361 ASSERT_TRUE(entry); 362 EXPECT_EQ("file:subdirectory_file_1_id", entry->resource_id()); 363 364 EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); 365 EXPECT_EQ(2, fake_drive_service_->directory_load_count()); 366 EXPECT_EQ(1, fake_drive_service_->blocked_resource_list_load_count()); 367 } 368 369 TEST_F(FileSystemTest, GetExistingDocument) { 370 const base::FilePath kFilePath( 371 FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc")); 372 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 373 ASSERT_TRUE(entry); 374 EXPECT_EQ("document:5_document_resource_id", entry->resource_id()); 375 } 376 377 TEST_F(FileSystemTest, GetNonExistingFile) { 378 const base::FilePath kFilePath( 379 FILE_PATH_LITERAL("drive/root/nonexisting.file")); 380 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 381 EXPECT_FALSE(entry); 382 } 383 384 TEST_F(FileSystemTest, GetExistingDirectory) { 385 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/Directory 1")); 386 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 387 ASSERT_TRUE(entry); 388 ASSERT_EQ("folder:1_folder_resource_id", entry->resource_id()); 389 390 // The changestamp should be propagated to the directory. 391 EXPECT_EQ(fake_drive_service_->largest_changestamp(), 392 entry->directory_specific_info().changestamp()); 393 } 394 395 TEST_F(FileSystemTest, GetInSubSubdir) { 396 const base::FilePath kFilePath( 397 FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/" 398 "Sub Sub Directory Folder")); 399 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 400 ASSERT_TRUE(entry); 401 ASSERT_EQ("folder:sub_sub_directory_folder_id", entry->resource_id()); 402 } 403 404 TEST_F(FileSystemTest, GetOrphanFile) { 405 ASSERT_TRUE(LoadFullResourceList()); 406 407 // Entry without parents are placed under "drive/other". 408 const base::FilePath kFilePath( 409 FILE_PATH_LITERAL("drive/other/Orphan File 1.txt")); 410 scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); 411 ASSERT_TRUE(entry); 412 EXPECT_EQ("file:1_orphanfile_resource_id", entry->resource_id()); 413 } 414 415 TEST_F(FileSystemTest, ReadDirectory_Root) { 416 // ReadDirectory() should kick off the resource list loading. 417 scoped_ptr<ResourceEntryVector> entries( 418 ReadDirectorySync(base::FilePath::FromUTF8Unsafe("drive"))); 419 // The root directory should be read correctly. 420 ASSERT_TRUE(entries); 421 ASSERT_EQ(3U, entries->size()); 422 423 // The found three directories should be /drive/root, /drive/other and 424 // /drive/trash. 425 std::set<base::FilePath> found; 426 for (size_t i = 0; i < entries->size(); ++i) 427 found.insert(base::FilePath::FromUTF8Unsafe((*entries)[i].title())); 428 EXPECT_EQ(3U, found.size()); 429 EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveMyDriveRootDirName))); 430 EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveOtherDirName))); 431 EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveTrashDirName))); 432 433 ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size()); 434 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")), 435 mock_directory_observer_->changed_directories()[0]); 436 } 437 438 TEST_F(FileSystemTest, ReadDirectory_NonRootDirectory) { 439 // ReadDirectory() should kick off the resource list loading. 440 scoped_ptr<ResourceEntryVector> entries( 441 ReadDirectorySync( 442 base::FilePath::FromUTF8Unsafe("drive/root/Directory 1"))); 443 // The non root directory should also be read correctly. 444 // There was a bug (crbug.com/181487), which broke this behavior. 445 // Make sure this is fixed. 446 ASSERT_TRUE(entries); 447 EXPECT_EQ(3U, entries->size()); 448 } 449 450 TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) { 451 ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP)); 452 453 // Kicks loading of cached file system and query for server update. 454 EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath())); 455 456 // SetUpTestFileSystem and "account_metadata.json" have the same 457 // changestamp (i.e. the local metadata is up-to-date), so no request for 458 // new resource list (i.e., call to GetResourceList) should happen. 459 EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); 460 EXPECT_EQ(0, fake_drive_service_->resource_list_load_count()); 461 462 // Since the file system has verified that it holds the latest snapshot, 463 // it should change its state to "loaded", which admits periodic refresh. 464 // To test it, call CheckForUpdates and verify it does try to check updates. 465 file_system_->CheckForUpdates(); 466 test_util::RunBlockingPoolTask(); 467 EXPECT_EQ(2, fake_drive_service_->about_resource_load_count()); 468 } 469 470 TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) { 471 ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); 472 473 // Make GetResourceList fail for simulating offline situation. This will 474 // leave the file system "loaded from cache, but not synced with server" 475 // state. 476 fake_drive_service_->set_offline(true); 477 478 // Load the root. 479 EXPECT_TRUE(ReadDirectorySync(util::GetDriveGrandRootPath())); 480 // Loading of about resource should not happen as it's offline. 481 EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); 482 483 // Load "My Drive". 484 EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath())); 485 EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); 486 487 // Tests that cached data can be loaded even if the server is not reachable. 488 EXPECT_TRUE(EntryExists(base::FilePath( 489 FILE_PATH_LITERAL("drive/root/File1")))); 490 EXPECT_TRUE(EntryExists(base::FilePath( 491 FILE_PATH_LITERAL("drive/root/Dir1")))); 492 EXPECT_TRUE(EntryExists(base::FilePath( 493 FILE_PATH_LITERAL("drive/root/Dir1/File2")))); 494 EXPECT_TRUE(EntryExists(base::FilePath( 495 FILE_PATH_LITERAL("drive/root/Dir1/SubDir2")))); 496 EXPECT_TRUE(EntryExists(base::FilePath( 497 FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3")))); 498 499 // Since the file system has at least succeeded to load cached snapshot, 500 // the file system should be able to start periodic refresh. 501 // To test it, call CheckForUpdates and verify it does try to check 502 // updates, which will cause directory changes. 503 fake_drive_service_->set_offline(false); 504 505 file_system_->CheckForUpdates(); 506 507 test_util::RunBlockingPoolTask(); 508 EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); 509 EXPECT_EQ(1, fake_drive_service_->change_list_load_count()); 510 511 ASSERT_LE(1u, mock_directory_observer_->changed_directories().size()); 512 } 513 514 TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) { 515 // Enter the "refreshing" state so the fast fetch will be performed. 516 ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); 517 file_system_->CheckForUpdates(); 518 519 // The list of resources in "drive/root/Dir1" should be fetched. 520 EXPECT_TRUE(ReadDirectorySync(base::FilePath( 521 FILE_PATH_LITERAL("drive/root/Dir1")))); 522 EXPECT_EQ(1, fake_drive_service_->directory_load_count()); 523 524 ASSERT_LE(1u, mock_directory_observer_->changed_directories().size()); 525 } 526 527 TEST_F(FileSystemTest, GetResourceEntryExistingWhileRefreshing) { 528 // Enter the "refreshing" state. 529 ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); 530 file_system_->CheckForUpdates(); 531 532 // If an entry is already found in local metadata, no directory fetch happens. 533 EXPECT_TRUE(GetResourceEntrySync(base::FilePath( 534 FILE_PATH_LITERAL("drive/root/Dir1/File2")))); 535 EXPECT_EQ(0, fake_drive_service_->directory_load_count()); 536 } 537 538 TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) { 539 // Enter the "refreshing" state so the fast fetch will be performed. 540 ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); 541 file_system_->CheckForUpdates(); 542 543 // If an entry is not found, parent directory's resource list is fetched. 544 EXPECT_FALSE(GetResourceEntrySync(base::FilePath( 545 FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile")))); 546 EXPECT_EQ(1, fake_drive_service_->directory_load_count()); 547 548 ASSERT_LE(1u, mock_directory_observer_->changed_directories().size()); 549 } 550 551 TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) { 552 // Intentionally *not* calling LoadFullResourceList(), for testing that 553 // CreateDirectory ensures the resource list is loaded before it runs. 554 555 base::FilePath existing_directory( 556 FILE_PATH_LITERAL("drive/root/Directory 1")); 557 FileError error = FILE_ERROR_FAILED; 558 file_system_->CreateDirectory( 559 existing_directory, 560 true, // is_exclusive 561 false, // is_recursive 562 google_apis::test_util::CreateCopyResultCallback(&error)); 563 test_util::RunBlockingPoolTask(); 564 565 // It should fail because is_exclusive is set to true. 566 EXPECT_EQ(FILE_ERROR_EXISTS, error); 567 } 568 569 TEST_F(FileSystemTest, CreateDirectoryRecursively) { 570 // Intentionally *not* calling LoadFullResourceList(), for testing that 571 // CreateDirectory ensures the resource list is loaded before it runs. 572 573 base::FilePath new_directory( 574 FILE_PATH_LITERAL("drive/root/Directory 1/a/b/c/d")); 575 FileError error = FILE_ERROR_FAILED; 576 file_system_->CreateDirectory( 577 new_directory, 578 true, // is_exclusive 579 true, // is_recursive 580 google_apis::test_util::CreateCopyResultCallback(&error)); 581 test_util::RunBlockingPoolTask(); 582 583 EXPECT_EQ(FILE_ERROR_OK, error); 584 585 scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(new_directory)); 586 ASSERT_TRUE(entry); 587 EXPECT_TRUE(entry->file_info().is_directory()); 588 } 589 590 TEST_F(FileSystemTest, PinAndUnpin) { 591 ASSERT_TRUE(LoadFullResourceList()); 592 593 base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); 594 595 // Get the file info. 596 scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path)); 597 ASSERT_TRUE(entry); 598 599 // Pin the file. 600 FileError error = FILE_ERROR_FAILED; 601 file_system_->Pin(file_path, 602 google_apis::test_util::CreateCopyResultCallback(&error)); 603 test_util::RunBlockingPoolTask(); 604 EXPECT_EQ(FILE_ERROR_OK, error); 605 606 FileCacheEntry cache_entry; 607 EXPECT_TRUE(cache_->GetCacheEntry(entry->local_id(), &cache_entry)); 608 EXPECT_TRUE(cache_entry.is_pinned()); 609 EXPECT_TRUE(cache_entry.is_present()); 610 611 // Unpin the file. 612 error = FILE_ERROR_FAILED; 613 file_system_->Unpin(file_path, 614 google_apis::test_util::CreateCopyResultCallback(&error)); 615 test_util::RunBlockingPoolTask(); 616 EXPECT_EQ(FILE_ERROR_OK, error); 617 618 EXPECT_TRUE(cache_->GetCacheEntry(entry->local_id(), &cache_entry)); 619 EXPECT_FALSE(cache_entry.is_pinned()); 620 621 // Pinned file gets synced and it results in entry state changes. 622 ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size()); 623 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive/root")), 624 mock_directory_observer_->changed_directories()[0]); 625 } 626 627 TEST_F(FileSystemTest, PinAndUnpin_NotSynced) { 628 ASSERT_TRUE(LoadFullResourceList()); 629 630 base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); 631 632 // Get the file info. 633 scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path)); 634 ASSERT_TRUE(entry); 635 636 // Unpin the file just after pinning. File fetch should be cancelled. 637 FileError error_pin = FILE_ERROR_FAILED; 638 file_system_->Pin( 639 file_path, 640 google_apis::test_util::CreateCopyResultCallback(&error_pin)); 641 642 FileError error_unpin = FILE_ERROR_FAILED; 643 file_system_->Unpin( 644 file_path, 645 google_apis::test_util::CreateCopyResultCallback(&error_unpin)); 646 647 test_util::RunBlockingPoolTask(); 648 EXPECT_EQ(FILE_ERROR_OK, error_pin); 649 EXPECT_EQ(FILE_ERROR_OK, error_unpin); 650 651 // No cache file available because the sync was cancelled by Unpin(). 652 FileCacheEntry cache_entry; 653 EXPECT_FALSE(cache_->GetCacheEntry(entry->local_id(), &cache_entry)); 654 } 655 656 TEST_F(FileSystemTest, GetAvailableSpace) { 657 FileError error = FILE_ERROR_OK; 658 int64 bytes_total; 659 int64 bytes_used; 660 file_system_->GetAvailableSpace( 661 google_apis::test_util::CreateCopyResultCallback( 662 &error, &bytes_total, &bytes_used)); 663 test_util::RunBlockingPoolTask(); 664 EXPECT_EQ(GG_LONGLONG(6789012345), bytes_used); 665 EXPECT_EQ(GG_LONGLONG(9876543210), bytes_total); 666 } 667 668 TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) { 669 ASSERT_TRUE(LoadFullResourceList()); 670 671 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 672 scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_in_root)); 673 ASSERT_TRUE(entry); 674 675 // Write to cache. 676 ASSERT_EQ(FILE_ERROR_OK, cache_->Store( 677 entry->local_id(), 678 entry->file_specific_info().md5(), 679 google_apis::test_util::GetTestFilePath("gdata/root_feed.json"), 680 internal::FileCache::FILE_OPERATION_COPY)); 681 682 // Test for mounting. 683 FileError error = FILE_ERROR_FAILED; 684 base::FilePath file_path; 685 file_system_->MarkCacheFileAsMounted( 686 file_in_root, 687 google_apis::test_util::CreateCopyResultCallback(&error, &file_path)); 688 test_util::RunBlockingPoolTask(); 689 EXPECT_EQ(FILE_ERROR_OK, error); 690 691 // Cannot remove a cache entry while it's being mounted. 692 EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->local_id())); 693 694 // Test for unmounting. 695 error = FILE_ERROR_FAILED; 696 file_system_->MarkCacheFileAsUnmounted( 697 file_path, 698 google_apis::test_util::CreateCopyResultCallback(&error)); 699 test_util::RunBlockingPoolTask(); 700 EXPECT_EQ(FILE_ERROR_OK, error); 701 702 // Now able to remove the cache entry. 703 EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->local_id())); 704 } 705 706 TEST_F(FileSystemTest, GetShareUrl) { 707 ASSERT_TRUE(LoadFullResourceList()); 708 709 const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt")); 710 const GURL kEmbedOrigin("chrome-extension://test-id"); 711 712 // Try to fetch the URL for the sharing dialog. 713 FileError error = FILE_ERROR_FAILED; 714 GURL share_url; 715 file_system_->GetShareUrl( 716 kFileInRoot, 717 kEmbedOrigin, 718 google_apis::test_util::CreateCopyResultCallback(&error, &share_url)); 719 test_util::RunBlockingPoolTask(); 720 721 // Verify the share url to the sharing dialog. 722 EXPECT_EQ(FILE_ERROR_OK, error); 723 EXPECT_EQ(GURL("https://file_link_share/"), share_url); 724 } 725 726 TEST_F(FileSystemTest, GetShareUrlNotAvailable) { 727 ASSERT_TRUE(LoadFullResourceList()); 728 729 const base::FilePath kFileInRoot( 730 FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); 731 const GURL kEmbedOrigin("chrome-extension://test-id"); 732 733 // Try to fetch the URL for the sharing dialog. 734 FileError error = FILE_ERROR_FAILED; 735 GURL share_url; 736 737 file_system_->GetShareUrl( 738 kFileInRoot, 739 kEmbedOrigin, 740 google_apis::test_util::CreateCopyResultCallback(&error, &share_url)); 741 test_util::RunBlockingPoolTask(); 742 743 // Verify the error and the share url, which should be empty. 744 EXPECT_EQ(FILE_ERROR_FAILED, error); 745 EXPECT_TRUE(share_url.is_empty()); 746 } 747 748 } // namespace drive 749