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