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