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

Side by Side Diff: chrome/browser/search_engines/template_url_service_sync_unittest.cc

Issue 7566036: Implement SyncableServices in TemplateURLService. Add related unittests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Addressed zea's unittest issues. Created 9 years, 4 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
Property Changes:
Added: svn:eol-style
+ LF
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 "base/memory/scoped_ptr.h"
6 #include "base/string_util.h"
7 #include "base/time.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/search_engines/template_url.h"
10 #include "chrome/browser/search_engines/template_url_service.h"
11 #include "chrome/browser/sync/protocol/search_engine_specifics.pb.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "chrome/test/base/testing_browser_process_test.h"
Nicolas Zea 2011/08/15 23:40:00 last include is out of abc order
SteveT 2011/08/16 00:08:59 Done.
14
15 using base::Time;
16
17 namespace {
Nicolas Zea 2011/08/15 23:40:00 nit: blank line after (and before close brace)
SteveT 2011/08/16 00:08:59 Done.
18 // Extract the GUID from a search engine SyncData.
19 std::string GetGUID(const SyncData& sync_data) {
20 return sync_data.GetSpecifics().GetExtension(
21 sync_pb::search_engine).sync_guid();
22 }
23
24 // Extract the keyword from a search engine SyncData.
25 std::string GetURL(const SyncData& sync_data) {
26 return sync_data.GetSpecifics().GetExtension(
27 sync_pb::search_engine).url();
28 }
29
30 // Extract the keyword from a search engine SyncData.
31 std::string GetKeyword(const SyncData& sync_data) {
32 return sync_data.GetSpecifics().GetExtension(
33 sync_pb::search_engine).keyword();
34 }
35 }
Nicolas Zea 2011/08/15 23:40:00 nit: extend the namespace to be the entire file an
SteveT 2011/08/16 00:08:59 Done.
36
37 // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
38 // back up to Sync.
39 class TestChangeProcessor : public SyncChangeProcessor {
40 public:
41 TestChangeProcessor() : erroneous_(false) {
42 }
43 virtual ~TestChangeProcessor() { }
44
45 // Store a copy of all the changes passed in so we can examine them later.
46 virtual SyncError ProcessSyncChanges(
47 const tracked_objects::Location& from_here,
48 const SyncChangeList& change_list) {
49 if (erroneous_)
50 return SyncError(FROM_HERE, "Some error.", syncable::SEARCH_ENGINES);
51
52 change_map_.erase(change_map_.begin(), change_map_.end());
53 for (SyncChangeList::const_iterator iter = change_list.begin();
54 iter != change_list.end(); ++iter) {
55 change_map_[GetGUID(iter->sync_data())] = *iter;
56 }
57
58 return SyncError();
59 }
60
61 bool ContainsGUID(const std::string& guid) {
62 return change_map_.find(guid) != change_map_.end();
63 }
64
65 SyncChange GetChangeByGUID(const std::string& guid) {
66 DCHECK(ContainsGUID(guid));
67 return change_map_[guid];
68 }
69
70 int change_list_size() { return change_map_.size(); }
71
72 void set_erroneous(bool erroneous) { erroneous_ = erroneous; }
73
74 private:
75 // Track the changes received in ProcessSyncChanges.
76 std::map<std::string, SyncChange> change_map_;
77 bool erroneous_;
78
79 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
80 };
81
82 class TemplateURLServiceSyncTest : public TestingBrowserProcessTest {
83 public:
84 typedef TemplateURLService::SyncDataMap SyncDataMap;
85
86 TemplateURLServiceSyncTest() {}
87
88 virtual void SetUp() {
89 profile_a_.reset(new TestingProfile);
90 model_a_.reset(new TemplateURLService(profile_a_.get()));
91 model_a_->Load();
92 profile_b_.reset(new TestingProfile);
93 model_b_.reset(new TemplateURLService(profile_b_.get()));
94 model_b_->Load();
95 }
96
97 virtual void TearDown() { }
98
99 TemplateURLService* model() { return model_a_.get(); }
100 // For readability, we redefine an accessor for Model A for use in tests that
101 // involve syncing two models.
102 TemplateURLService* model_a() { return model_a_.get(); }
103 TemplateURLService* model_b() { return model_b_.get(); }
104 TestChangeProcessor* processor() { return &processor_; }
105
106 // Create a TemplateURL with some test values. The caller owns the returned
107 // TemplateURL*.
108 TemplateURL* CreateTestTemplateURL() const {
109 TemplateURL* turl = new TemplateURL();
110 turl->SetURL("http://www.unittest.com/", 0, 0);
111 turl->set_keyword(ASCIIToUTF16("unittest"));
112 turl->set_short_name(ASCIIToUTF16("unittest"));
113 turl->set_safe_for_autoreplace(true);
114 GURL favicon_url("http://favicon.url");
115 turl->SetFaviconURL(favicon_url);
116 turl->set_date_created(Time::FromTimeT(100));
117 turl->set_last_modified(Time::FromTimeT(100));
118 turl->SetPrepopulateId(999999);
119 return turl;
120 }
121
122 // Convenience helpers for creating TemplateURLs with specific fields.
123 TemplateURL* CreateTestTemplateURL(std::string keyword,
124 std::string url) const {
125 TemplateURL* turl = CreateTestTemplateURL();
126 turl->set_keyword(UTF8ToUTF16(keyword));
127 turl->SetURL(url, 0, 0);
128 return turl;
129 }
130
131 TemplateURL* CreateTestTemplateURL(std::string keyword,
132 std::string url,
133 std::string guid) const {
134 TemplateURL* turl = CreateTestTemplateURL(keyword, url);
135 if (!guid.empty())
136 turl->set_sync_guid(guid);
137 return turl;
138 }
139
140 TemplateURL* CreateTestTemplateURL(std::string keyword,
141 std::string url,
142 std::string guid,
143 time_t last_mod) const {
144 TemplateURL* turl = CreateTestTemplateURL(keyword, url, guid);
145 turl->set_last_modified(Time::FromTimeT(last_mod));
146 return turl;
147 }
148
149 // Verifies the two TemplateURLs are equal.
150 // TODO(stevet): Share this with TemplateURLServiceTest.
151 void AssertEquals(const TemplateURL& expected,
152 const TemplateURL& actual) const {
153 ASSERT_TRUE(TemplateURLRef::SameUrlRefs(expected.url(), actual.url()));
154 ASSERT_TRUE(TemplateURLRef::SameUrlRefs(expected.suggestions_url(),
155 actual.suggestions_url()));
156 ASSERT_EQ(expected.keyword(), actual.keyword());
157 ASSERT_EQ(expected.short_name(), actual.short_name());
158 ASSERT_EQ(JoinString(expected.input_encodings(), ';'),
159 JoinString(actual.input_encodings(), ';'));
160 ASSERT_TRUE(expected.GetFaviconURL() == actual.GetFaviconURL());
161 ASSERT_EQ(expected.id(), actual.id());
162 ASSERT_EQ(expected.safe_for_autoreplace(), actual.safe_for_autoreplace());
163 ASSERT_EQ(expected.show_in_default_list(), actual.show_in_default_list());
164 ASSERT_TRUE(expected.date_created() == actual.date_created());
165 ASSERT_TRUE(expected.last_modified() == actual.last_modified());
166 }
167
168 // Expect that two SyncDataLists have equal contents, in terms of the
169 // sync_guid, keyword, and url fields.
170 void AssertEquals(const SyncDataList& data1,
171 const SyncDataList& data2) const {
172 SyncDataMap map1 = TemplateURLService::CreateGUIDToSyncDataMap(data1);
173 SyncDataMap map2 = TemplateURLService::CreateGUIDToSyncDataMap(data2);
174
175 for (SyncDataMap::const_iterator iter1 = map1.begin();
176 iter1 != map1.end(); iter1++) {
177 SyncDataMap::iterator iter2 = map2.find(iter1->first);
178 if (iter2 != map2.end()) {
179 ASSERT_EQ(GetKeyword(iter1->second), GetKeyword(iter2->second));
180 ASSERT_EQ(GetURL(iter1->second), GetURL(iter2->second));
181 map2.erase(iter2);
182 }
183 }
184 EXPECT_EQ(0U, map2.size());
185 }
186
187 // Convenience helper for creating SyncChanges.
188 SyncChange CreateTestSyncChange(SyncChange::SyncChangeType type,
189 TemplateURL* turl) const {
190 return SyncChange(type,
191 TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
192 }
193
194 // Helper that creates some initial sync data. We cheat a little by specifying
195 // GUIDs for easy identification later. We also make the last_modified times
196 // slightly older than CreateTestTemplateURL's default, to test conflict
197 // resolution.
198 SyncDataList CreateInitialSyncData() const {
199 SyncDataList list;
200
201 scoped_ptr<TemplateURL> turl(
202 CreateTestTemplateURL("key1", "http://key1.com", "key1", 90));
203 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
204 turl.reset(CreateTestTemplateURL("key2", "http://key2.com", "key2", 90));
205 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
206 turl.reset(CreateTestTemplateURL("key3", "http://key3.com", "key3", 90));
207 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
208
209 return list;
210 }
211
212 protected:
213 // We keep two TemplateURLServices to test syncing between them.
214 scoped_ptr<TestingProfile> profile_a_;
215 scoped_ptr<TemplateURLService> model_a_;
216 scoped_ptr<TestingProfile> profile_b_;
217 scoped_ptr<TemplateURLService> model_b_;
218
219 // Our dummy ChangeProcessor used to inspect changes pushed to Sync.
220 TestChangeProcessor processor_;
221
222 DISALLOW_COPY_AND_ASSIGN(TemplateURLServiceSyncTest);
223 };
224
225 TEST_F(TemplateURLServiceSyncTest, SerializeDeserialize) {
226 // Create a TemplateURL and convert it into a sync specific type.
227 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL());
228 SyncData sync_data = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
229 // Convert the specifics back to a TemplateURL.
230 scoped_ptr<TemplateURL> deserialized(
231 TemplateURLService::CreateTemplateURLFromSyncData(sync_data));
232 EXPECT_TRUE(deserialized.get());
233 // Ensure that the original and the deserialized TURLs are equal in values.
234 AssertEquals(*turl, *deserialized);
235 }
236
237 TEST_F(TemplateURLServiceSyncTest, GetAllSyncDataBasic) {
238 model()->Add(CreateTestTemplateURL("key1", "http://key1.com"));
239 model()->Add(CreateTestTemplateURL("key2", "http://key2.com"));
240 model()->Add(CreateTestTemplateURL("key3", "http://key3.com"));
241 SyncDataList all_sync_data =
242 model()->GetAllSyncData(syncable::SEARCH_ENGINES);
243
244 EXPECT_EQ(3U, all_sync_data.size());
245
246 for (SyncDataList::const_iterator iter = all_sync_data.begin();
247 iter != all_sync_data.end(); ++iter) {
248 std::string guid = GetGUID(*iter);
249 const TemplateURL* service_turl = model()->GetTemplateURLForGUID(guid);
250 const TemplateURL* deserialized =
251 TemplateURLService::CreateTemplateURLFromSyncData(*iter);
252 AssertEquals(*service_turl, *deserialized);
253 }
254 }
255
256 TEST_F(TemplateURLServiceSyncTest, GetAllSyncDataNoExtensions) {
257 model()->Add(CreateTestTemplateURL("key1", "http://key1.com"));
258 model()->Add(CreateTestTemplateURL("key2", "http://key2.com"));
259 model()->Add(CreateTestTemplateURL(
260 "key3", "chrome-extension://blahblahblah"));
261 SyncDataList all_sync_data =
262 model()->GetAllSyncData(syncable::SEARCH_ENGINES);
263
264 EXPECT_EQ(2U, all_sync_data.size());
265
266 for (SyncDataList::const_iterator iter = all_sync_data.begin();
267 iter != all_sync_data.end(); ++iter) {
268 std::string guid = GetGUID(*iter);
269 const TemplateURL* service_turl = model()->GetTemplateURLForGUID(guid);
270 const TemplateURL* deserialized =
271 TemplateURLService::CreateTemplateURLFromSyncData(*iter);
272 AssertEquals(*service_turl, *deserialized);
273 }
274 }
275
276 TEST_F(TemplateURLServiceSyncTest, UniquifyKeyword) {
277 model()->Add(CreateTestTemplateURL("key1", "http://key1.com"));
278 // Create a key that conflicts with something in the model.
279 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL("key1",
280 "http://new.com",
281 "xyz"));
282 string16 new_keyword = model()->UniquifyKeyword(*turl);
283
284 EXPECT_EQ(UTF8ToUTF16("new.com"), new_keyword);
285 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
286
287 turl->set_keyword(new_keyword);
288 model()->Add(turl.release());
289 // Test a second collision. This time it should be resolved by actually
290 // modifying the original keyword, since the autogenerated keyword is already
291 // used.
292 turl.reset(CreateTestTemplateURL("key1", "http://new.com"));
293 new_keyword = model()->UniquifyKeyword(*turl);
294
295 EXPECT_EQ(UTF8ToUTF16("key1_"), new_keyword);
296 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
297
298 turl->set_keyword(new_keyword);
299 model()->Add(turl.release());
300 // Test a third collision. This should collide on both the autogenerated
301 // keyword and the first uniquification attempt.
302 turl.reset(CreateTestTemplateURL("key1", "http://new.com"));
303 new_keyword = model()->UniquifyKeyword(*turl);
304
305 EXPECT_EQ(UTF8ToUTF16("key1__"), new_keyword);
306 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
307 }
308
309 TEST_F(TemplateURLServiceSyncTest, ResolveSyncKeywordConflict) {
310 TemplateURL* original_turl =
311 CreateTestTemplateURL("key1", "http://key1.com", "", 9000);
312 string16 original_turl_keyword = original_turl->keyword();
313 model()->Add(original_turl);
314
315 // Create a key that does not conflict with something in the model.
316 scoped_ptr<TemplateURL> sync_turl(CreateTestTemplateURL("unique",
317 "http://new.com"));
318 string16 sync_keyword = sync_turl->keyword();
319 SyncChangeList changes;
320
321 // No conflict, no TURLs changed, no changes.
322 EXPECT_FALSE(model()->ResolveSyncKeywordConflict(sync_turl.get(), changes));
323 EXPECT_EQ(original_turl_keyword, original_turl->keyword());
324 EXPECT_EQ(sync_keyword, sync_turl->keyword());
325 EXPECT_EQ(0U, changes.size());
326
327 // Change sync keyword to something that conflicts, and make it older.
328 // Conflict, sync keyword is uniquified, and a SyncChange is added.
329 sync_turl->set_keyword(original_turl->keyword());
330 sync_turl->set_last_modified(Time::FromTimeT(8999));
331 EXPECT_TRUE(model()->ResolveSyncKeywordConflict(sync_turl.get(), changes));
332 EXPECT_NE(sync_keyword, sync_turl->keyword());
333 EXPECT_EQ(original_turl_keyword, original_turl->keyword());
334 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(sync_turl->keyword()));
335 EXPECT_EQ(1U, changes.size());
336 changes.clear();
337
338 // Sync is newer. Original TemplateURL keyword is uniquified, no SyncChange
339 // is added.
340 sync_turl->set_keyword(original_turl->keyword());
341 sync_keyword = sync_turl->keyword();
342 sync_turl->set_last_modified(Time::FromTimeT(9001));
343 EXPECT_TRUE(model()->ResolveSyncKeywordConflict(sync_turl.get(), changes));
344 EXPECT_EQ(sync_keyword, sync_turl->keyword());
345 EXPECT_NE(original_turl_keyword, original_turl->keyword());
346 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(sync_turl->keyword()));
347 EXPECT_EQ(0U, changes.size());
348
349 // Equal times. Same result as above. Sync left alone, original uniquified so
350 // sync_turl can fit.
351 sync_turl->set_keyword(original_turl->keyword());
352 sync_keyword = sync_turl->keyword();
353 // Note that we have to reset original_turl's last_modified time as it was
354 // modified above.
355 original_turl->set_last_modified(Time::FromTimeT(9000));
356 sync_turl->set_last_modified(Time::FromTimeT(9000));
357 EXPECT_TRUE(model()->ResolveSyncKeywordConflict(sync_turl.get(), changes));
358 EXPECT_EQ(sync_keyword, sync_turl->keyword());
359 EXPECT_NE(original_turl_keyword, original_turl->keyword());
360 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(sync_turl->keyword()));
361 EXPECT_EQ(0U, changes.size());
362 }
363
364 TEST_F(TemplateURLServiceSyncTest, FindDuplicateOfSyncTemplateURL) {
365 TemplateURL* original_turl =
366 CreateTestTemplateURL("key1", "http://key1.com");
367 model()->Add(original_turl);
368
369 // No matches at all.
370 scoped_ptr<TemplateURL> sync_turl(
371 CreateTestTemplateURL("key2", "http://key2.com"));
372 EXPECT_EQ(NULL, model()->FindDuplicateOfSyncTemplateURL(*sync_turl));
373
374 // URL matches, but not keyword. No dupe.
375 sync_turl->SetURL("http://key1.com", 0 , 0);
376 EXPECT_EQ(NULL, model()->FindDuplicateOfSyncTemplateURL(*sync_turl));
377
378 // Keyword matches, but not URL. No dupe.
379 sync_turl->SetURL("http://key2.com", 0 , 0);
380 sync_turl->set_keyword(UTF8ToUTF16("key1"));
381 EXPECT_EQ(NULL, model()->FindDuplicateOfSyncTemplateURL(*sync_turl));
382
383 // Duplicate.
384 sync_turl->SetURL("http://key1.com", 0 , 0);
385 const TemplateURL* dupe_turl =
386 model()->FindDuplicateOfSyncTemplateURL(*sync_turl);
387 ASSERT_TRUE(dupe_turl);
388 EXPECT_EQ(dupe_turl->keyword(), sync_turl->keyword());
389 EXPECT_EQ(dupe_turl->url()->url(), sync_turl->url()->url());
390 }
391
392 TEST_F(TemplateURLServiceSyncTest, MergeSyncAndLocalURLDuplicates) {
393 TemplateURL* original_turl =
394 CreateTestTemplateURL("key1", "http://key1.com", "", 9000);
395 model()->Add(original_turl);
396 TemplateURL* sync_turl =
397 CreateTestTemplateURL("key1", "http://key1.com", "", 9001);
398 SyncChangeList changes;
399
400 // The sync TemplateURL is newer. It should replace the original TemplateURL.
401 model()->MergeSyncAndLocalURLDuplicates(sync_turl, original_turl, changes);
402 EXPECT_EQ(sync_turl, model()->GetTemplateURLForKeyword(sync_turl->keyword()));
403 EXPECT_EQ(0U, changes.size());
404
405 // The sync TemplateURL is older. The existing TemplateURL should win and a
406 // SyncChange should be added to the list.
407 TemplateURL* sync_turl2 =
408 CreateTestTemplateURL("key1", "http://key1.com", "", 8999);
409 model()->MergeSyncAndLocalURLDuplicates(sync_turl2, sync_turl, changes);
410 EXPECT_EQ(sync_turl,
411 model()->GetTemplateURLForKeyword(sync_turl2->keyword()));
412 EXPECT_EQ(1U, changes.size());
413 }
414
415 TEST_F(TemplateURLServiceSyncTest, StartSyncEmpty) {
416 model()->MergeDataAndStartSyncing(
417 syncable::SEARCH_ENGINES,
418 SyncDataList(), // Empty.
419 processor());
420
421 EXPECT_EQ(0U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
422 EXPECT_EQ(0, processor()->change_list_size());
423 }
424
425 TEST_F(TemplateURLServiceSyncTest, MergeIntoEmpty) {
426 SyncDataList initial_data = CreateInitialSyncData();
427
428 model()->MergeDataAndStartSyncing(
429 syncable::SEARCH_ENGINES,
430 initial_data,
431 processor());
432
433 EXPECT_EQ(3U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
434 // We expect the model to have accepted all of the initial sync data. Search
435 // through the model using the GUIDs to ensure that they're present.
436 for (SyncDataList::const_iterator iter = initial_data.begin();
437 iter != initial_data.end(); ++iter) {
438 std::string guid = GetGUID(*iter);
439 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
440 }
441
442 EXPECT_EQ(0, processor()->change_list_size());
443 }
444
445 TEST_F(TemplateURLServiceSyncTest, MergeInAllNewData) {
446 model()->Add(CreateTestTemplateURL(
447 "google.com", "http://google.com", "abc"));
448 model()->Add(CreateTestTemplateURL("yahoo.com", "http://yahoo.com", "def"));
449 model()->Add(CreateTestTemplateURL("bing.com", "http://bing.com", "xyz"));
450 SyncDataList initial_data = CreateInitialSyncData();
451
452 model()->MergeDataAndStartSyncing(
453 syncable::SEARCH_ENGINES,
454 initial_data,
455 processor());
456
457 EXPECT_EQ(6U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
458 // We expect the model to have accepted all of the initial sync data. Search
459 // through the model using the GUIDs to ensure that they're present.
460 for (SyncDataList::const_iterator iter = initial_data.begin();
461 iter != initial_data.end(); ++iter) {
462 std::string guid = GetGUID(*iter);
463 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
464 }
465 // All the original TemplateURLs should also remain in the model.
466 EXPECT_TRUE(model()->GetTemplateURLForKeyword(UTF8ToUTF16("google.com")));
467 EXPECT_TRUE(model()->GetTemplateURLForKeyword(UTF8ToUTF16("yahoo.com")));
468 EXPECT_TRUE(model()->GetTemplateURLForKeyword(UTF8ToUTF16("bing.com")));
469 // Ensure that Sync received the expected changes.
470 EXPECT_EQ(3, processor()->change_list_size());
471 EXPECT_TRUE(processor()->ContainsGUID("abc"));
472 EXPECT_TRUE(processor()->ContainsGUID("def"));
473 EXPECT_TRUE(processor()->ContainsGUID("xyz"));
474 }
475
476 TEST_F(TemplateURLServiceSyncTest, MergeSyncIsTheSame) {
477 // The local data is the same as the sync data merged in. i.e. - There have
478 // been no changes since the last time we synced. Even the last_modified
479 // timestamps are the same.
480 SyncDataList initial_data = CreateInitialSyncData();
481 for (SyncDataList::const_iterator iter = initial_data.begin();
482 iter != initial_data.end(); ++iter) {
483 TemplateURL* converted =
484 TemplateURLService::CreateTemplateURLFromSyncData(*iter);
485 model()->Add(converted);
486 }
487
488 model()->MergeDataAndStartSyncing(
489 syncable::SEARCH_ENGINES,
490 initial_data,
491 processor());
492
493 EXPECT_EQ(3U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
494 for (SyncDataList::const_iterator iter = initial_data.begin();
495 iter != initial_data.end(); ++iter) {
496 std::string guid = GetGUID(*iter);
497 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
498 }
499 EXPECT_EQ(0, processor()->change_list_size());
500 }
501
502 TEST_F(TemplateURLServiceSyncTest, MergeUpdateFromSync) {
503 // The local data is the same as the sync data merged in, but timestamps have
504 // changed. Ensure the right fields are merged in.
505 SyncDataList initial_data;
506 TemplateURL* turl1 = CreateTestTemplateURL(
507 "google.com", "http://google.com", "abc", 9000);
508 model()->Add(turl1);
509 TemplateURL* turl2 = CreateTestTemplateURL(
510 "bing.com", "http://bing.com", "xyz", 9000);
511 model()->Add(turl2);
512
513 TemplateURL turl1_newer(*turl1);
514 turl1_newer.set_last_modified(Time::FromTimeT(9999));
515 turl1_newer.SetURL("http://google.ca", 0, 0);
516 initial_data.push_back(
517 TemplateURLService::CreateSyncDataFromTemplateURL(turl1_newer));
518
519 TemplateURL turl2_older(*turl2);
520 turl2_older.set_last_modified(Time::FromTimeT(8888));
521 turl2_older.SetURL("http://bing.ca", 0, 0);
522 initial_data.push_back(
523 TemplateURLService::CreateSyncDataFromTemplateURL(turl2_older));
524
525 model()->MergeDataAndStartSyncing(
526 syncable::SEARCH_ENGINES,
527 initial_data,
528 processor());
529
530 // Both were local updates, so we expect the same count.
531 EXPECT_EQ(2U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
532
533 // Check that the first replaced the initial Google TemplateURL.
534 EXPECT_EQ(turl1, model()->GetTemplateURLForGUID("abc"));
535 EXPECT_EQ("http://google.ca", turl1->url()->url());
536
537 // Check that the second produced an upstream update to the Bing TemplateURL.
538 EXPECT_EQ(1, processor()->change_list_size());
539 ASSERT_TRUE(processor()->ContainsGUID("xyz"));
540 SyncChange change = processor()->GetChangeByGUID("xyz");
541 EXPECT_TRUE(change.change_type() == SyncChange::ACTION_UPDATE);
542 EXPECT_EQ("http://bing.com", GetURL(change.sync_data()));
543 }
544
545 TEST_F(TemplateURLServiceSyncTest, MergeAddFromOlderSyncData) {
546 // GUIDs all differ, so this is data to be added from Sync, but the timestamps
547 // from Sync are older. Set up the local data so that one is a dupe, one has a
548 // conflicting keyword, and the last has no conflicts (a clean ADD).
549 SyncDataList initial_data = CreateInitialSyncData();
550 TemplateURL* turl = TemplateURLService::CreateTemplateURLFromSyncData(
551 initial_data.at(0));
552 ASSERT_TRUE(turl);
553 turl->set_sync_guid("aaa");
554 turl->set_last_modified(Time::FromTimeT(100));
555 model()->Add(turl); // dupe
556
557 turl = TemplateURLService::CreateTemplateURLFromSyncData(
558 initial_data.at(1));
559 ASSERT_TRUE(turl);
560 turl->SetURL("http://expected.com", 0, 0);
561 turl->set_sync_guid("bbb");
562 turl->set_last_modified(Time::FromTimeT(100));
563 model()->Add(turl); // keyword conflict
564
565 model()->Add(CreateTestTemplateURL(
566 "unique", "http://unique.com", "ccc")); // add
567
568 model()->MergeDataAndStartSyncing(
569 syncable::SEARCH_ENGINES,
570 initial_data,
571 processor());
572
573 // The dupe results in a merge. The other two should be added to the model.
574 EXPECT_EQ(5U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
575
576 // The key1 duplicate results in the local copy winning. Ensure that Sync's
577 // copy was not added, and the local copy is pushed upstream to Sync as an
578 // update. The local copy should have received the sync data's GUID.
579 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
580 // Check changes for the UPDATE.
581 ASSERT_TRUE(processor()->ContainsGUID("key1"));
582 SyncChange key1_change = processor()->GetChangeByGUID("key1");
583 EXPECT_EQ(SyncChange::ACTION_UPDATE, key1_change.change_type());
584 EXPECT_FALSE(model()->GetTemplateURLForGUID("aaa"));
585
586 // The key2 keyword conflict results in the local copy winning, so ensure it
587 // retains the original keyword, and that an update to the sync copy is pushed
588 // upstream to Sync. Both TemplateURLs should be found locally, however.
589 const TemplateURL* key2 = model()->GetTemplateURLForGUID("bbb");
590 EXPECT_TRUE(key2);
591 EXPECT_EQ(UTF8ToUTF16("key2"), key2->keyword());
592 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
593 // Check changes for the UPDATE.
594 ASSERT_TRUE(processor()->ContainsGUID("key2"));
595 SyncChange key2_change = processor()->GetChangeByGUID("key2");
596 EXPECT_EQ(SyncChange::ACTION_UPDATE, key2_change.change_type());
597 EXPECT_EQ("key2.com", GetKeyword(key2_change.sync_data()));
598
599 // The last TemplateURL should have had no conflicts and was just added. It
600 // should not have replaced the third local TemplateURL.
601 EXPECT_TRUE(model()->GetTemplateURLForGUID("ccc"));
602 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
603
604 // Two UPDATEs and two ADDs.
605 EXPECT_EQ(4, processor()->change_list_size());
606 // Two ADDs should be pushed up to Sync.
607 ASSERT_TRUE(processor()->ContainsGUID("bbb"));
608 EXPECT_EQ(SyncChange::ACTION_ADD,
609 processor()->GetChangeByGUID("bbb").change_type());
610 ASSERT_TRUE(processor()->ContainsGUID("ccc"));
611 EXPECT_EQ(SyncChange::ACTION_ADD,
612 processor()->GetChangeByGUID("ccc").change_type());
613 }
614
615 TEST_F(TemplateURLServiceSyncTest, MergeAddFromNewerSyncData) {
616 // GUIDs all differ, so this is data to be added from Sync, but the timestamps
617 // from Sync are newer. Set up the local data so that one is a dupe, one has a
618 // conflicting keyword, and the last has no conflicts (a clean ADD).
619 SyncDataList initial_data = CreateInitialSyncData();
620 TemplateURL* turl = TemplateURLService::CreateTemplateURLFromSyncData(
621 initial_data.at(0));
622 ASSERT_TRUE(turl);
623 turl->set_sync_guid("aaa");
624 turl->set_last_modified(Time::FromTimeT(10));
625 model()->Add(turl); // dupe
626
627 turl = TemplateURLService::CreateTemplateURLFromSyncData(
628 initial_data.at(1));
629 ASSERT_TRUE(turl);
630 turl->SetURL("http://expected.com", 0, 0);
631 turl->set_sync_guid("bbb");
632 turl->set_last_modified(Time::FromTimeT(10));
633 model()->Add(turl); // keyword conflict
634
635 model()->Add(CreateTestTemplateURL(
636 "unique", "http://unique.com", "ccc", 10)); // add
637
638 model()->MergeDataAndStartSyncing(
639 syncable::SEARCH_ENGINES,
640 initial_data,
641 processor());
642
643 // The dupe results in a merge. The other two should be added to the model.
644 EXPECT_EQ(5U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
645
646 // The key1 duplicate results in Sync's copy winning. Ensure that Sync's
647 // copy replaced the local copy.
648 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
649 EXPECT_FALSE(model()->GetTemplateURLForGUID("aaa"));
650
651 // The key2 keyword conflict results in Sync's copy winning, so ensure it
652 // retains the original keyword. The local copy should get a uniquified
653 // keyword. Both TemplateURLs should be found locally.
654 const TemplateURL* key2_sync = model()->GetTemplateURLForGUID("key2");
655 EXPECT_TRUE(key2_sync);
656 EXPECT_EQ(UTF8ToUTF16("key2"), key2_sync->keyword());
657 const TemplateURL* key2_local = model()->GetTemplateURLForGUID("bbb");
658 EXPECT_TRUE(key2_local);
659 EXPECT_EQ(UTF8ToUTF16("expected.com"), key2_local->keyword());
660
661 // The last TemplateURL should have had no conflicts and was just added. It
662 // should not have replaced the third local TemplateURL.
663 EXPECT_TRUE(model()->GetTemplateURLForGUID("ccc"));
664 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
665
666 // Two ADDs.
667 EXPECT_EQ(2, processor()->change_list_size());
668 // Two ADDs should be pushed up to Sync.
669 ASSERT_TRUE(processor()->ContainsGUID("bbb"));
670 EXPECT_EQ(SyncChange::ACTION_ADD,
671 processor()->GetChangeByGUID("bbb").change_type());
672 ASSERT_TRUE(processor()->ContainsGUID("ccc"));
673 EXPECT_EQ(SyncChange::ACTION_ADD,
674 processor()->GetChangeByGUID("ccc").change_type());
675 }
676
677 TEST_F(TemplateURLServiceSyncTest, ProcessChangesEmptyModel) {
678 // We initially have no data.
679 model()->MergeDataAndStartSyncing(
680 syncable::SEARCH_ENGINES,
681 SyncDataList(),
682 processor());
683
684 // Set up a bunch of ADDs.
685 SyncChangeList changes;
686 changes.push_back(CreateTestSyncChange(
687 SyncChange::ACTION_ADD,
688 CreateTestTemplateURL("key1", "http://key1.com", "key1")));
689 changes.push_back(CreateTestSyncChange(
690 SyncChange::ACTION_ADD,
691 CreateTestTemplateURL("key2", "http://key2.com", "key2")));
692 changes.push_back(CreateTestSyncChange(
693 SyncChange::ACTION_ADD,
694 CreateTestTemplateURL("key3", "http://key3.com", "key3")));
695
696 model()->ProcessSyncChanges(FROM_HERE, changes);
697
698 EXPECT_EQ(3U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
699 EXPECT_EQ(0, processor()->change_list_size());
700 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
701 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
702 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
703 }
704
705 TEST_F(TemplateURLServiceSyncTest, ProcessChangesNoConflicts) {
706 model()->MergeDataAndStartSyncing(
707 syncable::SEARCH_ENGINES,
708 CreateInitialSyncData(),
709 processor());
710
711 // Process different types of changes, without conflicts.
712 SyncChangeList changes;
713 changes.push_back(CreateTestSyncChange(
714 SyncChange::ACTION_ADD,
715 CreateTestTemplateURL("key4", "http://key4.com", "key4")));
716 changes.push_back(CreateTestSyncChange(
717 SyncChange::ACTION_UPDATE,
718 CreateTestTemplateURL("newkeyword", "http://new.com", "key2")));
719 changes.push_back(CreateTestSyncChange(
720 SyncChange::ACTION_DELETE,
721 CreateTestTemplateURL("key3", "http://key3.com", "key3")));
722
723 model()->ProcessSyncChanges(FROM_HERE, changes);
724
725 // Add one, remove one, update one, so the number shouldn't change.
726 EXPECT_EQ(3U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
727 EXPECT_EQ(0, processor()->change_list_size());
728 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
729 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
730 const TemplateURL* turl = model()->GetTemplateURLForGUID("key2");
731 EXPECT_TRUE(turl);
732 EXPECT_EQ(UTF8ToUTF16("newkeyword"), turl->keyword());
733 EXPECT_EQ("http://new.com", turl->url()->url());
734 EXPECT_FALSE(model()->GetTemplateURLForGUID("key3"));
735 EXPECT_TRUE(model()->GetTemplateURLForGUID("key4"));
736 }
737
738 TEST_F(TemplateURLServiceSyncTest, ProcessChangesWithConflictsSyncWins) {
739 model()->MergeDataAndStartSyncing(
740 syncable::SEARCH_ENGINES,
741 CreateInitialSyncData(),
742 processor());
743
744 // Process different types of changes, with conflicts. Note that all this data
745 // has a newer timestamp, so Sync will win in these scenarios.
746 SyncChangeList changes;
747 changes.push_back(CreateTestSyncChange(
748 SyncChange::ACTION_ADD,
749 CreateTestTemplateURL("key2", "http://new.com", "aaa")));
750 changes.push_back(CreateTestSyncChange(
751 SyncChange::ACTION_UPDATE,
752 CreateTestTemplateURL("key3", "http://key3.com", "key1")));
753
754 model()->ProcessSyncChanges(FROM_HERE, changes);
755
756 // Add one, update one, so we're up to 4.
757 EXPECT_EQ(4U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
758 // Sync is always newer here, so it should always win (i.e. - local changes,
759 // nothing pushed to Sync).
760 EXPECT_EQ(0, processor()->change_list_size());
761
762 // aaa conflicts with key2 and wins, forcing key2's keyword to update.
763 EXPECT_TRUE(model()->GetTemplateURLForGUID("aaa"));
764 EXPECT_EQ(model()->GetTemplateURLForGUID("aaa"),
765 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key2")));
766 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
767 EXPECT_EQ(model()->GetTemplateURLForGUID("key2"),
768 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key2.com")));
769 // key1 update conflicts with key3 and wins, forcing key3's keyword to update.
770 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
771 EXPECT_EQ(model()->GetTemplateURLForGUID("key1"),
772 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key3")));
773 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
774 EXPECT_EQ(model()->GetTemplateURLForGUID("key3"),
775 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key3.com")));
776 }
777
778 TEST_F(TemplateURLServiceSyncTest, ProcessChangesWithConflictsLocalWins) {
779 model()->MergeDataAndStartSyncing(
780 syncable::SEARCH_ENGINES,
781 CreateInitialSyncData(),
782 processor());
783
784 // Process different types of changes, with conflicts. Note that all this data
785 // has an older timestamp, so the local data will win in these scenarios.
786 SyncChangeList changes;
787 changes.push_back(CreateTestSyncChange(
788 SyncChange::ACTION_ADD,
789 CreateTestTemplateURL("key2", "http://new.com", "aaa", 10)));
790 changes.push_back(CreateTestSyncChange(
791 SyncChange::ACTION_UPDATE,
792 CreateTestTemplateURL("key3", "http://key3.com", "key1", 10)));
793
794 model()->ProcessSyncChanges(FROM_HERE, changes);
795
796 // Add one, update one, so we're up to 4.
797 EXPECT_EQ(4U, model()->GetAllSyncData(syncable::SEARCH_ENGINES).size());
798 // Local data wins twice so two updates are pushed up to Sync.
799 EXPECT_EQ(2, processor()->change_list_size());
800
801 // aaa conflicts with key2 and loses, forcing it's keyword to update.
802 EXPECT_TRUE(model()->GetTemplateURLForGUID("aaa"));
803 EXPECT_EQ(model()->GetTemplateURLForGUID("aaa"),
804 model()->GetTemplateURLForKeyword(UTF8ToUTF16("new.com")));
805 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
806 EXPECT_EQ(model()->GetTemplateURLForGUID("key2"),
807 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key2")));
808 // key1 update conflicts with key3 and loses, forcing key1's keyword to
809 // update.
810 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
811 EXPECT_EQ(model()->GetTemplateURLForGUID("key1"),
812 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key3.com")));
813 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
814 EXPECT_EQ(model()->GetTemplateURLForGUID("key3"),
815 model()->GetTemplateURLForKeyword(UTF8ToUTF16("key3")));
816
817 ASSERT_TRUE(processor()->ContainsGUID("aaa"));
818 EXPECT_EQ(SyncChange::ACTION_UPDATE,
819 processor()->GetChangeByGUID("aaa").change_type());
820 ASSERT_TRUE(processor()->ContainsGUID("key1"));
821 EXPECT_EQ(SyncChange::ACTION_UPDATE,
822 processor()->GetChangeByGUID("key1").change_type());
823 }
824
825 TEST_F(TemplateURLServiceSyncTest, ProcessTemplateURLChange) {
826 // Ensure that ProcessTemplateURLChange is called and pushes the correct
827 // changes to Sync whenever local changes are made to TemplateURLs.
828 model()->MergeDataAndStartSyncing(
829 syncable::SEARCH_ENGINES,
830 CreateInitialSyncData(),
831 processor());
832
833 // Add a new search engine.
834 TemplateURL* new_turl =
835 CreateTestTemplateURL("baidu", "http://baidu.cn", "new");
836 model()->Add(new_turl);
837 EXPECT_EQ(1, processor()->change_list_size());
838 ASSERT_TRUE(processor()->ContainsGUID("new"));
839 SyncChange change = processor()->GetChangeByGUID("new");
840 EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type());
841 EXPECT_EQ("baidu", GetKeyword(change.sync_data()));
842 EXPECT_EQ("http://baidu.cn", GetURL(change.sync_data()));
843
844 // Change a keyword.
845 const TemplateURL* existing_turl = model()->GetTemplateURLForGUID("key1");
846 model()->ResetTemplateURL(existing_turl,
847 existing_turl->short_name(),
848 UTF8ToUTF16("k"),
849 existing_turl->url()->url());
850 EXPECT_EQ(1, processor()->change_list_size());
851 ASSERT_TRUE(processor()->ContainsGUID("key1"));
852 change = processor()->GetChangeByGUID("key1");
853 EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
854 EXPECT_EQ("k", GetKeyword(change.sync_data()));
855
856 // Remove an existing search engine.
857 existing_turl = model()->GetTemplateURLForGUID("key2");
858 model()->Remove(existing_turl);
859 EXPECT_EQ(1, processor()->change_list_size());
860 ASSERT_TRUE(processor()->ContainsGUID("key2"));
861 change = processor()->GetChangeByGUID("key2");
862 EXPECT_EQ(SyncChange::ACTION_DELETE, change.change_type());
863 }
864
865 TEST_F(TemplateURLServiceSyncTest, MergeTwoClientsBasic) {
866 // Start off B with some empty data.
867 model_b()->MergeDataAndStartSyncing(
868 syncable::SEARCH_ENGINES,
869 CreateInitialSyncData(),
870 processor());
871
872 // Merge A and B. All of B's data should transfer over to A, which initially
873 // has no data.
874 model_a()->MergeDataAndStartSyncing(
875 syncable::SEARCH_ENGINES,
876 model_b()->GetAllSyncData(syncable::SEARCH_ENGINES),
877 model_b());
878
879 // They should be consistent.
880 AssertEquals(model_a()->GetAllSyncData(syncable::SEARCH_ENGINES),
881 model_b()->GetAllSyncData(syncable::SEARCH_ENGINES));
882 }
883
884 TEST_F(TemplateURLServiceSyncTest, MergeTwoClientsDupesAndConflicts) {
885 // Start off B with some empty data.
886 model_b()->MergeDataAndStartSyncing(
887 syncable::SEARCH_ENGINES,
888 CreateInitialSyncData(),
889 processor());
890
891 // Set up A so we have some interesting duplicates and conflicts.
892 model_a()->Add(CreateTestTemplateURL(
893 "key4", "http://key4.com", "key4")); // Added
894 model_a()->Add(CreateTestTemplateURL(
895 "key2", "http://key2.com", "key2")); // Merge - Copy of key2.
896 model_a()->Add(CreateTestTemplateURL(
897 "key3", "http://key3.com", "key5", 10)); // Merge - Dupe of key3.
898 model_a()->Add(CreateTestTemplateURL(
899 "key1", "http://key6.com", "key6", 10)); // Keyword conflict with key1
900
901 // Merge A and B.
902 model_a()->MergeDataAndStartSyncing(
903 syncable::SEARCH_ENGINES,
904 model_b()->GetAllSyncData(syncable::SEARCH_ENGINES),
905 model_b());
906
907 // They should be consistent.
908 AssertEquals(model_a()->GetAllSyncData(syncable::SEARCH_ENGINES),
909 model_b()->GetAllSyncData(syncable::SEARCH_ENGINES));
910 }
911
912 TEST_F(TemplateURLServiceSyncTest, StopSyncing) {
913 SyncError error = model()->MergeDataAndStartSyncing(
914 syncable::SEARCH_ENGINES,
915 CreateInitialSyncData(),
916 processor());
917 ASSERT_FALSE(error.IsSet());
918 model()->StopSyncing(syncable::SEARCH_ENGINES);
919
920 SyncChangeList changes;
921 changes.push_back(CreateTestSyncChange(
922 SyncChange::ACTION_UPDATE,
923 CreateTestTemplateURL("newkeyword", "http://new.com", "key2")));
924 error = model()->ProcessSyncChanges(FROM_HERE, changes);
925 EXPECT_TRUE(error.IsSet());
926
927 // Ensure that the sync changes were not accepted.
928 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
929 EXPECT_FALSE(model()->GetTemplateURLForKeyword(UTF8ToUTF16("newkeyword")));
930 }
931
932 TEST_F(TemplateURLServiceSyncTest, SyncErrorOnInitialSync) {
933 processor()->set_erroneous(true);
934 SyncError error = model()->MergeDataAndStartSyncing(
935 syncable::SEARCH_ENGINES,
936 CreateInitialSyncData(),
937 processor());
938 EXPECT_TRUE(error.IsSet());
939
940 SyncChangeList changes;
941 changes.push_back(CreateTestSyncChange(
942 SyncChange::ACTION_UPDATE,
943 CreateTestTemplateURL("newkeyword", "http://new.com", "key2")));
944 processor()->set_erroneous(false);
Nicolas Zea 2011/08/15 23:40:00 This is a bit confusing as to why it would trigger
SteveT 2011/08/16 00:08:59 Ah, sorry. I should have put a comment here to exp
945 error = model()->ProcessSyncChanges(FROM_HERE, changes);
946 EXPECT_TRUE(error.IsSet());
947
948 // Ensure that the sync changes were not accepted.
949 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
950 EXPECT_FALSE(model()->GetTemplateURLForKeyword(UTF8ToUTF16("newkeyword")));
951 }
952
953 TEST_F(TemplateURLServiceSyncTest, SyncErrorOnLaterSync) {
954 SyncError error = model()->MergeDataAndStartSyncing(
955 syncable::SEARCH_ENGINES,
956 CreateInitialSyncData(),
957 processor());
958 ASSERT_FALSE(error.IsSet());
959
960 SyncChangeList changes;
961 changes.push_back(CreateTestSyncChange(
962 SyncChange::ACTION_UPDATE,
963 CreateTestTemplateURL("newkeyword", "http://new.com", "key2")));
964 processor()->set_erroneous(true);
965 error = model()->ProcessSyncChanges(FROM_HERE, changes);
966 EXPECT_TRUE(error.IsSet());
967 }
OLDNEW
« no previous file with comments | « chrome/browser/search_engines/template_url_service.cc ('k') | chrome/browser/search_engines/template_url_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698