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