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(sync_service_.get())).Times(1); 136 EXPECT_CALL(*mock_remote_service(), 137 AddFileStatusObserver(sync_service_.get())).Times(1); 138 EXPECT_CALL(*mock_remote_service(), 139 GetLocalChangeProcessor()) 140 .WillOnce(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 RegisterOriginForTrackingChanges(GURL(kOrigin), _)).Times(1); 168 169 sync_service_->InitializeForApp( 170 file_system_->file_system_context(), 171 GURL(kOrigin), 172 AssignAndQuitCallback(&run_loop, &status)); 173 run_loop.Run(); 174 175 EXPECT_EQ(SYNC_STATUS_OK, status); 176 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem()); 177 } 178 179 // Calls InitializeForApp after setting up the mock remote service to 180 // perform following when RegisterOriginForTrackingChanges is called: 181 // 1. Notify RemoteFileSyncService's observers of |state_to_notify| 182 // 2. Run the given callback with |status_to_return|. 183 // 184 // ..and verifies if following conditions are met: 185 // 1. The SyncEventObserver of the service is called with 186 // |expected_states| service state values. 187 // 2. InitializeForApp's callback is called with |expected_status| 188 // 3. GetCurrentState() is called at least |expected_current_state_calls| 189 // times (which means that the sync service tried to start sync). 190 void InitializeAppForObserverTest( 191 RemoteServiceState state_to_notify, 192 SyncStatusCode status_to_return, 193 const std::vector<SyncServiceState>& expected_states, 194 SyncStatusCode expected_status) { 195 StrictMock<MockSyncEventObserver> event_observer; 196 sync_service_->AddSyncEventObserver(&event_observer); 197 198 EnableSync(); 199 200 EXPECT_CALL(*mock_remote_service(), 201 RegisterOriginForTrackingChanges(GURL(kOrigin), _)) 202 .WillOnce(NotifyStateAndCallback(mock_remote_service(), 203 state_to_notify, 204 status_to_return)); 205 206 std::vector<SyncServiceState> actual_states; 207 EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _)) 208 .WillRepeatedly(RecordState(&actual_states)); 209 210 SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN; 211 base::RunLoop run_loop; 212 sync_service_->InitializeForApp( 213 file_system_->file_system_context(), 214 GURL(kOrigin), 215 AssignAndQuitCallback(&run_loop, &actual_status)); 216 run_loop.Run(); 217 218 EXPECT_EQ(expected_status, actual_status); 219 ASSERT_EQ(expected_states.size(), actual_states.size()); 220 for (size_t i = 0; i < actual_states.size(); ++i) 221 EXPECT_EQ(expected_states[i], actual_states[i]); 222 } 223 224 FileSystemURL URL(const std::string& path) const { 225 return file_system_->URL(path); 226 } 227 228 StrictMock<MockRemoteFileSyncService>* mock_remote_service() { 229 return remote_service_; 230 } 231 232 StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() { 233 return &local_change_processor_; 234 } 235 236 void EnableSync() { 237 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1); 238 sync_service_->SetSyncEnabledForTesting(true); 239 } 240 241 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; 242 243 content::TestBrowserThreadBundle thread_bundle_; 244 TestingProfile profile_; 245 scoped_ptr<CannedSyncableFileSystem> file_system_; 246 247 // Their ownerships are transferred to SyncFileSystemService. 248 LocalFileSyncService* local_service_; 249 StrictMock<MockRemoteFileSyncService>* remote_service_; 250 StrictMock<MockLocalChangeProcessor> local_change_processor_; 251 252 scoped_ptr<SyncFileSystemService> sync_service_; 253 }; 254 255 TEST_F(SyncFileSystemServiceTest, InitializeForApp) { 256 InitializeApp(); 257 } 258 259 TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) { 260 std::vector<SyncServiceState> expected_states; 261 expected_states.push_back(SYNC_SERVICE_RUNNING); 262 263 InitializeAppForObserverTest( 264 REMOTE_SERVICE_OK, 265 SYNC_STATUS_OK, 266 expected_states, 267 SYNC_STATUS_OK); 268 } 269 270 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) { 271 std::vector<SyncServiceState> expected_states; 272 expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE); 273 274 // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with 275 // SYNC_STATUS_NETWORK_ERROR. This should let the 276 // InitializeApp fail. 277 InitializeAppForObserverTest( 278 REMOTE_SERVICE_TEMPORARY_UNAVAILABLE, 279 SYNC_STATUS_NETWORK_ERROR, 280 expected_states, 281 SYNC_STATUS_NETWORK_ERROR); 282 } 283 284 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) { 285 std::vector<SyncServiceState> expected_states; 286 expected_states.push_back(SYNC_SERVICE_DISABLED); 287 288 // Notify REMOTE_SERVICE_DISABLED and callback with 289 // SYNC_STATUS_FAILED. This should let the InitializeApp fail. 290 InitializeAppForObserverTest( 291 REMOTE_SERVICE_DISABLED, 292 SYNC_STATUS_FAILED, 293 expected_states, 294 SYNC_STATUS_FAILED); 295 } 296 297 // Flaky. http://crbug.com/237710 298 TEST_F(SyncFileSystemServiceTest, DISABLED_SimpleLocalSyncFlow) { 299 InitializeApp(); 300 301 StrictMock<MockSyncStatusObserver> status_observer; 302 303 EnableSync(); 304 file_system_->backend()->sync_context()-> 305 set_mock_notify_changes_duration_in_sec(0); 306 file_system_->AddSyncStatusObserver(&status_observer); 307 308 // We'll test one local sync for this file. 309 const FileSystemURL kFile(file_system_->URL("foo")); 310 311 base::RunLoop run_loop; 312 313 // We should get called OnSyncEnabled and OnWriteEnabled on kFile. 314 // (We quit the run loop when OnWriteEnabled is called on kFile) 315 EXPECT_CALL(status_observer, OnSyncEnabled(kFile)) 316 .Times(AtLeast(1)); 317 EXPECT_CALL(status_observer, OnWriteEnabled(kFile)) 318 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 319 320 // We expect a set of method calls for starting a local sync. 321 EXPECT_CALL(*mock_remote_service(), GetCurrentState()) 322 .Times(AtLeast(2)) 323 .WillRepeatedly(Return(REMOTE_SERVICE_OK)); 324 325 // The local_change_processor's ApplyLocalChange should be called once 326 // with ADD_OR_UPDATE change for TYPE_FILE. 327 const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 328 SYNC_FILE_TYPE_FILE); 329 EXPECT_CALL(*mock_local_change_processor(), 330 ApplyLocalChange(change, _, _, kFile, _)) 331 .WillOnce(MockStatusCallback(SYNC_STATUS_OK)); 332 333 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 334 335 run_loop.Run(); 336 } 337 338 TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) { 339 InitializeApp(); 340 341 EnableSync(); 342 343 base::RunLoop run_loop; 344 345 // We expect a set of method calls for starting a remote sync. 346 EXPECT_CALL(*mock_remote_service(), GetCurrentState()) 347 .Times(AtLeast(1)) 348 .WillRepeatedly(Return(REMOTE_SERVICE_OK)); 349 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 350 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 351 352 // This should trigger a remote sync. 353 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1); 354 355 run_loop.Run(); 356 } 357 358 TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) { 359 InitializeApp(); 360 361 EnableSync(); 362 file_system_->backend()->sync_context()-> 363 set_mock_notify_changes_duration_in_sec(0); 364 365 const FileSystemURL kFile(file_system_->URL("foo")); 366 367 base::RunLoop run_loop; 368 369 // We expect a set of method calls for starting a remote sync. 370 EXPECT_CALL(*mock_remote_service(), GetCurrentState()) 371 .Times(AtLeast(3)) 372 .WillRepeatedly(Return(REMOTE_SERVICE_OK)); 373 374 { 375 InSequence sequence; 376 377 // Return with SYNC_STATUS_FILE_BUSY once. 378 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 379 .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY, 380 kFile)); 381 382 // ProcessRemoteChange should be called again when the becomes 383 // not busy. 384 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_)) 385 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); 386 } 387 388 // We might also see an activity for local sync as we're going to make 389 // a local write operation on kFile. 390 EXPECT_CALL(*mock_local_change_processor(), 391 ApplyLocalChange(_, _, _, kFile, _)) 392 .Times(AnyNumber()); 393 394 // This should trigger a remote sync. 395 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1); 396 397 // Start a local operation on the same file (to make it BUSY). 398 base::RunLoop verify_file_error_run_loop; 399 BrowserThread::PostTask( 400 BrowserThread::IO, 401 FROM_HERE, 402 base::Bind(&CannedSyncableFileSystem::DoCreateFile, 403 base::Unretained(file_system_.get()), 404 kFile, base::Bind(&VerifyFileError, 405 &verify_file_error_run_loop))); 406 407 run_loop.Run(); 408 409 mock_remote_service()->NotifyRemoteChangeQueueUpdated(0); 410 411 verify_file_error_run_loop.Run(); 412 } 413 414 TEST_F(SyncFileSystemServiceTest, GetFileSyncStatus) { 415 InitializeApp(); 416 417 const FileSystemURL kFile(file_system_->URL("foo")); 418 419 SyncStatusCode status; 420 SyncFileStatus sync_file_status; 421 422 // 1. The file is not in conflicting nor in pending change state. 423 { 424 base::RunLoop run_loop; 425 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 426 .WillOnce(Return(false)); 427 428 status = SYNC_STATUS_UNKNOWN; 429 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 430 sync_service_->GetFileSyncStatus( 431 kFile, 432 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 433 &run_loop, &status, &sync_file_status)); 434 run_loop.Run(); 435 436 EXPECT_EQ(SYNC_STATUS_OK, status); 437 EXPECT_EQ(SYNC_FILE_STATUS_SYNCED, sync_file_status); 438 } 439 440 // 2. Conflicting case. 441 { 442 base::RunLoop run_loop; 443 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 444 .WillOnce(Return(true)); 445 446 status = SYNC_STATUS_UNKNOWN; 447 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 448 sync_service_->GetFileSyncStatus( 449 kFile, 450 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 451 &run_loop, &status, &sync_file_status)); 452 run_loop.Run(); 453 454 EXPECT_EQ(SYNC_STATUS_OK, status); 455 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status); 456 } 457 458 // 3. The file has pending local changes. 459 { 460 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile)); 461 462 base::RunLoop run_loop; 463 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 464 .WillOnce(Return(false)); 465 466 status = SYNC_STATUS_UNKNOWN; 467 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 468 sync_service_->GetFileSyncStatus( 469 kFile, 470 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 471 &run_loop, &status, &sync_file_status)); 472 run_loop.Run(); 473 474 EXPECT_EQ(SYNC_STATUS_OK, status); 475 EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status); 476 } 477 478 // 4. The file has a conflict and pending local changes. In this case 479 // we return SYNC_FILE_STATUS_CONFLICTING. 480 { 481 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->TruncateFile(kFile, 1U)); 482 483 base::RunLoop run_loop; 484 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile)) 485 .WillOnce(Return(true)); 486 487 status = SYNC_STATUS_UNKNOWN; 488 sync_file_status = SYNC_FILE_STATUS_UNKNOWN; 489 sync_service_->GetFileSyncStatus( 490 kFile, 491 base::Bind(&AssignValueAndQuit<SyncFileStatus>, 492 &run_loop, &status, &sync_file_status)); 493 run_loop.Run(); 494 495 EXPECT_EQ(SYNC_STATUS_OK, status); 496 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status); 497 } 498 } 499 500 } // namespace sync_file_system 501