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