Home | History | Annotate | Download | only in internal_api
      1 // Copyright 2014 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 "sync/internal_api/sync_rollback_manager.h"
      6 
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/run_loop.h"
      9 #include "sync/internal_api/public/read_node.h"
     10 #include "sync/internal_api/public/read_transaction.h"
     11 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
     12 #include "sync/internal_api/public/test/test_internal_components_factory.h"
     13 #include "sync/internal_api/public/write_node.h"
     14 #include "sync/internal_api/public/write_transaction.h"
     15 #include "sync/internal_api/sync_backup_manager.h"
     16 #include "sync/syncable/entry.h"
     17 #include "sync/test/engine/fake_model_worker.h"
     18 #include "sync/test/test_directory_backing_store.h"
     19 #include "testing/gmock/include/gmock/gmock.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 using ::testing::_;
     23 using ::testing::DoDefault;
     24 using ::testing::Invoke;
     25 using ::testing::Truly;
     26 using ::testing::WithArgs;
     27 
     28 namespace syncer {
     29 
     30 namespace {
     31 
     32 class TestChangeDelegate : public SyncManager::ChangeDelegate {
     33  public:
     34   TestChangeDelegate() {
     35     ON_CALL(*this, OnChangesApplied(_, _, _, _))
     36         .WillByDefault(
     37             WithArgs<3>(Invoke(this,
     38                                &TestChangeDelegate::VerifyDeletes)));
     39   }
     40 
     41   void add_expected_delete(int64 v) {
     42     expected_deletes_.insert(v);
     43   }
     44 
     45   MOCK_METHOD4(OnChangesApplied,
     46                void(ModelType model_type,
     47                     int64 model_version,
     48                     const BaseTransaction* trans,
     49                     const ImmutableChangeRecordList& changes));
     50   MOCK_METHOD1(OnChangesComplete, void(ModelType model_type));
     51 
     52  private:
     53   void VerifyDeletes(const ImmutableChangeRecordList& changes) {
     54     std::set<int64> deleted;
     55     for (size_t i = 0; i < changes.Get().size(); ++i) {
     56       const ChangeRecord& change = (changes.Get())[i];
     57       EXPECT_EQ(ChangeRecord::ACTION_DELETE, change.action);
     58       EXPECT_TRUE(deleted.find(change.id) == deleted.end());
     59       deleted.insert(change.id);
     60     }
     61     EXPECT_TRUE(expected_deletes_ == deleted);
     62   }
     63 
     64   std::set<int64> expected_deletes_;
     65 };
     66 
     67 class SyncRollbackManagerTest : public testing::Test,
     68                                 public SyncManager::Observer {
     69  protected:
     70   virtual void SetUp() OVERRIDE {
     71     CHECK(temp_dir_.CreateUniqueTempDir());
     72 
     73     worker_ = new FakeModelWorker(GROUP_UI);
     74   }
     75 
     76   MOCK_METHOD1(OnSyncCycleCompleted,
     77                void(const sessions::SyncSessionSnapshot&));
     78   MOCK_METHOD1(OnConnectionStatusChange, void(ConnectionStatus));
     79   MOCK_METHOD4(OnInitializationComplete,
     80                void(const WeakHandle<JsBackend>&,
     81                     const WeakHandle<DataTypeDebugInfoListener>&,
     82                     bool, ModelTypeSet));
     83   MOCK_METHOD1(OnActionableError, void(const SyncProtocolError&));
     84   MOCK_METHOD1(OnMigrationRequested, void(ModelTypeSet));;
     85   MOCK_METHOD1(OnProtocolEvent, void(const ProtocolEvent&));
     86 
     87   void OnConfigDone(bool success) {
     88     EXPECT_TRUE(success);
     89   }
     90 
     91   int64 CreateEntry(UserShare* user_share, ModelType type,
     92                     const std::string& client_tag) {
     93     WriteTransaction trans(FROM_HERE, user_share);
     94     ReadNode type_root(&trans);
     95     EXPECT_EQ(BaseNode::INIT_OK, type_root.InitTypeRoot(type));
     96 
     97     WriteNode node(&trans);
     98     EXPECT_EQ(WriteNode::INIT_SUCCESS,
     99               node.InitUniqueByCreation(type, type_root, client_tag));
    100     return node.GetEntry()->GetMetahandle();
    101   }
    102 
    103   void InitManager(SyncManager* manager, ModelTypeSet types,
    104                    TestChangeDelegate* delegate, StorageOption storage_option) {
    105     manager_ = manager;
    106     types_ = types;
    107 
    108     EXPECT_CALL(*this, OnInitializationComplete(_, _, _, _))
    109         .WillOnce(WithArgs<2>(Invoke(this,
    110                                      &SyncRollbackManagerTest::HandleInit)));
    111 
    112     manager->AddObserver(this);
    113     TestInternalComponentsFactory factory(InternalComponentsFactory::Switches(),
    114                                           storage_option);
    115 
    116     base::RunLoop run_loop;
    117     manager->Init(temp_dir_.path(),
    118                   MakeWeakHandle(base::WeakPtr<JsEventHandler>()),
    119                   "", 0, true, scoped_ptr<HttpPostProviderFactory>().Pass(),
    120                   std::vector<scoped_refptr<ModelSafeWorker> >(1,
    121                                                                worker_.get()),
    122                   NULL, delegate, SyncCredentials(), "", "", "", &factory,
    123                   NULL, scoped_ptr<UnrecoverableErrorHandler>().Pass(),
    124                   NULL, NULL);
    125     loop_.PostTask(FROM_HERE, run_loop.QuitClosure());
    126     run_loop.Run();
    127   }
    128 
    129   // Create and persist an entry by unique tag in DB.
    130   void PrepopulateDb(ModelType type, const std::string& client_tag) {
    131     SyncBackupManager backup_manager;
    132     TestChangeDelegate delegate;
    133     InitManager(&backup_manager, ModelTypeSet(type), &delegate,
    134                 STORAGE_ON_DISK);
    135     CreateEntry(backup_manager.GetUserShare(), type, client_tag);
    136     backup_manager.ShutdownOnSyncThread();
    137   }
    138 
    139   // Verify entry with |client_tag| exists in sync directory.
    140   bool VerifyEntry(UserShare* user_share, ModelType type,
    141                    const std::string& client_tag) {
    142     ReadTransaction trans(FROM_HERE, user_share);
    143     ReadNode node(&trans);
    144     return BaseNode::INIT_OK == node.InitByClientTagLookup(type, client_tag);
    145   }
    146 
    147  private:
    148   void ConfigureSyncer() {
    149     manager_->ConfigureSyncer(
    150           CONFIGURE_REASON_NEW_CLIENT,
    151           types_,
    152           ModelTypeSet(), ModelTypeSet(), ModelTypeSet(),
    153           ModelSafeRoutingInfo(),
    154           base::Bind(&SyncRollbackManagerTest::OnConfigDone,
    155                      base::Unretained(this), true),
    156           base::Bind(&SyncRollbackManagerTest::OnConfigDone,
    157                      base::Unretained(this), false));
    158   }
    159 
    160   void HandleInit(bool success) {
    161     if (success) {
    162       loop_.PostTask(FROM_HERE,
    163                      base::Bind(&SyncRollbackManagerTest::ConfigureSyncer,
    164                                 base::Unretained(this)));
    165     } else {
    166       manager_->ShutdownOnSyncThread();
    167     }
    168   }
    169 
    170   base::ScopedTempDir temp_dir_;
    171   scoped_refptr<ModelSafeWorker> worker_;
    172   base::MessageLoop loop_;    // Needed for WeakHandle
    173   SyncManager* manager_;
    174   ModelTypeSet types_;
    175 };
    176 
    177 bool IsRollbackDoneAction(SyncProtocolError e) {
    178   return e.action == syncer::ROLLBACK_DONE;
    179 }
    180 
    181 TEST_F(SyncRollbackManagerTest, RollbackBasic) {
    182   PrepopulateDb(PREFERENCES, "pref1");
    183 
    184   TestChangeDelegate delegate;
    185   SyncRollbackManager rollback_manager;
    186   InitManager(&rollback_manager, ModelTypeSet(PREFERENCES), &delegate,
    187               STORAGE_ON_DISK);
    188 
    189   // Simulate a new entry added during type initialization.
    190   int64 new_pref_id =
    191       CreateEntry(rollback_manager.GetUserShare(), PREFERENCES, "pref2");
    192 
    193   delegate.add_expected_delete(new_pref_id);
    194   EXPECT_CALL(delegate, OnChangesApplied(_, _, _, _))
    195       .Times(1)
    196       .WillOnce(DoDefault());
    197   EXPECT_CALL(delegate, OnChangesComplete(_)).Times(1);
    198   EXPECT_CALL(*this, OnActionableError(Truly(IsRollbackDoneAction))).Times(1);
    199 
    200   ModelSafeRoutingInfo routing_info;
    201   routing_info[PREFERENCES] = GROUP_UI;
    202   rollback_manager.StartSyncingNormally(routing_info);
    203 }
    204 
    205 TEST_F(SyncRollbackManagerTest, NoRollbackOfTypesNotBackedUp) {
    206   PrepopulateDb(PREFERENCES, "pref1");
    207 
    208   TestChangeDelegate delegate;
    209   SyncRollbackManager rollback_manager;
    210   InitManager(&rollback_manager, ModelTypeSet(PREFERENCES, APPS), &delegate,
    211               STORAGE_ON_DISK);
    212 
    213   // Simulate new entry added during type initialization.
    214   int64 new_pref_id =
    215       CreateEntry(rollback_manager.GetUserShare(), PREFERENCES, "pref2");
    216   CreateEntry(rollback_manager.GetUserShare(), APPS, "app1");
    217 
    218   delegate.add_expected_delete(new_pref_id);
    219   EXPECT_CALL(delegate, OnChangesApplied(_, _, _, _))
    220       .Times(1)
    221       .WillOnce(DoDefault());
    222   EXPECT_CALL(delegate, OnChangesComplete(_)).Times(1);
    223 
    224   ModelSafeRoutingInfo routing_info;
    225   routing_info[PREFERENCES] = GROUP_UI;
    226   rollback_manager.StartSyncingNormally(routing_info);
    227 
    228   // APP entry is still valid.
    229   EXPECT_TRUE(VerifyEntry(rollback_manager.GetUserShare(), APPS, "app1"));
    230 }
    231 
    232 TEST_F(SyncRollbackManagerTest, BackupDbNotChangedOnAbort) {
    233   PrepopulateDb(PREFERENCES, "pref1");
    234 
    235   TestChangeDelegate delegate;
    236   scoped_ptr<SyncRollbackManager> rollback_manager(
    237       new SyncRollbackManager);
    238   InitManager(rollback_manager.get(), ModelTypeSet(PREFERENCES), &delegate,
    239               STORAGE_ON_DISK);
    240 
    241   // Simulate a new entry added during type initialization.
    242   CreateEntry(rollback_manager->GetUserShare(), PREFERENCES, "pref2");
    243 
    244   // Manager was shut down before sync starts.
    245   rollback_manager->ShutdownOnSyncThread();
    246 
    247   // Verify new entry was not persisted.
    248   rollback_manager.reset(new SyncRollbackManager);
    249   InitManager(rollback_manager.get(), ModelTypeSet(PREFERENCES), &delegate,
    250               STORAGE_ON_DISK);
    251   EXPECT_FALSE(VerifyEntry(rollback_manager->GetUserShare(), PREFERENCES,
    252                            "pref2"));
    253 }
    254 
    255 TEST_F(SyncRollbackManagerTest, OnInitializationFailure) {
    256   // Test graceful shutdown on initialization failure.
    257   scoped_ptr<SyncRollbackManager> rollback_manager(
    258       new SyncRollbackManager);
    259   InitManager(rollback_manager.get(), ModelTypeSet(PREFERENCES), NULL,
    260               STORAGE_ON_DISK);
    261 }
    262 
    263 }  // anonymous namespace
    264 
    265 }  // namespace syncer
    266