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