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