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