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