1 // Copyright (c) 2012 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.
5 #include "base/memory/scoped_ptr.h"
6 #include "base/memory/scoped_vector.h"
7 #include "base/run_loop.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/search_engines/search_terms_data.h"
13 #include "chrome/browser/search_engines/template_url.h"
14 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
15 #include "chrome/browser/search_engines/template_url_service.h"
16 #include "chrome/browser/search_engines/template_url_service_factory.h"
17 #include "chrome/browser/search_engines/template_url_service_test_util.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/common/url_constants.h"
20 #include "chrome/test/base/testing_pref_service_syncable.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "content/public/browser/notification_service.h"
23 #include "extensions/common/constants.h"
24 #include "net/base/net_util.h"
25 #include "sync/api/sync_error_factory.h"
26 #include "sync/api/sync_error_factory_mock.h"
27 #include "sync/protocol/search_engine_specifics.pb.h"
28 #include "sync/protocol/sync.pb.h"
29 #include "testing/gtest/include/gtest/gtest.h"
35 // Extract the GUID from a search engine syncer::SyncData.
36 std::string GetGUID(const syncer::SyncData& sync_data) {
37 return sync_data.GetSpecifics().search_engine().sync_guid();
40 // Extract the URL from a search engine syncer::SyncData.
41 std::string GetURL(const syncer::SyncData& sync_data) {
42 return sync_data.GetSpecifics().search_engine().url();
45 // Extract the keyword from a search engine syncer::SyncData.
46 std::string GetKeyword(const syncer::SyncData& sync_data) {
47 return sync_data.GetSpecifics().search_engine().keyword();
50 // Much like TemplateURLService::CreateSyncDataFromTemplateURL(), but allows the
51 // caller to override the keyword, URL, or GUID fields with empty strings, in
52 // order to create custom data that should be handled specially when synced to a
54 syncer::SyncData CreateCustomSyncData(const TemplateURL& turl,
55 bool autogenerate_keyword,
56 const std::string& url,
57 const std::string& sync_guid) {
58 sync_pb::EntitySpecifics specifics;
59 sync_pb::SearchEngineSpecifics* se_specifics =
60 specifics.mutable_search_engine();
61 se_specifics->set_short_name(UTF16ToUTF8(turl.short_name()));
62 se_specifics->set_keyword(
63 autogenerate_keyword ? std::string() : UTF16ToUTF8(turl.keyword()));
64 se_specifics->set_favicon_url(turl.favicon_url().spec());
65 se_specifics->set_url(url);
66 se_specifics->set_safe_for_autoreplace(turl.safe_for_autoreplace());
67 se_specifics->set_originating_url(turl.originating_url().spec());
68 se_specifics->set_date_created(turl.date_created().ToInternalValue());
69 se_specifics->set_input_encodings(JoinString(turl.input_encodings(), ';'));
70 se_specifics->set_show_in_default_list(turl.show_in_default_list());
71 se_specifics->set_suggestions_url(turl.suggestions_url());
72 se_specifics->set_prepopulate_id(turl.prepopulate_id());
73 se_specifics->set_autogenerate_keyword(autogenerate_keyword);
74 se_specifics->set_instant_url(turl.instant_url());
75 se_specifics->set_last_modified(turl.last_modified().ToInternalValue());
76 se_specifics->set_sync_guid(sync_guid);
77 return syncer::SyncData::CreateLocalData(turl.sync_guid(), // Must be valid!
78 se_specifics->keyword(), specifics);
82 // TestChangeProcessor --------------------------------------------------------
84 // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
86 class TestChangeProcessor : public syncer::SyncChangeProcessor {
88 TestChangeProcessor();
89 virtual ~TestChangeProcessor();
91 // Store a copy of all the changes passed in so we can examine them later.
92 virtual syncer::SyncError ProcessSyncChanges(
93 const tracked_objects::Location& from_here,
94 const syncer::SyncChangeList& change_list) OVERRIDE;
96 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
98 return syncer::SyncDataList();
101 bool contains_guid(const std::string& guid) const {
102 return change_map_.count(guid) != 0;
105 syncer::SyncChange change_for_guid(const std::string& guid) const {
106 DCHECK(contains_guid(guid));
107 return change_map_.find(guid)->second;
110 size_t change_list_size() { return change_map_.size(); }
112 void set_erroneous(bool erroneous) { erroneous_ = erroneous; }
115 // Track the changes received in ProcessSyncChanges.
116 std::map<std::string, syncer::SyncChange> change_map_;
119 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
122 TestChangeProcessor::TestChangeProcessor() : erroneous_(false) {
125 TestChangeProcessor::~TestChangeProcessor() {
128 syncer::SyncError TestChangeProcessor::ProcessSyncChanges(
129 const tracked_objects::Location& from_here,
130 const syncer::SyncChangeList& change_list) {
132 return syncer::SyncError(
134 syncer::SyncError::DATATYPE_ERROR,
136 syncer::SEARCH_ENGINES);
138 change_map_.erase(change_map_.begin(), change_map_.end());
139 for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
140 iter != change_list.end(); ++iter)
141 change_map_[GetGUID(iter->sync_data())] = *iter;
142 return syncer::SyncError();
146 // SyncChangeProcessorDelegate ------------------------------------------------
148 class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor {
150 explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient);
151 virtual ~SyncChangeProcessorDelegate();
153 // syncer::SyncChangeProcessor implementation.
154 virtual syncer::SyncError ProcessSyncChanges(
155 const tracked_objects::Location& from_here,
156 const syncer::SyncChangeList& change_list) OVERRIDE;
158 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
160 return recipient_->GetAllSyncData(type);
164 // The recipient of all sync changes.
165 syncer::SyncChangeProcessor* recipient_;
167 DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate);
170 SyncChangeProcessorDelegate::SyncChangeProcessorDelegate(
171 syncer::SyncChangeProcessor* recipient)
172 : recipient_(recipient) {
176 SyncChangeProcessorDelegate::~SyncChangeProcessorDelegate() {
179 syncer::SyncError SyncChangeProcessorDelegate::ProcessSyncChanges(
180 const tracked_objects::Location& from_here,
181 const syncer::SyncChangeList& change_list) {
182 return recipient_->ProcessSyncChanges(from_here, change_list);
188 // TemplateURLServiceSyncTest -------------------------------------------------
190 class TemplateURLServiceSyncTest : public testing::Test {
192 typedef TemplateURLService::SyncDataMap SyncDataMap;
194 TemplateURLServiceSyncTest();
196 virtual void SetUp() OVERRIDE;
197 virtual void TearDown() OVERRIDE;
199 TemplateURLService* model() { return test_util_a_.model(); }
200 // For readability, we redefine an accessor for Model A for use in tests that
201 // involve syncing two models.
202 TemplateURLService* model_a() { return test_util_a_.model(); }
203 TemplateURLService* model_b() { return model_b_.get(); }
204 TestingProfile* profile_a() { return test_util_a_.profile(); }
205 TestChangeProcessor* processor() { return sync_processor_.get(); }
206 scoped_ptr<syncer::SyncChangeProcessor> PassProcessor();
207 scoped_ptr<syncer::SyncErrorFactory> CreateAndPassSyncErrorFactory();
209 // Create a TemplateURL with some test values. The caller owns the returned
211 TemplateURL* CreateTestTemplateURL(const string16& keyword,
212 const std::string& url,
213 const std::string& guid = std::string(),
214 time_t last_mod = 100,
215 bool safe_for_autoreplace = false,
216 bool created_by_policy = false) const;
218 // Verifies the two TemplateURLs are equal.
219 // TODO(stevet): Share this with TemplateURLServiceTest.
220 void AssertEquals(const TemplateURL& expected,
221 const TemplateURL& actual) const;
223 // Expect that two syncer::SyncDataLists have equal contents, in terms of the
224 // sync_guid, keyword, and url fields.
225 void AssertEquals(const syncer::SyncDataList& data1,
226 const syncer::SyncDataList& data2) const;
228 // Convenience helper for creating SyncChanges. Takes ownership of |turl|.
229 syncer::SyncChange CreateTestSyncChange(
230 syncer::SyncChange::SyncChangeType type,
231 TemplateURL* turl) const;
233 // Helper that creates some initial sync data. We cheat a little by specifying
234 // GUIDs for easy identification later. We also make the last_modified times
235 // slightly older than CreateTestTemplateURL's default, to test conflict
237 syncer::SyncDataList CreateInitialSyncData() const;
240 TemplateURL* Deserialize(const syncer::SyncData& sync_data);
242 // Creates a new TemplateURL copying the fields of |turl| but replacing
243 // the |url| and |guid| and initializing the date_created and last_modified
244 // timestamps to a default value of 100. The caller owns the returned
246 TemplateURL* CopyTemplateURL(const TemplateURL* turl,
247 const std::string& url,
248 const std::string& guid);
251 // We keep two TemplateURLServices to test syncing between them.
252 TemplateURLServiceTestUtil test_util_a_;
253 scoped_ptr<TestingProfile> profile_b_;
254 scoped_ptr<TemplateURLService> model_b_;
256 // Our dummy ChangeProcessor used to inspect changes pushed to Sync.
257 scoped_ptr<TestChangeProcessor> sync_processor_;
258 scoped_ptr<SyncChangeProcessorDelegate> sync_processor_delegate_;
260 DISALLOW_COPY_AND_ASSIGN(TemplateURLServiceSyncTest);
263 TemplateURLServiceSyncTest::TemplateURLServiceSyncTest()
264 : sync_processor_(new TestChangeProcessor),
265 sync_processor_delegate_(new SyncChangeProcessorDelegate(
266 sync_processor_.get())) {
269 void TemplateURLServiceSyncTest::SetUp() {
270 test_util_a_.SetUp();
271 // Use ChangeToLoadState() instead of VerifyLoad() so we don't actually pull
272 // in the prepopulate data, which the sync tests don't care about (and would
273 // just foul them up).
274 test_util_a_.ChangeModelToLoadState();
275 profile_b_.reset(new TestingProfile);
276 TemplateURLServiceFactory::GetInstance()->RegisterUserPrefsOnBrowserContext(
278 model_b_.reset(new TemplateURLService(profile_b_.get()));
282 void TemplateURLServiceSyncTest::TearDown() {
283 test_util_a_.TearDown();
286 scoped_ptr<syncer::SyncChangeProcessor>
287 TemplateURLServiceSyncTest::PassProcessor() {
288 return sync_processor_delegate_.PassAs<syncer::SyncChangeProcessor>();
291 scoped_ptr<syncer::SyncErrorFactory> TemplateURLServiceSyncTest::
292 CreateAndPassSyncErrorFactory() {
293 return scoped_ptr<syncer::SyncErrorFactory>(
294 new syncer::SyncErrorFactoryMock());
297 TemplateURL* TemplateURLServiceSyncTest::CreateTestTemplateURL(
298 const string16& keyword,
299 const std::string& url,
300 const std::string& guid,
302 bool safe_for_autoreplace,
303 bool created_by_policy) const {
304 TemplateURLData data;
305 data.short_name = ASCIIToUTF16("unittest");
306 data.SetKeyword(keyword);
308 data.favicon_url = GURL("http://favicon.url");
309 data.safe_for_autoreplace = safe_for_autoreplace;
310 data.date_created = Time::FromTimeT(100);
311 data.last_modified = Time::FromTimeT(last_mod);
312 data.created_by_policy = created_by_policy;
313 data.prepopulate_id = 999999;
315 data.sync_guid = guid;
316 return new TemplateURL(NULL, data);
319 void TemplateURLServiceSyncTest::AssertEquals(const TemplateURL& expected,
320 const TemplateURL& actual) const {
321 ASSERT_EQ(expected.short_name(), actual.short_name());
322 ASSERT_EQ(expected.keyword(), actual.keyword());
323 ASSERT_EQ(expected.url(), actual.url());
324 ASSERT_EQ(expected.suggestions_url(), actual.suggestions_url());
325 ASSERT_EQ(expected.favicon_url(), actual.favicon_url());
326 ASSERT_EQ(expected.show_in_default_list(), actual.show_in_default_list());
327 ASSERT_EQ(expected.safe_for_autoreplace(), actual.safe_for_autoreplace());
328 ASSERT_EQ(expected.input_encodings(), actual.input_encodings());
329 ASSERT_EQ(expected.date_created(), actual.date_created());
330 ASSERT_EQ(expected.last_modified(), actual.last_modified());
333 void TemplateURLServiceSyncTest::AssertEquals(
334 const syncer::SyncDataList& data1,
335 const syncer::SyncDataList& data2) const {
336 SyncDataMap map1 = TemplateURLService::CreateGUIDToSyncDataMap(data1);
337 SyncDataMap map2 = TemplateURLService::CreateGUIDToSyncDataMap(data2);
339 for (SyncDataMap::const_iterator iter1 = map1.begin();
340 iter1 != map1.end(); iter1++) {
341 SyncDataMap::iterator iter2 = map2.find(iter1->first);
342 if (iter2 != map2.end()) {
343 ASSERT_EQ(GetKeyword(iter1->second), GetKeyword(iter2->second));
344 ASSERT_EQ(GetURL(iter1->second), GetURL(iter2->second));
348 EXPECT_EQ(0U, map2.size());
351 syncer::SyncChange TemplateURLServiceSyncTest::CreateTestSyncChange(
352 syncer::SyncChange::SyncChangeType type,
353 TemplateURL* turl) const {
354 // We take control of the TemplateURL so make sure it's cleaned up after
355 // we create data out of it.
356 scoped_ptr<TemplateURL> scoped_turl(turl);
357 return syncer::SyncChange(
360 TemplateURLService::CreateSyncDataFromTemplateURL(*scoped_turl));
363 syncer::SyncDataList TemplateURLServiceSyncTest::CreateInitialSyncData() const {
364 syncer::SyncDataList list;
366 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key1"),
367 "http://key1.com", "key1", 90));
368 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
369 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com",
371 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
372 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com",
374 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
379 TemplateURL* TemplateURLServiceSyncTest::Deserialize(
380 const syncer::SyncData& sync_data) {
381 syncer::SyncChangeList dummy;
382 return TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData(NULL,
383 NULL, sync_data, &dummy);
386 TemplateURL* TemplateURLServiceSyncTest::CopyTemplateURL(
387 const TemplateURL* turl,
388 const std::string& url,
389 const std::string& guid) {
390 TemplateURLData data (turl->data());
392 data.date_created = Time::FromTimeT(100);
393 data.last_modified = Time::FromTimeT(100);
394 data.sync_guid = guid;
395 return new TemplateURL(NULL, data);
398 // Actual tests ---------------------------------------------------------------
400 TEST_F(TemplateURLServiceSyncTest, SerializeDeserialize) {
401 // Create a TemplateURL and convert it into a sync specific type.
402 scoped_ptr<TemplateURL> turl(
403 CreateTestTemplateURL(
404 ASCIIToUTF16("unittest"), "http://www.unittest.com/"));
405 syncer::SyncData sync_data =
406 TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
407 // Convert the specifics back to a TemplateURL.
408 scoped_ptr<TemplateURL> deserialized(Deserialize(sync_data));
409 EXPECT_TRUE(deserialized.get());
410 // Ensure that the original and the deserialized TURLs are equal in values.
411 AssertEquals(*turl, *deserialized);
414 TEST_F(TemplateURLServiceSyncTest, GetAllSyncDataBasic) {
415 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com"));
416 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com"));
417 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com"));
418 syncer::SyncDataList all_sync_data =
419 model()->GetAllSyncData(syncer::SEARCH_ENGINES);
421 EXPECT_EQ(3U, all_sync_data.size());
423 for (syncer::SyncDataList::const_iterator iter = all_sync_data.begin();
424 iter != all_sync_data.end(); ++iter) {
425 std::string guid = GetGUID(*iter);
426 const TemplateURL* service_turl = model()->GetTemplateURLForGUID(guid);
427 scoped_ptr<TemplateURL> deserialized(Deserialize(*iter));
428 AssertEquals(*service_turl, *deserialized);
432 TEST_F(TemplateURLServiceSyncTest, GetAllSyncDataWithExtension) {
433 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com"));
434 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com"));
435 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key3"),
436 std::string(extensions::kExtensionScheme) + "://blahblahblah"));
437 syncer::SyncDataList all_sync_data =
438 model()->GetAllSyncData(syncer::SEARCH_ENGINES);
440 EXPECT_EQ(3U, all_sync_data.size());
442 for (syncer::SyncDataList::const_iterator iter = all_sync_data.begin();
443 iter != all_sync_data.end(); ++iter) {
444 std::string guid = GetGUID(*iter);
445 const TemplateURL* service_turl = model()->GetTemplateURLForGUID(guid);
446 scoped_ptr<TemplateURL> deserialized(Deserialize(*iter));
447 AssertEquals(*service_turl, *deserialized);
451 TEST_F(TemplateURLServiceSyncTest, GetAllSyncDataNoManagedEngines) {
452 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com"));
453 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com"));
454 TemplateURL* managed_turl = CreateTestTemplateURL(ASCIIToUTF16("key3"),
455 "http://key3.com", std::string(), 100, false, true);
456 model()->Add(managed_turl);
457 syncer::SyncDataList all_sync_data =
458 model()->GetAllSyncData(syncer::SEARCH_ENGINES);
460 EXPECT_EQ(2U, all_sync_data.size());
462 for (syncer::SyncDataList::const_iterator iter = all_sync_data.begin();
463 iter != all_sync_data.end(); ++iter) {
464 std::string guid = GetGUID(*iter);
465 TemplateURL* service_turl = model()->GetTemplateURLForGUID(guid);
466 scoped_ptr<TemplateURL> deserialized(Deserialize(*iter));
467 ASSERT_FALSE(service_turl->created_by_policy());
468 AssertEquals(*service_turl, *deserialized);
472 TEST_F(TemplateURLServiceSyncTest, UniquifyKeyword) {
473 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com"));
474 // Create a key that conflicts with something in the model.
475 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key1"),
476 "http://new.com", "xyz"));
477 string16 new_keyword = model()->UniquifyKeyword(*turl, false);
478 EXPECT_EQ(ASCIIToUTF16("new.com"), new_keyword);
479 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
480 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("new.com"), "http://new.com",
483 // Test a second collision. This time it should be resolved by actually
484 // modifying the original keyword, since the autogenerated keyword is already
486 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://new.com"));
487 new_keyword = model()->UniquifyKeyword(*turl, false);
488 EXPECT_EQ(ASCIIToUTF16("key1_"), new_keyword);
489 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
490 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1_"), "http://new.com"));
492 // Test a third collision. This should collide on both the autogenerated
493 // keyword and the first uniquification attempt.
494 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://new.com"));
495 new_keyword = model()->UniquifyKeyword(*turl, false);
496 EXPECT_EQ(ASCIIToUTF16("key1__"), new_keyword);
497 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
499 // If we force the method, it should uniquify the keyword even if it is
500 // currently unique, and skip the host-based autogenerated keyword.
502 CreateTestTemplateURL(ASCIIToUTF16("unique"), "http://unique.com"));
503 new_keyword = model()->UniquifyKeyword(*turl, true);
504 EXPECT_EQ(ASCIIToUTF16("unique_"), new_keyword);
505 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(new_keyword));
508 TEST_F(TemplateURLServiceSyncTest, IsLocalTemplateURLBetter) {
509 // Test some edge cases of this function.
513 bool local_is_default;
514 bool local_created_by_policy;
515 bool expected_result;
517 // Sync is better by timestamp but local is Default.
518 {10, 100, true, false, true},
519 // Sync is better by timestamp but local is Create by Policy.
520 {10, 100, false, true, true},
522 {100, 100, false, false, false},
525 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
526 TemplateURL* local_turl = CreateTestTemplateURL(
527 ASCIIToUTF16("localkey"), "www.local.com", "localguid",
528 test_cases[i].local_time, true, test_cases[i].local_created_by_policy);
529 model()->Add(local_turl);
530 if (test_cases[i].local_is_default)
531 model()->SetDefaultSearchProvider(local_turl);
533 scoped_ptr<TemplateURL> sync_turl(CreateTestTemplateURL(
534 ASCIIToUTF16("synckey"), "www.sync.com", "syncguid",
535 test_cases[i].sync_time));
536 EXPECT_EQ(test_cases[i].expected_result,
537 model()->IsLocalTemplateURLBetter(local_turl, sync_turl.get()));
540 if (test_cases[i].local_is_default)
541 model()->SetDefaultSearchProvider(NULL);
542 model()->Remove(local_turl);
546 TEST_F(TemplateURLServiceSyncTest, ResolveSyncKeywordConflict) {
547 // This tests cases where neither the sync nor the local TemplateURL are
548 // marked safe_for_autoreplace.
550 // Create a keyword that conflicts, and make it older. Sync keyword is
551 // uniquified, and a syncer::SyncChange is added.
552 string16 original_turl_keyword = ASCIIToUTF16("key1");
553 TemplateURL* original_turl = CreateTestTemplateURL(original_turl_keyword,
554 "http://key1.com", std::string(), 9000);
555 model()->Add(original_turl);
556 scoped_ptr<TemplateURL> sync_turl(CreateTestTemplateURL(original_turl_keyword,
557 "http://new.com", "remote", 8999));
558 syncer::SyncChangeList changes;
559 model()->ResolveSyncKeywordConflict(sync_turl.get(), original_turl, &changes);
560 EXPECT_NE(original_turl_keyword, sync_turl->keyword());
561 EXPECT_EQ(original_turl_keyword, original_turl->keyword());
562 ASSERT_EQ(1U, changes.size());
563 EXPECT_EQ("remote", GetGUID(changes[0].sync_data()));
564 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
566 model()->Remove(original_turl);
568 // Sync is newer. Original TemplateURL keyword is uniquified. A SyncChange
569 // is added (which in a normal run would be deleted by PruneSyncChanges() when
570 // the local GUID doesn't appear in the sync GUID list). Also ensure that
571 // this does not change the safe_for_autoreplace flag or the TemplateURLID in
573 original_turl = CreateTestTemplateURL(original_turl_keyword,
574 "http://key1.com", "local", 9000);
575 model()->Add(original_turl);
576 TemplateURLID original_id = original_turl->id();
577 sync_turl.reset(CreateTestTemplateURL(original_turl_keyword, "http://new.com",
578 std::string(), 9001));
579 model()->ResolveSyncKeywordConflict(sync_turl.get(), original_turl, &changes);
580 EXPECT_EQ(original_turl_keyword, sync_turl->keyword());
581 EXPECT_NE(original_turl_keyword, original_turl->keyword());
582 EXPECT_FALSE(original_turl->safe_for_autoreplace());
583 EXPECT_EQ(original_id, original_turl->id());
584 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(original_turl_keyword));
585 ASSERT_EQ(1U, changes.size());
586 EXPECT_EQ("local", GetGUID(changes[0].sync_data()));
587 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
589 model()->Remove(original_turl);
591 // Equal times. Same result as above. Sync left alone, original uniquified so
592 // sync_turl can fit.
593 original_turl = CreateTestTemplateURL(original_turl_keyword,
594 "http://key1.com", "local2", 9000);
595 model()->Add(original_turl);
596 sync_turl.reset(CreateTestTemplateURL(original_turl_keyword, "http://new.com",
597 std::string(), 9000));
598 model()->ResolveSyncKeywordConflict(sync_turl.get(), original_turl, &changes);
599 EXPECT_EQ(original_turl_keyword, sync_turl->keyword());
600 EXPECT_NE(original_turl_keyword, original_turl->keyword());
601 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(original_turl_keyword));
602 ASSERT_EQ(1U, changes.size());
603 EXPECT_EQ("local2", GetGUID(changes[0].sync_data()));
604 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
606 model()->Remove(original_turl);
608 // Sync is newer, but original TemplateURL is created by policy, so it wins.
609 // Sync keyword is uniquified, and a syncer::SyncChange is added.
610 original_turl = CreateTestTemplateURL(original_turl_keyword,
611 "http://key1.com", std::string(), 9000, false, true);
612 model()->Add(original_turl);
613 sync_turl.reset(CreateTestTemplateURL(original_turl_keyword, "http://new.com",
615 model()->ResolveSyncKeywordConflict(sync_turl.get(), original_turl, &changes);
616 EXPECT_NE(original_turl_keyword, sync_turl->keyword());
617 EXPECT_EQ(original_turl_keyword, original_turl->keyword());
618 EXPECT_EQ(NULL, model()->GetTemplateURLForKeyword(sync_turl->keyword()));
619 ASSERT_EQ(1U, changes.size());
620 EXPECT_EQ("remote2", GetGUID(changes[0].sync_data()));
621 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
623 model()->Remove(original_turl);
626 TEST_F(TemplateURLServiceSyncTest, StartSyncEmpty) {
627 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
628 syncer::SEARCH_ENGINES, syncer::SyncDataList(),
629 PassProcessor(), CreateAndPassSyncErrorFactory());
631 EXPECT_EQ(0U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
632 EXPECT_EQ(0U, processor()->change_list_size());
633 EXPECT_EQ(0, merge_result.num_items_added());
634 EXPECT_EQ(0, merge_result.num_items_modified());
635 EXPECT_EQ(0, merge_result.num_items_deleted());
636 EXPECT_EQ(0, merge_result.num_items_before_association());
637 EXPECT_EQ(0, merge_result.num_items_after_association());
640 TEST_F(TemplateURLServiceSyncTest, MergeIntoEmpty) {
641 syncer::SyncDataList initial_data = CreateInitialSyncData();
643 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
644 syncer::SEARCH_ENGINES, initial_data,
645 PassProcessor(), CreateAndPassSyncErrorFactory());
647 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
648 // We expect the model to have accepted all of the initial sync data. Search
649 // through the model using the GUIDs to ensure that they're present.
650 for (syncer::SyncDataList::const_iterator iter = initial_data.begin();
651 iter != initial_data.end(); ++iter) {
652 std::string guid = GetGUID(*iter);
653 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
656 EXPECT_EQ(0U, processor()->change_list_size());
658 // Locally the three new TemplateURL's should have been added.
659 EXPECT_EQ(3, merge_result.num_items_added());
660 EXPECT_EQ(0, merge_result.num_items_modified());
661 EXPECT_EQ(0, merge_result.num_items_deleted());
662 EXPECT_EQ(0, merge_result.num_items_before_association());
663 EXPECT_EQ(3, merge_result.num_items_after_association());
666 TEST_F(TemplateURLServiceSyncTest, MergeInAllNewData) {
667 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("abc.com"), "http://abc.com",
669 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("def.com"), "http://def.com",
671 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("xyz.com"), "http://xyz.com",
673 syncer::SyncDataList initial_data = CreateInitialSyncData();
675 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
676 syncer::SEARCH_ENGINES, initial_data,
677 PassProcessor(), CreateAndPassSyncErrorFactory());
679 EXPECT_EQ(6U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
680 // We expect the model to have accepted all of the initial sync data. Search
681 // through the model using the GUIDs to ensure that they're present.
682 for (syncer::SyncDataList::const_iterator iter = initial_data.begin();
683 iter != initial_data.end(); ++iter) {
684 std::string guid = GetGUID(*iter);
685 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
687 // All the original TemplateURLs should also remain in the model.
688 EXPECT_TRUE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("abc.com")));
689 EXPECT_TRUE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("def.com")));
690 EXPECT_TRUE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("xyz.com")));
691 // Ensure that Sync received the expected changes.
692 EXPECT_EQ(3U, processor()->change_list_size());
693 EXPECT_TRUE(processor()->contains_guid("abc"));
694 EXPECT_TRUE(processor()->contains_guid("def"));
695 EXPECT_TRUE(processor()->contains_guid("xyz"));
697 // Locally the three new TemplateURL's should have been added.
698 EXPECT_EQ(3, merge_result.num_items_added());
699 EXPECT_EQ(0, merge_result.num_items_modified());
700 EXPECT_EQ(0, merge_result.num_items_deleted());
701 EXPECT_EQ(3, merge_result.num_items_before_association());
702 EXPECT_EQ(6, merge_result.num_items_after_association());
705 TEST_F(TemplateURLServiceSyncTest, MergeSyncIsTheSame) {
706 // The local data is the same as the sync data merged in. i.e. - There have
707 // been no changes since the last time we synced. Even the last_modified
708 // timestamps are the same.
709 syncer::SyncDataList initial_data = CreateInitialSyncData();
710 for (syncer::SyncDataList::const_iterator iter = initial_data.begin();
711 iter != initial_data.end(); ++iter) {
712 TemplateURL* converted = Deserialize(*iter);
713 model()->Add(converted);
716 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
717 syncer::SEARCH_ENGINES, initial_data,
718 PassProcessor(), CreateAndPassSyncErrorFactory());
720 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
721 for (syncer::SyncDataList::const_iterator iter = initial_data.begin();
722 iter != initial_data.end(); ++iter) {
723 std::string guid = GetGUID(*iter);
724 EXPECT_TRUE(model()->GetTemplateURLForGUID(guid));
726 EXPECT_EQ(0U, processor()->change_list_size());
728 // Locally everything should remain the same.
729 EXPECT_EQ(0, merge_result.num_items_added());
730 EXPECT_EQ(0, merge_result.num_items_modified());
731 EXPECT_EQ(0, merge_result.num_items_deleted());
732 EXPECT_EQ(3, merge_result.num_items_before_association());
733 EXPECT_EQ(3, merge_result.num_items_after_association());
736 TEST_F(TemplateURLServiceSyncTest, MergeUpdateFromSync) {
737 // The local data is the same as the sync data merged in, but timestamps have
738 // changed. Ensure the right fields are merged in.
739 syncer::SyncDataList initial_data;
740 TemplateURL* turl1 = CreateTestTemplateURL(ASCIIToUTF16("abc.com"),
741 "http://abc.com", "abc", 9000);
743 TemplateURL* turl2 = CreateTestTemplateURL(ASCIIToUTF16("xyz.com"),
744 "http://xyz.com", "xyz", 9000);
747 scoped_ptr<TemplateURL> turl1_newer(CreateTestTemplateURL(
748 ASCIIToUTF16("abc.com"), "http://abc.ca", "abc", 9999));
749 initial_data.push_back(
750 TemplateURLService::CreateSyncDataFromTemplateURL(*turl1_newer));
752 scoped_ptr<TemplateURL> turl2_older(CreateTestTemplateURL(
753 ASCIIToUTF16("xyz.com"), "http://xyz.ca", "xyz", 8888));
754 initial_data.push_back(
755 TemplateURLService::CreateSyncDataFromTemplateURL(*turl2_older));
757 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
758 syncer::SEARCH_ENGINES, initial_data,
759 PassProcessor(), CreateAndPassSyncErrorFactory());
761 // Both were local updates, so we expect the same count.
762 EXPECT_EQ(2U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
764 // Check that the first replaced the initial abc TemplateURL.
765 EXPECT_EQ(turl1, model()->GetTemplateURLForGUID("abc"));
766 EXPECT_EQ("http://abc.ca", turl1->url());
768 // Check that the second produced an upstream update to the xyz TemplateURL.
769 EXPECT_EQ(1U, processor()->change_list_size());
770 ASSERT_TRUE(processor()->contains_guid("xyz"));
771 syncer::SyncChange change = processor()->change_for_guid("xyz");
772 EXPECT_TRUE(change.change_type() == syncer::SyncChange::ACTION_UPDATE);
773 EXPECT_EQ("http://xyz.com", GetURL(change.sync_data()));
775 // Locally only the older item should have been modified.
776 EXPECT_EQ(0, merge_result.num_items_added());
777 EXPECT_EQ(1, merge_result.num_items_modified());
778 EXPECT_EQ(0, merge_result.num_items_deleted());
779 EXPECT_EQ(2, merge_result.num_items_before_association());
780 EXPECT_EQ(2, merge_result.num_items_after_association());
783 TEST_F(TemplateURLServiceSyncTest, MergeAddFromOlderSyncData) {
784 // GUIDs all differ, so this is data to be added from Sync, but the timestamps
785 // from Sync are older. Set up the local data so that one is a dupe, one has a
786 // conflicting keyword, and the last has no conflicts (a clean ADD).
787 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com",
788 "aaa", 100)); // dupe
790 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"),
791 "http://expected.com", "bbb", 100)); // keyword conflict
793 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("unique"),
794 "http://unique.com", "ccc")); // add
796 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
797 syncer::SEARCH_ENGINES,
798 CreateInitialSyncData(), PassProcessor(),
799 CreateAndPassSyncErrorFactory());
801 // The dupe and conflict results in merges, as local values are always merged
802 // with sync values if there is a keyword conflict. The unique keyword should
804 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
806 // The key1 duplicate results in the local copy winning. Ensure that Sync's
807 // copy was not added, and the local copy is pushed upstream to Sync as an
808 // update. The local copy should have received the sync data's GUID.
809 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
810 // Check changes for the UPDATE.
811 ASSERT_TRUE(processor()->contains_guid("key1"));
812 syncer::SyncChange key1_change = processor()->change_for_guid("key1");
813 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key1_change.change_type());
814 // The local sync_guid should no longer be found.
815 EXPECT_FALSE(model()->GetTemplateURLForGUID("aaa"));
817 // The key2 keyword conflict results in a merge, with the values of the local
818 // copy winning, so ensure it retains the original URL, and that an update to
819 // the sync guid is pushed upstream to Sync.
820 const TemplateURL* key2 = model()->GetTemplateURLForGUID("key2");
822 EXPECT_EQ(ASCIIToUTF16("key2"), key2->keyword());
823 EXPECT_EQ("http://expected.com", key2->url());
824 // Check changes for the UPDATE.
825 ASSERT_TRUE(processor()->contains_guid("key2"));
826 syncer::SyncChange key2_change = processor()->change_for_guid("key2");
827 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key2_change.change_type());
828 EXPECT_EQ("key2", GetKeyword(key2_change.sync_data()));
829 EXPECT_EQ("http://expected.com", GetURL(key2_change.sync_data()));
830 // The local sync_guid should no longer be found.
831 EXPECT_FALSE(model()->GetTemplateURLForGUID("bbb"));
833 // The last TemplateURL should have had no conflicts and was just added. It
834 // should not have replaced the third local TemplateURL.
835 EXPECT_TRUE(model()->GetTemplateURLForGUID("ccc"));
836 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
838 // Two UPDATEs and one ADD.
839 EXPECT_EQ(3U, processor()->change_list_size());
840 // One ADDs should be pushed up to Sync.
841 ASSERT_TRUE(processor()->contains_guid("ccc"));
842 EXPECT_EQ(syncer::SyncChange::ACTION_ADD,
843 processor()->change_for_guid("ccc").change_type());
845 // All the sync items had new guids, but only one doesn't conflict and is
846 // added. The other two conflicting cases result in local modifications
847 // to override the local guids but preserve the local data.
848 EXPECT_EQ(1, merge_result.num_items_added());
849 EXPECT_EQ(2, merge_result.num_items_modified());
850 EXPECT_EQ(0, merge_result.num_items_deleted());
851 EXPECT_EQ(3, merge_result.num_items_before_association());
852 EXPECT_EQ(4, merge_result.num_items_after_association());
855 TEST_F(TemplateURLServiceSyncTest, MergeAddFromNewerSyncData) {
856 // GUIDs all differ, so Sync may overtake some entries, but the timestamps
857 // from Sync are newer. Set up the local data so that one is a dupe, one has a
858 // conflicting keyword, and the last has no conflicts (a clean ADD).
859 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com",
862 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"),
863 "http://expected.com", "bbb", 10)); // keyword conflict
865 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("unique"),
866 "http://unique.com", "ccc", 10)); // add
868 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
869 syncer::SEARCH_ENGINES,
870 CreateInitialSyncData(), PassProcessor(),
871 CreateAndPassSyncErrorFactory());
873 // The dupe and keyword conflict results in merges. The unique keyword be
874 // added to the model.
875 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
877 // The key1 duplicate results in Sync's copy winning. Ensure that Sync's
878 // copy replaced the local copy.
879 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
880 EXPECT_FALSE(model()->GetTemplateURLForGUID("aaa"));
881 EXPECT_FALSE(processor()->contains_guid("key1"));
882 EXPECT_FALSE(processor()->contains_guid("aaa"));
884 // The key2 keyword conflict results in Sync's copy winning, so ensure it
885 // retains the original keyword and is added. The local copy should be
887 const TemplateURL* key2_sync = model()->GetTemplateURLForGUID("key2");
888 ASSERT_TRUE(key2_sync);
889 EXPECT_EQ(ASCIIToUTF16("key2"), key2_sync->keyword());
890 EXPECT_FALSE(model()->GetTemplateURLForGUID("bbb"));
892 // The last TemplateURL should have had no conflicts and was just added. It
893 // should not have replaced the third local TemplateURL.
894 EXPECT_TRUE(model()->GetTemplateURLForGUID("ccc"));
895 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
898 EXPECT_EQ(1U, processor()->change_list_size());
899 // One ADDs should be pushed up to Sync.
900 ASSERT_TRUE(processor()->contains_guid("ccc"));
901 EXPECT_EQ(syncer::SyncChange::ACTION_ADD,
902 processor()->change_for_guid("ccc").change_type());
904 // One of the sync items is added directly without conflict. The other two
905 // conflict but are newer than the local items so are added while the local
907 EXPECT_EQ(3, merge_result.num_items_added());
908 EXPECT_EQ(0, merge_result.num_items_modified());
909 EXPECT_EQ(2, merge_result.num_items_deleted());
910 EXPECT_EQ(3, merge_result.num_items_before_association());
911 EXPECT_EQ(4, merge_result.num_items_after_association());
914 TEST_F(TemplateURLServiceSyncTest, ProcessChangesEmptyModel) {
915 // We initially have no data.
916 model()->MergeDataAndStartSyncing(
917 syncer::SEARCH_ENGINES, syncer::SyncDataList(),
918 PassProcessor(), CreateAndPassSyncErrorFactory());
920 // Set up a bunch of ADDs.
921 syncer::SyncChangeList changes;
922 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
923 CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com", "key1")));
924 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
925 CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com", "key2")));
926 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
927 CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com", "key3")));
929 model()->ProcessSyncChanges(FROM_HERE, changes);
931 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
932 EXPECT_EQ(0U, processor()->change_list_size());
933 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
934 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
935 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
938 TEST_F(TemplateURLServiceSyncTest, ProcessChangesNoConflicts) {
939 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
940 CreateInitialSyncData(), PassProcessor(),
941 CreateAndPassSyncErrorFactory());
943 // Process different types of changes, without conflicts.
944 syncer::SyncChangeList changes;
945 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
946 CreateTestTemplateURL(ASCIIToUTF16("key4"), "http://key4.com", "key4")));
947 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
948 CreateTestTemplateURL(ASCIIToUTF16("newkeyword"), "http://new.com",
950 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_DELETE,
951 CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com", "key3")));
953 model()->ProcessSyncChanges(FROM_HERE, changes);
955 // Add one, remove one, update one, so the number shouldn't change.
956 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
957 EXPECT_EQ(0U, processor()->change_list_size());
958 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
959 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
960 const TemplateURL* turl = model()->GetTemplateURLForGUID("key2");
962 EXPECT_EQ(ASCIIToUTF16("newkeyword"), turl->keyword());
963 EXPECT_EQ("http://new.com", turl->url());
964 EXPECT_FALSE(model()->GetTemplateURLForGUID("key3"));
965 EXPECT_TRUE(model()->GetTemplateURLForGUID("key4"));
968 TEST_F(TemplateURLServiceSyncTest, ProcessChangesWithConflictsSyncWins) {
969 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
970 CreateInitialSyncData(), PassProcessor(),
971 CreateAndPassSyncErrorFactory());
973 // Process different types of changes, with conflicts. Note that all this data
974 // has a newer timestamp, so Sync will win in these scenarios.
975 syncer::SyncChangeList changes;
976 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
977 CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://new.com", "aaa")));
978 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
979 CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com", "key1")));
981 model()->ProcessSyncChanges(FROM_HERE, changes);
983 // Add one, update one, so we're up to 4.
984 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
985 // Sync is always newer here, so it should always win. We should create
986 // SyncChanges for the changes to the local entities, since they're synced
988 EXPECT_EQ(2U, processor()->change_list_size());
989 ASSERT_TRUE(processor()->contains_guid("key2"));
990 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
991 processor()->change_for_guid("key2").change_type());
992 ASSERT_TRUE(processor()->contains_guid("key3"));
993 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
994 processor()->change_for_guid("key3").change_type());
996 // aaa conflicts with key2 and wins, forcing key2's keyword to update.
997 EXPECT_TRUE(model()->GetTemplateURLForGUID("aaa"));
998 EXPECT_EQ(model()->GetTemplateURLForGUID("aaa"),
999 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key2")));
1000 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
1001 EXPECT_EQ(model()->GetTemplateURLForGUID("key2"),
1002 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key2.com")));
1003 // key1 update conflicts with key3 and wins, forcing key3's keyword to update.
1004 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
1005 EXPECT_EQ(model()->GetTemplateURLForGUID("key1"),
1006 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key3")));
1007 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
1008 EXPECT_EQ(model()->GetTemplateURLForGUID("key3"),
1009 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key3.com")));
1012 TEST_F(TemplateURLServiceSyncTest, ProcessChangesWithConflictsLocalWins) {
1013 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1014 CreateInitialSyncData(), PassProcessor(),
1015 CreateAndPassSyncErrorFactory());
1017 // Process different types of changes, with conflicts. Note that all this data
1018 // has an older timestamp, so the local data will win in these scenarios.
1019 syncer::SyncChangeList changes;
1020 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1021 CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://new.com", "aaa",
1023 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
1024 CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com", "key1",
1027 model()->ProcessSyncChanges(FROM_HERE, changes);
1029 // Add one, update one, so we're up to 4.
1030 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1031 // Local data wins twice so two updates are pushed up to Sync.
1032 EXPECT_EQ(2U, processor()->change_list_size());
1034 // aaa conflicts with key2 and loses, forcing it's keyword to update.
1035 EXPECT_TRUE(model()->GetTemplateURLForGUID("aaa"));
1036 EXPECT_EQ(model()->GetTemplateURLForGUID("aaa"),
1037 model()->GetTemplateURLForKeyword(ASCIIToUTF16("new.com")));
1038 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
1039 EXPECT_EQ(model()->GetTemplateURLForGUID("key2"),
1040 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key2")));
1041 // key1 update conflicts with key3 and loses, forcing key1's keyword to
1043 EXPECT_TRUE(model()->GetTemplateURLForGUID("key1"));
1044 EXPECT_EQ(model()->GetTemplateURLForGUID("key1"),
1045 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key3.com")));
1046 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
1047 EXPECT_EQ(model()->GetTemplateURLForGUID("key3"),
1048 model()->GetTemplateURLForKeyword(ASCIIToUTF16("key3")));
1050 ASSERT_TRUE(processor()->contains_guid("aaa"));
1051 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
1052 processor()->change_for_guid("aaa").change_type());
1053 ASSERT_TRUE(processor()->contains_guid("key1"));
1054 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
1055 processor()->change_for_guid("key1").change_type());
1058 TEST_F(TemplateURLServiceSyncTest, ProcessTemplateURLChange) {
1059 // Ensure that ProcessTemplateURLChange is called and pushes the correct
1060 // changes to Sync whenever local changes are made to TemplateURLs.
1061 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1062 CreateInitialSyncData(), PassProcessor(),
1063 CreateAndPassSyncErrorFactory());
1065 // Add a new search engine.
1066 TemplateURL* new_turl =
1067 CreateTestTemplateURL(ASCIIToUTF16("baidu"), "http://baidu.cn", "new");
1068 model()->Add(new_turl);
1069 EXPECT_EQ(1U, processor()->change_list_size());
1070 ASSERT_TRUE(processor()->contains_guid("new"));
1071 syncer::SyncChange change = processor()->change_for_guid("new");
1072 EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change.change_type());
1073 EXPECT_EQ("baidu", GetKeyword(change.sync_data()));
1074 EXPECT_EQ("http://baidu.cn", GetURL(change.sync_data()));
1076 // Change a keyword.
1077 TemplateURL* existing_turl = model()->GetTemplateURLForGUID("key1");
1078 model()->ResetTemplateURL(existing_turl, existing_turl->short_name(),
1079 ASCIIToUTF16("k"), existing_turl->url());
1080 EXPECT_EQ(1U, processor()->change_list_size());
1081 ASSERT_TRUE(processor()->contains_guid("key1"));
1082 change = processor()->change_for_guid("key1");
1083 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
1084 EXPECT_EQ("k", GetKeyword(change.sync_data()));
1086 // Remove an existing search engine.
1087 existing_turl = model()->GetTemplateURLForGUID("key2");
1088 model()->Remove(existing_turl);
1089 EXPECT_EQ(1U, processor()->change_list_size());
1090 ASSERT_TRUE(processor()->contains_guid("key2"));
1091 change = processor()->change_for_guid("key2");
1092 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, change.change_type());
1095 TEST_F(TemplateURLServiceSyncTest, ProcessChangesWithLocalExtensions) {
1096 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1097 CreateInitialSyncData(), PassProcessor(),
1098 CreateAndPassSyncErrorFactory());
1100 // Add some extension keywords locally.
1101 TemplateURL* extension1 = CreateTestTemplateURL(ASCIIToUTF16("keyword1"),
1102 std::string(extensions::kExtensionScheme) + "://extension1");
1103 model()->Add(extension1);
1104 EXPECT_EQ(1U, processor()->change_list_size());
1105 TemplateURL* extension2 = CreateTestTemplateURL(ASCIIToUTF16("keyword2"),
1106 std::string(extensions::kExtensionScheme) + "://extension2");
1107 model()->Add(extension2);
1108 EXPECT_EQ(1U, processor()->change_list_size());
1110 // Create some sync changes that will conflict with the extension keywords.
1111 syncer::SyncChangeList changes;
1112 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1113 CreateTestTemplateURL(ASCIIToUTF16("keyword1"), "http://aaa.com",
1114 std::string(), 100, true)));
1115 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1116 CreateTestTemplateURL(ASCIIToUTF16("keyword2"), "http://bbb.com")));
1117 model()->ProcessSyncChanges(FROM_HERE, changes);
1119 // The existing extension keywords should be uniquified.
1120 EXPECT_FALSE(model()->GetTemplateURLForHost("aaa.com") == NULL);
1121 EXPECT_EQ(model()->GetTemplateURLForHost("aaa.com"),
1122 model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword1")));
1123 TemplateURL* url_for_keyword2 =
1124 model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword2"));
1125 EXPECT_NE(extension2, url_for_keyword2);
1126 EXPECT_EQ("http://bbb.com", url_for_keyword2->url());
1128 // Replaced extension keywords should be uniquified.
1129 EXPECT_EQ(extension1,
1130 model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword1_")));
1131 EXPECT_EQ(extension2,
1132 model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword2_")));
1135 TEST_F(TemplateURLServiceSyncTest, AutogeneratedKeywordMigrated) {
1136 // Create a couple of sync entries with autogenerated keywords.
1137 syncer::SyncDataList initial_data;
1138 scoped_ptr<TemplateURL> turl(
1139 CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com", "key1"));
1140 initial_data.push_back(
1141 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1142 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key2"),
1143 "{google:baseURL}search?q={searchTerms}", "key2"));
1144 initial_data.push_back(
1145 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1147 // Now try to sync the data locally.
1148 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1149 PassProcessor(), CreateAndPassSyncErrorFactory());
1151 // Both entries should have been added, with explicit keywords.
1152 TemplateURL* key1 = model()->GetTemplateURLForHost("key1.com");
1153 ASSERT_FALSE(key1 == NULL);
1154 EXPECT_EQ(ASCIIToUTF16("key1.com"), key1->keyword());
1155 GURL google_url(UIThreadSearchTermsData(profile_a()).GoogleBaseURLValue());
1156 TemplateURL* key2 = model()->GetTemplateURLForHost(google_url.host());
1157 ASSERT_FALSE(key2 == NULL);
1158 string16 google_keyword(net::StripWWWFromHost(google_url));
1159 EXPECT_EQ(google_keyword, key2->keyword());
1161 // We should also have gotten some corresponding UPDATEs pushed upstream.
1162 EXPECT_GE(processor()->change_list_size(), 2U);
1163 ASSERT_TRUE(processor()->contains_guid("key1"));
1164 syncer::SyncChange key1_change = processor()->change_for_guid("key1");
1165 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key1_change.change_type());
1166 EXPECT_EQ("key1.com", GetKeyword(key1_change.sync_data()));
1167 ASSERT_TRUE(processor()->contains_guid("key2"));
1168 syncer::SyncChange key2_change = processor()->change_for_guid("key2");
1169 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key2_change.change_type());
1170 EXPECT_EQ(google_keyword, UTF8ToUTF16(GetKeyword(key2_change.sync_data())));
1173 TEST_F(TemplateURLServiceSyncTest, AutogeneratedKeywordConflicts) {
1174 // Sync brings in some autogenerated keywords, but the generated keywords we
1175 // try to create conflict with ones in the model.
1176 string16 google_keyword(net::StripWWWFromHost(GURL(
1177 UIThreadSearchTermsData(profile_a()).GoogleBaseURLValue())));
1178 const std::string local_google_url =
1179 "{google:baseURL}1/search?q={searchTerms}";
1180 TemplateURL* google = CreateTestTemplateURL(google_keyword, local_google_url);
1181 model()->Add(google);
1182 TemplateURL* other =
1183 CreateTestTemplateURL(ASCIIToUTF16("other.com"), "http://other.com/foo");
1184 model()->Add(other);
1185 syncer::SyncDataList initial_data;
1186 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("sync1"),
1187 "{google:baseURL}2/search?q={searchTerms}", "sync1", 50));
1188 initial_data.push_back(
1189 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1190 const std::string synced_other_url =
1191 "http://other.com/search?q={searchTerms}";
1192 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("sync2"),
1193 synced_other_url, "sync2", 150));
1194 initial_data.push_back(
1195 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1197 // Before we merge the data, grab the local sync_guids so we can ensure that
1198 // they've been replaced.
1199 const std::string local_google_guid = google->sync_guid();
1200 const std::string local_other_guid = other->sync_guid();
1202 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1203 PassProcessor(), CreateAndPassSyncErrorFactory());
1205 // In this case, the conflicts should be handled just like any other keyword
1206 // conflicts -- the later-modified TemplateURL is assumed to be authoritative.
1207 // Since the initial TemplateURLs were local only, they should be merged with
1208 // the sync TemplateURLs (GUIDs transferred over).
1209 EXPECT_FALSE(model()->GetTemplateURLForGUID(local_google_guid));
1210 ASSERT_TRUE(model()->GetTemplateURLForGUID("sync1"));
1211 EXPECT_EQ(google_keyword, model()->GetTemplateURLForGUID("sync1")->keyword());
1212 EXPECT_FALSE(model()->GetTemplateURLForGUID(local_other_guid));
1213 ASSERT_TRUE(model()->GetTemplateURLForGUID("sync2"));
1214 EXPECT_EQ(ASCIIToUTF16("other.com"),
1215 model()->GetTemplateURLForGUID("sync2")->keyword());
1217 // Both synced URLs should have associated UPDATEs, since both needed their
1218 // keywords to be generated.
1219 EXPECT_EQ(processor()->change_list_size(), 2U);
1220 ASSERT_TRUE(processor()->contains_guid("sync1"));
1221 syncer::SyncChange sync1_change = processor()->change_for_guid("sync1");
1222 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, sync1_change.change_type());
1223 EXPECT_EQ(google_keyword, UTF8ToUTF16(GetKeyword(sync1_change.sync_data())));
1224 EXPECT_EQ(local_google_url, GetURL(sync1_change.sync_data()));
1225 ASSERT_TRUE(processor()->contains_guid("sync2"));
1226 syncer::SyncChange sync2_change = processor()->change_for_guid("sync2");
1227 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, sync2_change.change_type());
1228 EXPECT_EQ("other.com", GetKeyword(sync2_change.sync_data()));
1229 EXPECT_EQ(synced_other_url, GetURL(sync2_change.sync_data()));
1232 TEST_F(TemplateURLServiceSyncTest, TwoAutogeneratedKeywordsUsingGoogleBaseURL) {
1233 // Sync brings in two autogenerated keywords and both use Google base URLs.
1234 // We make the first older so that it will get renamed once before the second
1235 // and then again once after (when we resolve conflicts for the second).
1236 syncer::SyncDataList initial_data;
1237 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key1"),
1238 "{google:baseURL}1/search?q={searchTerms}", "key1", 50));
1239 initial_data.push_back(
1240 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1241 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key2"),
1242 "{google:baseURL}2/search?q={searchTerms}", "key2"));
1243 initial_data.push_back(
1244 CreateCustomSyncData(*turl, true, turl->url(), turl->sync_guid()));
1245 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1246 PassProcessor(), CreateAndPassSyncErrorFactory());
1248 // We should still have coalesced the updates to one each.
1249 string16 google_keyword(net::StripWWWFromHost(GURL(
1250 UIThreadSearchTermsData(profile_a()).GoogleBaseURLValue())));
1251 TemplateURL* keyword1 =
1252 model()->GetTemplateURLForKeyword(google_keyword + ASCIIToUTF16("_"));
1253 ASSERT_FALSE(keyword1 == NULL);
1254 EXPECT_EQ("key1", keyword1->sync_guid());
1255 TemplateURL* keyword2 = model()->GetTemplateURLForKeyword(google_keyword);
1256 ASSERT_FALSE(keyword2 == NULL);
1257 EXPECT_EQ("key2", keyword2->sync_guid());
1259 EXPECT_GE(processor()->change_list_size(), 2U);
1260 ASSERT_TRUE(processor()->contains_guid("key1"));
1261 syncer::SyncChange key1_change = processor()->change_for_guid("key1");
1262 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key1_change.change_type());
1263 EXPECT_EQ(keyword1->keyword(),
1264 UTF8ToUTF16(GetKeyword(key1_change.sync_data())));
1265 ASSERT_TRUE(processor()->contains_guid("key2"));
1266 syncer::SyncChange key2_change = processor()->change_for_guid("key2");
1267 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, key2_change.change_type());
1268 EXPECT_EQ(keyword2->keyword(),
1269 UTF8ToUTF16(GetKeyword(key2_change.sync_data())));
1272 TEST_F(TemplateURLServiceSyncTest, DuplicateEncodingsRemoved) {
1273 // Create a sync entry with duplicate encodings.
1274 syncer::SyncDataList initial_data;
1276 TemplateURLData data;
1277 data.short_name = ASCIIToUTF16("test");
1278 data.SetKeyword(ASCIIToUTF16("keyword"));
1279 data.SetURL("http://test/%s");
1280 data.input_encodings.push_back("UTF-8");
1281 data.input_encodings.push_back("UTF-8");
1282 data.input_encodings.push_back("UTF-16");
1283 data.input_encodings.push_back("UTF-8");
1284 data.input_encodings.push_back("Big5");
1285 data.input_encodings.push_back("UTF-16");
1286 data.input_encodings.push_back("Big5");
1287 data.input_encodings.push_back("Windows-1252");
1288 data.date_created = Time::FromTimeT(100);
1289 data.last_modified = Time::FromTimeT(100);
1290 data.sync_guid = "keyword";
1291 scoped_ptr<TemplateURL> turl(new TemplateURL(NULL, data));
1292 initial_data.push_back(
1293 TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
1295 // Now try to sync the data locally.
1296 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1297 PassProcessor(), CreateAndPassSyncErrorFactory());
1299 // The entry should have been added, with duplicate encodings removed.
1300 TemplateURL* keyword =
1301 model()->GetTemplateURLForKeyword(ASCIIToUTF16("keyword"));
1302 ASSERT_FALSE(keyword == NULL);
1303 EXPECT_EQ(4U, keyword->input_encodings().size());
1305 // We should also have gotten a corresponding UPDATE pushed upstream.
1306 EXPECT_GE(processor()->change_list_size(), 1U);
1307 ASSERT_TRUE(processor()->contains_guid("keyword"));
1308 syncer::SyncChange keyword_change = processor()->change_for_guid("keyword");
1309 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, keyword_change.change_type());
1310 EXPECT_EQ("UTF-8;UTF-16;Big5;Windows-1252", keyword_change.sync_data().
1311 GetSpecifics().search_engine().input_encodings());
1314 TEST_F(TemplateURLServiceSyncTest, MergeTwoClientsBasic) {
1315 // Start off B with some empty data.
1316 model_b()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1317 CreateInitialSyncData(), PassProcessor(),
1318 CreateAndPassSyncErrorFactory());
1320 // Merge A and B. All of B's data should transfer over to A, which initially
1322 scoped_ptr<SyncChangeProcessorDelegate> delegate_b(
1323 new SyncChangeProcessorDelegate(model_b()));
1324 model_a()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1325 model_b()->GetAllSyncData(syncer::SEARCH_ENGINES),
1326 delegate_b.PassAs<syncer::SyncChangeProcessor>(),
1327 CreateAndPassSyncErrorFactory());
1329 // They should be consistent.
1330 AssertEquals(model_a()->GetAllSyncData(syncer::SEARCH_ENGINES),
1331 model_b()->GetAllSyncData(syncer::SEARCH_ENGINES));
1334 TEST_F(TemplateURLServiceSyncTest, MergeTwoClientsDupesAndConflicts) {
1335 // Start off B with some empty data.
1336 model_b()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1337 CreateInitialSyncData(), PassProcessor(),
1338 CreateAndPassSyncErrorFactory());
1340 // Set up A so we have some interesting duplicates and conflicts.
1341 model_a()->Add(CreateTestTemplateURL(ASCIIToUTF16("key4"), "http://key4.com",
1343 model_a()->Add(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com",
1344 "key2")); // Merge - Copy of key2.
1345 model_a()->Add(CreateTestTemplateURL(ASCIIToUTF16("key3"), "http://key3.com",
1346 "key5", 10)); // Merge - Dupe of key3.
1347 model_a()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key6.com",
1348 "key6", 10)); // Conflict with key1
1351 scoped_ptr<SyncChangeProcessorDelegate> delegate_b(
1352 new SyncChangeProcessorDelegate(model_b()));
1353 model_a()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
1354 model_b()->GetAllSyncData(syncer::SEARCH_ENGINES),
1355 delegate_b.PassAs<syncer::SyncChangeProcessor>(),
1356 CreateAndPassSyncErrorFactory());
1358 // They should be consistent.
1359 AssertEquals(model_a()->GetAllSyncData(syncer::SEARCH_ENGINES),
1360 model_b()->GetAllSyncData(syncer::SEARCH_ENGINES));
1363 TEST_F(TemplateURLServiceSyncTest, StopSyncing) {
1364 syncer::SyncError error =
1365 model()->MergeDataAndStartSyncing(
1366 syncer::SEARCH_ENGINES,
1367 CreateInitialSyncData(),
1369 CreateAndPassSyncErrorFactory()).error();
1370 ASSERT_FALSE(error.IsSet());
1371 model()->StopSyncing(syncer::SEARCH_ENGINES);
1373 syncer::SyncChangeList changes;
1374 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
1375 CreateTestTemplateURL(ASCIIToUTF16("newkeyword"), "http://new.com",
1377 error = model()->ProcessSyncChanges(FROM_HERE, changes);
1378 EXPECT_TRUE(error.IsSet());
1380 // Ensure that the sync changes were not accepted.
1381 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
1382 EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("newkeyword")));
1385 TEST_F(TemplateURLServiceSyncTest, SyncErrorOnInitialSync) {
1386 processor()->set_erroneous(true);
1387 syncer::SyncError error =
1388 model()->MergeDataAndStartSyncing(
1389 syncer::SEARCH_ENGINES,
1390 CreateInitialSyncData(),
1392 CreateAndPassSyncErrorFactory()).error();
1393 EXPECT_TRUE(error.IsSet());
1395 // Ensure that if the initial merge was erroneous, then subsequence attempts
1396 // to push data into the local model are rejected, since the model was never
1397 // successfully associated with Sync in the first place.
1398 syncer::SyncChangeList changes;
1399 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
1400 CreateTestTemplateURL(ASCIIToUTF16("newkeyword"), "http://new.com",
1402 processor()->set_erroneous(false);
1403 error = model()->ProcessSyncChanges(FROM_HERE, changes);
1404 EXPECT_TRUE(error.IsSet());
1406 // Ensure that the sync changes were not accepted.
1407 EXPECT_TRUE(model()->GetTemplateURLForGUID("key2"));
1408 EXPECT_FALSE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("newkeyword")));
1411 TEST_F(TemplateURLServiceSyncTest, SyncErrorOnLaterSync) {
1412 // Ensure that if the SyncProcessor succeeds in the initial merge, but fails
1413 // in future ProcessSyncChanges, we still return an error.
1414 syncer::SyncError error =
1415 model()->MergeDataAndStartSyncing(
1416 syncer::SEARCH_ENGINES,
1417 CreateInitialSyncData(),
1419 CreateAndPassSyncErrorFactory()).error();
1420 ASSERT_FALSE(error.IsSet());
1422 syncer::SyncChangeList changes;
1423 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
1424 CreateTestTemplateURL(ASCIIToUTF16("newkeyword"), "http://new.com",
1426 processor()->set_erroneous(true);
1427 error = model()->ProcessSyncChanges(FROM_HERE, changes);
1428 EXPECT_TRUE(error.IsSet());
1431 TEST_F(TemplateURLServiceSyncTest, MergeTwiceWithSameSyncData) {
1432 // Ensure that a second merge with the same data as the first does not
1433 // actually update the local data.
1434 syncer::SyncDataList initial_data;
1435 initial_data.push_back(CreateInitialSyncData()[0]);
1437 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com",
1438 "key1", 10)); // earlier
1440 syncer::SyncError error =
1441 model()->MergeDataAndStartSyncing(
1442 syncer::SEARCH_ENGINES,
1445 CreateAndPassSyncErrorFactory()).error();
1446 ASSERT_FALSE(error.IsSet());
1448 // We should have updated the original TemplateURL with Sync's version.
1449 // Keep a copy of it so we can compare it after we re-merge.
1450 TemplateURL* key1_url = model()->GetTemplateURLForGUID("key1");
1451 ASSERT_TRUE(key1_url);
1452 scoped_ptr<TemplateURL> updated_turl(new TemplateURL(key1_url->profile(),
1454 EXPECT_EQ(Time::FromTimeT(90), updated_turl->last_modified());
1456 // Modify a single field of the initial data. This should not be updated in
1457 // the second merge, as the last_modified timestamp remains the same.
1458 scoped_ptr<TemplateURL> temp_turl(Deserialize(initial_data[0]));
1459 TemplateURLData data(temp_turl->data());
1460 data.short_name = ASCIIToUTF16("SomethingDifferent");
1461 temp_turl.reset(new TemplateURL(temp_turl->profile(), data));
1462 initial_data.clear();
1463 initial_data.push_back(
1464 TemplateURLService::CreateSyncDataFromTemplateURL(*temp_turl));
1466 // Remerge the data again. This simulates shutting down and syncing again
1467 // at a different time, but the cloud data has not changed.
1468 model()->StopSyncing(syncer::SEARCH_ENGINES);
1469 sync_processor_delegate_.reset(new SyncChangeProcessorDelegate(
1470 sync_processor_.get()));
1471 error = model()->MergeDataAndStartSyncing(
1472 syncer::SEARCH_ENGINES,
1475 CreateAndPassSyncErrorFactory()).error();
1476 ASSERT_FALSE(error.IsSet());
1478 // Check that the TemplateURL was not modified.
1479 const TemplateURL* reupdated_turl = model()->GetTemplateURLForGUID("key1");
1480 ASSERT_TRUE(reupdated_turl);
1481 AssertEquals(*updated_turl, *reupdated_turl);
1484 TEST_F(TemplateURLServiceSyncTest, SyncedDefaultGUIDArrivesFirst) {
1485 syncer::SyncDataList initial_data = CreateInitialSyncData();
1486 // The default search provider should support replacement.
1487 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key2"),
1488 "http://key2.com/{searchTerms}", "key2", 90));
1489 initial_data[1] = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
1490 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1491 PassProcessor(), CreateAndPassSyncErrorFactory());
1492 model()->SetDefaultSearchProvider(model()->GetTemplateURLForGUID("key2"));
1494 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1495 const TemplateURL* default_search = model()->GetDefaultSearchProvider();
1496 ASSERT_TRUE(default_search);
1498 // Change kSyncedDefaultSearchProviderGUID to a GUID that does not exist in
1499 // the model yet. Ensure that the default has not changed in any way.
1500 profile_a()->GetTestingPrefService()->SetString(
1501 prefs::kSyncedDefaultSearchProviderGUID, "newdefault");
1503 ASSERT_EQ(default_search, model()->GetDefaultSearchProvider());
1505 // Bring in a random new search engine with a different GUID. Ensure that
1506 // it doesn't change the default.
1507 syncer::SyncChangeList changes1;
1508 changes1.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1509 CreateTestTemplateURL(ASCIIToUTF16("random"), "http://random.com",
1511 model()->ProcessSyncChanges(FROM_HERE, changes1);
1513 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1514 ASSERT_EQ(default_search, model()->GetDefaultSearchProvider());
1516 // Finally, bring in the expected entry with the right GUID. Ensure that
1517 // the default has changed to the new search engine.
1518 syncer::SyncChangeList changes2;
1519 changes2.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1520 CreateTestTemplateURL(ASCIIToUTF16("new"), "http://new.com/{searchTerms}",
1522 model()->ProcessSyncChanges(FROM_HERE, changes2);
1524 EXPECT_EQ(5U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1525 ASSERT_NE(default_search, model()->GetDefaultSearchProvider());
1526 ASSERT_EQ("newdefault", model()->GetDefaultSearchProvider()->sync_guid());
1529 TEST_F(TemplateURLServiceSyncTest, DefaultGuidDeletedBeforeNewDSPArrives) {
1530 syncer::SyncDataList initial_data;
1531 // The default search provider should support replacement.
1532 scoped_ptr<TemplateURL> turl1(CreateTestTemplateURL(ASCIIToUTF16("key1"),
1533 "http://key1.com/{searchTerms}", "key1", 90));
1534 // Create a second default search provider for the
1535 // FindNewDefaultSearchProvider method to find.
1536 TemplateURLData data;
1537 data.short_name = ASCIIToUTF16("unittest");
1538 data.SetKeyword(ASCIIToUTF16("key2"));
1539 data.SetURL("http://key2.com/{searchTerms}");
1540 data.favicon_url = GURL("http://favicon.url");
1541 data.safe_for_autoreplace = false;
1542 data.date_created = Time::FromTimeT(100);
1543 data.last_modified = Time::FromTimeT(100);
1544 data.created_by_policy = false;
1545 data.prepopulate_id = 999999;
1546 data.sync_guid = "key2";
1547 data.show_in_default_list = true;
1548 scoped_ptr<TemplateURL> turl2(new TemplateURL(NULL, data));
1549 initial_data.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(
1551 initial_data.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(
1553 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1554 PassProcessor(), CreateAndPassSyncErrorFactory());
1555 model()->SetDefaultSearchProvider(model()->GetTemplateURLForGUID("key1"));
1556 ASSERT_EQ("key1", model()->GetDefaultSearchProvider()->sync_guid());
1558 EXPECT_EQ(2U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1559 const TemplateURL* default_search = model()->GetDefaultSearchProvider();
1560 ASSERT_TRUE(default_search);
1562 // Change kSyncedDefaultSearchProviderGUID to a GUID that does not exist in
1563 // the model yet. Ensure that the default has not changed in any way.
1564 profile_a()->GetTestingPrefService()->SetString(
1565 prefs::kSyncedDefaultSearchProviderGUID, "newdefault");
1567 ASSERT_EQ("key1", model()->GetDefaultSearchProvider()->sync_guid());
1568 EXPECT_EQ("newdefault", profile_a()->GetTestingPrefService()->GetString(
1569 prefs::kSyncedDefaultSearchProviderGUID));
1571 // Simulate a situation where an ACTION_DELETE on the default arrives before
1572 // the new default search provider entry. This should fail to delete the
1573 // target entry, and instead send up an "undelete" to the server, after
1574 // further uniquifying the keyword to avoid infinite sync loops. The synced
1575 // default GUID should not be changed so that when the expected default entry
1576 // arrives, it can still be set as the default.
1577 syncer::SyncChangeList changes1;
1578 changes1.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_DELETE,
1580 model()->ProcessSyncChanges(FROM_HERE, changes1);
1582 EXPECT_TRUE(model()->GetTemplateURLForKeyword(ASCIIToUTF16("key1_")));
1583 EXPECT_EQ(2U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1584 EXPECT_EQ("key1", model()->GetDefaultSearchProvider()->sync_guid());
1585 EXPECT_EQ("newdefault", profile_a()->GetTestingPrefService()->GetString(
1586 prefs::kSyncedDefaultSearchProviderGUID));
1587 syncer::SyncChange undelete = processor()->change_for_guid("key1");
1588 EXPECT_EQ(syncer::SyncChange::ACTION_ADD, undelete.change_type());
1590 undelete.sync_data().GetSpecifics().search_engine().keyword());
1592 // Finally, bring in the expected entry with the right GUID. Ensure that
1593 // the default has changed to the new search engine.
1594 syncer::SyncChangeList changes2;
1595 changes2.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1596 CreateTestTemplateURL(ASCIIToUTF16("new"), "http://new.com/{searchTerms}",
1598 model()->ProcessSyncChanges(FROM_HERE, changes2);
1600 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1601 EXPECT_EQ("newdefault", model()->GetDefaultSearchProvider()->sync_guid());
1602 EXPECT_EQ("newdefault", profile_a()->GetTestingPrefService()->GetString(
1603 prefs::kSyncedDefaultSearchProviderGUID));
1606 TEST_F(TemplateURLServiceSyncTest, SyncedDefaultArrivesAfterStartup) {
1607 // Start with the default set to something in the model before we start
1609 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("what"),
1610 "http://thewhat.com/{searchTerms}",
1612 model()->SetDefaultSearchProvider(
1613 model()->GetTemplateURLForGUID("initdefault"));
1615 const TemplateURL* default_search = model()->GetDefaultSearchProvider();
1616 ASSERT_TRUE(default_search);
1618 // Set kSyncedDefaultSearchProviderGUID to something that is not yet in
1619 // the model but is expected in the initial sync. Ensure that this doesn't
1620 // change our default since we're not quite syncing yet.
1621 profile_a()->GetTestingPrefService()->SetString(
1622 prefs::kSyncedDefaultSearchProviderGUID, "key2");
1624 EXPECT_EQ(default_search, model()->GetDefaultSearchProvider());
1626 // Now sync the initial data, which will include the search engine entry
1627 // destined to become the new default.
1628 syncer::SyncDataList initial_data = CreateInitialSyncData();
1629 // The default search provider should support replacement.
1630 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key2"),
1631 "http://key2.com/{searchTerms}", "key2", 90));
1632 initial_data[1] = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
1634 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1635 PassProcessor(), CreateAndPassSyncErrorFactory());
1637 // Ensure that the new default has been set.
1638 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1639 ASSERT_NE(default_search, model()->GetDefaultSearchProvider());
1640 ASSERT_EQ("key2", model()->GetDefaultSearchProvider()->sync_guid());
1643 TEST_F(TemplateURLServiceSyncTest, SyncedDefaultAlreadySetOnStartup) {
1644 // Start with the default set to something in the model before we start
1646 const char kGUID[] = "initdefault";
1647 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("what"),
1648 "http://thewhat.com/{searchTerms}",
1650 model()->SetDefaultSearchProvider(model()->GetTemplateURLForGUID(kGUID));
1652 const TemplateURL* default_search = model()->GetDefaultSearchProvider();
1653 ASSERT_TRUE(default_search);
1655 // Set kSyncedDefaultSearchProviderGUID to the current default.
1656 profile_a()->GetTestingPrefService()->SetString(
1657 prefs::kSyncedDefaultSearchProviderGUID, kGUID);
1659 EXPECT_EQ(default_search, model()->GetDefaultSearchProvider());
1661 // Now sync the initial data.
1662 syncer::SyncDataList initial_data = CreateInitialSyncData();
1663 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1664 PassProcessor(), CreateAndPassSyncErrorFactory());
1666 // Ensure that the new entries were added and the default has not changed.
1667 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1668 ASSERT_EQ(default_search, model()->GetDefaultSearchProvider());
1671 TEST_F(TemplateURLServiceSyncTest, NewDefaultIsAlreadySynced) {
1672 // Ensure that if the synced DSP pref changed to another synced entry (as
1673 // opposed to coming in as a new entry), it gets reset correctly.
1674 // Start by setting kSyncedDefaultSearchProviderGUID to the entry that should
1675 // end up as the default. Note that this must be done before the initial
1676 // entries are added as otherwise this call will set the DSP immediately.
1677 profile_a()->GetTestingPrefService()->SetString(
1678 prefs::kSyncedDefaultSearchProviderGUID, "key2");
1680 syncer::SyncDataList initial_data = CreateInitialSyncData();
1681 // Ensure that our candidate default supports replacement.
1682 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key2"),
1683 "http://key2.com/{searchTerms}", "key2", 90));
1684 initial_data[1] = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
1685 for (syncer::SyncDataList::const_iterator iter = initial_data.begin();
1686 iter != initial_data.end(); ++iter) {
1687 TemplateURL* converted = Deserialize(*iter);
1688 model()->Add(converted);
1691 // Set the initial default to something other than the desired default.
1692 model()->SetDefaultSearchProvider(model()->GetTemplateURLForGUID("key1"));
1694 // Merge in the same data (i.e. already synced entries).
1695 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1696 PassProcessor(), CreateAndPassSyncErrorFactory());
1698 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1699 TemplateURL* current_default = model()->GetDefaultSearchProvider();
1700 ASSERT_TRUE(current_default);
1701 EXPECT_EQ("key2", current_default->sync_guid());
1702 EXPECT_EQ(ASCIIToUTF16("key2"), current_default->keyword());
1705 TEST_F(TemplateURLServiceSyncTest, SyncWithManagedDefaultSearch) {
1706 // First start off with a few entries and make sure we can set an unmanaged
1707 // default search provider.
1708 syncer::SyncDataList initial_data = CreateInitialSyncData();
1709 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1710 PassProcessor(), CreateAndPassSyncErrorFactory());
1711 model()->SetDefaultSearchProvider(model()->GetTemplateURLForGUID("key2"));
1713 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1714 ASSERT_FALSE(model()->is_default_search_managed());
1715 ASSERT_TRUE(model()->GetDefaultSearchProvider());
1717 // Change the default search provider to a managed one.
1718 const char kName[] = "manageddefault";
1719 const char kSearchURL[] = "http://manageddefault.com/search?t={searchTerms}";
1720 const char kIconURL[] = "http://manageddefault.com/icon.jpg";
1721 const char kEncodings[] = "UTF-16;UTF-32";
1722 const char kAlternateURL[] =
1723 "http://manageddefault.com/search#t={searchTerms}";
1724 const char kSearchTermsReplacementKey[] = "espv";
1725 test_util_a_.SetManagedDefaultSearchPreferences(true, kName, kName,
1726 kSearchURL, std::string(), kIconURL, kEncodings, kAlternateURL,
1727 kSearchTermsReplacementKey);
1728 const TemplateURL* dsp_turl = model()->GetDefaultSearchProvider();
1730 EXPECT_TRUE(model()->is_default_search_managed());
1732 // Add a new entry from Sync. It should still sync in despite the default
1734 syncer::SyncChangeList changes;
1735 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
1736 CreateTestTemplateURL(ASCIIToUTF16("newkeyword"),
1737 "http://new.com/{searchTerms}",
1739 model()->ProcessSyncChanges(FROM_HERE, changes);
1741 EXPECT_EQ(4U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1743 // Change kSyncedDefaultSearchProviderGUID to point to the new entry and
1744 // ensure that the DSP remains managed.
1745 profile_a()->GetTestingPrefService()->SetString(
1746 prefs::kSyncedDefaultSearchProviderGUID,
1749 EXPECT_EQ(dsp_turl, model()->GetDefaultSearchProvider());
1750 EXPECT_TRUE(model()->is_default_search_managed());
1752 // Go unmanaged. Ensure that the DSP changes to the expected pending entry
1754 const TemplateURL* expected_default =
1755 model()->GetTemplateURLForGUID("newdefault");
1756 test_util_a_.RemoveManagedDefaultSearchPreferences();
1758 EXPECT_EQ(expected_default, model()->GetDefaultSearchProvider());
1761 TEST_F(TemplateURLServiceSyncTest, SyncMergeDeletesDefault) {
1762 // If the value from Sync is a duplicate of the local default and is newer, it
1763 // should safely replace the local value and set as the new default.
1764 TemplateURL* default_turl = CreateTestTemplateURL(ASCIIToUTF16("key1"),
1765 "http://key1.com/{searchTerms}", "whateverguid", 10);
1766 model()->Add(default_turl);
1767 model()->SetDefaultSearchProvider(default_turl);
1769 syncer::SyncDataList initial_data = CreateInitialSyncData();
1770 // The key1 entry should be a duplicate of the default.
1771 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(ASCIIToUTF16("key1"),
1772 "http://key1.com/{searchTerms}", "key1", 90));
1773 initial_data[0] = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
1775 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1776 PassProcessor(), CreateAndPassSyncErrorFactory());
1778 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1779 EXPECT_FALSE(model()->GetTemplateURLForGUID("whateverguid"));
1780 EXPECT_EQ(model()->GetDefaultSearchProvider(),
1781 model()->GetTemplateURLForGUID("key1"));
1784 TEST_F(TemplateURLServiceSyncTest, LocalDefaultWinsConflict) {
1785 // We expect that the local default always wins keyword conflict resolution.
1786 const string16 keyword(ASCIIToUTF16("key1"));
1787 const std::string url("http://whatever.com/{searchTerms}");
1788 TemplateURL* default_turl = CreateTestTemplateURL(keyword,
1792 model()->Add(default_turl);
1793 model()->SetDefaultSearchProvider(default_turl);
1795 syncer::SyncDataList initial_data = CreateInitialSyncData();
1796 // The key1 entry should be different from the default but conflict in the
1798 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(keyword,
1799 "http://key1.com/{searchTerms}", "key1", 90));
1800 initial_data[0] = TemplateURLService::CreateSyncDataFromTemplateURL(*turl);
1802 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1803 PassProcessor(), CreateAndPassSyncErrorFactory());
1805 // Since the local default was not yet synced, it should be merged with the
1806 // conflicting TemplateURL. However, its values should have been preserved
1807 // since it would have won conflict resolution due to being the default.
1808 EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
1809 const TemplateURL* winner = model()->GetTemplateURLForGUID("key1");
1810 ASSERT_TRUE(winner);
1811 EXPECT_EQ(model()->GetDefaultSearchProvider(), winner);
1812 EXPECT_EQ(keyword, winner->keyword());
1813 EXPECT_EQ(url, winner->url());
1814 ASSERT_TRUE(processor()->contains_guid("key1"));
1815 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
1816 processor()->change_for_guid("key1").change_type());
1817 EXPECT_EQ(url, GetURL(processor()->change_for_guid("key1").sync_data()));
1819 // There is no loser, as the two were merged together. The local sync_guid
1820 // should no longer be found in the model.
1821 const TemplateURL* loser = model()->GetTemplateURLForGUID("whateverguid");
1822 ASSERT_FALSE(loser);
1825 TEST_F(TemplateURLServiceSyncTest, DeleteBogusData) {
1826 // Create a couple of bogus entries to sync.
1827 syncer::SyncDataList initial_data;
1828 scoped_ptr<TemplateURL> turl(
1829 CreateTestTemplateURL(ASCIIToUTF16("key1"), "http://key1.com", "key1"));
1830 initial_data.push_back(
1831 CreateCustomSyncData(*turl, false, std::string(), turl->sync_guid()));
1832 turl.reset(CreateTestTemplateURL(ASCIIToUTF16("key2"), "http://key2.com"));
1833 initial_data.push_back(
1834 CreateCustomSyncData(*turl, false, turl->url(), std::string()));
1836 // Now try to sync the data locally.
1837 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1838 PassProcessor(), CreateAndPassSyncErrorFactory());
1840 // Nothing should have been added, and both bogus entries should be marked for
1842 EXPECT_EQ(0U, model()->GetTemplateURLs().size());
1843 EXPECT_EQ(2U, processor()->change_list_size());
1844 ASSERT_TRUE(processor()->contains_guid("key1"));
1845 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
1846 processor()->change_for_guid("key1").change_type());
1847 ASSERT_TRUE(processor()->contains_guid(std::string()));
1848 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
1849 processor()->change_for_guid(std::string()).change_type());
1852 TEST_F(TemplateURLServiceSyncTest, PreSyncDeletes) {
1853 model()->pre_sync_deletes_.insert("key1");
1854 model()->pre_sync_deletes_.insert("key2");
1855 model()->pre_sync_deletes_.insert("aaa");
1856 model()->Add(CreateTestTemplateURL(ASCIIToUTF16("whatever"),
1857 "http://key1.com", "bbb"));
1858 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
1859 syncer::SEARCH_ENGINES,
1860 CreateInitialSyncData(), PassProcessor(),
1861 CreateAndPassSyncErrorFactory());
1863 // We expect the model to have GUIDs {bbb, key3} after our initial merge.
1864 EXPECT_TRUE(model()->GetTemplateURLForGUID("bbb"));
1865 EXPECT_TRUE(model()->GetTemplateURLForGUID("key3"));
1866 syncer::SyncChange change = processor()->change_for_guid("key1");
1867 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, change.change_type());
1868 change = processor()->change_for_guid("key2");
1869 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, change.change_type());
1870 // "aaa" should have been pruned out on account of not being from Sync.
1871 EXPECT_FALSE(processor()->contains_guid("aaa"));
1872 // The set of pre-sync deletes should be cleared so they're not reused if
1873 // MergeDataAndStartSyncing gets called again.
1874 EXPECT_TRUE(model()->pre_sync_deletes_.empty());
1876 // Those sync items deleted via pre-sync-deletes should not get added. The
1877 // remaining sync item (key3) should though.
1878 EXPECT_EQ(1, merge_result.num_items_added());
1879 EXPECT_EQ(0, merge_result.num_items_modified());
1880 EXPECT_EQ(0, merge_result.num_items_deleted());
1881 EXPECT_EQ(1, merge_result.num_items_before_association());
1882 EXPECT_EQ(2, merge_result.num_items_after_association());
1885 TEST_F(TemplateURLServiceSyncTest, PreSyncUpdates) {
1886 const char* kNewKeyword = "somethingnew";
1887 // Fetch the prepopulate search engines so we know what they are.
1888 size_t default_search_provider_index = 0;
1889 ScopedVector<TemplateURL> prepop_turls =
1890 TemplateURLPrepopulateData::GetPrepopulatedEngines(
1891 profile_a(), &default_search_provider_index);
1893 // We have to prematurely exit this test if for some reason this machine does
1894 // not have any prepopulate TemplateURLs.
1895 ASSERT_FALSE(prepop_turls.empty());
1897 // Create a copy of the first TemplateURL with a really old timestamp and a
1898 // new keyword. Add it to the model.
1899 TemplateURLData data_copy(prepop_turls[0]->data());
1900 data_copy.last_modified = Time::FromTimeT(10);
1901 string16 original_keyword = data_copy.keyword();
1902 data_copy.SetKeyword(ASCIIToUTF16(kNewKeyword));
1903 // Set safe_for_autoreplace to false so our keyword survives.
1904 data_copy.safe_for_autoreplace = false;
1905 model()->Add(new TemplateURL(prepop_turls[0]->profile(), data_copy));
1907 // Merge the prepopulate search engines.
1908 base::Time pre_merge_time = base::Time::Now();
1909 base::RunLoop().RunUntilIdle();
1910 test_util_a_.ResetModel(true);
1912 // The newly added search engine should have been safely merged, with an
1914 TemplateURL* added_turl = model()->GetTemplateURLForKeyword(
1915 ASCIIToUTF16(kNewKeyword));
1916 base::Time new_timestamp = added_turl->last_modified();
1917 EXPECT_GE(new_timestamp, pre_merge_time);
1918 ASSERT_TRUE(added_turl);
1919 std::string sync_guid = added_turl->sync_guid();
1921 // Bring down a copy of the prepopulate engine from Sync with the old values,
1922 // including the old timestamp and the same GUID. Ensure that it loses
1923 // conflict resolution against the local value, and an update is sent to the
1924 // server. The new timestamp should be preserved.
1925 syncer::SyncDataList initial_data;
1926 data_copy.SetKeyword(original_keyword);
1927 data_copy.sync_guid = sync_guid;
1928 scoped_ptr<TemplateURL> sync_turl(
1929 new TemplateURL(prepop_turls[0]->profile(), data_copy));
1930 initial_data.push_back(
1931 TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl));
1933 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
1934 syncer::SEARCH_ENGINES,
1935 initial_data, PassProcessor(), CreateAndPassSyncErrorFactory());
1937 ASSERT_EQ(added_turl, model()->GetTemplateURLForKeyword(
1938 ASCIIToUTF16(kNewKeyword)));
1939 EXPECT_EQ(new_timestamp, added_turl->last_modified());
1940 syncer::SyncChange change = processor()->change_for_guid(sync_guid);
1941 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
1942 EXPECT_EQ(kNewKeyword,
1943 change.sync_data().GetSpecifics().search_engine().keyword());
1944 EXPECT_EQ(new_timestamp, base::Time::FromInternalValue(
1945 change.sync_data().GetSpecifics().search_engine().last_modified()));
1947 // All the sync data is old, so nothing should change locally.
1948 EXPECT_EQ(0, merge_result.num_items_added());
1949 EXPECT_EQ(0, merge_result.num_items_modified());
1950 EXPECT_EQ(0, merge_result.num_items_deleted());
1951 EXPECT_EQ(static_cast<int>(prepop_turls.size()),
1952 merge_result.num_items_before_association());
1953 EXPECT_EQ(static_cast<int>(prepop_turls.size()),
1954 merge_result.num_items_after_association());
1957 TEST_F(TemplateURLServiceSyncTest, SyncBaseURLs) {
1958 // Verify that bringing in a remote TemplateURL that uses Google base URLs
1959 // causes it to get a local keyword that matches the local base URL.
1960 test_util_a_.SetGoogleBaseURL(GURL("http://google.com/"));
1961 syncer::SyncDataList initial_data;
1962 scoped_ptr<TemplateURL> turl(CreateTestTemplateURL(
1963 ASCIIToUTF16("google.co.uk"), "{google:baseURL}search?q={searchTerms}",
1965 initial_data.push_back(
1966 TemplateURLService::CreateSyncDataFromTemplateURL(*turl));
1967 model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
1968 PassProcessor(), CreateAndPassSyncErrorFactory());
1969 TemplateURL* synced_turl = model()->GetTemplateURLForGUID("guid");
1970 ASSERT_TRUE(synced_turl);
1971 EXPECT_EQ(ASCIIToUTF16("google.com"), synced_turl->keyword());
1972 EXPECT_EQ(0U, processor()->change_list_size());
1974 // Remote updates to this URL's keyword should be silently ignored.
1975 syncer::SyncChangeList changes;
1976 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
1977 CreateTestTemplateURL(ASCIIToUTF16("google.de"),
1978 "{google:baseURL}search?q={searchTerms}", "guid")));
1979 model()->ProcessSyncChanges(FROM_HERE, changes);
1980 EXPECT_EQ(ASCIIToUTF16("google.com"), synced_turl->keyword());
1981 EXPECT_EQ(0U, processor()->change_list_size());
1983 // A local change to the Google base URL should update the keyword and
1984 // generate a sync change.
1985 test_util_a_.SetGoogleBaseURL(GURL("http://google.co.in/"));
1986 EXPECT_EQ(ASCIIToUTF16("google.co.in"), synced_turl->keyword());
1987 EXPECT_EQ(1U, processor()->change_list_size());
1988 ASSERT_TRUE(processor()->contains_guid("guid"));
1989 syncer::SyncChange change(processor()->change_for_guid("guid"));
1990 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
1991 EXPECT_EQ("google.co.in", GetKeyword(change.sync_data()));
1994 TEST_F(TemplateURLServiceSyncTest, MergeInSyncTemplateURL) {
1995 // An enumeration used to indicate which TemplateURL test value is expected
1996 // for a particular test result.
1997 enum ExpectedTemplateURL {
2004 // Sets up and executes a MergeInSyncTemplateURL test given a number of
2005 // expected start and end states:
2006 // * |conflict_winner| denotes which TemplateURL should win the
2008 // * |synced_at_start| denotes which of the TemplateURLs should known
2010 // * |update_sent| denotes which TemplateURL should have an
2011 // ACTION_UPDATE sent to the server after the merge.
2012 // * |turl_uniquified| denotes which TemplateURL should have its
2013 // keyword updated after the merge.
2014 // * |present_in_model| denotes which TemplateURL should be found in
2015 // the model after the merge.
2016 // * If |keywords_conflict| is true, the TemplateURLs are set up with
2017 // the same keyword.
2019 ExpectedTemplateURL conflict_winner;
2020 ExpectedTemplateURL synced_at_start;
2021 ExpectedTemplateURL update_sent;
2022 ExpectedTemplateURL turl_uniquified;
2023 ExpectedTemplateURL present_in_model;
2024 bool keywords_conflict;
2025 int merge_results[3]; // in Added, Modified, Deleted order.
2027 // Both are synced and the new sync entry is better: Local is uniquified and
2028 // UPDATE sent. Sync is added.
2029 {SYNC, BOTH, LOCAL, LOCAL, BOTH, true, {1, 1, 0}},
2030 // Both are synced and the local entry is better: Sync is uniquified and
2031 // added to the model. An UPDATE is sent for it.
2032 {LOCAL, BOTH, SYNC, SYNC, BOTH, true, {1, 1, 0}},
2033 // Local was not known to Sync and the new sync entry is better: Sync is
2034 // added. Local is removed. No updates.
2035 {SYNC, SYNC, NEITHER, NEITHER, SYNC, true, {1, 0, 1}},
2036 // Local was not known to sync and the local entry is better: Local is
2037 // updated with sync GUID, Sync is not added. UPDATE sent for Sync.
2038 {LOCAL, SYNC, SYNC, NEITHER, SYNC, true, {0, 1, 0}},
2039 // No conflicting keyword. Both should be added with their original
2040 // keywords, with no updates sent. Note that MergeDataAndStartSyncing is
2041 // responsible for creating the ACTION_ADD for the local TemplateURL.
2042 {NEITHER, SYNC, NEITHER, NEITHER, BOTH, false, {1, 0, 0}},
2045 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
2046 // Assert all the valid states of ExpectedTemplateURLs.
2047 ASSERT_FALSE(test_cases[i].conflict_winner == BOTH);
2048 ASSERT_FALSE(test_cases[i].synced_at_start == NEITHER);
2049 ASSERT_FALSE(test_cases[i].synced_at_start == LOCAL);
2050 ASSERT_FALSE(test_cases[i].update_sent == BOTH);
2051 ASSERT_FALSE(test_cases[i].turl_uniquified == BOTH);
2052 ASSERT_FALSE(test_cases[i].present_in_model == NEITHER);
2054 const string16 local_keyword = ASCIIToUTF16("localkeyword");
2055 const string16 sync_keyword = test_cases[i].keywords_conflict ?
2056 local_keyword : ASCIIToUTF16("synckeyword");
2057 const std::string local_url = "www.localurl.com";
2058 const std::string sync_url = "www.syncurl.com";
2059 const time_t local_last_modified = 100;
2060 const time_t sync_last_modified =
2061 test_cases[i].conflict_winner == SYNC ? 110 : 90;
2062 const std::string local_guid = "local_guid";
2063 const std::string sync_guid = "sync_guid";
2065 // Initialize expectations.
2066 string16 expected_local_keyword = local_keyword;
2067 string16 expected_sync_keyword = sync_keyword;
2069 // Create the data and run the actual test.
2070 TemplateURL* local_turl = CreateTestTemplateURL(
2071 local_keyword, local_url, local_guid, local_last_modified);
2072 model()->Add(local_turl);
2073 scoped_ptr<TemplateURL> sync_turl(CreateTestTemplateURL(
2074 sync_keyword, sync_url, sync_guid, sync_last_modified));
2076 SyncDataMap sync_data;
2077 if (test_cases[i].synced_at_start == SYNC ||
2078 test_cases[i].synced_at_start == BOTH) {
2079 sync_data[sync_turl->sync_guid()] =
2080 TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl);
2082 if (test_cases[i].synced_at_start == BOTH) {
2083 sync_data[local_turl->sync_guid()] =
2084 TemplateURLService::CreateSyncDataFromTemplateURL(*local_turl);
2086 SyncDataMap initial_data;
2087 initial_data[local_turl->sync_guid()] =
2088 TemplateURLService::CreateSyncDataFromTemplateURL(*local_turl);
2090 syncer::SyncChangeList change_list;
2091 syncer::SyncMergeResult merge_result(syncer::SEARCH_ENGINES);
2092 model()->MergeInSyncTemplateURL(sync_turl.get(),
2098 // Verify the merge results were set appropriately.
2099 EXPECT_EQ(test_cases[i].merge_results[0], merge_result.num_items_added());
2100 EXPECT_EQ(test_cases[i].merge_results[1],
2101 merge_result.num_items_modified());
2102 EXPECT_EQ(test_cases[i].merge_results[2], merge_result.num_items_deleted());
2104 // Check for expected updates, if any.
2105 std::string expected_update_guid;
2106 if (test_cases[i].update_sent == LOCAL)
2107 expected_update_guid = local_guid;
2108 else if (test_cases[i].update_sent == SYNC)
2109 expected_update_guid = sync_guid;
2110 if (!expected_update_guid.empty()) {
2111 ASSERT_EQ(1U, change_list.size());
2112 EXPECT_EQ(expected_update_guid, GetGUID(change_list[0].sync_data()));
2113 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
2114 change_list[0].change_type());
2116 EXPECT_EQ(0U, change_list.size());
2119 // Adjust the expectations based on the expectation enums.
2120 if (test_cases[i].turl_uniquified == LOCAL) {
2121 DCHECK(test_cases[i].keywords_conflict);
2122 expected_local_keyword = ASCIIToUTF16("localkeyword_");
2124 if (test_cases[i].turl_uniquified == SYNC) {
2125 DCHECK(test_cases[i].keywords_conflict);
2126 expected_sync_keyword = ASCIIToUTF16("localkeyword_");
2129 // Check for TemplateURLs expected in the model. Note that this is checked
2130 // by GUID rather than the initial pointer, as a merge could occur (the
2131 // Sync TemplateURL overtakes the local one). Also remove the present
2132 // TemplateURL when done so the next test case starts with a clean slate.
2133 if (test_cases[i].present_in_model == LOCAL ||
2134 test_cases[i].present_in_model == BOTH) {
2135 ASSERT_TRUE(model()->GetTemplateURLForGUID(local_guid));
2136 EXPECT_EQ(expected_local_keyword, local_turl->keyword());
2137 EXPECT_EQ(local_url, local_turl->url());
2138 EXPECT_EQ(local_last_modified, local_turl->last_modified().ToTimeT());
2139 model()->Remove(model()->GetTemplateURLForGUID(local_guid));
2141 if (test_cases[i].present_in_model == SYNC ||
2142 test_cases[i].present_in_model == BOTH) {
2143 ASSERT_TRUE(model()->GetTemplateURLForGUID(sync_guid));
2144 EXPECT_EQ(expected_sync_keyword, sync_turl->keyword());
2145 EXPECT_EQ(sync_url, sync_turl->url());
2146 EXPECT_EQ(sync_last_modified, sync_turl->last_modified().ToTimeT());
2147 model()->Remove(model()->GetTemplateURLForGUID(sync_guid));
2152 TEST_F(TemplateURLServiceSyncTest, MergePrepopulatedEngine) {
2153 scoped_ptr<TemplateURL> default_turl(
2154 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL));
2156 // Merge with an initial list containing a prepopulated engine with a wrong
2158 syncer::SyncDataList list;
2159 scoped_ptr<TemplateURL> sync_turl(CopyTemplateURL(default_turl.get(),
2160 "http://wrong.url.com?q={searchTerms}", "default"));
2161 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl));
2162 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
2163 syncer::SEARCH_ENGINES, list, PassProcessor(),
2164 CreateAndPassSyncErrorFactory());
2166 const TemplateURL* result_turl = model()->GetTemplateURLForGUID("default");
2167 EXPECT_TRUE(result_turl);
2168 EXPECT_EQ(default_turl->keyword(), result_turl->keyword());
2169 EXPECT_EQ(default_turl->short_name(), result_turl->short_name());
2170 EXPECT_EQ(default_turl->url(), result_turl->url());
2173 TEST_F(TemplateURLServiceSyncTest, AddPrepopulatedEngine) {
2174 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
2175 syncer::SEARCH_ENGINES, syncer::SyncDataList(), PassProcessor(),
2176 CreateAndPassSyncErrorFactory());
2178 scoped_ptr<TemplateURL> default_turl(
2179 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL));
2180 TemplateURL* sync_turl = CopyTemplateURL(default_turl.get(),
2181 "http://wrong.url.com?q={searchTerms}", "default");
2183 // Add a prepopulated engine with a wrong URL.
2184 syncer::SyncChangeList changes;
2185 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_ADD,
2187 model()->ProcessSyncChanges(FROM_HERE, changes);
2189 const TemplateURL* result_turl = model()->GetTemplateURLForGUID("default");
2190 EXPECT_TRUE(result_turl);
2191 EXPECT_EQ(default_turl->keyword(), result_turl->keyword());
2192 EXPECT_EQ(default_turl->short_name(), result_turl->short_name());
2193 EXPECT_EQ(default_turl->url(), result_turl->url());
2196 TEST_F(TemplateURLServiceSyncTest, UpdatePrepopulatedEngine) {
2197 scoped_ptr<TemplateURL> default_turl(
2198 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL));
2200 TemplateURLData data(default_turl->data());
2201 data.SetURL("http://old.wrong.url.com?q={searchTerms}");
2202 data.sync_guid = "default";
2203 model()->Add(new TemplateURL(NULL, data));
2205 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
2206 syncer::SEARCH_ENGINES, syncer::SyncDataList(), PassProcessor(),
2207 CreateAndPassSyncErrorFactory());
2209 TemplateURL* sync_turl = CopyTemplateURL(default_turl.get(),
2210 "http://new.wrong.url.com?q={searchTerms}", "default");
2212 // Update the engine in the model, which is prepopulated, with a new one.
2213 // Both have wrong URLs, but it should still get corrected.
2214 syncer::SyncChangeList changes;
2215 changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
2217 model()->ProcessSyncChanges(FROM_HERE, changes);
2219 const TemplateURL* result_turl = model()->GetTemplateURLForGUID("default");
2220 EXPECT_TRUE(result_turl);
2221 EXPECT_EQ(default_turl->keyword(), result_turl->keyword());
2222 EXPECT_EQ(default_turl->short_name(), result_turl->short_name());
2223 EXPECT_EQ(default_turl->url(), result_turl->url());
2226 TEST_F(TemplateURLServiceSyncTest, MergeEditedPrepopulatedEngine) {
2227 scoped_ptr<TemplateURL> default_turl(
2228 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL));
2230 TemplateURLData data(default_turl->data());
2231 data.safe_for_autoreplace = false;
2232 data.SetKeyword(ASCIIToUTF16("new_kw"));
2233 data.short_name = ASCIIToUTF16("my name");
2234 data.SetURL("http://wrong.url.com?q={searchTerms}");
2235 data.date_created = Time::FromTimeT(50);
2236 data.last_modified = Time::FromTimeT(50);
2237 data.sync_guid = "default";
2238 model()->Add(new TemplateURL(NULL, data));
2240 data.date_created = Time::FromTimeT(100);
2241 data.last_modified = Time::FromTimeT(100);
2242 scoped_ptr<TemplateURL> sync_turl(new TemplateURL(NULL, data));
2243 syncer::SyncDataList list;
2244 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl));
2245 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
2246 syncer::SEARCH_ENGINES, list, PassProcessor(),
2247 CreateAndPassSyncErrorFactory());
2249 const TemplateURL* result_turl = model()->GetTemplateURLForGUID("default");
2250 EXPECT_TRUE(result_turl);
2251 EXPECT_EQ(ASCIIToUTF16("new_kw"), result_turl->keyword());
2252 EXPECT_EQ(ASCIIToUTF16("my name"), result_turl->short_name());
2253 EXPECT_EQ(default_turl->url(), result_turl->url());
2256 TEST_F(TemplateURLServiceSyncTest, MergeNonEditedPrepopulatedEngine) {
2257 scoped_ptr<TemplateURL> default_turl(
2258 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL));
2260 TemplateURLData data(default_turl->data());
2261 data.safe_for_autoreplace = true; // Can be replaced with built-in values.
2262 data.SetKeyword(ASCIIToUTF16("new_kw"));
2263 data.short_name = ASCIIToUTF16("my name");
2264 data.SetURL("http://wrong.url.com?q={searchTerms}");
2265 data.date_created = Time::FromTimeT(50);
2266 data.last_modified = Time::FromTimeT(50);
2267 data.sync_guid = "default";
2268 model()->Add(new TemplateURL(NULL, data));
2270 data.date_created = Time::FromTimeT(100);
2271 data.last_modified = Time::FromTimeT(100);
2272 scoped_ptr<TemplateURL> sync_turl(new TemplateURL(NULL, data));
2273 syncer::SyncDataList list;
2274 list.push_back(TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl));
2275 syncer::SyncMergeResult merge_result = model()->MergeDataAndStartSyncing(
2276 syncer::SEARCH_ENGINES, list, PassProcessor(),
2277 CreateAndPassSyncErrorFactory());
2279 const TemplateURL* result_turl = model()->GetTemplateURLForGUID("default");
2280 EXPECT_TRUE(result_turl);
2281 EXPECT_EQ(default_turl->keyword(), result_turl->keyword());
2282 EXPECT_EQ(default_turl->short_name(), result_turl->short_name());
2283 EXPECT_EQ(default_turl->url(), result_turl->url());