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