profile_sync_service_preference_unittest.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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 <map> 6#include <string> 7 8#include "base/json/json_reader.h" 9#include "base/stl_util-inl.h" 10#include "base/task.h" 11#include "chrome/browser/sync/abstract_profile_sync_service_test.h" 12#include "chrome/browser/sync/engine/syncapi.h" 13#include "chrome/browser/sync/glue/preference_change_processor.h" 14#include "chrome/browser/sync/glue/preference_data_type_controller.h" 15#include "chrome/browser/sync/glue/preference_model_associator.h" 16#include "chrome/browser/sync/glue/sync_backend_host.h" 17#include "chrome/browser/sync/profile_sync_test_util.h" 18#include "chrome/browser/sync/protocol/preference_specifics.pb.h" 19#include "chrome/browser/sync/syncable/model_type.h" 20#include "chrome/browser/sync/test_profile_sync_service.h" 21#include "chrome/common/json_value_serializer.h" 22#include "chrome/common/pref_names.h" 23#include "chrome/test/testing_profile.h" 24#include "testing/gmock/include/gmock/gmock.h" 25#include "testing/gtest/include/gtest/gtest.h" 26 27using base::JSONReader; 28using browser_sync::PreferenceChangeProcessor; 29using browser_sync::PreferenceDataTypeController; 30using browser_sync::PreferenceModelAssociator; 31using browser_sync::SyncBackendHost; 32using sync_api::SyncManager; 33using testing::_; 34using testing::Return; 35 36typedef std::map<const std::wstring, const Value*> PreferenceValues; 37 38class ProfileSyncServicePreferenceTest 39 : public AbstractProfileSyncServiceTest { 40 protected: 41 ProfileSyncServicePreferenceTest() 42 : example_url0_("http://example.com/0"), 43 example_url1_("http://example.com/1"), 44 example_url2_("http://example.com/2"), 45 not_synced_preference_name_(L"nonsense_pref_name"), 46 not_synced_preference_default_value_("default"), 47 non_default_charset_value_("foo") {} 48 49 virtual void SetUp() { 50 profile_.reset(new TestingProfile()); 51 profile_->set_has_history_service(true); 52 prefs_ = profile_->GetPrefs(); 53 54 prefs_->RegisterStringPref(not_synced_preference_name_.c_str(), 55 not_synced_preference_default_value_); 56 } 57 58 virtual void TearDown() { 59 service_.reset(); 60 profile_.reset(); 61 MessageLoop::current()->RunAllPending(); 62 } 63 64 bool StartSyncService(Task* task, bool will_fail_association) { 65 if (service_.get()) 66 return false; 67 68 service_.reset(new TestProfileSyncService( 69 &factory_, profile_.get(), false, false, task)); 70 71 // Register the preference data type. 72 model_associator_ = 73 new PreferenceModelAssociator(service_.get()); 74 change_processor_ = new PreferenceChangeProcessor(model_associator_, 75 service_.get()); 76 EXPECT_CALL(factory_, CreatePreferenceSyncComponents(_, _)). 77 WillOnce(Return(ProfileSyncFactory::SyncComponents( 78 model_associator_, change_processor_))); 79 80 EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). 81 WillOnce(ReturnNewDataTypeManager()); 82 83 service_->set_num_expected_resumes(will_fail_association ? 0 : 1); 84 85 service_->RegisterDataTypeController( 86 new PreferenceDataTypeController(&factory_, 87 service_.get())); 88 service_->Initialize(); 89 MessageLoop::current()->Run(); 90 return true; 91 } 92 93 SyncBackendHost* backend() { return service_->backend_.get(); } 94 95 const Value& GetPreferenceValue(const std::wstring& name) { 96 const PrefService::Preference* preference = 97 prefs_->FindPreference(name.c_str()); 98 return *preference->GetValue(); 99 } 100 101 // Caller gets ownership of the returned value. 102 const Value* GetSyncedValue(const std::wstring& name) { 103 sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle()); 104 sync_api::ReadNode node(&trans); 105 106 int64 node_id = model_associator_->GetSyncIdFromChromeId(name); 107 if (node_id == sync_api::kInvalidId) 108 return NULL; 109 if (!node.InitByIdLookup(node_id)) 110 return NULL; 111 112 const sync_pb::PreferenceSpecifics& specifics( 113 node.GetPreferenceSpecifics()); 114 115 JSONReader reader; 116 return reader.JsonToValue(specifics.value(), false, false); 117 } 118 119 int64 SetSyncedValue(const std::wstring& name, const Value& value) { 120 sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); 121 sync_api::ReadNode root(&trans); 122 if (!root.InitByTagLookup(browser_sync::kPreferencesTag)) 123 return sync_api::kInvalidId; 124 125 sync_api::WriteNode node(&trans); 126 127 int64 node_id = model_associator_->GetSyncIdFromChromeId(name); 128 if (node_id == sync_api::kInvalidId) { 129 if (!node.InitUniqueByCreation(syncable::PREFERENCES, 130 root, 131 WideToUTF8(name))) { 132 return sync_api::kInvalidId; 133 } 134 } else { 135 if (!node.InitByIdLookup(node_id)) { 136 return sync_api::kInvalidId; 137 } 138 } 139 140 std::string serialized; 141 JSONStringValueSerializer json(&serialized); 142 EXPECT_TRUE(json.Serialize(value)); 143 144 sync_pb::PreferenceSpecifics preference; 145 preference.set_name(WideToUTF8(name)); 146 preference.set_value(serialized); 147 node.SetPreferenceSpecifics(preference); 148 node.SetTitle(name); 149 150 return node.GetId(); 151 } 152 153 SyncManager::ChangeRecord* MakeChangeRecord(const std::wstring& name, 154 SyncManager::ChangeRecord) { 155 int64 node_id = model_associator_->GetSyncIdFromChromeId(name); 156 SyncManager::ChangeRecord* record = new SyncManager::ChangeRecord(); 157 record->action = SyncManager::ChangeRecord::ACTION_UPDATE; 158 record->id = node_id; 159 return record; 160 } 161 162 bool IsSynced(const std::wstring& pref_name) { 163 return model_associator_->synced_preferences().count(pref_name) > 0; 164 } 165 166 std::string ValueString(const Value& value) { 167 std::string serialized; 168 JSONStringValueSerializer json(&serialized); 169 json.Serialize(value); 170 return serialized; 171 } 172 173 friend class AddPreferenceEntriesTask; 174 175 scoped_ptr<TestingProfile> profile_; 176 TestingPrefService* prefs_; 177 178 PreferenceModelAssociator* model_associator_; 179 PreferenceChangeProcessor* change_processor_; 180 std::string example_url0_; 181 std::string example_url1_; 182 std::string example_url2_; 183 std::wstring not_synced_preference_name_; 184 std::string not_synced_preference_default_value_; 185 std::string non_default_charset_value_; 186}; 187 188class AddPreferenceEntriesTask : public Task { 189 public: 190 AddPreferenceEntriesTask(ProfileSyncServicePreferenceTest* test, 191 const PreferenceValues& entries) 192 : test_(test), entries_(entries), success_(false) { 193 } 194 195 virtual void Run() { 196 if (!test_->CreateRoot(syncable::PREFERENCES)) 197 return; 198 for (PreferenceValues::const_iterator i = entries_.begin(); 199 i != entries_.end(); ++i) { 200 if (test_->SetSyncedValue(i->first, *i->second) == sync_api::kInvalidId) 201 return; 202 } 203 success_ = true; 204 } 205 206 bool success() { return success_; } 207 208 private: 209 ProfileSyncServicePreferenceTest* test_; 210 const PreferenceValues& entries_; 211 bool success_; 212}; 213 214TEST_F(ProfileSyncServicePreferenceTest, WritePreferenceToNode) { 215 prefs_->SetString(prefs::kHomePage, example_url0_); 216 CreateRootTask task(this, syncable::PREFERENCES); 217 ASSERT_TRUE(StartSyncService(&task, false)); 218 ASSERT_TRUE(task.success()); 219 220 const PrefService::Preference* pref = 221 prefs_->FindPreference(prefs::kHomePage); 222 sync_api::WriteTransaction trans(service_->backend()->GetUserShareHandle()); 223 sync_api::WriteNode node(&trans); 224 EXPECT_TRUE(node.InitByClientTagLookup(syncable::PREFERENCES, 225 WideToUTF8(prefs::kHomePage))); 226 227 EXPECT_TRUE(PreferenceModelAssociator::WritePreferenceToNode( 228 pref->name(), *pref->GetValue(), &node)); 229 EXPECT_EQ(std::wstring(prefs::kHomePage), node.GetTitle()); 230 const sync_pb::PreferenceSpecifics& specifics(node.GetPreferenceSpecifics()); 231 EXPECT_EQ(WideToUTF8(prefs::kHomePage), specifics.name()); 232 233 base::JSONReader reader; 234 scoped_ptr<Value> value(reader.JsonToValue(specifics.value(), false, false)); 235 EXPECT_TRUE(pref->GetValue()->Equals(value.get())); 236} 237 238TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationDoNotSyncDefaults) { 239 const PrefService::Preference* pref = 240 prefs_->FindPreference(prefs::kHomePage); 241 EXPECT_TRUE(pref->IsDefaultValue()); 242 CreateRootTask task(this, syncable::PREFERENCES); 243 ASSERT_TRUE(StartSyncService(&task, false)); 244 ASSERT_TRUE(task.success()); 245 EXPECT_TRUE(IsSynced(prefs::kHomePage)); 246 EXPECT_TRUE(pref->IsDefaultValue()); 247 EXPECT_TRUE(GetSyncedValue(prefs::kHomePage) == NULL); 248 EXPECT_TRUE(GetSyncedValue(not_synced_preference_name_) == NULL); 249} 250 251TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationEmptyCloud) { 252 prefs_->SetString(prefs::kHomePage, example_url0_); 253 ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup); 254 url_list->Append(Value::CreateStringValue(example_url0_)); 255 url_list->Append(Value::CreateStringValue(example_url1_)); 256 CreateRootTask task(this, syncable::PREFERENCES); 257 ASSERT_TRUE(StartSyncService(&task, false)); 258 ASSERT_TRUE(task.success()); 259 260 scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); 261 ASSERT_TRUE(value.get()); 262 EXPECT_TRUE(GetPreferenceValue(prefs::kHomePage).Equals(value.get())); 263 value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); 264 ASSERT_TRUE(value.get()); 265 EXPECT_TRUE( 266 GetPreferenceValue(prefs::kURLsToRestoreOnStartup).Equals(value.get())); 267} 268 269TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationCloudHasData) { 270 prefs_->SetString(prefs::kHomePage, example_url0_); 271 ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup); 272 url_list->Append(Value::CreateStringValue(example_url0_)); 273 url_list->Append(Value::CreateStringValue(example_url1_)); 274 275 PreferenceValues cloud_data; 276 cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); 277 ListValue* urls_to_restore = new ListValue; 278 urls_to_restore->Append(Value::CreateStringValue(example_url1_)); 279 urls_to_restore->Append(Value::CreateStringValue(example_url2_)); 280 cloud_data[prefs::kURLsToRestoreOnStartup] = urls_to_restore; 281 cloud_data[prefs::kDefaultCharset] = 282 Value::CreateStringValue(non_default_charset_value_); 283 284 AddPreferenceEntriesTask task(this, cloud_data); 285 ASSERT_TRUE(StartSyncService(&task, false)); 286 ASSERT_TRUE(task.success()); 287 288 scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); 289 ASSERT_TRUE(value.get()); 290 std::string string_value; 291 EXPECT_TRUE(static_cast<const StringValue*>(value.get())-> 292 GetAsString(&string_value)); 293 EXPECT_EQ(example_url1_, string_value); 294 EXPECT_EQ(example_url1_, prefs_->GetString(prefs::kHomePage)); 295 296 scoped_ptr<ListValue> expected_urls(new ListValue); 297 expected_urls->Append(Value::CreateStringValue(example_url1_)); 298 expected_urls->Append(Value::CreateStringValue(example_url2_)); 299 expected_urls->Append(Value::CreateStringValue(example_url0_)); 300 value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); 301 ASSERT_TRUE(value.get()); 302 EXPECT_TRUE(value->Equals(expected_urls.get())); 303 EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartup). 304 Equals(expected_urls.get())); 305 306 value.reset(GetSyncedValue(prefs::kDefaultCharset)); 307 ASSERT_TRUE(value.get()); 308 EXPECT_TRUE(static_cast<const StringValue*>(value.get())-> 309 GetAsString(&string_value)); 310 EXPECT_EQ(non_default_charset_value_, string_value); 311 EXPECT_EQ(non_default_charset_value_, 312 prefs_->GetString(prefs::kDefaultCharset)); 313 STLDeleteValues(&cloud_data); 314} 315 316TEST_F(ProfileSyncServicePreferenceTest, FailModelAssociation) { 317 ASSERT_TRUE(StartSyncService(NULL, true)); 318 EXPECT_TRUE(service_->unrecoverable_error_detected()); 319} 320 321TEST_F(ProfileSyncServicePreferenceTest, UpdatedPreferenceWithDefaultValue) { 322 const PrefService::Preference* pref = 323 prefs_->FindPreference(prefs::kHomePage); 324 EXPECT_TRUE(pref->IsDefaultValue()); 325 326 CreateRootTask task(this, syncable::PREFERENCES); 327 ASSERT_TRUE(StartSyncService(&task, false)); 328 ASSERT_TRUE(task.success()); 329 330 scoped_ptr<Value> expected(Value::CreateStringValue(example_url0_)); 331 profile_->GetPrefs()->Set(prefs::kHomePage, *expected); 332 333 scoped_ptr<const Value> actual(GetSyncedValue(prefs::kHomePage)); 334 ASSERT_TRUE(actual.get()); 335 EXPECT_TRUE(expected->Equals(actual.get())); 336} 337 338TEST_F(ProfileSyncServicePreferenceTest, UpdatedPreferenceWithValue) { 339 profile_->GetPrefs()->SetString(prefs::kHomePage, example_url0_); 340 CreateRootTask task(this, syncable::PREFERENCES); 341 ASSERT_TRUE(StartSyncService(&task, false)); 342 ASSERT_TRUE(task.success()); 343 344 scoped_ptr<Value> expected(Value::CreateStringValue(example_url1_)); 345 profile_->GetPrefs()->Set(prefs::kHomePage, *expected); 346 347 scoped_ptr<const Value> actual(GetSyncedValue(prefs::kHomePage)); 348 ASSERT_TRUE(actual.get()); 349 EXPECT_TRUE(expected->Equals(actual.get())); 350} 351 352TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNodeActionUpdate) { 353 profile_->GetPrefs()->SetString(prefs::kHomePage, example_url0_); 354 CreateRootTask task(this, syncable::PREFERENCES); 355 ASSERT_TRUE(StartSyncService(&task, false)); 356 ASSERT_TRUE(task.success()); 357 358 scoped_ptr<Value> expected(Value::CreateStringValue(example_url1_)); 359 ASSERT_NE(SetSyncedValue(prefs::kHomePage, *expected), sync_api::kInvalidId); 360 int64 node_id = model_associator_->GetSyncIdFromChromeId(prefs::kHomePage); 361 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 362 record->action = SyncManager::ChangeRecord::ACTION_UPDATE; 363 record->id = node_id; 364 { 365 sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); 366 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 367 } 368 369 const Value& actual = GetPreferenceValue(prefs::kHomePage); 370 EXPECT_TRUE(expected->Equals(&actual)); 371} 372 373TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNodeActionAdd) { 374 CreateRootTask task(this, syncable::PREFERENCES); 375 ASSERT_TRUE(StartSyncService(&task, false)); 376 ASSERT_TRUE(task.success()); 377 378 scoped_ptr<Value> expected(Value::CreateStringValue(example_url0_)); 379 int64 node_id = SetSyncedValue(prefs::kHomePage, *expected); 380 ASSERT_NE(node_id, sync_api::kInvalidId); 381 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 382 record->action = SyncManager::ChangeRecord::ACTION_ADD; 383 record->id = node_id; 384 { 385 sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); 386 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 387 } 388 389 const Value& actual = GetPreferenceValue(prefs::kHomePage); 390 EXPECT_TRUE(expected->Equals(&actual)); 391 EXPECT_EQ(node_id, 392 model_associator_->GetSyncIdFromChromeId(prefs::kHomePage)); 393} 394 395TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNodeUnknownPreference) { 396 CreateRootTask task(this, syncable::PREFERENCES); 397 ASSERT_TRUE(StartSyncService(&task, false)); 398 ASSERT_TRUE(task.success()); 399 400 scoped_ptr<Value> expected(Value::CreateStringValue(example_url0_)); 401 int64 node_id = SetSyncedValue(L"unknown preference", *expected); 402 ASSERT_NE(node_id, sync_api::kInvalidId); 403 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 404 record->action = SyncManager::ChangeRecord::ACTION_ADD; 405 record->id = node_id; 406 { 407 sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); 408 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 409 } 410 411 // Nothing interesting happens on the client when it gets an update 412 // of an unknown preference. We just should not crash. 413} 414 415TEST_F(ProfileSyncServicePreferenceTest, ManagedPreferences) { 416 // Make the homepage preference managed. 417 scoped_ptr<Value> managed_value( 418 Value::CreateStringValue(L"http://example.com")); 419 prefs_->SetManagedPref(prefs::kHomePage, managed_value->DeepCopy()); 420 421 CreateRootTask task(this, syncable::PREFERENCES); 422 ASSERT_TRUE(StartSyncService(&task, false)); 423 ASSERT_TRUE(task.success()); 424 425 // Changing the homepage preference should not sync anything. 426 scoped_ptr<Value> user_value( 427 Value::CreateStringValue(L"http://chromium..com")); 428 prefs_->SetUserPref(prefs::kHomePage, user_value->DeepCopy()); 429 EXPECT_EQ(NULL, GetSyncedValue(prefs::kHomePage)); 430 431 // An incoming sync transaction shouldn't change the user value. 432 scoped_ptr<Value> sync_value( 433 Value::CreateStringValue(L"http://crbug.com")); 434 int64 node_id = SetSyncedValue(prefs::kHomePage, *sync_value); 435 ASSERT_NE(node_id, sync_api::kInvalidId); 436 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 437 record->action = SyncManager::ChangeRecord::ACTION_UPDATE; 438 record->id = node_id; 439 { 440 sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); 441 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 442 } 443 EXPECT_TRUE(managed_value->Equals(prefs_->GetManagedPref(prefs::kHomePage))); 444 EXPECT_TRUE(user_value->Equals(prefs_->GetUserPref(prefs::kHomePage))); 445} 446