Home | History | Annotate | Download | only in sync_file_system
      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