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