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