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