Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: chrome/browser/extensions/extension_settings_sync_unittest.cc

Issue 7775008: Enable sync for the settings from the Extension Settings API. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Comments (against future change containing GROUP_FILE) Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "testing/gtest/include/gtest/gtest.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop.h"
14 #include "base/task.h"
15 #include "chrome/browser/extensions/extension_settings.h"
16 #include "chrome/browser/extensions/extension_settings_storage_cache.h"
17 #include "chrome/browser/extensions/extension_settings_noop_storage.h"
18 #include "chrome/browser/extensions/extension_settings_sync_util.h"
19 #include "chrome/browser/extensions/syncable_extension_settings_storage.h"
20 #include "chrome/browser/extensions/test_extension_service.h"
21 #include "chrome/browser/sync/api/sync_change_processor.h"
22 #include "content/browser/browser_thread.h"
23
24 namespace {
25
26 // Gets the pretty-printed JSON for a value.
27 static std::string GetJson(const Value& value) {
28 std::string json;
29 base::JSONWriter::Write(&value, true, &json);
30 return json;
31 }
32
33 // Returns whether two Values are equal.
34 testing::AssertionResult ValuesEq(
35 const char* expected_expr,
36 const char* actual_expr,
37 const Value* expected,
38 const Value* actual) {
39 if (expected == actual) {
40 return testing::AssertionSuccess();
41 }
42 if (expected == NULL && actual != NULL) {
43 return testing::AssertionFailure() <<
44 "Expected NULL, actual: " << GetJson(*actual);
45 }
46 if (expected != NULL && actual == NULL) {
47 return testing::AssertionFailure() <<
48 "Expected: " << GetJson(*expected) << ", actual NULL";
49 }
50 if (!expected->Equals(actual)) {
51 return testing::AssertionFailure() <<
52 "Expected: " << GetJson(*expected) << ", actual: " << GetJson(*actual);
53 }
54 return testing::AssertionSuccess();
55 }
56
57 // Returns whether the result of a storage operation is an expected value.
58 // Logs when different.
59 testing::AssertionResult SettingsEq(
60 const char* expected_expr,
61 const char* actual_expr,
62 const DictionaryValue* expected,
63 const ExtensionSettingsStorage::Result actual) {
64 if (actual.HasError()) {
65 return testing::AssertionFailure() <<
66 "Expected: " << GetJson(*expected) <<
67 ", actual has error: " << actual.GetError();
68 }
69 return ValuesEq(
70 expected_expr, actual_expr, expected, actual.GetSettings());
71 }
72
73 // SyncChangeProcessor which just records the changes made, accessed after
74 // being converted to the more useful ExtensionSettingSyncData via changes().
75 class MockSyncChangeProcessor : public SyncChangeProcessor {
76 public:
77 virtual SyncError ProcessSyncChanges(
78 const tracked_objects::Location& from_here,
79 const SyncChangeList& change_list) OVERRIDE {
80 for (SyncChangeList::const_iterator it = change_list.begin();
81 it != change_list.end(); ++it) {
82 ExtensionSettingSyncData data(*it);
83 DCHECK(data.value() != NULL);
84 changes_.push_back(data);
85 }
86 return SyncError();
87 }
88
89 const ExtensionSettingSyncDataList& changes() { return changes_; }
90
91 void ClearChanges() {
92 changes_.clear();
93 }
94
95 // Returns the only change for a given extension setting. If there is not
96 // exactly 1 change for that key, a test assertion will fail.
97 ExtensionSettingSyncData GetOnlyChange(
98 const std::string& extension_id, const std::string& key) {
99 ExtensionSettingSyncDataList matching_changes;
100 for (ExtensionSettingSyncDataList::iterator it = changes_.begin();
101 it != changes_.end(); ++it) {
102 if (it->extension_id() == extension_id && it->key() == key) {
103 matching_changes.push_back(*it);
104 }
105 }
106 DCHECK_EQ(1u, matching_changes.size()) <<
107 "Not exactly 1 change for " << extension_id << "/" << key <<
108 " (out of " << changes_.size() << ")";
109 return matching_changes[0];
110 }
111
112 private:
113 ExtensionSettingSyncDataList changes_;
114 };
115
116 // Fake ExtensionService which just allows Extensions with a specific id to be
117 // added to it and returned.
118 class FakeExtensionService : public TestExtensionService {
119 public:
120 explicit FakeExtensionService(const FilePath& file_path)
121 : file_path_(file_path) {}
122
123 virtual const ExtensionList* extensions() const OVERRIDE {
124 return &extensions_;
125 }
126
127 // Adds an extension to those that will be returned from extensions().
128 void AddExtension(const std::string& extension_id) {
129 DictionaryValue fake_manifest;
130 fake_manifest.SetString(extension_manifest_keys::kVersion, "1.0.0.0");
131 fake_manifest.SetString(extension_manifest_keys::kName, "unused");
132
133 std::string error;
134 scoped_refptr<Extension> extension = Extension::CreateWithId(
135 file_path_.AppendASCII(extension_id),
136 Extension::INTERNAL,
137 fake_manifest,
138 Extension::STRICT_ERROR_CHECKS,
139 extension_id,
140 &error);
141 DCHECK(extension != NULL);
142 extensions_.push_back(extension);
143 }
144
145 private:
146 FilePath file_path_;
147 ExtensionList extensions_;
148 };
149
150 } // namespace
151
152 class ExtensionSettingsSyncUnittest : public testing::Test {
153 public:
154 virtual void SetUp() {
155 // Need these so that the DCHECKs for running on FILE or UI threads pass.
156 ui_message_loop_.reset(new MessageLoopForUI());
157 ui_thread_.reset(
158 new BrowserThread(BrowserThread::UI, MessageLoop::current()));
159 file_thread_.reset(
160 new BrowserThread(BrowserThread::FILE, MessageLoop::current()));
161 sync_.reset(new MockSyncChangeProcessor());
162
163 FilePath temp_dir;
164 file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir);
165 settings_ = new ExtensionSettings(temp_dir);
166 service_.reset(new FakeExtensionService(temp_dir));
167 settings_->SetExtensionService(service_.get());
168 }
169
170 protected:
171 // Creates a new extension storage object and adds a record of the extension
172 // to the extension service.
173 SyncableExtensionSettingsStorage* GetStorage(
174 const std::string& extension_id) {
175 bool have_extension = false;
176 for (ExtensionList::const_iterator it = service_->extensions()->begin();
177 it != service_->extensions()->end(); ++it) {
178 if ((*it)->id() == extension_id) {
179 have_extension = true;
180 break;
181 }
182 }
183 if (!have_extension) {
184 service_->AddExtension(extension_id);
185 }
186 return static_cast<SyncableExtensionSettingsStorage*>(
187 settings_->GetStorage(extension_id));
188 }
189
190 scoped_ptr<MockSyncChangeProcessor> sync_;
191 scoped_refptr<ExtensionSettings> settings_;
192 scoped_ptr<FakeExtensionService> service_;
193
194 // Gets all the sync data from settings_ as a map from extension id to its
195 // sync data.
196 std::map<std::string, ExtensionSettingSyncDataList> GetAllSyncData() {
197 SyncDataList as_list =
198 settings_->GetAllSyncData(syncable::EXTENSION_SETTINGS);
199 std::map<std::string, ExtensionSettingSyncDataList> as_map;
200 for (SyncDataList::iterator it = as_list.begin();
201 it != as_list.end(); ++it) {
202 ExtensionSettingSyncData sync_data(*it);
203 as_map[sync_data.extension_id()].push_back(sync_data);
204 }
205 return as_map;
206 }
207
208 private:
209 // Need these so that the DCHECKs for running on FILE or UI threads pass.
210 scoped_ptr<MessageLoopForUI> ui_message_loop_;
211 scoped_ptr<BrowserThread> ui_thread_;
212 scoped_ptr<BrowserThread> file_thread_;
213 };
214
215 TEST_F(ExtensionSettingsSyncUnittest, NoDataDoesNotInvokeSync) {
216 ASSERT_EQ(0u, GetAllSyncData().size());
217
218 // Have one extension created before sync is set up, the other created after.
219 GetStorage("s1");
220 ASSERT_EQ(0u, GetAllSyncData().size());
221
222 settings_->MergeDataAndStartSyncing(
223 syncable::EXTENSION_SETTINGS,
224 SyncDataList(),
225 sync_.get());
226
227 GetStorage("s2");
228 ASSERT_EQ(0u, GetAllSyncData().size());
229
230 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
231
232 ASSERT_EQ(0u, sync_->changes().size());
233 ASSERT_EQ(0u, GetAllSyncData().size());
234 }
235
236 TEST_F(ExtensionSettingsSyncUnittest, InSyncDataDoesNotInvokeSync) {
237 StringValue value1("fooValue");
238 ListValue value2;
239 value2.Append(StringValue::CreateStringValue("barValue"));
240
241 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
242 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
243
244 storage1->Set("foo", value1);
245 storage2->Set("bar", value2);
246
247 std::map<std::string, ExtensionSettingSyncDataList> all_sync_data =
248 GetAllSyncData();
249 ASSERT_EQ(2u, all_sync_data.size());
250 ASSERT_EQ(1u, all_sync_data["s1"].size());
251 ASSERT_PRED_FORMAT2(ValuesEq, &value1, all_sync_data["s1"][0].value());
252 ASSERT_EQ(1u, all_sync_data["s2"].size());
253 ASSERT_PRED_FORMAT2(ValuesEq, &value2, all_sync_data["s2"][0].value());
254
255 SyncDataList sync_data;
256 sync_data.push_back(extension_settings_sync_util::CreateData(
257 "s1", "foo", value1));
258 sync_data.push_back(extension_settings_sync_util::CreateData(
259 "s2", "bar", value2));
260
261 settings_->MergeDataAndStartSyncing(
262 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
263 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
264
265 // Already in sync, so no changes.
266 ASSERT_EQ(0u, sync_->changes().size());
267 }
268
269 TEST_F(ExtensionSettingsSyncUnittest, LocalDataWithNoSyncDataIsPushedToSync) {
270 StringValue value1("fooValue");
271 ListValue value2;
272 value2.Append(StringValue::CreateStringValue("barValue"));
273
274 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
275 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
276
277 storage1->Set("foo", value1);
278 storage2->Set("bar", value2);
279
280 settings_->MergeDataAndStartSyncing(
281 syncable::EXTENSION_SETTINGS, SyncDataList(), sync_.get());
282
283 // All settings should have been pushed to sync.
284 ASSERT_EQ(2u, sync_->changes().size());
285 ExtensionSettingSyncData change = sync_->GetOnlyChange("s1", "foo");
286 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
287 ASSERT_TRUE(value1.Equals(change.value()));
288 change = sync_->GetOnlyChange("s2", "bar");
289 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
290 ASSERT_TRUE(value2.Equals(change.value()));
291
292 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
293 }
294
295 TEST_F(ExtensionSettingsSyncUnittest, AnySyncDataOverwritesLocalData) {
296 StringValue value1("fooValue");
297 ListValue value2;
298 value2.Append(StringValue::CreateStringValue("barValue"));
299
300 // Maintain dictionaries mirrored to the expected values of the settings in
301 // each storage area.
302 DictionaryValue expected1, expected2;
303
304 // Pre-populate one of the storage areas.
305 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
306 storage1->Set("overwriteMe", value1);
307
308 SyncDataList sync_data;
309 sync_data.push_back(extension_settings_sync_util::CreateData(
310 "s1", "foo", value1));
311 sync_data.push_back(extension_settings_sync_util::CreateData(
312 "s2", "bar", value2));
313 settings_->MergeDataAndStartSyncing(
314 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
315 expected1.Set("foo", value1.DeepCopy());
316 expected2.Set("bar", value2.DeepCopy());
317
318 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
319
320 // All changes should be local, so no sync changes.
321 ASSERT_EQ(0u, sync_->changes().size());
322
323 // Sync settings should have been pushed to local settings.
324 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
325 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
326
327 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
328 }
329
330 TEST_F(ExtensionSettingsSyncUnittest, ProcessSyncChanges) {
331 StringValue value1("fooValue");
332 ListValue value2;
333 value2.Append(StringValue::CreateStringValue("barValue"));
334
335 // Maintain dictionaries mirrored to the expected values of the settings in
336 // each storage area.
337 DictionaryValue expected1, expected2;
338
339 // Make storage1 initialised from local data, storage2 initialised from sync.
340 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
341 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
342
343 storage1->Set("foo", value1);
344 expected1.Set("foo", value1.DeepCopy());
345
346 SyncDataList sync_data;
347 sync_data.push_back(extension_settings_sync_util::CreateData(
348 "s2", "bar", value2));
349
350 settings_->MergeDataAndStartSyncing(
351 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
352 expected2.Set("bar", value2.DeepCopy());
353
354 // Make sync add some settings.
355 SyncChangeList change_list;
356 change_list.push_back(extension_settings_sync_util::CreateAdd(
357 "s1", "bar", value2));
358 change_list.push_back(extension_settings_sync_util::CreateAdd(
359 "s2", "foo", value1));
360 settings_->ProcessSyncChanges(FROM_HERE, change_list);
361 expected1.Set("bar", value2.DeepCopy());
362 expected2.Set("foo", value1.DeepCopy());
363
364 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
365 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
366
367 // Make sync update some settings, storage1 the new setting, storage2 the
368 // initial setting.
369 change_list.clear();
370 change_list.push_back(extension_settings_sync_util::CreateUpdate(
371 "s1", "bar", value2));
372 change_list.push_back(extension_settings_sync_util::CreateUpdate(
373 "s2", "bar", value1));
374 settings_->ProcessSyncChanges(FROM_HERE, change_list);
375 expected1.Set("bar", value2.DeepCopy());
376 expected2.Set("bar", value1.DeepCopy());
377
378 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
379 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
380
381 // Make sync remove some settings, storage1 the initial setting, storage2 the
382 // new setting.
383 change_list.clear();
384 change_list.push_back(extension_settings_sync_util::CreateDelete(
385 "s1", "foo"));
386 change_list.push_back(extension_settings_sync_util::CreateDelete(
387 "s2", "foo"));
388 settings_->ProcessSyncChanges(FROM_HERE, change_list);
389 expected1.Remove("foo", NULL);
390 expected2.Remove("foo", NULL);
391
392 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
393 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
394
395 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
396 }
397
398 TEST_F(ExtensionSettingsSyncUnittest, PushToSync) {
399 StringValue value1("fooValue");
400 ListValue value2;
401 value2.Append(StringValue::CreateStringValue("barValue"));
402
403 // Make storage1/2 initialised from local data, storage3/4 initialised from
404 // sync.
405 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
406 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
407 SyncableExtensionSettingsStorage* storage3 = GetStorage("s3");
408 SyncableExtensionSettingsStorage* storage4 = GetStorage("s4");
409
410 storage1->Set("foo", value1);
411 storage2->Set("foo", value1);
412
413 SyncDataList sync_data;
414 sync_data.push_back(extension_settings_sync_util::CreateData(
415 "s3", "bar", value2));
416 sync_data.push_back(extension_settings_sync_util::CreateData(
417 "s4", "bar", value2));
418
419 settings_->MergeDataAndStartSyncing(
420 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
421
422 // Add something locally.
423 storage1->Set("bar", value2);
424 storage2->Set("bar", value2);
425 storage3->Set("foo", value1);
426 storage4->Set("foo", value1);
427
428 ExtensionSettingSyncData change = sync_->GetOnlyChange("s1", "bar");
429 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
430 ASSERT_TRUE(value2.Equals(change.value()));
431 sync_->GetOnlyChange("s2", "bar");
432 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
433 ASSERT_TRUE(value2.Equals(change.value()));
434 change = sync_->GetOnlyChange("s3", "foo");
435 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
436 ASSERT_TRUE(value1.Equals(change.value()));
437 change = sync_->GetOnlyChange("s4", "foo");
438 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
439 ASSERT_TRUE(value1.Equals(change.value()));
440
441 // Change something locally, storage1/3 the new setting and storage2/4 the
442 // initial setting, for all combinations of local vs sync intialisation and
443 // new vs initial.
444 sync_->ClearChanges();
445 storage1->Set("bar", value1);
446 storage2->Set("foo", value2);
447 storage3->Set("bar", value1);
448 storage4->Set("foo", value2);
449
450 change = sync_->GetOnlyChange("s1", "bar");
451 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
452 ASSERT_TRUE(value1.Equals(change.value()));
453 change = sync_->GetOnlyChange("s2", "foo");
454 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
455 ASSERT_TRUE(value2.Equals(change.value()));
456 change = sync_->GetOnlyChange("s3", "bar");
457 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
458 ASSERT_TRUE(value1.Equals(change.value()));
459 change = sync_->GetOnlyChange("s4", "foo");
460 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
461 ASSERT_TRUE(value2.Equals(change.value()));
462
463 // Remove something locally, storage1/3 the new setting and storage2/4 the
464 // initial setting, for all combinations of local vs sync intialisation and
465 // new vs initial.
466 sync_->ClearChanges();
467 storage1->Remove("foo");
468 storage2->Remove("bar");
469 storage3->Remove("foo");
470 storage4->Remove("bar");
471
472 ASSERT_EQ(
473 SyncChange::ACTION_DELETE,
474 sync_->GetOnlyChange("s1", "foo").change_type());
475 ASSERT_EQ(
476 SyncChange::ACTION_DELETE,
477 sync_->GetOnlyChange("s2", "bar").change_type());
478 ASSERT_EQ(
479 SyncChange::ACTION_DELETE,
480 sync_->GetOnlyChange("s3", "foo").change_type());
481 ASSERT_EQ(
482 SyncChange::ACTION_DELETE,
483 sync_->GetOnlyChange("s4", "bar").change_type());
484
485 // Remove some nonexistent settings.
486 sync_->ClearChanges();
487 storage1->Remove("foo");
488 storage2->Remove("bar");
489 storage3->Remove("foo");
490 storage4->Remove("bar");
491
492 ASSERT_EQ(0u, sync_->changes().size());
493
494 // Clear the rest of the settings. Add the removed ones back first so that
495 // more than one setting is cleared.
496 storage1->Set("foo", value1);
497 storage2->Set("bar", value2);
498 storage3->Set("foo", value1);
499 storage4->Set("bar", value2);
500
501 sync_->ClearChanges();
502 storage1->Clear();
503 storage2->Clear();
504 storage3->Clear();
505 storage4->Clear();
506
507 ASSERT_EQ(
508 SyncChange::ACTION_DELETE,
509 sync_->GetOnlyChange("s1", "foo").change_type());
510 ASSERT_EQ(
511 SyncChange::ACTION_DELETE,
512 sync_->GetOnlyChange("s1", "bar").change_type());
513 ASSERT_EQ(
514 SyncChange::ACTION_DELETE,
515 sync_->GetOnlyChange("s2", "foo").change_type());
516 ASSERT_EQ(
517 SyncChange::ACTION_DELETE,
518 sync_->GetOnlyChange("s2", "bar").change_type());
519 ASSERT_EQ(
520 SyncChange::ACTION_DELETE,
521 sync_->GetOnlyChange("s3", "foo").change_type());
522 ASSERT_EQ(
523 SyncChange::ACTION_DELETE,
524 sync_->GetOnlyChange("s3", "bar").change_type());
525 ASSERT_EQ(
526 SyncChange::ACTION_DELETE,
527 sync_->GetOnlyChange("s4", "foo").change_type());
528 ASSERT_EQ(
529 SyncChange::ACTION_DELETE,
530 sync_->GetOnlyChange("s4", "bar").change_type());
531
532 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
533 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698