1 // Copyright 2013 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/change_list_loader.h" 6 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/prefs/testing_pref_service.h" 10 #include "base/run_loop.h" 11 #include "chrome/browser/chromeos/drive/change_list_loader_observer.h" 12 #include "chrome/browser/chromeos/drive/file_cache.h" 13 #include "chrome/browser/chromeos/drive/file_system_util.h" 14 #include "chrome/browser/chromeos/drive/job_scheduler.h" 15 #include "chrome/browser/chromeos/drive/resource_metadata.h" 16 #include "chrome/browser/chromeos/drive/test_util.h" 17 #include "chrome/browser/drive/fake_drive_service.h" 18 #include "chrome/browser/google_apis/test_util.h" 19 #include "content/public/test/test_browser_thread_bundle.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 namespace drive { 23 namespace internal { 24 25 class TestChangeListLoaderObserver : public ChangeListLoaderObserver { 26 public: 27 explicit TestChangeListLoaderObserver(ChangeListLoader* loader) 28 : loader_(loader), 29 load_from_server_complete_count_(0), 30 initial_load_complete_count_(0) { 31 loader_->AddObserver(this); 32 } 33 34 virtual ~TestChangeListLoaderObserver() { 35 loader_->RemoveObserver(this); 36 } 37 38 const std::set<base::FilePath>& changed_directories() const { 39 return changed_directories_; 40 } 41 void clear_changed_directories() { changed_directories_.clear(); } 42 43 int load_from_server_complete_count() const { 44 return load_from_server_complete_count_; 45 } 46 int initial_load_complete_count() const { 47 return initial_load_complete_count_; 48 } 49 50 // ChageListObserver overrides: 51 virtual void OnDirectoryChanged( 52 const base::FilePath& directory_path) OVERRIDE { 53 changed_directories_.insert(directory_path); 54 } 55 virtual void OnLoadFromServerComplete() OVERRIDE { 56 ++load_from_server_complete_count_; 57 } 58 virtual void OnInitialLoadComplete() OVERRIDE { 59 ++initial_load_complete_count_; 60 } 61 62 private: 63 ChangeListLoader* loader_; 64 std::set<base::FilePath> changed_directories_; 65 int load_from_server_complete_count_; 66 int initial_load_complete_count_; 67 68 DISALLOW_COPY_AND_ASSIGN(TestChangeListLoaderObserver); 69 }; 70 71 class TestDriveService : public FakeDriveService { 72 public: 73 TestDriveService() : never_return_all_resource_list_(false), 74 blocked_call_count_(0) {} 75 76 void set_never_return_all_resource_list(bool value) { 77 never_return_all_resource_list_ = value; 78 } 79 80 int blocked_call_count() const { return blocked_call_count_; } 81 82 // FakeDriveService override. 83 virtual google_apis::CancelCallback GetAllResourceList( 84 const google_apis::GetResourceListCallback& callback) OVERRIDE { 85 if (never_return_all_resource_list_) { 86 ++blocked_call_count_; 87 return google_apis::CancelCallback(); 88 } 89 return FakeDriveService::GetAllResourceList(callback); 90 } 91 92 private: 93 // GetAllResourceList never returns result when this is set to true. 94 // Used to emulate the real server's slowness. 95 bool never_return_all_resource_list_; 96 97 int blocked_call_count_; // Number of blocked method calls. 98 }; 99 100 class ChangeListLoaderTest : public testing::Test { 101 protected: 102 virtual void SetUp() OVERRIDE { 103 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 104 pref_service_.reset(new TestingPrefServiceSimple); 105 test_util::RegisterDrivePrefs(pref_service_->registry()); 106 107 drive_service_.reset(new TestDriveService); 108 ASSERT_TRUE(drive_service_->LoadResourceListForWapi( 109 "gdata/root_feed.json")); 110 ASSERT_TRUE(drive_service_->LoadAccountMetadataForWapi( 111 "gdata/account_metadata.json")); 112 113 scheduler_.reset(new JobScheduler(pref_service_.get(), 114 drive_service_.get(), 115 base::MessageLoopProxy::current().get())); 116 metadata_storage_.reset(new ResourceMetadataStorage( 117 temp_dir_.path(), base::MessageLoopProxy::current().get())); 118 ASSERT_TRUE(metadata_storage_->Initialize()); 119 120 metadata_.reset(new ResourceMetadata( 121 metadata_storage_.get(), base::MessageLoopProxy::current().get())); 122 ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize()); 123 124 cache_.reset(new FileCache(metadata_storage_.get(), 125 temp_dir_.path(), 126 base::MessageLoopProxy::current().get(), 127 NULL /* free_disk_space_getter */)); 128 ASSERT_TRUE(cache_->Initialize()); 129 130 change_list_loader_.reset( 131 new ChangeListLoader(base::MessageLoopProxy::current().get(), 132 metadata_.get(), 133 scheduler_.get())); 134 } 135 136 // Adds a new file to the root directory of the service. 137 scoped_ptr<google_apis::ResourceEntry> AddNewFile(const std::string& title) { 138 google_apis::GDataErrorCode error = google_apis::GDATA_FILE_ERROR; 139 scoped_ptr<google_apis::ResourceEntry> entry; 140 drive_service_->AddNewFile( 141 "text/plain", 142 "content text", 143 drive_service_->GetRootResourceId(), 144 title, 145 false, // shared_with_me 146 google_apis::test_util::CreateCopyResultCallback(&error, &entry)); 147 base::RunLoop().RunUntilIdle(); 148 EXPECT_EQ(google_apis::HTTP_CREATED, error); 149 return entry.Pass(); 150 } 151 152 content::TestBrowserThreadBundle thread_bundle_; 153 base::ScopedTempDir temp_dir_; 154 scoped_ptr<TestingPrefServiceSimple> pref_service_; 155 scoped_ptr<TestDriveService> drive_service_; 156 scoped_ptr<JobScheduler> scheduler_; 157 scoped_ptr<ResourceMetadataStorage, 158 test_util::DestroyHelperForTests> metadata_storage_; 159 scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_; 160 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 161 scoped_ptr<ChangeListLoader> change_list_loader_; 162 }; 163 164 TEST_F(ChangeListLoaderTest, LoadIfNeeded) { 165 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 166 167 // Start initial load. 168 TestChangeListLoaderObserver observer(change_list_loader_.get()); 169 170 FileError error = FILE_ERROR_FAILED; 171 change_list_loader_->LoadIfNeeded( 172 DirectoryFetchInfo(), 173 google_apis::test_util::CreateCopyResultCallback(&error)); 174 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 175 base::RunLoop().RunUntilIdle(); 176 EXPECT_EQ(FILE_ERROR_OK, error); 177 178 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 179 EXPECT_LT(0, metadata_->GetLargestChangestamp()); 180 EXPECT_EQ(1, drive_service_->resource_list_load_count()); 181 EXPECT_EQ(1, observer.initial_load_complete_count()); 182 EXPECT_EQ(1, observer.load_from_server_complete_count()); 183 EXPECT_TRUE(observer.changed_directories().empty()); 184 185 base::FilePath file_path = 186 util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt"); 187 ResourceEntry entry; 188 EXPECT_EQ(FILE_ERROR_OK, 189 metadata_->GetResourceEntryByPath(file_path, &entry)); 190 191 // Reload. This should result in no-op. 192 int64 previous_changestamp = metadata_->GetLargestChangestamp(); 193 int previous_resource_list_load_count = 194 drive_service_->resource_list_load_count(); 195 change_list_loader_->LoadIfNeeded( 196 DirectoryFetchInfo(), 197 google_apis::test_util::CreateCopyResultCallback(&error)); 198 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 199 base::RunLoop().RunUntilIdle(); 200 EXPECT_EQ(FILE_ERROR_OK, error); 201 202 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 203 EXPECT_EQ(previous_changestamp, metadata_->GetLargestChangestamp()); 204 EXPECT_EQ(previous_resource_list_load_count, 205 drive_service_->resource_list_load_count()); 206 } 207 208 TEST_F(ChangeListLoaderTest, LoadIfNeeded_LocalMetadataAvailable) { 209 // Prepare metadata. 210 FileError error = FILE_ERROR_FAILED; 211 change_list_loader_->LoadIfNeeded( 212 DirectoryFetchInfo(), 213 google_apis::test_util::CreateCopyResultCallback(&error)); 214 base::RunLoop().RunUntilIdle(); 215 EXPECT_EQ(FILE_ERROR_OK, error); 216 217 // Reset loader. 218 change_list_loader_.reset( 219 new ChangeListLoader(base::MessageLoopProxy::current().get(), 220 metadata_.get(), 221 scheduler_.get())); 222 223 // Add a file to the service. 224 scoped_ptr<google_apis::ResourceEntry> gdata_entry = AddNewFile("New File"); 225 ASSERT_TRUE(gdata_entry); 226 227 // Start loading. Because local metadata is available, the load results in 228 // returning FILE_ERROR_OK without fetching full list of resources. 229 const int previous_resource_list_load_count = 230 drive_service_->resource_list_load_count(); 231 TestChangeListLoaderObserver observer(change_list_loader_.get()); 232 233 change_list_loader_->LoadIfNeeded( 234 DirectoryFetchInfo(), 235 google_apis::test_util::CreateCopyResultCallback(&error)); 236 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 237 base::RunLoop().RunUntilIdle(); 238 EXPECT_EQ(FILE_ERROR_OK, error); 239 EXPECT_EQ(previous_resource_list_load_count, 240 drive_service_->resource_list_load_count()); 241 EXPECT_EQ(1, observer.initial_load_complete_count()); 242 243 // Update should be checked by LoadIfNeeded(). 244 EXPECT_EQ(drive_service_->largest_changestamp(), 245 metadata_->GetLargestChangestamp()); 246 EXPECT_EQ(1, drive_service_->change_list_load_count()); 247 EXPECT_EQ(1, observer.load_from_server_complete_count()); 248 EXPECT_EQ(1U, observer.changed_directories().count( 249 util::GetDriveMyDriveRootPath())); 250 251 base::FilePath file_path = 252 util::GetDriveMyDriveRootPath().AppendASCII(gdata_entry->title()); 253 ResourceEntry entry; 254 EXPECT_EQ(FILE_ERROR_OK, 255 metadata_->GetResourceEntryByPath(file_path, &entry)); 256 } 257 258 TEST_F(ChangeListLoaderTest, LoadIfNeeded_MyDrive) { 259 TestChangeListLoaderObserver observer(change_list_loader_.get()); 260 261 // Emulate the slowness of GetAllResourceList(). 262 drive_service_->set_never_return_all_resource_list(true); 263 264 // Load grand root. 265 FileError error = FILE_ERROR_FAILED; 266 change_list_loader_->LoadIfNeeded( 267 DirectoryFetchInfo(util::kDriveGrandRootSpecialResourceId, 0), 268 google_apis::test_util::CreateCopyResultCallback(&error)); 269 base::RunLoop().RunUntilIdle(); 270 EXPECT_EQ(FILE_ERROR_OK, error); 271 EXPECT_EQ(1U, observer.changed_directories().count( 272 util::GetDriveGrandRootPath())); 273 observer.clear_changed_directories(); 274 275 // GetAllResourceList() was called. 276 EXPECT_EQ(1, drive_service_->blocked_call_count()); 277 278 // My Drive is present in the local metadata, but its child is not. 279 ResourceEntry entry; 280 EXPECT_EQ(FILE_ERROR_OK, 281 metadata_->GetResourceEntryByPath(util::GetDriveMyDriveRootPath(), 282 &entry)); 283 const int64 mydrive_changestamp = 284 entry.directory_specific_info().changestamp(); 285 286 base::FilePath file_path = 287 util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt"); 288 EXPECT_EQ(FILE_ERROR_NOT_FOUND, 289 metadata_->GetResourceEntryByPath(file_path, &entry)); 290 291 // Load My Drive. 292 change_list_loader_->LoadIfNeeded( 293 DirectoryFetchInfo(drive_service_->GetRootResourceId(), 294 mydrive_changestamp), 295 google_apis::test_util::CreateCopyResultCallback(&error)); 296 base::RunLoop().RunUntilIdle(); 297 EXPECT_EQ(FILE_ERROR_OK, error); 298 EXPECT_EQ(1U, observer.changed_directories().count( 299 util::GetDriveMyDriveRootPath())); 300 301 // Now the file is present. 302 EXPECT_EQ(FILE_ERROR_OK, 303 metadata_->GetResourceEntryByPath(file_path, &entry)); 304 } 305 306 TEST_F(ChangeListLoaderTest, LoadIfNeeded_NewDirectories) { 307 // Make local metadata up to date. 308 FileError error = FILE_ERROR_FAILED; 309 change_list_loader_->LoadIfNeeded( 310 DirectoryFetchInfo(), 311 google_apis::test_util::CreateCopyResultCallback(&error)); 312 base::RunLoop().RunUntilIdle(); 313 EXPECT_EQ(FILE_ERROR_OK, error); 314 315 // Add a new file. 316 scoped_ptr<google_apis::ResourceEntry> file = AddNewFile("New File"); 317 ASSERT_TRUE(file); 318 319 // Emulate the slowness of GetAllResourceList(). 320 drive_service_->set_never_return_all_resource_list(true); 321 322 // Enter refreshing state. 323 FileError check_for_updates_error = FILE_ERROR_FAILED; 324 change_list_loader_->CheckForUpdates( 325 google_apis::test_util::CreateCopyResultCallback( 326 &check_for_updates_error)); 327 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 328 329 // Load My Drive. 330 TestChangeListLoaderObserver observer(change_list_loader_.get()); 331 change_list_loader_->LoadIfNeeded( 332 DirectoryFetchInfo(drive_service_->GetRootResourceId(), 333 metadata_->GetLargestChangestamp()), 334 google_apis::test_util::CreateCopyResultCallback(&error)); 335 base::RunLoop().RunUntilIdle(); 336 EXPECT_EQ(FILE_ERROR_OK, error); 337 EXPECT_EQ(1U, observer.changed_directories().count( 338 util::GetDriveMyDriveRootPath())); 339 340 // The new file is present in the local metadata. 341 base::FilePath file_path = 342 util::GetDriveMyDriveRootPath().AppendASCII(file->title()); 343 ResourceEntry entry; 344 EXPECT_EQ(FILE_ERROR_OK, 345 metadata_->GetResourceEntryByPath(file_path, &entry)); 346 } 347 348 TEST_F(ChangeListLoaderTest, LoadIfNeeded_MultipleCalls) { 349 TestChangeListLoaderObserver observer(change_list_loader_.get()); 350 351 // Load grand root. 352 FileError error = FILE_ERROR_FAILED; 353 change_list_loader_->LoadIfNeeded( 354 DirectoryFetchInfo(util::kDriveGrandRootSpecialResourceId, 0), 355 google_apis::test_util::CreateCopyResultCallback(&error)); 356 357 // Load grand root again without waiting for the result. 358 FileError error2 = FILE_ERROR_FAILED; 359 change_list_loader_->LoadIfNeeded( 360 DirectoryFetchInfo(util::kDriveGrandRootSpecialResourceId, 0), 361 google_apis::test_util::CreateCopyResultCallback(&error2)); 362 base::RunLoop().RunUntilIdle(); 363 364 // Callback is called for each method call. 365 EXPECT_EQ(FILE_ERROR_OK, error); 366 EXPECT_EQ(FILE_ERROR_OK, error2); 367 368 // No duplicated resource list load and observer events. 369 EXPECT_EQ(1, drive_service_->resource_list_load_count()); 370 EXPECT_EQ(1, observer.initial_load_complete_count()); 371 EXPECT_EQ(1, observer.load_from_server_complete_count()); 372 } 373 374 TEST_F(ChangeListLoaderTest, CheckForUpdates) { 375 // CheckForUpdates() results in no-op before load. 376 FileError check_for_updates_error = FILE_ERROR_FAILED; 377 change_list_loader_->CheckForUpdates( 378 google_apis::test_util::CreateCopyResultCallback( 379 &check_for_updates_error)); 380 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 381 base::RunLoop().RunUntilIdle(); 382 EXPECT_EQ(FILE_ERROR_FAILED, 383 check_for_updates_error); // Callback was not run. 384 EXPECT_EQ(0, metadata_->GetLargestChangestamp()); 385 EXPECT_EQ(0, drive_service_->resource_list_load_count()); 386 387 // Start initial load. 388 FileError load_error = FILE_ERROR_FAILED; 389 change_list_loader_->LoadIfNeeded( 390 DirectoryFetchInfo(), 391 google_apis::test_util::CreateCopyResultCallback(&load_error)); 392 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 393 394 // CheckForUpdates() while loading. 395 change_list_loader_->CheckForUpdates( 396 google_apis::test_util::CreateCopyResultCallback( 397 &check_for_updates_error)); 398 399 base::RunLoop().RunUntilIdle(); 400 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 401 EXPECT_EQ(FILE_ERROR_OK, load_error); 402 EXPECT_EQ(FILE_ERROR_OK, check_for_updates_error); 403 EXPECT_LT(0, metadata_->GetLargestChangestamp()); 404 EXPECT_EQ(1, drive_service_->resource_list_load_count()); 405 406 int64 previous_changestamp = metadata_->GetLargestChangestamp(); 407 // CheckForUpdates() results in no update. 408 change_list_loader_->CheckForUpdates( 409 google_apis::test_util::CreateCopyResultCallback( 410 &check_for_updates_error)); 411 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 412 base::RunLoop().RunUntilIdle(); 413 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 414 EXPECT_EQ(previous_changestamp, metadata_->GetLargestChangestamp()); 415 416 // Add a file to the service. 417 scoped_ptr<google_apis::ResourceEntry> gdata_entry = AddNewFile("New File"); 418 ASSERT_TRUE(gdata_entry); 419 420 // CheckForUpdates() results in update. 421 TestChangeListLoaderObserver observer(change_list_loader_.get()); 422 change_list_loader_->CheckForUpdates( 423 google_apis::test_util::CreateCopyResultCallback( 424 &check_for_updates_error)); 425 EXPECT_TRUE(change_list_loader_->IsRefreshing()); 426 base::RunLoop().RunUntilIdle(); 427 EXPECT_FALSE(change_list_loader_->IsRefreshing()); 428 EXPECT_LT(previous_changestamp, metadata_->GetLargestChangestamp()); 429 EXPECT_EQ(1, observer.load_from_server_complete_count()); 430 EXPECT_EQ(1U, observer.changed_directories().count( 431 util::GetDriveMyDriveRootPath())); 432 433 // The new file is found in the local metadata. 434 base::FilePath new_file_path = 435 util::GetDriveMyDriveRootPath().AppendASCII(gdata_entry->title()); 436 ResourceEntry entry; 437 EXPECT_EQ(FILE_ERROR_OK, 438 metadata_->GetResourceEntryByPath(new_file_path, &entry)); 439 } 440 441 } // namespace internal 442 } // namespace drive 443