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

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

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

Powered by Google App Engine
This is Rietveld 408576698