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

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: Review #1 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
akalin 2011/09/15 19:56:44 Add TODO somewhere to also write integration tests
not at google - send to devlin 2011/09/16 05:18:59 Done.
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 && actual) {
43 return testing::AssertionFailure() <<
44 "Expected NULL, actual: " << GetJson(*actual);
45 }
46 if (expected && !actual) {
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());
akalin 2011/09/15 19:56:44 prefer if (!data.value()) { ADD_FAILURE(); c
not at google - send to devlin 2011/09/16 05:18:59 Done.
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()) <<
akalin 2011/09/15 19:56:44 prefer: if (matching_changes.size() != 1u) { AD
not at google - send to devlin 2011/09/16 05:18:59 Done.
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 virtual const ExtensionList* disabled_extensions() const OVERRIDE {
128 return &disabled_extensions_;
129 }
130
131 virtual const ExtensionList* terminated_extensions() const OVERRIDE {
132 return &terminated_extensions_;
133 }
134
135 // Adds an extension to those that will be returned from extensions().
136 void AddExtension(const std::string& extension_id) {
137 DictionaryValue fake_manifest;
138 fake_manifest.SetString(extension_manifest_keys::kVersion, "1.0.0.0");
139 fake_manifest.SetString(extension_manifest_keys::kName, "unused");
140
141 std::string error;
142 scoped_refptr<Extension> extension = Extension::CreateWithId(
143 file_path_.AppendASCII(extension_id),
144 Extension::INTERNAL,
145 fake_manifest,
146 Extension::STRICT_ERROR_CHECKS,
147 extension_id,
148 &error);
149 DCHECK(extension);
150 next_extension_list()->push_back(extension);
151 }
152
153 private:
154 // Gets the next extension list to add the extension to.
155 ExtensionList* next_extension_list() {
156 static int next = 0;
akalin 2011/09/15 19:56:44 doesn't need to be static, just make it a member v
not at google - send to devlin 2011/09/16 05:18:59 :( (Though don't need this any more after adding
157 switch (next++ % 3) {
158 case 0: return &extensions_;
159 case 1: return &disabled_extensions_;
160 case 2: return &terminated_extensions_;
161 }
162 NOTREACHED();
163 return NULL;
164 }
165
166 FilePath file_path_;
167 ExtensionList extensions_;
168 ExtensionList disabled_extensions_;
169 ExtensionList terminated_extensions_;
170 };
171
172 } // namespace
173
174 class ExtensionSettingsSyncUnittest : public testing::Test {
175 public:
176 virtual void SetUp() {
177 // Need these so that the DCHECKs for running on FILE or UI threads pass.
178 ui_message_loop_.reset(new MessageLoopForUI());
179 ui_thread_.reset(
180 new BrowserThread(BrowserThread::UI, MessageLoop::current()));
181 file_thread_.reset(
182 new BrowserThread(BrowserThread::FILE, MessageLoop::current()));
183 sync_.reset(new MockSyncChangeProcessor());
184
185 FilePath temp_dir;
186 file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir);
187 settings_ = new ExtensionSettings(temp_dir);
188 service_.reset(new FakeExtensionService(temp_dir));
189 settings_->SetExtensionService(service_.get());
190 }
191
192 protected:
193 // Creates a new extension storage object and adds a record of the extension
194 // to the extension service.
195 SyncableExtensionSettingsStorage* GetStorage(
196 const std::string& extension_id) {
197 bool have_extension = false;
198 for (ExtensionList::const_iterator it = service_->extensions()->begin();
199 it != service_->extensions()->end(); ++it) {
200 if ((*it)->id() == extension_id) {
201 have_extension = true;
202 break;
203 }
204 }
205 if (!have_extension) {
206 service_->AddExtension(extension_id);
207 }
208 return static_cast<SyncableExtensionSettingsStorage*>(
209 settings_->GetStorage(extension_id));
210 }
211
212 scoped_ptr<MockSyncChangeProcessor> sync_;
213 scoped_refptr<ExtensionSettings> settings_;
214 scoped_ptr<FakeExtensionService> service_;
215
216 // Gets all the sync data from settings_ as a map from extension id to its
217 // sync data.
218 std::map<std::string, ExtensionSettingSyncDataList> GetAllSyncData() {
219 SyncDataList as_list =
220 settings_->GetAllSyncData(syncable::EXTENSION_SETTINGS);
221 std::map<std::string, ExtensionSettingSyncDataList> as_map;
222 for (SyncDataList::iterator it = as_list.begin();
223 it != as_list.end(); ++it) {
224 ExtensionSettingSyncData sync_data(*it);
225 as_map[sync_data.extension_id()].push_back(sync_data);
226 }
227 return as_map;
228 }
229
230 private:
231 // Need these so that the DCHECKs for running on FILE or UI threads pass.
232 scoped_ptr<MessageLoopForUI> ui_message_loop_;
akalin 2011/09/15 19:56:44 No need to use scoped_ptr, just set them up in con
not at google - send to devlin 2011/09/16 05:18:59 Done.
233 scoped_ptr<BrowserThread> ui_thread_;
234 scoped_ptr<BrowserThread> file_thread_;
235 };
236
237 TEST_F(ExtensionSettingsSyncUnittest, NoDataDoesNotInvokeSync) {
238 ASSERT_EQ(0u, GetAllSyncData().size());
239
240 // Have one extension created before sync is set up, the other created after.
241 GetStorage("s1");
242 ASSERT_EQ(0u, GetAllSyncData().size());
243
244 settings_->MergeDataAndStartSyncing(
245 syncable::EXTENSION_SETTINGS,
246 SyncDataList(),
247 sync_.get());
248
249 GetStorage("s2");
250 ASSERT_EQ(0u, GetAllSyncData().size());
251
252 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
253
254 ASSERT_EQ(0u, sync_->changes().size());
255 ASSERT_EQ(0u, GetAllSyncData().size());
256 }
257
258 TEST_F(ExtensionSettingsSyncUnittest, InSyncDataDoesNotInvokeSync) {
259 StringValue value1("fooValue");
260 ListValue value2;
261 value2.Append(StringValue::CreateStringValue("barValue"));
262
263 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
264 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
265
266 storage1->Set("foo", value1);
267 storage2->Set("bar", value2);
268
269 std::map<std::string, ExtensionSettingSyncDataList> all_sync_data =
270 GetAllSyncData();
271 ASSERT_EQ(2u, all_sync_data.size());
272 ASSERT_EQ(1u, all_sync_data["s1"].size());
273 ASSERT_PRED_FORMAT2(ValuesEq, &value1, all_sync_data["s1"][0].value());
274 ASSERT_EQ(1u, all_sync_data["s2"].size());
275 ASSERT_PRED_FORMAT2(ValuesEq, &value2, all_sync_data["s2"][0].value());
276
277 SyncDataList sync_data;
278 sync_data.push_back(extension_settings_sync_util::CreateData(
279 "s1", "foo", value1));
280 sync_data.push_back(extension_settings_sync_util::CreateData(
281 "s2", "bar", value2));
282
283 settings_->MergeDataAndStartSyncing(
284 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
285 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
286
287 // Already in sync, so no changes.
288 ASSERT_EQ(0u, sync_->changes().size());
289 }
290
291 TEST_F(ExtensionSettingsSyncUnittest, LocalDataWithNoSyncDataIsPushedToSync) {
292 StringValue value1("fooValue");
293 ListValue value2;
294 value2.Append(StringValue::CreateStringValue("barValue"));
295
296 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
297 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
298
299 storage1->Set("foo", value1);
300 storage2->Set("bar", value2);
301
302 settings_->MergeDataAndStartSyncing(
303 syncable::EXTENSION_SETTINGS, SyncDataList(), sync_.get());
304
305 // All settings should have been pushed to sync.
306 ASSERT_EQ(2u, sync_->changes().size());
307 ExtensionSettingSyncData change = sync_->GetOnlyChange("s1", "foo");
308 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
309 ASSERT_TRUE(value1.Equals(change.value()));
310 change = sync_->GetOnlyChange("s2", "bar");
311 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
312 ASSERT_TRUE(value2.Equals(change.value()));
313
314 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
315 }
316
317 TEST_F(ExtensionSettingsSyncUnittest, AnySyncDataOverwritesLocalData) {
318 StringValue value1("fooValue");
319 ListValue value2;
320 value2.Append(StringValue::CreateStringValue("barValue"));
321
322 // Maintain dictionaries mirrored to the expected values of the settings in
323 // each storage area.
324 DictionaryValue expected1, expected2;
325
326 // Pre-populate one of the storage areas.
327 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
328 storage1->Set("overwriteMe", value1);
329
330 SyncDataList sync_data;
331 sync_data.push_back(extension_settings_sync_util::CreateData(
332 "s1", "foo", value1));
333 sync_data.push_back(extension_settings_sync_util::CreateData(
334 "s2", "bar", value2));
335 settings_->MergeDataAndStartSyncing(
336 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
337 expected1.Set("foo", value1.DeepCopy());
338 expected2.Set("bar", value2.DeepCopy());
339
340 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
341
342 // All changes should be local, so no sync changes.
343 ASSERT_EQ(0u, sync_->changes().size());
344
345 // Sync settings should have been pushed to local settings.
346 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
347 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
348
349 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
350 }
351
352 TEST_F(ExtensionSettingsSyncUnittest, ProcessSyncChanges) {
353 StringValue value1("fooValue");
354 ListValue value2;
355 value2.Append(StringValue::CreateStringValue("barValue"));
356
357 // Maintain dictionaries mirrored to the expected values of the settings in
358 // each storage area.
359 DictionaryValue expected1, expected2;
360
361 // Make storage1 initialised from local data, storage2 initialised from sync.
362 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
363 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
364
365 storage1->Set("foo", value1);
366 expected1.Set("foo", value1.DeepCopy());
367
368 SyncDataList sync_data;
369 sync_data.push_back(extension_settings_sync_util::CreateData(
370 "s2", "bar", value2));
371
372 settings_->MergeDataAndStartSyncing(
373 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
374 expected2.Set("bar", value2.DeepCopy());
375
376 // Make sync add some settings.
377 SyncChangeList change_list;
378 change_list.push_back(extension_settings_sync_util::CreateAdd(
379 "s1", "bar", value2));
380 change_list.push_back(extension_settings_sync_util::CreateAdd(
381 "s2", "foo", value1));
382 settings_->ProcessSyncChanges(FROM_HERE, change_list);
383 expected1.Set("bar", value2.DeepCopy());
384 expected2.Set("foo", value1.DeepCopy());
385
386 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
387 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
388
389 // Make sync update some settings, storage1 the new setting, storage2 the
390 // initial setting.
391 change_list.clear();
392 change_list.push_back(extension_settings_sync_util::CreateUpdate(
393 "s1", "bar", value2));
394 change_list.push_back(extension_settings_sync_util::CreateUpdate(
395 "s2", "bar", value1));
396 settings_->ProcessSyncChanges(FROM_HERE, change_list);
397 expected1.Set("bar", value2.DeepCopy());
398 expected2.Set("bar", value1.DeepCopy());
399
400 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
401 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
402
403 // Make sync remove some settings, storage1 the initial setting, storage2 the
404 // new setting.
405 change_list.clear();
406 change_list.push_back(extension_settings_sync_util::CreateDelete(
407 "s1", "foo"));
408 change_list.push_back(extension_settings_sync_util::CreateDelete(
409 "s2", "foo"));
410 settings_->ProcessSyncChanges(FROM_HERE, change_list);
411 expected1.Remove("foo", NULL);
412 expected2.Remove("foo", NULL);
413
414 ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
415 ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
416
417 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
418 }
419
420 TEST_F(ExtensionSettingsSyncUnittest, PushToSync) {
421 StringValue value1("fooValue");
422 ListValue value2;
423 value2.Append(StringValue::CreateStringValue("barValue"));
424
425 // Make storage1/2 initialised from local data, storage3/4 initialised from
426 // sync.
427 SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
428 SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
429 SyncableExtensionSettingsStorage* storage3 = GetStorage("s3");
430 SyncableExtensionSettingsStorage* storage4 = GetStorage("s4");
431
432 storage1->Set("foo", value1);
433 storage2->Set("foo", value1);
434
435 SyncDataList sync_data;
436 sync_data.push_back(extension_settings_sync_util::CreateData(
437 "s3", "bar", value2));
438 sync_data.push_back(extension_settings_sync_util::CreateData(
439 "s4", "bar", value2));
440
441 settings_->MergeDataAndStartSyncing(
442 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
443
444 // Add something locally.
445 storage1->Set("bar", value2);
446 storage2->Set("bar", value2);
447 storage3->Set("foo", value1);
448 storage4->Set("foo", value1);
449
450 ExtensionSettingSyncData change = sync_->GetOnlyChange("s1", "bar");
451 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
452 ASSERT_TRUE(value2.Equals(change.value()));
453 sync_->GetOnlyChange("s2", "bar");
454 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
455 ASSERT_TRUE(value2.Equals(change.value()));
456 change = sync_->GetOnlyChange("s3", "foo");
457 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
458 ASSERT_TRUE(value1.Equals(change.value()));
459 change = sync_->GetOnlyChange("s4", "foo");
460 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
461 ASSERT_TRUE(value1.Equals(change.value()));
462
463 // Change 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->Set("bar", value1);
468 storage2->Set("foo", value2);
469 storage3->Set("bar", value1);
470 storage4->Set("foo", value2);
471
472 change = sync_->GetOnlyChange("s1", "bar");
473 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
474 ASSERT_TRUE(value1.Equals(change.value()));
475 change = sync_->GetOnlyChange("s2", "foo");
476 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
477 ASSERT_TRUE(value2.Equals(change.value()));
478 change = sync_->GetOnlyChange("s3", "bar");
479 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
480 ASSERT_TRUE(value1.Equals(change.value()));
481 change = sync_->GetOnlyChange("s4", "foo");
482 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
483 ASSERT_TRUE(value2.Equals(change.value()));
484
485 // Remove something locally, storage1/3 the new setting and storage2/4 the
486 // initial setting, for all combinations of local vs sync intialisation and
487 // new vs initial.
488 sync_->ClearChanges();
489 storage1->Remove("foo");
490 storage2->Remove("bar");
491 storage3->Remove("foo");
492 storage4->Remove("bar");
493
494 ASSERT_EQ(
495 SyncChange::ACTION_DELETE,
496 sync_->GetOnlyChange("s1", "foo").change_type());
497 ASSERT_EQ(
498 SyncChange::ACTION_DELETE,
499 sync_->GetOnlyChange("s2", "bar").change_type());
500 ASSERT_EQ(
501 SyncChange::ACTION_DELETE,
502 sync_->GetOnlyChange("s3", "foo").change_type());
503 ASSERT_EQ(
504 SyncChange::ACTION_DELETE,
505 sync_->GetOnlyChange("s4", "bar").change_type());
506
507 // Remove some nonexistent settings.
508 sync_->ClearChanges();
509 storage1->Remove("foo");
510 storage2->Remove("bar");
511 storage3->Remove("foo");
512 storage4->Remove("bar");
513
514 ASSERT_EQ(0u, sync_->changes().size());
515
516 // Clear the rest of the settings. Add the removed ones back first so that
517 // more than one setting is cleared.
518 storage1->Set("foo", value1);
519 storage2->Set("bar", value2);
520 storage3->Set("foo", value1);
521 storage4->Set("bar", value2);
522
523 sync_->ClearChanges();
524 storage1->Clear();
525 storage2->Clear();
526 storage3->Clear();
527 storage4->Clear();
528
529 ASSERT_EQ(
530 SyncChange::ACTION_DELETE,
531 sync_->GetOnlyChange("s1", "foo").change_type());
532 ASSERT_EQ(
533 SyncChange::ACTION_DELETE,
534 sync_->GetOnlyChange("s1", "bar").change_type());
535 ASSERT_EQ(
536 SyncChange::ACTION_DELETE,
537 sync_->GetOnlyChange("s2", "foo").change_type());
538 ASSERT_EQ(
539 SyncChange::ACTION_DELETE,
540 sync_->GetOnlyChange("s2", "bar").change_type());
541 ASSERT_EQ(
542 SyncChange::ACTION_DELETE,
543 sync_->GetOnlyChange("s3", "foo").change_type());
544 ASSERT_EQ(
545 SyncChange::ACTION_DELETE,
546 sync_->GetOnlyChange("s3", "bar").change_type());
547 ASSERT_EQ(
548 SyncChange::ACTION_DELETE,
549 sync_->GetOnlyChange("s4", "foo").change_type());
550 ASSERT_EQ(
551 SyncChange::ACTION_DELETE,
552 sync_->GetOnlyChange("s4", "bar").change_type());
553
554 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
555 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698