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 "base/bind.h"
6#include "base/callback.h"
7#include "base/json/json_reader.h"
8#include "base/prefs/testing_pref_store.h"
9#include "base/strings/string_util.h"
10#include "chrome/browser/supervised_user/supervised_user_settings_service.h"
11#include "sync/api/fake_sync_change_processor.h"
12#include "sync/api/sync_change.h"
13#include "sync/api/sync_change_processor_wrapper_for_test.h"
14#include "sync/api/sync_error_factory_mock.h"
15#include "sync/protocol/sync.pb.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace {
19
20class MockSyncErrorFactory : public syncer::SyncErrorFactory {
21 public:
22  explicit MockSyncErrorFactory(syncer::ModelType type);
23  virtual ~MockSyncErrorFactory();
24
25  // SyncErrorFactory implementation:
26  virtual syncer::SyncError CreateAndUploadError(
27      const tracked_objects::Location& location,
28      const std::string& message) OVERRIDE;
29
30 private:
31  syncer::ModelType type_;
32
33  DISALLOW_COPY_AND_ASSIGN(MockSyncErrorFactory);
34};
35
36MockSyncErrorFactory::MockSyncErrorFactory(syncer::ModelType type)
37    : type_(type) {}
38
39MockSyncErrorFactory::~MockSyncErrorFactory() {}
40
41syncer::SyncError MockSyncErrorFactory::CreateAndUploadError(
42    const tracked_objects::Location& location,
43    const std::string& message) {
44  return syncer::SyncError(location,
45                           syncer::SyncError::DATATYPE_ERROR,
46                           message,
47                           type_);
48}
49
50}  // namespace
51
52const char kAtomicItemName[] = "X-Wombat";
53const char kSettingsName[] = "TestingSetting";
54const char kSettingsValue[] = "SettingsValue";
55const char kSplitItemName[] = "X-SuperMoosePowers";
56
57class SupervisedUserSettingsServiceTest : public ::testing::Test {
58 protected:
59  SupervisedUserSettingsServiceTest() {}
60  virtual ~SupervisedUserSettingsServiceTest() {}
61
62  scoped_ptr<syncer::SyncChangeProcessor> CreateSyncProcessor() {
63    sync_processor_.reset(new syncer::FakeSyncChangeProcessor);
64    return scoped_ptr<syncer::SyncChangeProcessor>(
65        new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get()));
66  }
67
68  void StartSyncing(const syncer::SyncDataList& initial_sync_data) {
69    scoped_ptr<syncer::SyncErrorFactory> error_handler(
70        new MockSyncErrorFactory(syncer::SUPERVISED_USER_SETTINGS));
71    syncer::SyncMergeResult result = settings_service_.MergeDataAndStartSyncing(
72        syncer::SUPERVISED_USER_SETTINGS,
73        initial_sync_data,
74        CreateSyncProcessor(),
75        error_handler.Pass());
76    EXPECT_FALSE(result.error().IsSet());
77  }
78
79  void UploadSplitItem(const std::string& key, const std::string& value) {
80    split_items_.SetStringWithoutPathExpansion(key, value);
81    settings_service_.UploadItem(
82        SupervisedUserSettingsService::MakeSplitSettingKey(kSplitItemName,
83                                                           key),
84        scoped_ptr<base::Value>(new base::StringValue(value)));
85  }
86
87  void UploadAtomicItem(const std::string& value) {
88    atomic_setting_value_.reset(new base::StringValue(value));
89    settings_service_.UploadItem(
90        kAtomicItemName,
91        scoped_ptr<base::Value>(new base::StringValue(value)));
92  }
93
94  void VerifySyncDataItem(syncer::SyncData sync_data) {
95    const sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
96        sync_data.GetSpecifics().managed_user_setting();
97    base::Value* expected_value = NULL;
98    if (supervised_user_setting.name() == kAtomicItemName) {
99      expected_value = atomic_setting_value_.get();
100    } else {
101      EXPECT_TRUE(StartsWithASCII(supervised_user_setting.name(),
102                                  std::string(kSplitItemName) + ':',
103                                  true));
104      std::string key =
105          supervised_user_setting.name().substr(strlen(kSplitItemName) + 1);
106      EXPECT_TRUE(split_items_.GetWithoutPathExpansion(key, &expected_value));
107    }
108
109    scoped_ptr<base::Value> value(
110        base::JSONReader::Read(supervised_user_setting.value()));
111    EXPECT_TRUE(expected_value->Equals(value.get()));
112  }
113
114  void OnNewSettingsAvailable(const base::DictionaryValue* settings) {
115    if (!settings)
116      settings_.reset();
117    else
118      settings_.reset(settings->DeepCopy());
119  }
120
121  // testing::Test overrides:
122  virtual void SetUp() OVERRIDE {
123    TestingPrefStore* pref_store = new TestingPrefStore;
124    settings_service_.Init(pref_store);
125    settings_service_.Subscribe(
126        base::Bind(&SupervisedUserSettingsServiceTest::OnNewSettingsAvailable,
127                   base::Unretained(this)));
128    pref_store->SetInitializationCompleted();
129    ASSERT_FALSE(settings_);
130    settings_service_.SetActive(true);
131    ASSERT_TRUE(settings_);
132  }
133
134  virtual void TearDown() OVERRIDE {
135    settings_service_.Shutdown();
136  }
137
138  base::DictionaryValue split_items_;
139  scoped_ptr<base::Value> atomic_setting_value_;
140  SupervisedUserSettingsService settings_service_;
141  scoped_ptr<base::DictionaryValue> settings_;
142
143  scoped_ptr<syncer::FakeSyncChangeProcessor> sync_processor_;
144};
145
146TEST_F(SupervisedUserSettingsServiceTest, ProcessAtomicSetting) {
147  StartSyncing(syncer::SyncDataList());
148  ASSERT_TRUE(settings_);
149  const base::Value* value = NULL;
150  EXPECT_FALSE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
151
152  settings_.reset();
153  syncer::SyncData data =
154      SupervisedUserSettingsService::CreateSyncDataForSetting(
155          kSettingsName, base::StringValue(kSettingsValue));
156  syncer::SyncChangeList change_list;
157  change_list.push_back(
158      syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, data));
159  syncer::SyncError error =
160      settings_service_.ProcessSyncChanges(FROM_HERE, change_list);
161  EXPECT_FALSE(error.IsSet()) << error.ToString();
162  ASSERT_TRUE(settings_);
163  ASSERT_TRUE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
164  std::string string_value;
165  EXPECT_TRUE(value->GetAsString(&string_value));
166  EXPECT_EQ(kSettingsValue, string_value);
167}
168
169TEST_F(SupervisedUserSettingsServiceTest, ProcessSplitSetting) {
170  StartSyncing(syncer::SyncDataList());
171  ASSERT_TRUE(settings_);
172  const base::Value* value = NULL;
173  EXPECT_FALSE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
174
175  base::DictionaryValue dict;
176  dict.SetString("foo", "bar");
177  dict.SetBoolean("awesomesauce", true);
178  dict.SetInteger("eaudecologne", 4711);
179
180  settings_.reset();
181  syncer::SyncChangeList change_list;
182  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
183    syncer::SyncData data =
184        SupervisedUserSettingsService::CreateSyncDataForSetting(
185            SupervisedUserSettingsService::MakeSplitSettingKey(kSettingsName,
186                                                               it.key()),
187            it.value());
188    change_list.push_back(
189        syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, data));
190  }
191  syncer::SyncError error =
192      settings_service_.ProcessSyncChanges(FROM_HERE, change_list);
193  EXPECT_FALSE(error.IsSet()) << error.ToString();
194  ASSERT_TRUE(settings_);
195  ASSERT_TRUE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
196  const base::DictionaryValue* dict_value = NULL;
197  ASSERT_TRUE(value->GetAsDictionary(&dict_value));
198  EXPECT_TRUE(dict_value->Equals(&dict));
199}
200
201TEST_F(SupervisedUserSettingsServiceTest, SetLocalSetting) {
202  const base::Value* value = NULL;
203  EXPECT_FALSE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
204
205  settings_.reset();
206  settings_service_.SetLocalSettingForTesting(
207      kSettingsName,
208      scoped_ptr<base::Value>(new base::StringValue(kSettingsValue)));
209  ASSERT_TRUE(settings_);
210  ASSERT_TRUE(settings_->GetWithoutPathExpansion(kSettingsName, &value));
211  std::string string_value;
212  EXPECT_TRUE(value->GetAsString(&string_value));
213  EXPECT_EQ(kSettingsValue, string_value);
214}
215
216TEST_F(SupervisedUserSettingsServiceTest, UploadItem) {
217  UploadSplitItem("foo", "bar");
218  UploadSplitItem("blurp", "baz");
219  UploadAtomicItem("hurdle");
220
221  // Uploading should produce changes when we start syncing.
222  StartSyncing(syncer::SyncDataList());
223  ASSERT_EQ(3u, sync_processor_->changes().size());
224  for (syncer::SyncChangeList::const_iterator it =
225           sync_processor_->changes().begin();
226       it != sync_processor_->changes().end();
227       ++it) {
228    ASSERT_TRUE(it->IsValid());
229    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, it->change_type());
230    VerifySyncDataItem(it->sync_data());
231  }
232
233  // It should also show up in local Sync data.
234  syncer::SyncDataList sync_data =
235      settings_service_.GetAllSyncData(syncer::SUPERVISED_USER_SETTINGS);
236  EXPECT_EQ(3u, sync_data.size());
237  for (syncer::SyncDataList::const_iterator it = sync_data.begin();
238       it != sync_data.end(); ++it) {
239    VerifySyncDataItem(*it);
240  }
241
242  // Uploading after we have started syncing should work too.
243  sync_processor_->changes().clear();
244  UploadSplitItem("froodle", "narf");
245  ASSERT_EQ(1u, sync_processor_->changes().size());
246  syncer::SyncChange change = sync_processor_->changes()[0];
247  ASSERT_TRUE(change.IsValid());
248  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type());
249  VerifySyncDataItem(change.sync_data());
250
251  sync_data = settings_service_.GetAllSyncData(
252      syncer::SUPERVISED_USER_SETTINGS);
253  EXPECT_EQ(4u, sync_data.size());
254  for (syncer::SyncDataList::const_iterator it = sync_data.begin();
255       it != sync_data.end(); ++it) {
256    VerifySyncDataItem(*it);
257  }
258
259  // Uploading an item with a previously seen key should create an UPDATE
260  // action.
261  sync_processor_->changes().clear();
262  UploadSplitItem("blurp", "snarl");
263  ASSERT_EQ(1u, sync_processor_->changes().size());
264  change = sync_processor_->changes()[0];
265  ASSERT_TRUE(change.IsValid());
266  EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
267  VerifySyncDataItem(change.sync_data());
268
269  sync_data = settings_service_.GetAllSyncData(
270      syncer::SUPERVISED_USER_SETTINGS);
271  EXPECT_EQ(4u, sync_data.size());
272  for (syncer::SyncDataList::const_iterator it = sync_data.begin();
273       it != sync_data.end(); ++it) {
274    VerifySyncDataItem(*it);
275  }
276
277  sync_processor_->changes().clear();
278  UploadAtomicItem("fjord");
279  ASSERT_EQ(1u, sync_processor_->changes().size());
280  change = sync_processor_->changes()[0];
281  ASSERT_TRUE(change.IsValid());
282  EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
283  VerifySyncDataItem(change.sync_data());
284
285  sync_data = settings_service_.GetAllSyncData(
286      syncer::SUPERVISED_USER_SETTINGS);
287  EXPECT_EQ(4u, sync_data.size());
288  for (syncer::SyncDataList::const_iterator it = sync_data.begin();
289       it != sync_data.end(); ++it) {
290    VerifySyncDataItem(*it);
291  }
292
293  // The uploaded items should not show up as settings.
294  const base::Value* value = NULL;
295  EXPECT_FALSE(settings_->GetWithoutPathExpansion(kAtomicItemName, &value));
296  EXPECT_FALSE(settings_->GetWithoutPathExpansion(kSplitItemName, &value));
297
298  // Restarting sync should not create any new changes.
299  settings_service_.StopSyncing(syncer::SUPERVISED_USER_SETTINGS);
300  StartSyncing(sync_data);
301  ASSERT_EQ(0u, sync_processor_->changes().size());
302}
303