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