Home | History | Annotate | Download | only in sync
      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 
     31 using base::JSONReader;
     32 using browser_sync::PreferenceChangeProcessor;
     33 using browser_sync::PreferenceDataTypeController;
     34 using browser_sync::PreferenceModelAssociator;
     35 using browser_sync::SyncBackendHost;
     36 using sync_api::SyncManager;
     37 using testing::_;
     38 using testing::Return;
     39 
     40 typedef std::map<const std::string, const Value*> PreferenceValues;
     41 
     42 class 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 
    191 class 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 
    217 TEST_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 
    241 TEST_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 
    254 TEST_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 
    275 TEST_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 
    325 TEST_F(ProfileSyncServicePreferenceTest, FailModelAssociation) {
    326   ASSERT_TRUE(StartSyncService(NULL, true));
    327   EXPECT_TRUE(service_->unrecoverable_error_detected());
    328 }
    329 
    330 TEST_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 
    347 TEST_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 
    361 TEST_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 
    382 TEST_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 
    404 TEST_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 
    424 TEST_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 
    458 TEST_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