1 // Copyright (c) 2012 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/run_loop.h" 10 #include "base/stl_util.h" 11 #include "base/synchronization/waitable_event.h" 12 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" 13 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" 14 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h" 15 #include "chrome/browser/sync_file_system/local/mock_sync_status_observer.h" 16 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 17 #include "chrome/browser/sync_file_system/mock_remote_file_sync_service.h" 18 #include "chrome/browser/sync_file_system/sync_callbacks.h" 19 #include "chrome/browser/sync_file_system/sync_event_observer.h" 20 #include "chrome/browser/sync_file_system/sync_file_metadata.h" 21 #include "chrome/browser/sync_file_system/sync_file_system_service.h" 22 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h" 23 #include "chrome/browser/sync_file_system/sync_status_code.h" 24 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 25 #include "chrome/test/base/testing_profile.h" 26 #include "content/public/browser/browser_thread.h" 27 #include "content/public/test/test_browser_thread_bundle.h" 28 #include "content/public/test/test_utils.h" 29 #include "testing/gtest/include/gtest/gtest.h" 30 #include "webkit/browser/fileapi/file_system_context.h" 31 32 using content::BrowserThread; 33 using fileapi::FileSystemURL; 34 using fileapi::FileSystemURLSet; 35 using ::testing::AnyNumber; 36 using ::testing::AtLeast; 37 using ::testing::InSequence; 38 using ::testing::InvokeWithoutArgs; 39 using ::testing::Return; 40 using ::testing::StrictMock; 41 using ::testing::_; 42 43 namespace sync_file_system { 44 45 namespace { 46 47 const char kOrigin[] = "http://example.com"; 48 49 template <typename R> struct AssignTrait { 50 typedef const R& ArgumentType; 51 }; 52 53 template <> struct AssignTrait<SyncFileStatus> { 54 typedef SyncFileStatus ArgumentType; 55 }; 56 57 template <typename R> 58 void AssignValueAndQuit(base::RunLoop* run_loop, 59 SyncStatusCode* status_out, R* value_out, 60 SyncStatusCode status, 61 typename AssignTrait<R>::ArgumentType value) { 62 DCHECK(status_out); 63 DCHECK(value_out); 64 DCHECK(run_loop); 65 *status_out = status; 66 *value_out = value; 67 run_loop->Quit(); 68 } 69 70 // This is called on IO thread. 71 void VerifyFileError(base::RunLoop* run_loop, 72 base::PlatformFileError error) { 73 DCHECK(run_loop); 74 EXPECT_EQ(base::PLATFORM_FILE_OK, error); 75 run_loop->Quit(); 76 } 77 78 } // namespace 79 80 class MockSyncEventObserver : public SyncEventObserver { 81 public: 82 MockSyncEventObserver() {} 83 virtual ~MockSyncEventObserver() {} 84 85 MOCK_METHOD3(OnSyncStateUpdated, 86 void(const GURL& app_origin, 87 SyncServiceState state, 88 const std::string& description)); 89 MOCK_METHOD4(OnFileSynced, 90 void(const fileapi::FileSystemURL& url, 91 SyncFileStatus status, 92 SyncAction action, 93 SyncDirection direction)); 94 }; 95 96 ACTION_P3(NotifyStateAndCallback, 97 mock_remote_service, service_state, operation_status) { 98 mock_remote_service->NotifyRemoteServiceStateUpdated( 99 service_state, "Test event."); 100 base::MessageLoopProxy::current()->PostTask( 101 FROM_HERE, base::Bind(arg1, operation_status)); 102 } 103 104 ACTION_P(RecordState, states) { 105 states->push_back(arg1); 106 } 107 108 ACTION_P(MockStatusCallback, status) { 109 base::MessageLoopProxy::current()->PostTask( 110 FROM_HERE, base::Bind(arg4, status)); 111 } 112 113 ACTION_P2(MockSyncFileCallback, status, url) { 114 base::MessageLoopProxy::current()->PostTask( 115 FROM_HERE, base::Bind(arg0, status, url)); 116 } 117 118 class SyncFileSystemServiceTest : public testing::Test { 119 protected: 120 SyncFileSystemServiceTest() 121 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD | 122 content::TestBrowserThreadBundle::REAL_IO_THREAD) {} 123 124 virtual void SetUp() OVERRIDE { 125 file_system_.reset(new CannedSyncableFileSystem( 126 GURL(kOrigin), 127 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), 128 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE))); 129 130 local_service_ = new LocalFileSyncService(&profile_); 131 remote_service_ = new StrictMock<MockRemoteFileSyncService>; 132 sync_service_.reset(new SyncFileSystemService(&profile_)); 133 134 EXPECT_CALL(*mock_remote_service(), 135 AddServiceObserver(_)).Times(1); 136 EXPECT_CALL(*mock_remote_service(), 137 AddFileStatusObserver(sync_service_.get())).Times(1); 138 EXPECT_CALL(*mock_remote_service(), 139 GetLocalChangeProcessor()) 140 .WillRepeatedly(Return(&local_change_processor_)); 141 EXPECT_CALL(*mock_remote_service(), 142 SetRemoteChangeProcessor(local_service_)).Times(1); 143 144 sync_service_->Initialize( 145 make_scoped_ptr(local_service_), 146 scoped_ptr<RemoteFileSyncService>(remote_service_)); 147 148 // Disable auto sync by default. 149 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1); 150 sync_service_->SetSyncEnabledForTesting(false); 151 152 file_system_->SetUp(); 153 } 154 155 virtual void TearDown() OVERRIDE { 156 sync_service_->Shutdown(); 157 file_system_->TearDown(); 158 RevokeSyncableFileSystem(); 159 content::RunAllPendingInMessageLoop(BrowserThread::FILE); 160 } 161 162 void InitializeApp() { 163 base::RunLoop run_loop; 164 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 165 166 EXPECT_CALL(*mock_remote_service(), 167 RegisterOrigin(GURL(kOrigin), _)).Times(1); 168 169 // GetCurrentState may be called when a remote or local sync is scheduled 170 // by change notifications or by a timer. 171 EXPECT_CALL(*mock_remote_service(), GetCurrentState()) 172 .Times(AnyNumber()) 173 .WillRepeatedly(Return(REMOTE_SERVICE_OK)); 174 175 sync_service_->InitializeForApp( 176 file_system_->file_system_context(), 177 GURL(kOrigin), 178 AssignAndQuitCallback(&run_loop, &status)); 179 run_loop.Run(); 180 181 EXPECT_EQ(SYNC_STATUS_OK, status); 182 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem()); 183 } 184 185 // Calls InitializeForApp after setting up the mock remote service to 186 // perform following when RegisterOrigin is called: 187 // 1. Notify RemoteFileSyncService's observers of |state_to_notify| 188 // 2. Run the given callback with |status_to_return|. 189 // 190 // ..and verifies if following conditions are met: 191 // 1. The SyncEventObserver of the service is called with 192 // |expected_states| service state values. 193 // 2. InitializeForApp's callback is called with |expected_status| 194 void InitializeAppForObserverTest( 195 RemoteServiceState state_to_notify, 196 SyncStatusCode status_to_return, 197 const std::vector<SyncServiceState>& expected_states, 198 SyncStatusCode expected_status) { 199 StrictMock<MockSyncEventObserver> event_observer; 200 sync_service_->AddSyncEventObserver(&event_observer); 201 202 EnableSync(); 203 204 EXPECT_CALL(*mock_remote_service(), GetCurrentState()) 205 .Times(AnyNumber()) 206 .WillRepeatedly(Return(state_to_notify)); 207 208 EXPECT_CALL(*mock_remote_service(), 209 RegisterOrigin(GURL(kOrigin), _)) 210 .WillOnce(NotifyStateAndCallback(mock_remote_service(), 211 state_to_notify, 212 status_to_return)); 213 214 std::vector<SyncServiceState> actual_states; 215 EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _)) 216 .WillRepeatedly(RecordState(&actual_states)); 217 218 SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN; 219 base::RunLoop run_loop; 220 sync_service_->InitializeForApp( 221 file_system_->file_system_context(), 222 GURL(kOrigin), 223 AssignAndQuitCallback(&run_loop, &actual_status)); 224 run_loop.Run(); 225 226 EXPECT_EQ(expected_status, actual_status); 227 ASSERT_EQ(expected_states.size(), actual_states.size()); 228 for (size_t i = 0; i < actual_states.size(); ++i) 229 EXPECT_EQ(expected_states[i], actual_states[i]); 230 231 sync_service_->RemoveSyncEventObserver(&event_observer); 232 } 233 234 FileSystemURL URL(const std::string& path) const { 235 return file_system_->URL(path); 236 } 237 238 StrictMock<MockRemoteFileSyncService>* mock_remote_service() { 239 return remote_service_; 240 } 241 242 StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() { 243 return &local_change_processor_; 244 } 245 246 void EnableSync() { 247 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1); 248 sync_service_->SetSyncEnabledForTesting(true); 249 } 250 251 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; 252 253 content::TestBrowserThreadBundle thread_bundle_; 254 TestingProfile profile_; 255 scoped_ptr<CannedSyncableFileSystem> file_system_; 256 257 // Their ownerships are transferred to SyncFileSystemService. 258 LocalFileSyncService* local_service_; 259 StrictMock<MockRemoteFileSyncService>* remote_service_; 260 StrictMock<MockLocalChangeProcessor> local_change_processor_; 261 262 scoped_ptr<SyncFileSystemService> sync_service_; 263 }; 264 265 TEST_F(SyncFileSystemServiceTest, InitializeForApp) { 266 InitializeApp(); 267 } 268 269 TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) { 270 std::vector<SyncServiceState> expected_states; 271 expected_states.push_back(SYNC_SERVICE_RUNNING); 272 273 InitializeAppForObserverTest( 274 REMOTE_SERVICE_OK, 275 SYNC_STATUS_OK, 276 expected_states, 277 SYNC_STATUS_OK); 278 } 279 280 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) { 281 std::vector<SyncServiceState> expected_states; 282 expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE); 283 284 // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with 285 // SYNC_STATUS_NETWORK_ERROR. This should let the 286 // InitializeApp fail. 287 InitializeAppForObserverTest( 288 REMOTE_SERVICE_TEMPORARY_UNAVAILABLE, 289 SYNC_STATUS_NETWORK_ERROR, 290 expected_states, 291 SYNC_STATUS_NETWORK_ERROR); 292 } 293 294 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) { 295 std::vector<SyncServiceState> expected_states; 296 expected_states.push_back(SYNC_SERVICE_DISABLED); 297 298 // Notify REMOTE_SERVICE_DISABLED and callback with 299 // SYNC_STATUS_FAILED. This should let the InitializeApp fail. 300 InitializeAppForObserverTest( 301 REMOTE_SERVICE_DISABLED, 302 SYNC_STATUS_FAILED, 303 expected_states, 304 SYNC_STATUS_FAILED); 305 } 306 307 TEST_F(SyncFileSystemServiceTest, SimpleLocalSyncFlow) { 308 InitializeApp(); 309 310 StrictMock<MockSyncStatusObserver> status_observer; 311 312 EnableSync(); 313 file_system_->backend()->sync_context()-> 314 set_mock_notify_changes_duration_in_sec(0); 315 file_system_->AddSyncStatusObserver(&status_observer); 316 317 // We'll test one local sync for this file. 318 const FileSystemURL kFile(file_system_->URL("foo")); 319 320 base::RunLoop run_loop; 321 322 // We should get called OnSyncEnabled and OnWriteEnabled on kFile as in: 323 // 1. OnWriteEnabled when PrepareForSync(SYNC_SHARED) is finished and 324 // the target file is unlocked for writing 325 // 2. OnSyncEnabled x 3 times; 1) when CreateFile is finished, 2) when 326 // file is unlocked after PrepareForSync, and 3) when the sync is 327 // finished. 328 EXPECT_CALL(status_observer, OnWriteEnabled(kFile)) 329 .Times(AtLeast(1)); 330 331 { 332 ::testing::InSequence sequence; 333 EXPECT_CALL(status_observer, OnSyncEnabled(kFile)) 334 .Times(AtLeast(2)); 335 EXPECT_CALL(status_observer, OnSyncEnabled(kFile)) 336 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 337 } 338 339 // The local_change_processor's ApplyLocalChange should be called once 340 // with ADD_OR_UPDATE change for TYPE_FILE. 341 const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 342 SYNC_FILE_TYPE_FILE); 343 EXPECT_CALL(*mock_local_change_processor(), 344 ApplyLocalChange(change, _, _, kFile, _)) 345 .WillOnce(MockStatusCallback(SYNC_STATUS_OK)); 346 347 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 348 349 run_loop.Run(); 350 351 file_system_->RemoveSyncStatusObserver(&status_observer); 352 } 353 354 TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) { 355 InitializeApp(); 356 357 EnableSync(); 358 359 base::RunLoop run_loop; 360 361 // We expect a set of method calls for starting a remote sync. 362 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 363 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 364 365 // This should trigger a remote sync. 366 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1); 367 368 run_loop.Run(); 369 } 370 371 TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) { 372 InitializeApp(); 373 374 EnableSync(); 375 file_system_->backend()->sync_context()-> 376 set_mock_notify_changes_duration_in_sec(0); 377 378 const FileSystemURL kFile(file_system_->URL("foo")); 379 380 base::RunLoop run_loop; 381 382 { 383 InSequence sequence; 384 385 // Return with SYNC_STATUS_FILE_BUSY once. 386 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 387 .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY, 388 kFile)); 389 390 // ProcessRemoteChange should be called again when the becomes 391 // not busy. 392 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 393 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 394 } 395 396 // We might also see an activity for local sync as we're going to make 397 // a local write operation on kFile. 398 EXPECT_CALL(*mock_local_change_processor(), 399 ApplyLocalChange(_, _, _, kFile, _)) 400 .Times(AnyNumber()); 401 402 // This should trigger a remote sync. 403 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1); 404 405 // Start a local operation on the same file (to make it BUSY). 406 base::RunLoop verify_file_error_run_loop; 407 BrowserThread::PostTask( 408 BrowserThread::IO, 409 FROM_HERE, 410 base::Bind(&CannedSyncableFileSystem::DoCreateFile, 411 base::Unretained(file_system_.get()), 412 kFile, base::Bind(&VerifyFileError, 413 &verify_file_error_run_loop))); 414 415 run_loop.Run(); 416 417 mock_remote_service()->NotifyRemoteChangeQueueUpdated(0); 418 419 verify_file_error_run_loop.Run(); 420 } 421 422 #if defined(THREAD_SANITIZER) 423 // SyncFileSystemServiceTest.GetFileSyncStatus fails under ThreadSanitizer, 424 // see http://crbug.com/294904. 425 #define MAYBE_GetFileSyncStatus DISABLED_GetFileSyncStatus 426 #else 427 #define MAYBE_GetFileSyncStatus GetFileSyncStatus 428 #endif 429 TEST_F(SyncFileSystemServiceTest, MAYBE_GetFileSyncStatus) { 430 InitializeApp(); 431 432 const FileSystemURL kFile(file_system_->URL("foo")); 433 434 SyncStatusCode status; 435 SyncFileStatus sync_file_status; 436 437 // 1. The file is not in conflicting nor in pending change state. 438 { 439 base::RunLoop run_loop; 440 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 441 .WillOnce(Return(false)); 442 443 status = SYNC_STATUS_UNKNOWN; 444 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 445 sync_service_->GetFileSyncStatus( 446 kFile, 447 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 448 &run_loop, &status, &sync_file_status)); 449 run_loop.Run(); 450 451 EXPECT_EQ(SYNC_STATUS_OK, status); 452 EXPECT_EQ(SYNC_FILE_STATUS_SYNCED, sync_file_status); 453 } 454 455 // 2. Conflicting case. 456 { 457 base::RunLoop run_loop; 458 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 459 .WillOnce(Return(true)); 460 461 status = SYNC_STATUS_UNKNOWN; 462 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 463 sync_service_->GetFileSyncStatus( 464 kFile, 465 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 466 &run_loop, &status, &sync_file_status)); 467 run_loop.Run(); 468 469 EXPECT_EQ(SYNC_STATUS_OK, status); 470 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status); 471 } 472 473 // 3. The file has pending local changes. 474 { 475 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 476 477 base::RunLoop run_loop; 478 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 479 .WillOnce(Return(false)); 480 481 status = SYNC_STATUS_UNKNOWN; 482 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 483 sync_service_->GetFileSyncStatus( 484 kFile, 485 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 486 &run_loop, &status, &sync_file_status)); 487 run_loop.Run(); 488 489 EXPECT_EQ(SYNC_STATUS_OK, status); 490 EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status); 491 } 492 493 // 4. The file has a conflict and pending local changes. In this case 494 // we return SYNC_FILE_STATUS_CONFLICTING. 495 { 496 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->TruncateFile(kFile, 1U)); 497 498 base::RunLoop run_loop; 499 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 500 .WillOnce(Return(true)); 501 502 status = SYNC_STATUS_UNKNOWN; 503 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 504 sync_service_->GetFileSyncStatus( 505 kFile, 506 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 507 &run_loop, &status, &sync_file_status)); 508 run_loop.Run(); 509 510 EXPECT_EQ(SYNC_STATUS_OK, status); 511 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status); 512 } 513 } 514 515 } // namespace sync_file_system 516