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