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