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 <vector>
6
7#include "testing/gtest/include/gtest/gtest.h"
8
9#include "base/memory/ref_counted.h"
10#include "base/string16.h"
11#include "base/threading/thread.h"
12#include "base/time.h"
13#include "chrome/browser/history/history_backend.h"
14#include "chrome/browser/history/history_notifications.h"
15#include "chrome/browser/history/history_types.h"
16#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
17#include "chrome/browser/sync/engine/syncapi.h"
18#include "chrome/browser/sync/glue/sync_backend_host.h"
19#include "chrome/browser/sync/glue/sync_backend_host_mock.h"
20#include "chrome/browser/sync/glue/typed_url_change_processor.h"
21#include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
22#include "chrome/browser/sync/glue/typed_url_model_associator.h"
23#include "chrome/browser/sync/profile_sync_factory.h"
24#include "chrome/browser/sync/profile_sync_factory_mock.h"
25#include "chrome/browser/sync/profile_sync_service.h"
26#include "chrome/browser/sync/profile_sync_test_util.h"
27#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
28#include "chrome/browser/sync/syncable/directory_manager.h"
29#include "chrome/browser/sync/test_profile_sync_service.h"
30#include "chrome/common/net/gaia/gaia_constants.h"
31#include "chrome/test/profile_mock.h"
32#include "chrome/test/sync/engine/test_id_factory.h"
33#include "chrome/test/testing_profile.h"
34#include "content/common/notification_service.h"
35#include "googleurl/src/gurl.h"
36#include "testing/gmock/include/gmock/gmock.h"
37
38using base::Time;
39using base::Thread;
40using browser_sync::SyncBackendHost;
41using browser_sync::SyncBackendHostMock;
42using browser_sync::TestIdFactory;
43using browser_sync::TypedUrlChangeProcessor;
44using browser_sync::TypedUrlDataTypeController;
45using browser_sync::TypedUrlModelAssociator;
46using browser_sync::UnrecoverableErrorHandler;
47using history::HistoryBackend;
48using history::URLID;
49using history::URLRow;
50using sync_api::SyncManager;
51using sync_api::UserShare;
52using syncable::BASE_VERSION;
53using syncable::CREATE;
54using syncable::DirectoryManager;
55using syncable::IS_DEL;
56using syncable::IS_DIR;
57using syncable::IS_UNAPPLIED_UPDATE;
58using syncable::IS_UNSYNCED;
59using syncable::MutableEntry;
60using syncable::SERVER_IS_DIR;
61using syncable::SERVER_VERSION;
62using syncable::SPECIFICS;
63using syncable::ScopedDirLookup;
64using syncable::UNIQUE_SERVER_TAG;
65using syncable::UNITTEST;
66using syncable::WriteTransaction;
67using testing::_;
68using testing::DoAll;
69using testing::DoDefault;
70using testing::Invoke;
71using testing::Return;
72using testing::SetArgumentPointee;
73using testing::WithArgs;
74
75class HistoryBackendMock : public HistoryBackend {
76 public:
77  HistoryBackendMock() : HistoryBackend(FilePath(), NULL, NULL) {}
78  MOCK_METHOD1(GetAllTypedURLs, bool(std::vector<history::URLRow>* entries));
79  MOCK_METHOD2(GetVisitsForURL, bool(history::URLID id,
80                                     history::VisitVector* visits));
81  MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url));
82  MOCK_METHOD3(AddVisits, bool(const GURL& url,
83                               const std::vector<base::Time>& visits,
84                               history::VisitSource visit_source));
85  MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits));
86  MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row));
87  MOCK_METHOD2(SetPageTitle, void(const GURL& url, const string16& title));
88  MOCK_METHOD1(DeleteURL, void(const GURL& url));
89};
90
91class HistoryServiceMock : public HistoryService {
92 public:
93  HistoryServiceMock() {}
94  MOCK_METHOD2(ScheduleDBTask, Handle(HistoryDBTask*,
95                                      CancelableRequestConsumerBase*));
96};
97
98class RunOnDBThreadTask : public Task {
99 public:
100  RunOnDBThreadTask(HistoryBackend* backend, HistoryDBTask* task)
101      : backend_(backend), task_(task) {}
102  virtual void Run() {
103    task_->RunOnDBThread(backend_, NULL);
104    task_ = NULL;
105  }
106 private:
107  HistoryBackend* backend_;
108  scoped_refptr<HistoryDBTask> task_;
109};
110
111ACTION_P2(RunTaskOnDBThread, thread, backend) {
112 thread->message_loop()->PostTask(
113    FROM_HERE,
114    new RunOnDBThreadTask(backend, arg0));
115 return 0;
116}
117
118ACTION_P3(MakeTypedUrlSyncComponents, service, hb, dtc) {
119  TypedUrlModelAssociator* model_associator =
120      new TypedUrlModelAssociator(service, hb);
121  TypedUrlChangeProcessor* change_processor =
122      new TypedUrlChangeProcessor(model_associator, hb, service);
123  return ProfileSyncFactory::SyncComponents(model_associator, change_processor);
124}
125
126class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
127 protected:
128  ProfileSyncServiceTypedUrlTest()
129      : history_thread_("history") {
130  }
131
132  virtual void SetUp() {
133    profile_.CreateRequestContext();
134    history_backend_ = new HistoryBackendMock();
135    history_service_ = new HistoryServiceMock();
136    EXPECT_CALL((*history_service_.get()), ScheduleDBTask(_, _))
137        .WillRepeatedly(RunTaskOnDBThread(&history_thread_,
138                                          history_backend_.get()));
139    history_thread_.Start();
140
141    notification_service_ =
142      new ThreadNotificationService(&history_thread_);
143    notification_service_->Init();
144  }
145
146  virtual void TearDown() {
147    history_backend_ = NULL;
148    history_service_ = NULL;
149    service_.reset();
150    notification_service_->TearDown();
151    history_thread_.Stop();
152    {
153      // The request context gets deleted on the I/O thread. To prevent a leak
154      // supply one here.
155      BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
156      profile_.ResetRequestContext();
157    }
158    MessageLoop::current()->RunAllPending();
159  }
160
161  void StartSyncService(Task* task) {
162    if (!service_.get()) {
163      service_.reset(
164          new TestProfileSyncService(&factory_, &profile_, "test", false,
165                                     task));
166      TypedUrlDataTypeController* data_type_controller =
167          new TypedUrlDataTypeController(&factory_,
168                                         &profile_,
169                                         service_.get());
170
171      EXPECT_CALL(factory_, CreateTypedUrlSyncComponents(_, _, _)).
172          WillOnce(MakeTypedUrlSyncComponents(service_.get(),
173                                              history_backend_.get(),
174                                              data_type_controller));
175      EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
176          WillOnce(ReturnNewDataTypeManager());
177
178      EXPECT_CALL(profile_, GetHistoryServiceWithoutCreating()).
179          WillRepeatedly(Return(history_service_.get()));
180
181      EXPECT_CALL(profile_, GetPasswordStore(_)).
182          WillOnce(Return(static_cast<PasswordStore*>(NULL)));
183
184      EXPECT_CALL(profile_, GetHistoryService(_)).
185          WillRepeatedly(Return(history_service_.get()));
186
187      token_service_.IssueAuthTokenForTest(
188          GaiaConstants::kSyncService, "token");
189
190      EXPECT_CALL(profile_, GetTokenService()).
191          WillRepeatedly(Return(&token_service_));
192
193      service_->RegisterDataTypeController(data_type_controller);
194
195      service_->Initialize();
196      MessageLoop::current()->Run();
197    }
198  }
199
200  void AddTypedUrlSyncNode(const history::URLRow& url,
201                           const history::VisitVector& visits) {
202    sync_api::WriteTransaction trans(service_->GetUserShare());
203    sync_api::ReadNode typed_url_root(&trans);
204    ASSERT_TRUE(typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag));
205
206    sync_api::WriteNode node(&trans);
207    std::string tag = url.url().spec();
208    ASSERT_TRUE(node.InitUniqueByCreation(syncable::TYPED_URLS,
209                                          typed_url_root,
210                                          tag));
211    TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node);
212  }
213
214  void GetTypedUrlsFromSyncDB(std::vector<history::URLRow>* urls) {
215    sync_api::ReadTransaction trans(service_->GetUserShare());
216    sync_api::ReadNode typed_url_root(&trans);
217    if (!typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag))
218      return;
219
220    int64 child_id = typed_url_root.GetFirstChildId();
221    while (child_id != sync_api::kInvalidId) {
222      sync_api::ReadNode child_node(&trans);
223      if (!child_node.InitByIdLookup(child_id))
224        return;
225
226      const sync_pb::TypedUrlSpecifics& typed_url(
227          child_node.GetTypedUrlSpecifics());
228      history::URLRow new_url(GURL(typed_url.url()));
229
230      new_url.set_title(UTF8ToUTF16(typed_url.title()));
231      new_url.set_typed_count(typed_url.typed_count());
232      DCHECK(typed_url.visit_size());
233      new_url.set_visit_count(typed_url.visit_size());
234      new_url.set_last_visit(base::Time::FromInternalValue(
235          typed_url.visit(typed_url.visit_size() - 1)));
236      new_url.set_hidden(typed_url.hidden());
237
238      urls->push_back(new_url);
239      child_id = child_node.GetSuccessorId();
240    }
241  }
242
243  void SetIdleChangeProcessorExpectations() {
244    EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0);
245    EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0);
246    EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0);
247    EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0);
248  }
249
250  static bool URLsEqual(history::URLRow& lhs, history::URLRow& rhs) {
251    return (lhs.url().spec().compare(rhs.url().spec()) == 0) &&
252           (lhs.title().compare(rhs.title()) == 0) &&
253           (lhs.visit_count() == rhs.visit_count()) &&
254           (lhs.typed_count() == rhs.typed_count()) &&
255           (lhs.last_visit() == rhs.last_visit()) &&
256           (lhs.hidden() == rhs.hidden());
257  }
258
259  static history::URLRow MakeTypedUrlEntry(const char* url,
260                                           const char* title,
261                                           int typed_count,
262                                           int64 last_visit,
263                                           bool hidden,
264                                           history::VisitVector* visits) {
265    GURL gurl(url);
266    URLRow history_url(gurl);
267    history_url.set_title(UTF8ToUTF16(title));
268    history_url.set_typed_count(typed_count);
269    history_url.set_last_visit(
270        base::Time::FromInternalValue(last_visit));
271    history_url.set_hidden(hidden);
272    visits->push_back(history::VisitRow(
273        history_url.id(), history_url.last_visit(), 0, 0, 0));
274    history_url.set_visit_count(visits->size());
275    return history_url;
276  }
277
278  friend class AddTypedUrlEntriesTask;
279  friend class CreateTypedUrlRootTask;
280
281  Thread history_thread_;
282  scoped_refptr<ThreadNotificationService> notification_service_;
283
284  ProfileMock profile_;
285  ProfileSyncFactoryMock factory_;
286  scoped_refptr<HistoryBackendMock> history_backend_;
287  scoped_refptr<HistoryServiceMock> history_service_;
288};
289
290class AddTypedUrlEntriesTask : public Task {
291 public:
292  AddTypedUrlEntriesTask(ProfileSyncServiceTypedUrlTest* test,
293                         const std::vector<history::URLRow>& entries)
294      : test_(test), entries_(entries) {
295  }
296
297  virtual void Run() {
298    test_->CreateRoot(syncable::TYPED_URLS);
299    for (size_t i = 0; i < entries_.size(); ++i) {
300      history::VisitVector visits;
301      visits.push_back(history::VisitRow(
302          entries_[i].id(), entries_[i].last_visit(), 0, 0, 0));
303      test_->AddTypedUrlSyncNode(entries_[i], visits);
304    }
305  }
306
307 private:
308  ProfileSyncServiceTypedUrlTest* test_;
309  const std::vector<history::URLRow>& entries_;
310};
311
312TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) {
313  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
314      WillOnce(Return(true));
315  SetIdleChangeProcessorExpectations();
316  CreateRootTask task(this, syncable::TYPED_URLS);
317  StartSyncService(&task);
318  std::vector<history::URLRow> sync_entries;
319  GetTypedUrlsFromSyncDB(&sync_entries);
320  EXPECT_EQ(0U, sync_entries.size());
321}
322
323TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) {
324  std::vector<history::URLRow> entries;
325  history::VisitVector visits;
326  entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar",
327                                      2, 15, false, &visits));
328
329  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
330      WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
331  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
332      WillRepeatedly(DoAll(SetArgumentPointee<1>(visits), Return(true)));
333  SetIdleChangeProcessorExpectations();
334  CreateRootTask task(this, syncable::TYPED_URLS);
335  StartSyncService(&task);
336  std::vector<history::URLRow> sync_entries;
337  GetTypedUrlsFromSyncDB(&sync_entries);
338  ASSERT_EQ(1U, sync_entries.size());
339  EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0]));
340}
341
342TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) {
343  history::VisitVector native_visits;
344  history::VisitVector sync_visits;
345  history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
346                                                 2, 15, false, &native_visits));
347  history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
348                                               3, 16, false, &sync_visits));
349
350  std::vector<history::URLRow> native_entries;
351  native_entries.push_back(native_entry);
352  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
353      WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
354  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
355      WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true)));
356  EXPECT_CALL((*history_backend_.get()),
357      AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true));
358
359  std::vector<history::URLRow> sync_entries;
360  sync_entries.push_back(sync_entry);
361  AddTypedUrlEntriesTask task(this, sync_entries);
362
363  EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
364      WillRepeatedly(Return(true));
365  StartSyncService(&task);
366
367  std::map<std::string, history::URLRow> expected;
368  expected[native_entry.url().spec()] = native_entry;
369  expected[sync_entry.url().spec()] = sync_entry;
370
371  std::vector<history::URLRow> new_sync_entries;
372  GetTypedUrlsFromSyncDB(&new_sync_entries);
373
374  EXPECT_TRUE(new_sync_entries.size() == expected.size());
375  for (std::vector<history::URLRow>::iterator entry = new_sync_entries.begin();
376       entry != new_sync_entries.end(); ++entry) {
377    EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry));
378  }
379}
380
381TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) {
382  history::VisitVector native_visits;
383  history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
384                                                 2, 15, false, &native_visits));
385  history::VisitVector sync_visits;
386  history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name",
387                                               1, 17, false, &sync_visits));
388  history::VisitVector merged_visits;
389  merged_visits.push_back(history::VisitRow(
390      sync_entry.id(), base::Time::FromInternalValue(15), 0, 0, 0));
391
392  history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name",
393                                                 2, 17, false, &merged_visits));
394
395  std::vector<history::URLRow> native_entries;
396  native_entries.push_back(native_entry);
397  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
398      WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
399  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
400      WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true)));
401  EXPECT_CALL((*history_backend_.get()),
402      AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true));
403
404  std::vector<history::URLRow> sync_entries;
405  sync_entries.push_back(sync_entry);
406  AddTypedUrlEntriesTask task(this, sync_entries);
407
408  EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
409      WillRepeatedly(Return(true));
410  EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).
411      WillRepeatedly(Return());
412  StartSyncService(&task);
413
414  std::vector<history::URLRow> new_sync_entries;
415  GetTypedUrlsFromSyncDB(&new_sync_entries);
416  ASSERT_EQ(1U, new_sync_entries.size());
417  EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0]));
418}
419
420TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) {
421  history::VisitVector added_visits;
422  history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
423                                                2, 15, false, &added_visits));
424
425  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
426      WillOnce(Return(true));
427  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
428      WillOnce(DoAll(SetArgumentPointee<1>(added_visits), Return(true)));
429
430  SetIdleChangeProcessorExpectations();
431  CreateRootTask task(this, syncable::TYPED_URLS);
432  StartSyncService(&task);
433
434  history::URLsModifiedDetails details;
435  details.changed_urls.push_back(added_entry);
436  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
437  notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED,
438                   Details<history::URLsModifiedDetails>(&details));
439
440  std::vector<history::URLRow> new_sync_entries;
441  GetTypedUrlsFromSyncDB(&new_sync_entries);
442  ASSERT_EQ(1U, new_sync_entries.size());
443  EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
444}
445
446TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) {
447  history::VisitVector original_visits;
448  history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
449                                                   2, 15, false,
450                                                   &original_visits));
451  std::vector<history::URLRow> original_entries;
452  original_entries.push_back(original_entry);
453
454  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
455      WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
456  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
457      WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits),
458                           Return(true)));
459  CreateRootTask task(this, syncable::TYPED_URLS);
460  StartSyncService(&task);
461
462  history::VisitVector updated_visits;
463  history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
464                                                  7, 15, false,
465                                                  &updated_visits));
466
467  history::URLsModifiedDetails details;
468  details.changed_urls.push_back(updated_entry);
469  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
470  notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED,
471                   Details<history::URLsModifiedDetails>(&details));
472
473  std::vector<history::URLRow> new_sync_entries;
474  GetTypedUrlsFromSyncDB(&new_sync_entries);
475  ASSERT_EQ(1U, new_sync_entries.size());
476  EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0]));
477}
478
479TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) {
480  history::VisitVector original_visits1;
481  history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
482                                                    2, 15, false,
483                                                    &original_visits1));
484  history::VisitVector original_visits2;
485  history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
486                                                    "entry2",
487                                                    3, 15, false,
488                                                    &original_visits2));
489  std::vector<history::URLRow> original_entries;
490  original_entries.push_back(original_entry1);
491  original_entries.push_back(original_entry2);
492
493  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
494      WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
495  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
496      WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1),
497                           Return(true)));
498  CreateRootTask task(this, syncable::TYPED_URLS);
499  StartSyncService(&task);
500
501  history::URLsDeletedDetails changes;
502  changes.all_history = false;
503  changes.urls.insert(GURL("http://mine.com"));
504  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
505  notifier->Notify(NotificationType::HISTORY_URLS_DELETED,
506                   Details<history::URLsDeletedDetails>(&changes));
507
508  std::vector<history::URLRow> new_sync_entries;
509  GetTypedUrlsFromSyncDB(&new_sync_entries);
510  ASSERT_EQ(1U, new_sync_entries.size());
511  EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0]));
512}
513
514TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) {
515  history::VisitVector original_visits1;
516  history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
517                                                    2, 15, false,
518                                                    &original_visits1));
519  history::VisitVector original_visits2;
520  history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
521                                                    "entry2",
522                                                    3, 15, false,
523                                                    &original_visits2));
524  std::vector<history::URLRow> original_entries;
525  original_entries.push_back(original_entry1);
526  original_entries.push_back(original_entry2);
527
528  EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
529      WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
530  EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)).
531      WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1),
532                           Return(true)));
533  CreateRootTask task(this, syncable::TYPED_URLS);
534  StartSyncService(&task);
535
536  history::URLsDeletedDetails changes;
537  changes.all_history = true;
538  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
539  notifier->Notify(NotificationType::HISTORY_URLS_DELETED,
540                   Details<history::URLsDeletedDetails>(&changes));
541
542  std::vector<history::URLRow> new_sync_entries;
543  GetTypedUrlsFromSyncDB(&new_sync_entries);
544  ASSERT_EQ(0U, new_sync_entries.size());
545}
546