• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
TestChangeDelegate()34   TestChangeDelegate() {
35     ON_CALL(*this, OnChangesApplied(_, _, _, _))
36         .WillByDefault(
37             WithArgs<3>(Invoke(this,
38                                &TestChangeDelegate::VerifyDeletes)));
39   }
40 
add_expected_delete(int64 v)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:
VerifyDeletes(const ImmutableChangeRecordList & changes)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:
SetUp()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 
OnConfigDone(bool success)87   void OnConfigDone(bool success) {
88     EXPECT_TRUE(success);
89   }
90 
CreateEntry(UserShare * user_share,ModelType type,const std::string & client_tag)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 
InitManager(SyncManager * manager,ModelTypeSet types,TestChangeDelegate * delegate,StorageOption storage_option)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.
PrepopulateDb(ModelType type,const std::string & client_tag)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.
VerifyEntry(UserShare * user_share,ModelType type,const std::string & client_tag)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:
ConfigureSyncer()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 
HandleInit(bool success)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 
IsRollbackDoneAction(SyncProtocolError e)177 bool IsRollbackDoneAction(SyncProtocolError e) {
178   return e.action == syncer::ROLLBACK_DONE;
179 }
180 
TEST_F(SyncRollbackManagerTest,RollbackBasic)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 
TEST_F(SyncRollbackManagerTest,NoRollbackOfTypesNotBackedUp)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 
TEST_F(SyncRollbackManagerTest,BackupDbNotChangedOnAbort)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 
TEST_F(SyncRollbackManagerTest,OnInitializationFailure)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