1 // Copyright 2019 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 "components/history/core/browser/sync/delete_directive_handler.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/test/mock_callback.h"
12 #include "base/test/task_environment.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "components/history/core/browser/history_backend.h"
16 #include "components/history/core/browser/history_backend_client.h"
17 #include "components/history/core/browser/history_database_params.h"
18 #include "components/history/core/browser/history_db_task.h"
19 #include "components/history/core/browser/history_types.h"
20 #include "components/history/core/browser/in_memory_history_backend.h"
21 #include "components/history/core/test/test_history_database.h"
22 #include "components/sync/model/fake_sync_change_processor.h"
23 #include "components/sync/model/sync_change_processor_wrapper_for_test.h"
24 #include "components/sync/model/sync_error_factory.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 
28 namespace history {
29 
30 namespace {
31 
UnixUsecToTime(int64_t usec)32 base::Time UnixUsecToTime(int64_t usec) {
33   return base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(usec);
34 }
35 
36 class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
37  public:
TestHistoryBackendDelegate()38   TestHistoryBackendDelegate() {}
39 
NotifyProfileError(sql::InitStatus init_status,const std::string & diagnostics)40   void NotifyProfileError(sql::InitStatus init_status,
41                           const std::string& diagnostics) override {}
SetInMemoryBackend(std::unique_ptr<InMemoryHistoryBackend> backend)42   void SetInMemoryBackend(
43       std::unique_ptr<InMemoryHistoryBackend> backend) override {}
NotifyFaviconsChanged(const std::set<GURL> & page_urls,const GURL & icon_url)44   void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
45                              const GURL& icon_url) override {}
NotifyURLVisited(ui::PageTransition transition,const URLRow & row,const RedirectList & redirects,base::Time visit_time)46   void NotifyURLVisited(ui::PageTransition transition,
47                         const URLRow& row,
48                         const RedirectList& redirects,
49                         base::Time visit_time) override {}
NotifyURLsModified(const URLRows & changed_urls)50   void NotifyURLsModified(const URLRows& changed_urls) override {}
NotifyURLsDeleted(DeletionInfo deletion_info)51   void NotifyURLsDeleted(DeletionInfo deletion_info) override {}
NotifyKeywordSearchTermUpdated(const URLRow & row,KeywordID keyword_id,const base::string16 & term)52   void NotifyKeywordSearchTermUpdated(const URLRow& row,
53                                       KeywordID keyword_id,
54                                       const base::string16& term) override {}
NotifyKeywordSearchTermDeleted(URLID url_id)55   void NotifyKeywordSearchTermDeleted(URLID url_id) override {}
DBLoaded()56   void DBLoaded() override {}
57 
58  private:
59   DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate);
60 };
61 
ScheduleDBTask(scoped_refptr<HistoryBackend> history_backend,const base::Location & location,std::unique_ptr<HistoryDBTask> task,base::CancelableTaskTracker * tracker)62 void ScheduleDBTask(scoped_refptr<HistoryBackend> history_backend,
63                     const base::Location& location,
64                     std::unique_ptr<HistoryDBTask> task,
65                     base::CancelableTaskTracker* tracker) {
66   base::CancelableTaskTracker::IsCanceledCallback is_canceled;
67   tracker->NewTrackedTaskId(&is_canceled);
68   history_backend->ProcessDBTask(
69       std::move(task), base::ThreadTaskRunnerHandle::Get(), is_canceled);
70 }
71 
72 // Closure function that runs periodically to check result of delete directive
73 // processing. Stop when timeout or processing ends indicated by the creation
74 // of sync changes.
CheckDirectiveProcessingResult(base::Time timeout,const syncer::FakeSyncChangeProcessor * change_processor,uint32_t num_changes)75 void CheckDirectiveProcessingResult(
76     base::Time timeout,
77     const syncer::FakeSyncChangeProcessor* change_processor,
78     uint32_t num_changes) {
79   if (base::Time::Now() > timeout ||
80       change_processor->changes().size() >= num_changes) {
81     return;
82   }
83 
84   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
85   base::ThreadTaskRunnerHandle::Get()->PostTask(
86       FROM_HERE, base::BindOnce(&CheckDirectiveProcessingResult, timeout,
87                                 change_processor, num_changes));
88 }
89 
90 class HistoryDeleteDirectiveHandlerTest : public testing::Test {
91  public:
HistoryDeleteDirectiveHandlerTest()92   HistoryDeleteDirectiveHandlerTest()
93       : history_backend_(base::MakeRefCounted<HistoryBackend>(
94             std::make_unique<TestHistoryBackendDelegate>(),
95             /*backend_client=*/nullptr,
96             base::ThreadTaskRunnerHandle::Get())) {}
97 
SetUp()98   void SetUp() override {
99     ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
100     history_backend_->Init(
101         false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath()));
102 
103     delete_directive_handler_ = std::make_unique<DeleteDirectiveHandler>(
104         base::BindRepeating(&ScheduleDBTask, history_backend_));
105   }
106 
AddPage(const GURL & url,base::Time t)107   void AddPage(const GURL& url, base::Time t) {
108     history::HistoryAddPageArgs args;
109     args.url = url;
110     args.time = t;
111     history_backend_->AddPage(args);
112   }
113 
QueryURL(const GURL & url)114   QueryURLResult QueryURL(const GURL& url) {
115     return history_backend_->QueryURL(url, /*want_visits=*/true);
116   }
117 
~HistoryDeleteDirectiveHandlerTest()118   ~HistoryDeleteDirectiveHandlerTest() override { history_backend_->Closing(); }
119 
history_backend()120   scoped_refptr<HistoryBackend> history_backend() { return history_backend_; }
121 
handler()122   DeleteDirectiveHandler* handler() { return delete_directive_handler_.get(); }
123 
124  private:
125   base::test::TaskEnvironment task_environment_;
126   base::ScopedTempDir test_dir_;
127   scoped_refptr<HistoryBackend> history_backend_;
128   std::unique_ptr<DeleteDirectiveHandler> delete_directive_handler_;
129 
130   DISALLOW_COPY_AND_ASSIGN(HistoryDeleteDirectiveHandlerTest);
131 };
132 
133 // Tests calling WaitUntilReadyToSync() after the backend has already been
134 // loaded, which should report completion immediately.
TEST_F(HistoryDeleteDirectiveHandlerTest,SyncAlreadyReadyToSync)135 TEST_F(HistoryDeleteDirectiveHandlerTest, SyncAlreadyReadyToSync) {
136   base::MockCallback<base::OnceClosure> ready_cb;
137   handler()->OnBackendLoaded();
138   EXPECT_CALL(ready_cb, Run());
139   handler()->WaitUntilReadyToSync(ready_cb.Get());
140 }
141 
142 // Tests calling WaitUntilReadyToSync() befire the backend has been loaded,
143 // which should only report completion after the backend loading is completed.
TEST_F(HistoryDeleteDirectiveHandlerTest,WaitUntilReadyToSync)144 TEST_F(HistoryDeleteDirectiveHandlerTest, WaitUntilReadyToSync) {
145   base::MockCallback<base::OnceClosure> ready_cb;
146   EXPECT_CALL(ready_cb, Run()).Times(0);
147   handler()->WaitUntilReadyToSync(ready_cb.Get());
148   EXPECT_CALL(ready_cb, Run());
149   handler()->OnBackendLoaded();
150 }
151 
152 // Create a local delete directive and process it while sync is
153 // online, and then when offline. The delete directive should be sent to sync,
154 // no error should be returned for the first time, and an error should be
155 // returned for the second time.
TEST_F(HistoryDeleteDirectiveHandlerTest,ProcessLocalDeleteDirectiveSyncOnline)156 TEST_F(HistoryDeleteDirectiveHandlerTest,
157        ProcessLocalDeleteDirectiveSyncOnline) {
158   const GURL test_url("http://www.google.com/");
159   for (int64_t i = 1; i <= 10; ++i) {
160     AddPage(test_url, UnixUsecToTime(i));
161   }
162 
163   sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
164   sync_pb::GlobalIdDirective* global_id_directive =
165       delete_directive.mutable_global_id_directive();
166   global_id_directive->add_global_id(
167       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1))
168           .ToInternalValue());
169 
170   syncer::FakeSyncChangeProcessor change_processor;
171 
172   EXPECT_FALSE(
173       handler()
174           ->MergeDataAndStartSyncing(
175               syncer::HISTORY_DELETE_DIRECTIVES, syncer::SyncDataList(),
176               std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
177                   &change_processor),
178               std::unique_ptr<syncer::SyncErrorFactory>())
179           .error()
180           .IsSet());
181 
182   syncer::SyncError err =
183       handler()->ProcessLocalDeleteDirective(delete_directive);
184   EXPECT_FALSE(err.IsSet());
185   EXPECT_EQ(1u, change_processor.changes().size());
186 
187   handler()->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES);
188   err = handler()->ProcessLocalDeleteDirective(delete_directive);
189   EXPECT_TRUE(err.IsSet());
190   EXPECT_EQ(1u, change_processor.changes().size());
191 }
192 
193 // Create a delete directive for a few specific history entries,
194 // including ones that don't exist. The expected entries should be
195 // deleted.
TEST_F(HistoryDeleteDirectiveHandlerTest,ProcessGlobalIdDeleteDirective)196 TEST_F(HistoryDeleteDirectiveHandlerTest, ProcessGlobalIdDeleteDirective) {
197   const GURL test_url("http://www.google.com/");
198   for (int64_t i = 1; i <= 20; i++) {
199     AddPage(test_url, UnixUsecToTime(i));
200   }
201 
202   {
203     QueryURLResult query = QueryURL(test_url);
204     EXPECT_TRUE(query.success);
205     EXPECT_EQ(20, query.row.visit_count());
206   }
207 
208   syncer::SyncDataList directives;
209   // 1st directive.
210   sync_pb::EntitySpecifics entity_specs;
211   sync_pb::GlobalIdDirective* global_id_directive =
212       entity_specs.mutable_history_delete_directive()
213           ->mutable_global_id_directive();
214   global_id_directive->add_global_id(
215       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6))
216           .ToInternalValue());
217   global_id_directive->set_start_time_usec(3);
218   global_id_directive->set_end_time_usec(10);
219   directives.push_back(syncer::SyncData::CreateRemoteData(1, entity_specs));
220 
221   // 2nd directive.
222   global_id_directive->Clear();
223   global_id_directive->add_global_id(
224       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17))
225           .ToInternalValue());
226   global_id_directive->set_start_time_usec(13);
227   global_id_directive->set_end_time_usec(19);
228   directives.push_back(syncer::SyncData::CreateRemoteData(2, entity_specs));
229 
230   syncer::FakeSyncChangeProcessor change_processor;
231   EXPECT_FALSE(handler()
232                    ->MergeDataAndStartSyncing(
233                        syncer::HISTORY_DELETE_DIRECTIVES, directives,
234                        std::unique_ptr<syncer::SyncChangeProcessor>(
235                            new syncer::SyncChangeProcessorWrapperForTest(
236                                &change_processor)),
237                        std::unique_ptr<syncer::SyncErrorFactory>())
238                    .error()
239                    .IsSet());
240 
241   // Inject a task to check status and keep message loop filled before directive
242   // processing finishes.
243   base::ThreadTaskRunnerHandle::Get()->PostTask(
244       FROM_HERE,
245       base::BindOnce(&CheckDirectiveProcessingResult,
246                      base::Time::Now() + base::TimeDelta::FromSeconds(10),
247                      &change_processor, 2));
248   base::RunLoop().RunUntilIdle();
249 
250   QueryURLResult query = QueryURL(test_url);
251   EXPECT_TRUE(query.success);
252   ASSERT_EQ(5, query.row.visit_count());
253   EXPECT_EQ(UnixUsecToTime(1), query.visits[0].visit_time);
254   EXPECT_EQ(UnixUsecToTime(2), query.visits[1].visit_time);
255   EXPECT_EQ(UnixUsecToTime(11), query.visits[2].visit_time);
256   EXPECT_EQ(UnixUsecToTime(12), query.visits[3].visit_time);
257   EXPECT_EQ(UnixUsecToTime(20), query.visits[4].visit_time);
258 
259   // Expect two sync changes for deleting processed directives.
260   const syncer::SyncChangeList& sync_changes = change_processor.changes();
261   ASSERT_EQ(2u, sync_changes.size());
262   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
263   EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
264   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
265   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
266 }
267 
268 // Create delete directives for time ranges.  The expected entries should be
269 // deleted.
TEST_F(HistoryDeleteDirectiveHandlerTest,ProcessTimeRangeDeleteDirective)270 TEST_F(HistoryDeleteDirectiveHandlerTest, ProcessTimeRangeDeleteDirective) {
271   const GURL test_url("http://www.google.com/");
272   for (int64_t i = 1; i <= 10; ++i) {
273     AddPage(test_url, UnixUsecToTime(i));
274   }
275 
276   {
277     QueryURLResult query = QueryURL(test_url);
278     EXPECT_TRUE(query.success);
279     EXPECT_EQ(10, query.row.visit_count());
280   }
281 
282   syncer::SyncDataList directives;
283   // 1st directive.
284   sync_pb::EntitySpecifics entity_specs;
285   sync_pb::TimeRangeDirective* time_range_directive =
286       entity_specs.mutable_history_delete_directive()
287           ->mutable_time_range_directive();
288   time_range_directive->set_start_time_usec(2);
289   time_range_directive->set_end_time_usec(5);
290   directives.push_back(syncer::SyncData::CreateRemoteData(1, entity_specs));
291 
292   // 2nd directive.
293   time_range_directive->Clear();
294   time_range_directive->set_start_time_usec(8);
295   time_range_directive->set_end_time_usec(10);
296   directives.push_back(syncer::SyncData::CreateRemoteData(2, entity_specs));
297 
298   syncer::FakeSyncChangeProcessor change_processor;
299   EXPECT_FALSE(handler()
300                    ->MergeDataAndStartSyncing(
301                        syncer::HISTORY_DELETE_DIRECTIVES, directives,
302                        std::unique_ptr<syncer::SyncChangeProcessor>(
303                            new syncer::SyncChangeProcessorWrapperForTest(
304                                &change_processor)),
305                        std::unique_ptr<syncer::SyncErrorFactory>())
306                    .error()
307                    .IsSet());
308 
309   // Inject a task to check status and keep message loop filled before
310   // directive processing finishes.
311   base::ThreadTaskRunnerHandle::Get()->PostTask(
312       FROM_HERE,
313       base::BindOnce(&CheckDirectiveProcessingResult,
314                      base::Time::Now() + base::TimeDelta::FromSeconds(10),
315                      &change_processor, 2));
316   base::RunLoop().RunUntilIdle();
317 
318   QueryURLResult query = QueryURL(test_url);
319   EXPECT_TRUE(query.success);
320   ASSERT_EQ(3, query.row.visit_count());
321   EXPECT_EQ(UnixUsecToTime(1), query.visits[0].visit_time);
322   EXPECT_EQ(UnixUsecToTime(6), query.visits[1].visit_time);
323   EXPECT_EQ(UnixUsecToTime(7), query.visits[2].visit_time);
324 
325   // Expect two sync changes for deleting processed directives.
326   const syncer::SyncChangeList& sync_changes = change_processor.changes();
327   ASSERT_EQ(2u, sync_changes.size());
328   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
329   EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
330   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
331   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
332 }
333 
334 // Create a delete directive for urls.  The expected entries should be
335 // deleted.
TEST_F(HistoryDeleteDirectiveHandlerTest,ProcessUrlDeleteDirective)336 TEST_F(HistoryDeleteDirectiveHandlerTest, ProcessUrlDeleteDirective) {
337   const GURL test_url1("http://www.google.com/");
338   const GURL test_url2("http://maps.google.com/");
339 
340   AddPage(test_url1, UnixUsecToTime(3));
341   AddPage(test_url2, UnixUsecToTime(6));
342   AddPage(test_url1, UnixUsecToTime(10));
343 
344   {
345     QueryURLResult query = QueryURL(test_url1);
346     EXPECT_TRUE(query.success);
347     ASSERT_EQ(2, query.row.visit_count());
348     EXPECT_TRUE(QueryURL(test_url2).success);
349   }
350 
351   // Delete the first visit of url1 and all visits of url2.
352   syncer::SyncDataList directives;
353   sync_pb::EntitySpecifics entity_specs1;
354   sync_pb::UrlDirective* url_directive =
355       entity_specs1.mutable_history_delete_directive()->mutable_url_directive();
356   url_directive->set_url(test_url1.spec());
357   url_directive->set_end_time_usec(8);
358   directives.push_back(syncer::SyncData::CreateRemoteData(1, entity_specs1));
359   sync_pb::EntitySpecifics entity_specs2;
360   url_directive =
361       entity_specs2.mutable_history_delete_directive()->mutable_url_directive();
362   url_directive->set_url(test_url2.spec());
363   url_directive->set_end_time_usec(8);
364   directives.push_back(syncer::SyncData::CreateRemoteData(2, entity_specs2));
365 
366   syncer::FakeSyncChangeProcessor change_processor;
367   EXPECT_FALSE(handler()
368                    ->MergeDataAndStartSyncing(
369                        syncer::HISTORY_DELETE_DIRECTIVES, directives,
370                        std::unique_ptr<syncer::SyncChangeProcessor>(
371                            new syncer::SyncChangeProcessorWrapperForTest(
372                                &change_processor)),
373                        std::unique_ptr<syncer::SyncErrorFactory>())
374                    .error()
375                    .IsSet());
376 
377   // Inject a task to check status and keep message loop filled before
378   // directive processing finishes.
379   base::ThreadTaskRunnerHandle::Get()->PostTask(
380       FROM_HERE,
381       base::BindOnce(&CheckDirectiveProcessingResult,
382                      base::Time::Now() + base::TimeDelta::FromSeconds(10),
383                      &change_processor, 2));
384   base::RunLoop().RunUntilIdle();
385 
386   QueryURLResult query = QueryURL(test_url1);
387   EXPECT_TRUE(query.success);
388   EXPECT_EQ(UnixUsecToTime(10), query.visits[0].visit_time);
389   EXPECT_FALSE(QueryURL(test_url2).success);
390 
391   // Expect a sync change for deleting processed directives.
392   const syncer::SyncChangeList& sync_changes = change_processor.changes();
393   ASSERT_EQ(2u, sync_changes.size());
394   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
395   EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
396   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
397   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
398 }
399 
400 }  // namespace
401 
402 }  // namespace history
403