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

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: Reordering 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_helper.h"
19 #include "chrome/browser/extensions/syncable_extension_settings_storage.h"
20 #include "chrome/browser/sync/api/sync_change_processor.h"
21 #include "content/browser/browser_thread.h"
22
23 namespace {
24
25 // Define macro to get the __LINE__ expansion.
26 #define NEW_CALLBACK(expected) \
27 (new AssertEqualsCallback((expected), __LINE__))
28
29 // SyncChangeProcessor which just records the changes made, accessed after
30 // being converted to the more useful ExtensionSettingSyncData via changes().
31 class MockSyncChangeProcessor : public SyncChangeProcessor {
32 public:
33 virtual SyncError ProcessSyncChanges(
34 const tracked_objects::Location& from_here,
35 const SyncChangeList& change_list) OVERRIDE {
36 for (SyncChangeList::const_iterator it = change_list.begin();
37 it != change_list.end(); ++it) {
38 ExtensionSettingSyncData data(*it);
39 DCHECK(data.value() != NULL);
40 changes_.push_back(data);
41 }
42 return SyncError();
43 }
44
45 const ExtensionSettingSyncDataList& changes() { return changes_; }
46
47 void ClearChanges() {
48 changes_.clear();
49 }
50
51 // Returns the only change for a given extension setting. If there is not
52 // exactly 1 change for that key, a test assertion will fail.
53 ExtensionSettingSyncData GetOnlyChange(
54 const std::string& extension_id, const std::string& key) {
55 ExtensionSettingSyncDataList matching_changes;
56 for (ExtensionSettingSyncDataList::iterator it = changes_.begin();
57 it != changes_.end(); ++it) {
58 if (it->extension_id() == extension_id && it->key() == key) {
59 matching_changes.push_back(*it);
60 }
61 }
62 DCHECK_EQ(1u, matching_changes.size()) <<
63 "Not exactly 1 change for " << extension_id << "/" << key <<
64 " (out of " << changes_.size() << ")";
65 return matching_changes[0];
66 }
67
68 private:
69 ExtensionSettingSyncDataList changes_;
70 };
71
72 // Callback from storage methods which performs the test assertions.
73 class AssertEqualsCallback : public ExtensionSettingsStorage::Callback {
74 public:
75 AssertEqualsCallback(DictionaryValue* expected, int line)
76 : expected_(expected), line_(line), called_(false) {
77 }
78
79 ~AssertEqualsCallback() {
80 // Need to DCHECK since ASSERT_* can't be used from destructors.
81 DCHECK(called_);
82 }
83
84 virtual void OnSuccess(DictionaryValue* actual) OVERRIDE {
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86 ASSERT_FALSE(called_) << "Callback has already been called";
87 called_ = true;
88 if (expected_ == NULL) {
89 ASSERT_TRUE(actual == NULL) << "Values are different:\n" <<
90 "Line: " << line_ << "\n" <<
91 "Expected: NULL\n" <<
92 "Got: " << GetJson(actual);
93 } else {
94 ASSERT_TRUE(expected_->Equals(actual)) << "Values are different:\n" <<
95 "Line: " << line_ << "\n" <<
96 "Expected: " << GetJson(expected_) <<
97 "Got: " << GetJson(actual);
98 delete actual;
99 }
100 }
101
102 virtual void OnFailure(const std::string& message) OVERRIDE {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104 ASSERT_FALSE(called_) << "Callback has already been called";
105 called_ = true;
106 // No tests allow failure (yet).
107 ASSERT_TRUE(false) << "Callback failed on line " << line_;
108 }
109
110 private:
111 std::string GetJson(Value* value) {
112 std::string json;
113 base::JSONWriter::Write(value, true, &json);
114 return json;
115 }
116
117 DictionaryValue* expected_;
118 int line_;
119 bool called_;
120 };
121
122 // Callback from storage methods which does nothing.
123 class NoopCallback : public ExtensionSettingsStorage::Callback {
124 public:
125 virtual ~NoopCallback() {}
126 virtual void OnSuccess(DictionaryValue* s) OVERRIDE { delete s; }
127 virtual void OnFailure(const std::string& message) OVERRIDE {}
128 };
129
130 } // namespace
131
132 class ExtensionSettingsSyncUnittest : public testing::Test {
133 public:
134 virtual void SetUp() {
135 ui_message_loop_.reset(new MessageLoopForUI());
136 // Use the same message loop for the UI and FILE threads, giving a test
137 // pattern where storage API calls get posted to the same message loop (the
138 // current one), then all run with MessageLoop::current()->RunAllPending().
139 ui_thread_.reset(
140 new BrowserThread(BrowserThread::UI, MessageLoop::current()));
141 file_thread_.reset(
142 new BrowserThread(BrowserThread::FILE, MessageLoop::current()));
143 sync_.reset(new MockSyncChangeProcessor());
144
145 FilePath temp_dir;
146 file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir);
147 settings_ = new ExtensionSettings(temp_dir);
148 }
149
150 protected:
151 void GetStorage(
152 const std::string& extension_id,
153 SyncableExtensionSettingsStorage** storage) {
154 settings_->GetStorage(
155 extension_id,
156 base::Bind(
157 &ExtensionSettingsSyncUnittest::AssignStorage,
158 base::Unretained(this),
159 storage));
160 MessageLoop::current()->RunAllPending();
161 }
162
163 scoped_ptr<MockSyncChangeProcessor> sync_;
164 scoped_refptr<ExtensionSettings> settings_;
165
166 private:
167 void AssignStorage(
168 SyncableExtensionSettingsStorage** assign_to,
169 SyncableExtensionSettingsStorage* storage) {
170 DCHECK(storage != NULL);
171 *assign_to = storage;
172 }
173
174 scoped_ptr<MessageLoopForUI> ui_message_loop_;
175 scoped_ptr<BrowserThread> ui_thread_;
176 scoped_ptr<BrowserThread> file_thread_;
177 };
178
179 TEST_F(ExtensionSettingsSyncUnittest, NoDataDoesNotInvokeSync) {
180 SyncableExtensionSettingsStorage* storage1;
181 SyncableExtensionSettingsStorage* storage2;
182 DictionaryValue empty_dict;
183
184 // Have one extension created before sync is set up, the other created after.
185 GetStorage("storage1", &storage1);
186 settings_->MergeDataAndStartSyncing(
187 syncable::EXTENSION_SETTINGS,
188 SyncDataList(),
189 sync_.get());
190 MessageLoop::current()->RunAllPending();
191 GetStorage("storage2", &storage2);
192
193 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
194 MessageLoop::current()->RunAllPending();
195
196 ASSERT_EQ(0u, sync_->changes().size());
197 }
198
199 TEST_F(ExtensionSettingsSyncUnittest, InSyncDataDoesNotInvokeSync) {
200 StringValue value1("fooValue");
201 ListValue value2;
202 value2.Append(StringValue::CreateStringValue("barValue"));
203
204 SyncableExtensionSettingsStorage* storage1;
205 SyncableExtensionSettingsStorage* storage2;
206
207 GetStorage("storage1", &storage1);
208 GetStorage("storage2", &storage2);
209
210 storage1->Set("foo", value1, new NoopCallback());
211 storage2->Set("bar", value2, new NoopCallback());
212 MessageLoop::current()->RunAllPending();
213
214 SyncDataList sync_data;
215 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
216 "storage1", "foo", value1));
217 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
218 "storage2", "bar", value2));
219
220 settings_->MergeDataAndStartSyncing(
221 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
222 MessageLoop::current()->RunAllPending();
223 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
224 MessageLoop::current()->RunAllPending();
225
226 // Already in sync, so no changes.
227 ASSERT_EQ(0u, sync_->changes().size());
228 }
229
230 TEST_F(ExtensionSettingsSyncUnittest, LocalDataWithNoSyncDataIsPushedToSync) {
231 StringValue value1("fooValue");
232 ListValue value2;
233 value2.Append(StringValue::CreateStringValue("barValue"));
234
235 SyncableExtensionSettingsStorage* storage1;
236 SyncableExtensionSettingsStorage* storage2;
237
238 GetStorage("storage1", &storage1);
239 GetStorage("storage2", &storage2);
240
241 storage1->Set("foo", value1, new NoopCallback());
242 storage2->Set("bar", value2, new NoopCallback());
243 MessageLoop::current()->RunAllPending();
244
245 settings_->MergeDataAndStartSyncing(
246 syncable::EXTENSION_SETTINGS, SyncDataList(), sync_.get());
247 MessageLoop::current()->RunAllPending();
248
249 // All settings should have been pushed to sync.
250 ASSERT_EQ(2u, sync_->changes().size());
251 ExtensionSettingSyncData change = sync_->GetOnlyChange("storage1", "foo");
252 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
253 ASSERT_TRUE(value1.Equals(change.value()));
254 change = sync_->GetOnlyChange("storage2", "bar");
255 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
256 ASSERT_TRUE(value2.Equals(change.value()));
257
258 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
259 MessageLoop::current()->RunAllPending();
260 }
261
262 TEST_F(ExtensionSettingsSyncUnittest, AnySyncDataOverwritesLocalData) {
263 StringValue value1("fooValue");
264 ListValue value2;
265 value2.Append(StringValue::CreateStringValue("barValue"));
266
267 SyncableExtensionSettingsStorage* storage1;
268 SyncableExtensionSettingsStorage* storage2;
269
270 // Pre-populate one of the storage areas.
271 GetStorage("storage1", &storage1);
272 storage1->Set(
273 "overwriteMe", value1, new NoopCallback());
274 MessageLoop::current()->RunAllPending();
275
276 SyncDataList sync_data;
277 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
278 "storage1", "foo", value1));
279 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
280 "storage2", "bar", value2));
281 settings_->MergeDataAndStartSyncing(
282 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
283 MessageLoop::current()->RunAllPending();
284
285 GetStorage("storage2", &storage2);
286
287 // All changes should be local, so no sync changes.
288 ASSERT_EQ(0u, sync_->changes().size());
289
290 // Sync settings should have been pushed to local settings.
291 DictionaryValue expected1;
292 expected1.Set("foo", value1.DeepCopy());
293 DictionaryValue expected2;
294 expected2.Set("bar", value2.DeepCopy());
295 storage1->Get(NEW_CALLBACK(&expected1));
296 storage2->Get(NEW_CALLBACK(&expected2));
297 MessageLoop::current()->RunAllPending();
298
299 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
300 MessageLoop::current()->RunAllPending();
301 }
302
303 TEST_F(ExtensionSettingsSyncUnittest, ProcessSyncChanges) {
304 StringValue value1("fooValue");
305 ListValue value2;
306 value2.Append(StringValue::CreateStringValue("barValue"));
307
308 SyncableExtensionSettingsStorage* storage1;
309 SyncableExtensionSettingsStorage* storage2;
310 // Maintain dictionaries mirrored to the expected values of the settings in
311 // each storage area.
312 DictionaryValue expected1, expected2;
313
314 // Make storage1 initialised from local data, storage2 initialised from sync.
315 GetStorage("storage1", &storage1);
316 GetStorage("storage2", &storage2);
317
318 storage1->Set("foo", value1, new NoopCallback());
319 expected1.Set("foo", value1.DeepCopy());
320 MessageLoop::current()->RunAllPending();
321
322 SyncDataList sync_data;
323 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
324 "storage2", "bar", value2));
325 expected2.Set("bar", value2.DeepCopy());
326
327 settings_->MergeDataAndStartSyncing(
328 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
329 MessageLoop::current()->RunAllPending();
330
331 // Make sync add some settings.
332 SyncChangeList change_list;
333 change_list.push_back(ExtensionSettingsSyncHelper::CreateAdd(
334 "storage1", "bar", value2));
335 expected1.Set("bar", value2.DeepCopy());
336 change_list.push_back(ExtensionSettingsSyncHelper::CreateAdd(
337 "storage2", "foo", value1));
338 expected2.Set("foo", value1.DeepCopy());
339 settings_->ProcessSyncChanges(FROM_HERE, change_list);
340 MessageLoop::current()->RunAllPending();
341
342 storage1->Get(NEW_CALLBACK(&expected1));
343 storage2->Get(NEW_CALLBACK(&expected2));
344 MessageLoop::current()->RunAllPending();
345
346 // Make sync update some settings, storage1 the new setting, storage2 the
347 // initial setting.
348 change_list.clear();
349 change_list.push_back(ExtensionSettingsSyncHelper::CreateUpdate(
350 "storage1", "bar", value2));
351 expected1.Set("bar", value2.DeepCopy());
352 change_list.push_back(ExtensionSettingsSyncHelper::CreateUpdate(
353 "storage2", "bar", value1));
354 expected2.Set("bar", value1.DeepCopy());
355 settings_->ProcessSyncChanges(FROM_HERE, change_list);
356 MessageLoop::current()->RunAllPending();
357
358 storage1->Get(NEW_CALLBACK(&expected1));
359 storage2->Get(NEW_CALLBACK(&expected2));
360 MessageLoop::current()->RunAllPending();
361
362 // Make sync remove some settings, storage1 the initial setting, storage2 the
363 // new setting.
364 change_list.clear();
365 change_list.push_back(ExtensionSettingsSyncHelper::CreateDelete(
366 "storage1", "foo"));
367 expected1.Remove("foo", NULL);
368 change_list.push_back(ExtensionSettingsSyncHelper::CreateDelete(
369 "storage2", "foo"));
370 expected2.Remove("foo", NULL);
371 settings_->ProcessSyncChanges(FROM_HERE, change_list);
372 MessageLoop::current()->RunAllPending();
373
374 storage1->Get(NEW_CALLBACK(&expected1));
375 storage2->Get(NEW_CALLBACK(&expected2));
376 MessageLoop::current()->RunAllPending();
377
378 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
379 MessageLoop::current()->RunAllPending();
380 }
381
382 TEST_F(ExtensionSettingsSyncUnittest, PushToSync) {
383 StringValue value1("fooValue");
384 ListValue value2;
385 value2.Append(StringValue::CreateStringValue("barValue"));
386
387 SyncableExtensionSettingsStorage* storage1;
388 SyncableExtensionSettingsStorage* storage2;
389 SyncableExtensionSettingsStorage* storage3;
390 SyncableExtensionSettingsStorage* storage4;
391
392 // Make storage1/2 initialised from local data, storage3/4 initialised from
393 // sync.
394 GetStorage("storage1", &storage1);
395 GetStorage("storage2", &storage2);
396 GetStorage("storage3", &storage3);
397 GetStorage("storage4", &storage4);
398
399 storage1->Set("foo", value1, new NoopCallback());
400 storage2->Set("foo", value1, new NoopCallback());
401 MessageLoop::current()->RunAllPending();
402
403 SyncDataList sync_data;
404 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
405 "storage3", "bar", value2));
406 sync_data.push_back(ExtensionSettingsSyncHelper::CreateData(
407 "storage4", "bar", value2));
408
409 settings_->MergeDataAndStartSyncing(
410 syncable::EXTENSION_SETTINGS, sync_data, sync_.get());
411 MessageLoop::current()->RunAllPending();
412
413 // Add something locally.
414 storage1->Set("bar", value2, new NoopCallback());
415 storage2->Set("bar", value2, new NoopCallback());
416 storage3->Set("foo", value1, new NoopCallback());
417 storage4->Set("foo", value1, new NoopCallback());
418 MessageLoop::current()->RunAllPending();
419
420 ExtensionSettingSyncData change = sync_->GetOnlyChange("storage1", "bar");
421 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
422 ASSERT_TRUE(value2.Equals(change.value()));
423 sync_->GetOnlyChange("storage2", "bar");
424 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
425 ASSERT_TRUE(value2.Equals(change.value()));
426 change = sync_->GetOnlyChange("storage3", "foo");
427 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
428 ASSERT_TRUE(value1.Equals(change.value()));
429 change = sync_->GetOnlyChange("storage4", "foo");
430 ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
431 ASSERT_TRUE(value1.Equals(change.value()));
432
433 // Change something locally, storage1/3 the new setting and storage2/4 the
434 // initial setting, for all combinations of local vs sync intialisation and
435 // new vs initial.
436 sync_->ClearChanges();
437 storage1->Set("bar", value1, new NoopCallback());
438 storage2->Set("foo", value2, new NoopCallback());
439 storage3->Set("bar", value1, new NoopCallback());
440 storage4->Set("foo", value2, new NoopCallback());
441 MessageLoop::current()->RunAllPending();
442
443 change = sync_->GetOnlyChange("storage1", "bar");
444 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
445 ASSERT_TRUE(value1.Equals(change.value()));
446 change = sync_->GetOnlyChange("storage2", "foo");
447 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
448 ASSERT_TRUE(value2.Equals(change.value()));
449 change = sync_->GetOnlyChange("storage3", "bar");
450 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
451 ASSERT_TRUE(value1.Equals(change.value()));
452 change = sync_->GetOnlyChange("storage4", "foo");
453 ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
454 ASSERT_TRUE(value2.Equals(change.value()));
455
456 // Remove something locally, storage1/3 the new setting and storage2/4 the
457 // initial setting, for all combinations of local vs sync intialisation and
458 // new vs initial.
459 sync_->ClearChanges();
460 storage1->Remove("foo", new NoopCallback());
461 storage2->Remove("bar", new NoopCallback());
462 storage3->Remove("foo", new NoopCallback());
463 storage4->Remove("bar", new NoopCallback());
464 MessageLoop::current()->RunAllPending();
465
466 ASSERT_EQ(
467 SyncChange::ACTION_DELETE,
468 sync_->GetOnlyChange("storage1", "foo").change_type());
469 ASSERT_EQ(
470 SyncChange::ACTION_DELETE,
471 sync_->GetOnlyChange("storage2", "bar").change_type());
472 ASSERT_EQ(
473 SyncChange::ACTION_DELETE,
474 sync_->GetOnlyChange("storage3", "foo").change_type());
475 ASSERT_EQ(
476 SyncChange::ACTION_DELETE,
477 sync_->GetOnlyChange("storage4", "bar").change_type());
478
479 // Remove some nonexistent settings.
480 sync_->ClearChanges();
481 storage1->Remove("foo", new NoopCallback());
482 storage2->Remove("bar", new NoopCallback());
483 storage3->Remove("foo", new NoopCallback());
484 storage4->Remove("bar", new NoopCallback());
485 MessageLoop::current()->RunAllPending();
486
487 ASSERT_EQ(0u, sync_->changes().size());
488
489 // Clear the rest of the settings. Add the removed ones back first so that
490 // more than one setting is cleared.
491 storage1->Set("foo", value1, new NoopCallback());
492 storage2->Set("bar", value2, new NoopCallback());
493 storage3->Set("foo", value1, new NoopCallback());
494 storage4->Set("bar", value2, new NoopCallback());
495 MessageLoop::current()->RunAllPending();
496
497 sync_->ClearChanges();
498 storage1->Clear(new NoopCallback());
499 storage2->Clear(new NoopCallback());
500 storage3->Clear(new NoopCallback());
501 storage4->Clear(new NoopCallback());
502 MessageLoop::current()->RunAllPending();
503
504 ASSERT_EQ(
505 SyncChange::ACTION_DELETE,
506 sync_->GetOnlyChange("storage1", "foo").change_type());
507 ASSERT_EQ(
508 SyncChange::ACTION_DELETE,
509 sync_->GetOnlyChange("storage1", "bar").change_type());
510 ASSERT_EQ(
511 SyncChange::ACTION_DELETE,
512 sync_->GetOnlyChange("storage2", "foo").change_type());
513 ASSERT_EQ(
514 SyncChange::ACTION_DELETE,
515 sync_->GetOnlyChange("storage2", "bar").change_type());
516 ASSERT_EQ(
517 SyncChange::ACTION_DELETE,
518 sync_->GetOnlyChange("storage3", "foo").change_type());
519 ASSERT_EQ(
520 SyncChange::ACTION_DELETE,
521 sync_->GetOnlyChange("storage3", "bar").change_type());
522 ASSERT_EQ(
523 SyncChange::ACTION_DELETE,
524 sync_->GetOnlyChange("storage4", "foo").change_type());
525 ASSERT_EQ(
526 SyncChange::ACTION_DELETE,
527 sync_->GetOnlyChange("storage4", "bar").change_type());
528
529 settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
530 MessageLoop::current()->RunAllPending();
531 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698