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 "base/basictypes.h" 6 #include "base/bind.h" 7 #include "base/file_util.h" 8 #include "base/location.h" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/run_loop.h" 11 #include "base/stl_util.h" 12 #include "base/threading/thread.h" 13 #include "chrome/browser/sync_file_system/file_change.h" 14 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" 15 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 16 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" 17 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h" 18 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h" 19 #include "chrome/browser/sync_file_system/local/mock_sync_status_observer.h" 20 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 21 #include "chrome/browser/sync_file_system/mock_local_change_processor.h" 22 #include "chrome/browser/sync_file_system/sync_file_metadata.h" 23 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h" 24 #include "chrome/browser/sync_file_system/sync_status_code.h" 25 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 26 #include "chrome/test/base/testing_profile.h" 27 #include "content/public/browser/browser_thread.h" 28 #include "content/public/test/test_browser_thread_bundle.h" 29 #include "content/public/test/test_utils.h" 30 #include "testing/gmock/include/gmock/gmock.h" 31 #include "testing/gtest/include/gtest/gtest.h" 32 #include "webkit/browser/fileapi/file_system_context.h" 33 34 using content::BrowserThread; 35 using fileapi::FileSystemURL; 36 using ::testing::_; 37 using ::testing::AtLeast; 38 using ::testing::InvokeWithoutArgs; 39 using ::testing::StrictMock; 40 41 namespace sync_file_system { 42 43 namespace { 44 45 const char kOrigin[] = "http://example.com"; 46 47 void DidPrepareForProcessRemoteChange(const tracked_objects::Location& where, 48 const base::Closure& oncompleted, 49 SyncStatusCode expected_status, 50 const SyncFileMetadata& expected_metadata, 51 SyncStatusCode status, 52 const SyncFileMetadata& metadata, 53 const FileChangeList& changes) { 54 SCOPED_TRACE(testing::Message() << where.ToString()); 55 ASSERT_EQ(expected_status, status); 56 ASSERT_EQ(expected_metadata.file_type, metadata.file_type); 57 ASSERT_EQ(expected_metadata.size, metadata.size); 58 ASSERT_TRUE(changes.empty()); 59 oncompleted.Run(); 60 } 61 62 void OnSyncCompleted(const tracked_objects::Location& where, 63 const base::Closure& oncompleted, 64 SyncStatusCode expected_status, 65 const FileSystemURL& expected_url, 66 SyncStatusCode status, 67 const FileSystemURL& url) { 68 SCOPED_TRACE(testing::Message() << where.ToString()); 69 ASSERT_EQ(expected_status, status); 70 ASSERT_EQ(expected_url, url); 71 oncompleted.Run(); 72 } 73 74 void OnGetFileMetadata(const tracked_objects::Location& where, 75 const base::Closure& oncompleted, 76 SyncStatusCode* status_out, 77 SyncFileMetadata* metadata_out, 78 SyncStatusCode status, 79 const SyncFileMetadata& metadata) { 80 SCOPED_TRACE(testing::Message() << where.ToString()); 81 *status_out = status; 82 *metadata_out = metadata; 83 oncompleted.Run(); 84 } 85 86 ACTION_P(MockStatusCallback, status) { 87 base::MessageLoopProxy::current()->PostTask( 88 FROM_HERE, base::Bind(arg4, status)); 89 } 90 91 ACTION_P2(MockStatusCallbackAndRecordChange, status, changes) { 92 base::MessageLoopProxy::current()->PostTask( 93 FROM_HERE, base::Bind(arg4, status)); 94 changes->push_back(arg0); 95 } 96 97 } // namespace 98 99 class LocalFileSyncServiceTest 100 : public testing::Test, 101 public LocalFileSyncService::Observer { 102 protected: 103 LocalFileSyncServiceTest() 104 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD | 105 content::TestBrowserThreadBundle::REAL_IO_THREAD), 106 num_changes_(0) {} 107 108 virtual void SetUp() OVERRIDE { 109 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 110 111 file_system_.reset(new CannedSyncableFileSystem( 112 GURL(kOrigin), 113 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), 114 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE))); 115 116 local_service_.reset(new LocalFileSyncService(&profile_)); 117 118 file_system_->SetUp(); 119 120 base::RunLoop run_loop; 121 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 122 local_service_->MaybeInitializeFileSystemContext( 123 GURL(kOrigin), file_system_->file_system_context(), 124 AssignAndQuitCallback(&run_loop, &status)); 125 run_loop.Run(); 126 127 local_service_->AddChangeObserver(this); 128 129 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem()); 130 131 file_system_->backend()->sync_context()-> 132 set_mock_notify_changes_duration_in_sec(0); 133 } 134 135 virtual void TearDown() OVERRIDE { 136 local_service_->Shutdown(); 137 file_system_->TearDown(); 138 RevokeSyncableFileSystem(); 139 content::RunAllPendingInMessageLoop(BrowserThread::FILE); 140 } 141 142 // LocalChangeObserver overrides. 143 virtual void OnLocalChangeAvailable(int64 num_changes) OVERRIDE { 144 num_changes_ = num_changes; 145 } 146 147 void PrepareForProcessRemoteChange( 148 const FileSystemURL& url, 149 const tracked_objects::Location& where, 150 SyncStatusCode expected_status, 151 const SyncFileMetadata& expected_metadata) { 152 base::RunLoop run_loop; 153 local_service_->PrepareForProcessRemoteChange( 154 url, 155 base::Bind(&DidPrepareForProcessRemoteChange, 156 where, 157 run_loop.QuitClosure(), 158 expected_status, 159 expected_metadata)); 160 run_loop.Run(); 161 } 162 163 SyncStatusCode ApplyRemoteChange(const FileChange& change, 164 const base::FilePath& local_path, 165 const FileSystemURL& url) { 166 base::RunLoop run_loop; 167 SyncStatusCode sync_status = SYNC_STATUS_UNKNOWN; 168 local_service_->ApplyRemoteChange( 169 change, local_path, url, 170 AssignAndQuitCallback(&run_loop, &sync_status)); 171 run_loop.Run(); 172 return sync_status; 173 } 174 175 int64 GetNumChangesInTracker() const { 176 return file_system_->backend()->change_tracker()->num_changes(); 177 } 178 179 content::TestBrowserThreadBundle thread_bundle_; 180 181 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; 182 TestingProfile profile_; 183 184 base::ScopedTempDir temp_dir_; 185 186 scoped_ptr<CannedSyncableFileSystem> file_system_; 187 scoped_ptr<LocalFileSyncService> local_service_; 188 189 int64 num_changes_; 190 }; 191 192 // More complete tests for PrepareForProcessRemoteChange and ApplyRemoteChange 193 // are also in content_unittest:LocalFileSyncContextTest. 194 TEST_F(LocalFileSyncServiceTest, RemoteSyncStepsSimple) { 195 const FileSystemURL kFile(file_system_->URL("file")); 196 const FileSystemURL kDir(file_system_->URL("dir")); 197 const char kTestFileData[] = "0123456789"; 198 const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1); 199 200 base::FilePath local_path; 201 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), 202 &local_path)); 203 ASSERT_EQ(kTestFileDataSize, 204 file_util::WriteFile(local_path, kTestFileData, kTestFileDataSize)); 205 206 // Run PrepareForProcessRemoteChange for kFile. 207 SyncFileMetadata expected_metadata; 208 expected_metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; 209 expected_metadata.size = 0; 210 PrepareForProcessRemoteChange(kFile, FROM_HERE, 211 SYNC_STATUS_OK, 212 expected_metadata); 213 214 // Run ApplyRemoteChange for kFile. 215 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 216 SYNC_FILE_TYPE_FILE); 217 EXPECT_EQ(SYNC_STATUS_OK, 218 ApplyRemoteChange(change, local_path, kFile)); 219 220 // Verify the file is synced. 221 EXPECT_EQ(base::PLATFORM_FILE_OK, 222 file_system_->VerifyFile(kFile, kTestFileData)); 223 224 // Run PrepareForProcessRemoteChange for kDir. 225 PrepareForProcessRemoteChange(kDir, FROM_HERE, 226 SYNC_STATUS_OK, 227 expected_metadata); 228 229 // Run ApplyRemoteChange for kDir. 230 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 231 SYNC_FILE_TYPE_DIRECTORY); 232 EXPECT_EQ(SYNC_STATUS_OK, 233 ApplyRemoteChange(change, base::FilePath(), kDir)); 234 235 // Verify the directory. 236 EXPECT_EQ(base::PLATFORM_FILE_OK, 237 file_system_->DirectoryExists(kDir)); 238 239 // Run PrepareForProcessRemoteChange and ApplyRemoteChange for 240 // kDir once again for deletion. 241 expected_metadata.file_type = SYNC_FILE_TYPE_DIRECTORY; 242 expected_metadata.size = 0; 243 PrepareForProcessRemoteChange(kDir, FROM_HERE, 244 SYNC_STATUS_OK, 245 expected_metadata); 246 247 change = FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN); 248 EXPECT_EQ(SYNC_STATUS_OK, ApplyRemoteChange(change, base::FilePath(), kDir)); 249 250 // Now the directory must have deleted. 251 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, 252 file_system_->DirectoryExists(kDir)); 253 } 254 255 TEST_F(LocalFileSyncServiceTest, LocalChangeObserver) { 256 const FileSystemURL kFile(file_system_->URL("file")); 257 const FileSystemURL kDir(file_system_->URL("dir")); 258 const char kTestFileData[] = "0123456789"; 259 const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1); 260 261 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 262 263 EXPECT_EQ(1, num_changes_); 264 265 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateDirectory(kDir)); 266 EXPECT_EQ(kTestFileDataSize, 267 file_system_->WriteString(kFile, kTestFileData)); 268 269 EXPECT_EQ(2, num_changes_); 270 } 271 272 #if defined(OS_WIN) 273 // Flaky: http://crbug.com/171487 274 #define MAYBE_LocalChangeObserverMultipleContexts\ 275 DISABLED_LocalChangeObserverMultipleContexts 276 #else 277 #define MAYBE_LocalChangeObserverMultipleContexts\ 278 LocalChangeObserverMultipleContexts 279 #endif 280 281 TEST_F(LocalFileSyncServiceTest, MAYBE_LocalChangeObserverMultipleContexts) { 282 const char kOrigin2[] = "http://foo"; 283 CannedSyncableFileSystem file_system2( 284 GURL(kOrigin2), 285 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), 286 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); 287 file_system2.SetUp(); 288 289 base::RunLoop run_loop; 290 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 291 local_service_->MaybeInitializeFileSystemContext( 292 GURL(kOrigin2), file_system2.file_system_context(), 293 AssignAndQuitCallback(&run_loop, &status)); 294 run_loop.Run(); 295 296 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.OpenFileSystem()); 297 file_system2.backend()->sync_context()-> 298 set_mock_notify_changes_duration_in_sec(0); 299 300 const FileSystemURL kFile1(file_system_->URL("file1")); 301 const FileSystemURL kFile2(file_system_->URL("file2")); 302 const FileSystemURL kFile3(file_system2.URL("file3")); 303 const FileSystemURL kFile4(file_system2.URL("file4")); 304 305 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile1)); 306 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile2)); 307 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.CreateFile(kFile3)); 308 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.CreateFile(kFile4)); 309 310 EXPECT_EQ(4, num_changes_); 311 312 file_system2.TearDown(); 313 } 314 315 TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateFile) { 316 const FileSystemURL kFile(file_system_->URL("foo")); 317 const char kTestFileData[] = "0123456789"; 318 const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1); 319 320 base::RunLoop run_loop; 321 322 // We should get called OnSyncEnabled on kFile. 323 StrictMock<MockSyncStatusObserver> status_observer; 324 EXPECT_CALL(status_observer, OnSyncEnabled(kFile)) 325 .Times(AtLeast(1)); 326 file_system_->AddSyncStatusObserver(&status_observer); 327 328 // Creates and writes into a file. 329 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 330 EXPECT_EQ(kTestFileDataSize, 331 file_system_->WriteString(kFile, std::string(kTestFileData))); 332 333 // Retrieve the expected platform_path. 334 base::PlatformFileInfo info; 335 base::FilePath platform_path; 336 EXPECT_EQ(base::PLATFORM_FILE_OK, 337 file_system_->GetMetadataAndPlatformPath( 338 kFile, &info, &platform_path)); 339 340 ASSERT_FALSE(info.is_directory); 341 ASSERT_EQ(kTestFileDataSize, info.size); 342 343 SyncFileMetadata metadata; 344 metadata.file_type = SYNC_FILE_TYPE_FILE; 345 metadata.size = info.size; 346 metadata.last_modified = info.last_modified; 347 348 // The local_change_processor's ApplyLocalChange should be called once 349 // with ADD_OR_UPDATE change for TYPE_FILE. 350 StrictMock<MockLocalChangeProcessor> local_change_processor; 351 const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 352 SYNC_FILE_TYPE_FILE); 353 EXPECT_CALL(local_change_processor, 354 ApplyLocalChange(change, platform_path, metadata, kFile, _)) 355 .WillOnce(MockStatusCallback(SYNC_STATUS_OK)); 356 357 local_service_->SetLocalChangeProcessor(&local_change_processor); 358 local_service_->ProcessLocalChange( 359 base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(), 360 SYNC_STATUS_OK, kFile)); 361 362 run_loop.Run(); 363 364 file_system_->RemoveSyncStatusObserver(&status_observer); 365 366 EXPECT_EQ(0, GetNumChangesInTracker()); 367 } 368 369 TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveFile) { 370 const FileSystemURL kFile(file_system_->URL("foo")); 371 372 base::RunLoop run_loop; 373 374 // We should get called OnSyncEnabled and OnWriteEnabled on kFile. 375 StrictMock<MockSyncStatusObserver> status_observer; 376 EXPECT_CALL(status_observer, OnSyncEnabled(kFile)) 377 .Times(AtLeast(1)); 378 file_system_->AddSyncStatusObserver(&status_observer); 379 380 // Creates and then deletes a file. 381 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 382 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->Remove(kFile, false)); 383 384 // The local_change_processor's ApplyLocalChange should be called once 385 // with DELETE change for TYPE_FILE. 386 // The file will NOT exist in the remote side and the processor might 387 // return SYNC_FILE_ERROR_NOT_FOUND (as mocked). 388 StrictMock<MockLocalChangeProcessor> local_change_processor; 389 const FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE); 390 EXPECT_CALL(local_change_processor, ApplyLocalChange(change, _, _, kFile, _)) 391 .WillOnce(MockStatusCallback(SYNC_FILE_ERROR_NOT_FOUND)); 392 393 // The sync should succeed anyway. 394 local_service_->SetLocalChangeProcessor(&local_change_processor); 395 local_service_->ProcessLocalChange( 396 base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(), 397 SYNC_STATUS_OK, kFile)); 398 399 run_loop.Run(); 400 401 file_system_->RemoveSyncStatusObserver(&status_observer); 402 403 EXPECT_EQ(0, GetNumChangesInTracker()); 404 } 405 406 TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveDirectory) { 407 const FileSystemURL kDir(file_system_->URL("foo")); 408 409 base::RunLoop run_loop; 410 411 // OnSyncEnabled is expected to be called at least or more than once. 412 StrictMock<MockSyncStatusObserver> status_observer; 413 EXPECT_CALL(status_observer, OnSyncEnabled(kDir)).Times(AtLeast(1)); 414 file_system_->AddSyncStatusObserver(&status_observer); 415 416 // Creates and then deletes a directory. 417 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateDirectory(kDir)); 418 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->Remove(kDir, false)); 419 420 // The local_change_processor's ApplyLocalChange should never be called. 421 StrictMock<MockLocalChangeProcessor> local_change_processor; 422 423 local_service_->SetLocalChangeProcessor(&local_change_processor); 424 local_service_->ProcessLocalChange( 425 base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(), 426 SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL())); 427 428 run_loop.Run(); 429 430 file_system_->RemoveSyncStatusObserver(&status_observer); 431 432 EXPECT_EQ(0, GetNumChangesInTracker()); 433 } 434 435 TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_MultipleChanges) { 436 const FileSystemURL kPath(file_system_->URL("foo")); 437 const FileSystemURL kOther(file_system_->URL("bar")); 438 439 base::RunLoop run_loop; 440 441 // We should get called OnSyncEnabled and OnWriteEnabled on kPath and 442 // OnSyncEnabled on kOther. 443 StrictMock<MockSyncStatusObserver> status_observer; 444 EXPECT_CALL(status_observer, OnSyncEnabled(kPath)).Times(AtLeast(1)); 445 EXPECT_CALL(status_observer, OnSyncEnabled(kOther)).Times(AtLeast(1)); 446 file_system_->AddSyncStatusObserver(&status_observer); 447 448 // Creates a file, delete the file and creates a directory with the same 449 // name. 450 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kPath)); 451 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->Remove(kPath, false)); 452 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateDirectory(kPath)); 453 454 // Creates one more file. 455 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kOther)); 456 457 // The local_change_processor's ApplyLocalChange will be called 458 // twice for FILE_TYPE and FILE_DIRECTORY. 459 StrictMock<MockLocalChangeProcessor> local_change_processor; 460 std::vector<FileChange> changes; 461 EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kPath, _)) 462 .Times(2) 463 .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes)) 464 .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes)); 465 local_service_->SetLocalChangeProcessor(&local_change_processor); 466 local_service_->ProcessLocalChange( 467 base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(), 468 SYNC_STATUS_OK, kPath)); 469 470 run_loop.Run(); 471 472 EXPECT_EQ(2U, changes.size()); 473 EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE), 474 changes[0]); 475 EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 476 SYNC_FILE_TYPE_DIRECTORY), 477 changes[1]); 478 479 file_system_->RemoveSyncStatusObserver(&status_observer); 480 481 // We have one more change for kOther. 482 EXPECT_EQ(1, GetNumChangesInTracker()); 483 } 484 485 TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_GetLocalMetadata) { 486 const FileSystemURL kURL(file_system_->URL("foo")); 487 const base::Time kTime = base::Time::FromDoubleT(333); 488 const int kSize = 555; 489 490 base::RunLoop run_loop; 491 492 // Creates a file. 493 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kURL)); 494 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->TruncateFile(kURL, kSize)); 495 EXPECT_EQ(base::PLATFORM_FILE_OK, 496 file_system_->TouchFile(kURL, base::Time(), kTime)); 497 498 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 499 SyncFileMetadata metadata; 500 local_service_->GetLocalFileMetadata( 501 kURL, 502 base::Bind(&OnGetFileMetadata, FROM_HERE, run_loop.QuitClosure(), 503 &status, &metadata)); 504 505 run_loop.Run(); 506 507 EXPECT_EQ(SYNC_STATUS_OK, status); 508 EXPECT_EQ(kTime, metadata.last_modified); 509 EXPECT_EQ(kSize, metadata.size); 510 } 511 512 TEST_F(LocalFileSyncServiceTest, RecordFakeChange) { 513 const FileSystemURL kURL(file_system_->URL("foo")); 514 515 // Create a file and reset the changes (as preparation). 516 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kURL)); 517 file_system_->ClearChangeForURLInTracker(kURL); 518 519 EXPECT_EQ(0, GetNumChangesInTracker()); 520 521 fileapi::FileSystemURLSet urlset; 522 file_system_->GetChangedURLsInTracker(&urlset); 523 EXPECT_TRUE(urlset.empty()); 524 525 const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 526 SYNC_FILE_TYPE_FILE); 527 528 // Call RecordFakeLocalChange to add an ADD_OR_UPDATE change. 529 { 530 base::RunLoop run_loop; 531 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 532 local_service_->RecordFakeLocalChange( 533 kURL, change, AssignAndQuitCallback(&run_loop, &status)); 534 run_loop.Run(); 535 EXPECT_EQ(SYNC_STATUS_OK, status); 536 } 537 538 EXPECT_EQ(1, GetNumChangesInTracker()); 539 file_system_->GetChangedURLsInTracker(&urlset); 540 EXPECT_EQ(1U, urlset.size()); 541 EXPECT_TRUE(urlset.find(kURL) != urlset.end()); 542 543 // Next local sync should pick up the recorded change. 544 StrictMock<MockLocalChangeProcessor> local_change_processor; 545 std::vector<FileChange> changes; 546 EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kURL, _)) 547 .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes)); 548 { 549 base::RunLoop run_loop; 550 local_service_->SetLocalChangeProcessor(&local_change_processor); 551 local_service_->ProcessLocalChange( 552 base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(), 553 SYNC_STATUS_OK, kURL)); 554 run_loop.Run(); 555 } 556 557 EXPECT_EQ(1U, changes.size()); 558 EXPECT_EQ(change, changes[0]); 559 } 560 561 // TODO(kinuko): Add tests for multiple file changes and multiple 562 // FileSystemContexts. 563 564 // Unit test for OriginChangeMap --------------------------------------------- 565 566 class OriginChangeMapTest : public testing::Test { 567 protected: 568 OriginChangeMapTest() {} 569 virtual ~OriginChangeMapTest() {} 570 571 bool NextOriginToProcess(GURL* origin) { 572 return map_.NextOriginToProcess(origin); 573 } 574 575 int64 GetTotalChangeCount() const { 576 return map_.GetTotalChangeCount(); 577 } 578 579 void SetOriginChangeCount(const GURL& origin, int64 changes) { 580 map_.SetOriginChangeCount(origin, changes); 581 } 582 583 void SetOriginEnabled(const GURL& origin, bool enabled) { 584 map_.SetOriginEnabled(origin, enabled); 585 } 586 587 LocalFileSyncService::OriginChangeMap map_; 588 }; 589 590 TEST_F(OriginChangeMapTest, Basic) { 591 const GURL kOrigin1("chrome-extension://foo"); 592 const GURL kOrigin2("chrome-extension://bar"); 593 const GURL kOrigin3("chrome-extension://baz"); 594 595 ASSERT_EQ(0, GetTotalChangeCount()); 596 597 SetOriginChangeCount(kOrigin1, 1); 598 SetOriginChangeCount(kOrigin2, 2); 599 600 ASSERT_EQ(1 + 2, GetTotalChangeCount()); 601 602 SetOriginChangeCount(kOrigin3, 4); 603 604 ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount()); 605 606 const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 }; 607 std::set<GURL> all_origins; 608 all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins)); 609 610 GURL origin; 611 while (!all_origins.empty()) { 612 ASSERT_TRUE(NextOriginToProcess(&origin)); 613 ASSERT_TRUE(ContainsKey(all_origins, origin)); 614 all_origins.erase(origin); 615 } 616 617 // Set kOrigin2's change count 0. 618 SetOriginChangeCount(kOrigin2, 0); 619 ASSERT_EQ(1 + 4, GetTotalChangeCount()); 620 621 // kOrigin2 won't return this time. 622 all_origins.insert(kOrigin1); 623 all_origins.insert(kOrigin3); 624 while (!all_origins.empty()) { 625 ASSERT_TRUE(NextOriginToProcess(&origin)); 626 ASSERT_TRUE(ContainsKey(all_origins, origin)); 627 all_origins.erase(origin); 628 } 629 630 // Calling NextOriginToProcess() again will just return 631 // the same set of origins (as far as we don't change the 632 // change count). 633 all_origins.insert(kOrigin1); 634 all_origins.insert(kOrigin3); 635 while (!all_origins.empty()) { 636 ASSERT_TRUE(NextOriginToProcess(&origin)); 637 ASSERT_TRUE(ContainsKey(all_origins, origin)); 638 all_origins.erase(origin); 639 } 640 641 // Set kOrigin2's change count 8. 642 SetOriginChangeCount(kOrigin2, 8); 643 ASSERT_EQ(1 + 4 + 8, GetTotalChangeCount()); 644 645 all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins)); 646 while (!all_origins.empty()) { 647 ASSERT_TRUE(NextOriginToProcess(&origin)); 648 ASSERT_TRUE(ContainsKey(all_origins, origin)); 649 all_origins.erase(origin); 650 } 651 } 652 653 TEST_F(OriginChangeMapTest, WithDisabled) { 654 const GURL kOrigin1("chrome-extension://foo"); 655 const GURL kOrigin2("chrome-extension://bar"); 656 const GURL kOrigin3("chrome-extension://baz"); 657 const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 }; 658 659 ASSERT_EQ(0, GetTotalChangeCount()); 660 661 SetOriginChangeCount(kOrigin1, 1); 662 SetOriginChangeCount(kOrigin2, 2); 663 SetOriginChangeCount(kOrigin3, 4); 664 665 ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount()); 666 667 std::set<GURL> all_origins; 668 all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins)); 669 670 GURL origin; 671 while (!all_origins.empty()) { 672 ASSERT_TRUE(NextOriginToProcess(&origin)); 673 ASSERT_TRUE(ContainsKey(all_origins, origin)); 674 all_origins.erase(origin); 675 } 676 677 SetOriginEnabled(kOrigin2, false); 678 ASSERT_EQ(1 + 4, GetTotalChangeCount()); 679 680 // kOrigin2 won't return this time. 681 all_origins.insert(kOrigin1); 682 all_origins.insert(kOrigin3); 683 while (!all_origins.empty()) { 684 ASSERT_TRUE(NextOriginToProcess(&origin)); 685 ASSERT_TRUE(ContainsKey(all_origins, origin)); 686 all_origins.erase(origin); 687 } 688 689 // kOrigin1 and kOrigin2 are now disabled. 690 SetOriginEnabled(kOrigin1, false); 691 ASSERT_EQ(4, GetTotalChangeCount()); 692 693 ASSERT_TRUE(NextOriginToProcess(&origin)); 694 ASSERT_EQ(kOrigin3, origin); 695 696 // Re-enable kOrigin2. 697 SetOriginEnabled(kOrigin2, true); 698 ASSERT_EQ(2 + 4, GetTotalChangeCount()); 699 700 // kOrigin1 won't return this time. 701 all_origins.insert(kOrigin2); 702 all_origins.insert(kOrigin3); 703 while (!all_origins.empty()) { 704 ASSERT_TRUE(NextOriginToProcess(&origin)); 705 ASSERT_TRUE(ContainsKey(all_origins, origin)); 706 all_origins.erase(origin); 707 } 708 } 709 710 } // namespace sync_file_system 711