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