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.
4 
5 // History unit tests come in two flavors:
6 //
7 // 1. The more complicated style is that the unit test creates a full history
8 //    service. This spawns a background thread for the history backend, and
9 //    all communication is asynchronous. This is useful for testing more
10 //    complicated things or end-to-end behavior.
11 //
12 // 2. The simpler style is to create a history backend on this thread and
13 //    access it directly without a HistoryService object. This is much simpler
14 //    because communication is synchronous. Generally, sets should go through
15 //    the history backend (since there is a lot of logic) but gets can come
16 //    directly from the HistoryDatabase. This is because the backend generally
17 //    has no logic in the getter except threading stuff, which we don't want
18 //    to run.
19 
20 #include "components/history/core/browser/history_service.h"
21 
22 #include <stdint.h>
23 
24 #include <algorithm>
25 #include <array>
26 #include <string>
27 #include <vector>
28 
29 #include "base/bind.h"
30 #include "base/files/file_util.h"
31 #include "base/files/scoped_temp_dir.h"
32 #include "base/location.h"
33 #include "base/macros.h"
34 #include "base/run_loop.h"
35 #include "base/single_thread_task_runner.h"
36 #include "base/strings/utf_string_conversions.h"
37 #include "base/test/bind.h"
38 #include "base/test/metrics/histogram_tester.h"
39 #include "base/test/task_environment.h"
40 #include "base/threading/thread_task_runner_handle.h"
41 #include "components/history/core/browser/history_backend.h"
42 #include "components/history/core/browser/history_database_params.h"
43 #include "components/history/core/browser/history_db_task.h"
44 #include "components/history/core/test/database_test_utils.h"
45 #include "components/history/core/test/test_history_database.h"
46 #include "components/sync/model/sync_change.h"
47 #include "components/sync/model/sync_change_processor.h"
48 #include "components/sync/model/sync_error.h"
49 #include "components/sync/model/sync_error_factory.h"
50 #include "components/sync/protocol/history_delete_directive_specifics.pb.h"
51 #include "components/sync/protocol/sync.pb.h"
52 #include "components/sync/test/model/fake_sync_change_processor.h"
53 #include "components/sync/test/model/sync_change_processor_wrapper_for_test.h"
54 #include "testing/gtest/include/gtest/gtest.h"
55 
56 namespace history {
57 
58 class HistoryServiceTest : public testing::Test {
59  public:
60   HistoryServiceTest() = default;
~HistoryServiceTest()61   ~HistoryServiceTest() override {}
62 
63  protected:
64   friend class BackendDelegate;
65 
66   // testing::Test
SetUp()67   void SetUp() override {
68     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
69     history_dir_ = temp_dir_.GetPath().AppendASCII("HistoryServiceTest");
70     ASSERT_TRUE(base::CreateDirectory(history_dir_));
71     history_service_.reset(new history::HistoryService);
72     if (!history_service_->Init(
73             TestHistoryDatabaseParamsForPath(history_dir_))) {
74       history_service_.reset();
75       ADD_FAILURE();
76     }
77   }
78 
TearDown()79   void TearDown() override {
80     if (history_service_)
81       CleanupHistoryService();
82 
83     // Make sure we don't have any event pending that could disrupt the next
84     // test.
85     base::RunLoop().RunUntilIdle();
86   }
87 
CleanupHistoryService()88   void CleanupHistoryService() {
89     DCHECK(history_service_);
90 
91     base::RunLoop run_loop;
92     history_service_->ClearCachedDataForContextID(nullptr);
93     history_service_->SetOnBackendDestroyTask(run_loop.QuitClosure());
94     history_service_->Cleanup();
95     history_service_.reset();
96 
97     // Wait for the backend class to terminate before deleting the files and
98     // moving to the next test. Note: if this never terminates, somebody is
99     // probably leaking a reference to the history backend, so it never calls
100     // our destroy task.
101     run_loop.Run();
102   }
103 
104   // Fills the query_url_result_ structures with the information about the given
105   // URL and whether the operation succeeded or not.
QueryURL(history::HistoryService * history,const GURL & url)106   bool QueryURL(history::HistoryService* history, const GURL& url) {
107     base::RunLoop run_loop;
108     history_service_->QueryURL(
109         url, true,
110         base::BindLambdaForTesting([&](history::QueryURLResult result) {
111           query_url_result_ = std::move(result);
112           run_loop.Quit();
113         }),
114         &tracker_);
115     run_loop.Run();  // Will be exited in SaveURLAndQuit.
116     return query_url_result_.success;
117   }
118 
119   // Fills in saved_redirects_ with the redirect information for the given URL,
120   // returning true on success. False means the URL was not found.
QueryRedirectsFrom(history::HistoryService * history,const GURL & url)121   void QueryRedirectsFrom(history::HistoryService* history, const GURL& url) {
122     base::RunLoop run_loop;
123     history_service_->QueryRedirectsFrom(
124         url,
125         base::BindOnce(&HistoryServiceTest::OnRedirectQueryComplete,
126                        base::Unretained(this), run_loop.QuitClosure()),
127         &tracker_);
128     run_loop.Run();  // Will be exited in *QueryComplete.
129   }
130 
131   // Callback for QueryRedirects.
OnRedirectQueryComplete(base::OnceClosure done,history::RedirectList redirects)132   void OnRedirectQueryComplete(base::OnceClosure done,
133                                history::RedirectList redirects) {
134     saved_redirects_ = std::move(redirects);
135     std::move(done).Run();
136   }
137 
QueryMostVisitedURLs()138   void QueryMostVisitedURLs() {
139     const int kResultCount = 20;
140     const int kDaysBack = 90;
141 
142     base::RunLoop run_loop;
143     history_service_->QueryMostVisitedURLs(
144         kResultCount, kDaysBack,
145         base::BindLambdaForTesting([&](MostVisitedURLList urls) {
146           most_visited_urls_ = urls;
147           run_loop.Quit();
148         }),
149         &tracker_);
150     run_loop.Run();  // Will be exited in *QueryComplete.
151   }
152 
153   base::ScopedTempDir temp_dir_;
154 
155   base::test::TaskEnvironment task_environment_;
156 
157   MostVisitedURLList most_visited_urls_;
158 
159   // When non-NULL, this will be deleted on tear down and we will block until
160   // the backend thread has completed. This allows tests for the history
161   // service to use this feature, but other tests to ignore this.
162   std::unique_ptr<history::HistoryService> history_service_;
163 
164   // names of the database files
165   base::FilePath history_dir_;
166 
167   // Set by the redirect callback when we get data. You should be sure to
168   // clear this before issuing a redirect request.
169   history::RedirectList saved_redirects_;
170 
171   // For history requests.
172   base::CancelableTaskTracker tracker_;
173 
174   // For saving URL info after a call to QueryURL
175   history::QueryURLResult query_url_result_;
176 };
177 
178 // Simple test that removes a bookmark. This test exercises the code paths in
179 // History that block till BookmarkModel is loaded.
TEST_F(HistoryServiceTest,RemoveNotification)180 TEST_F(HistoryServiceTest, RemoveNotification) {
181   ASSERT_TRUE(history_service_.get());
182 
183   // Add a URL.
184   GURL url("http://www.google.com");
185 
186   history_service_->AddPage(url, base::Time::Now(), nullptr, 1, GURL(),
187                             RedirectList(), ui::PAGE_TRANSITION_TYPED,
188                             SOURCE_BROWSED, false, false);
189 
190   // This won't actually delete the URL, rather it'll empty out the visits.
191   // This triggers blocking on the BookmarkModel.
192   history_service_->DeleteURLs({url});
193 }
194 
TEST_F(HistoryServiceTest,AddPage)195 TEST_F(HistoryServiceTest, AddPage) {
196   ASSERT_TRUE(history_service_.get());
197   // Add the page once from a child frame.
198   const GURL test_url("http://www.google.com/");
199   history_service_->AddPage(test_url, base::Time::Now(), nullptr, 0, GURL(),
200                             history::RedirectList(),
201                             ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
202                             history::SOURCE_BROWSED, false, false);
203   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
204   EXPECT_EQ(1, query_url_result_.row.visit_count());
205   EXPECT_EQ(0, query_url_result_.row.typed_count());
206   EXPECT_TRUE(
207       query_url_result_.row.hidden());  // Hidden because of child frame.
208 
209   // Add the page once from the main frame (should unhide it).
210   history_service_->AddPage(test_url, base::Time::Now(), nullptr, 0, GURL(),
211                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
212                             history::SOURCE_BROWSED, false, false);
213   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
214   EXPECT_EQ(2, query_url_result_.row.visit_count());  // Added twice.
215   EXPECT_EQ(0, query_url_result_.row.typed_count());  // Never typed.
216   EXPECT_FALSE(
217       query_url_result_.row.hidden());  // Because loaded in main frame.
218 }
219 
TEST_F(HistoryServiceTest,AddRedirect)220 TEST_F(HistoryServiceTest, AddRedirect) {
221   ASSERT_TRUE(history_service_.get());
222   history::RedirectList first_redirects = {GURL("http://first.page.com/"),
223                                            GURL("http://second.page.com/")};
224 
225   // Add the sequence of pages as a server with no referrer. Note that we need
226   // to have a non-NULL page ID scope.
227   history_service_->AddPage(first_redirects.back(), base::Time::Now(),
228                             reinterpret_cast<ContextID>(1), 0, GURL(),
229                             first_redirects, ui::PAGE_TRANSITION_LINK,
230                             history::SOURCE_BROWSED, true, false);
231 
232   // The first page should be added once with a link visit type (because we set
233   // LINK when we added the original URL, and a referrer of nowhere (0).
234   EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[0]));
235   EXPECT_EQ(1, query_url_result_.row.visit_count());
236   ASSERT_EQ(1U, query_url_result_.visits.size());
237   int64_t first_visit = query_url_result_.visits[0].visit_id;
238   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
239       query_url_result_.visits[0].transition,
240       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
241                                 ui::PAGE_TRANSITION_CHAIN_START)));
242   EXPECT_EQ(0, query_url_result_.visits[0].referring_visit);  // No referrer.
243 
244   // The second page should be a server redirect type with a referrer of the
245   // first page.
246   EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[1]));
247   EXPECT_EQ(1, query_url_result_.row.visit_count());
248   ASSERT_EQ(1U, query_url_result_.visits.size());
249   int64_t second_visit = query_url_result_.visits[0].visit_id;
250   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
251       query_url_result_.visits[0].transition,
252       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_SERVER_REDIRECT |
253                                 ui::PAGE_TRANSITION_CHAIN_END)));
254   EXPECT_EQ(first_visit, query_url_result_.visits[0].referring_visit);
255 
256   // Check that the redirect finding function successfully reports it.
257   saved_redirects_.clear();
258   QueryRedirectsFrom(history_service_.get(), first_redirects[0]);
259   ASSERT_EQ(1U, saved_redirects_.size());
260   EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
261 
262   // Now add a client redirect from that second visit to a third, client
263   // redirects are tracked by the RenderView prior to updating history,
264   // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
265   history::RedirectList second_redirects = {first_redirects[1],
266                                             GURL("http://last.page.com/")};
267   history_service_->AddPage(
268       second_redirects[1], base::Time::Now(), reinterpret_cast<ContextID>(1), 1,
269       second_redirects[0], second_redirects,
270       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
271                                 ui::PAGE_TRANSITION_CLIENT_REDIRECT),
272       history::SOURCE_BROWSED, true, false);
273 
274   // The last page (source of the client redirect) should NOT have an
275   // additional visit added, because it was a client redirect (normally it
276   // would). We should only have 1 left over from the first sequence.
277   EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[0]));
278   EXPECT_EQ(1, query_url_result_.row.visit_count());
279 
280   // The final page should be set as a client redirect from the previous visit.
281   EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[1]));
282   EXPECT_EQ(1, query_url_result_.row.visit_count());
283   ASSERT_EQ(1U, query_url_result_.visits.size());
284   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
285       query_url_result_.visits[0].transition,
286       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_CLIENT_REDIRECT |
287                                 ui::PAGE_TRANSITION_CHAIN_END)));
288   EXPECT_EQ(second_visit, query_url_result_.visits[0].referring_visit);
289 }
290 
TEST_F(HistoryServiceTest,MakeIntranetURLsTyped)291 TEST_F(HistoryServiceTest, MakeIntranetURLsTyped) {
292   ASSERT_TRUE(history_service_.get());
293 
294   // Add a non-typed visit to an intranet URL on an unvisited host.  This should
295   // get promoted to a typed visit.
296   const GURL test_url("http://intranet_host/path");
297   history_service_->AddPage(test_url, base::Time::Now(), nullptr, 0, GURL(),
298                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
299                             history::SOURCE_BROWSED, false, false);
300   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
301   EXPECT_EQ(1, query_url_result_.row.visit_count());
302   EXPECT_EQ(1, query_url_result_.row.typed_count());
303   ASSERT_EQ(1U, query_url_result_.visits.size());
304   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
305       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_TYPED));
306 
307   // Add more visits on the same host.  None of these should be promoted since
308   // there is already a typed visit.
309 
310   // Different path.
311   const GURL test_url2("http://intranet_host/different_path");
312   history_service_->AddPage(test_url2, base::Time::Now(), nullptr, 0, GURL(),
313                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
314                             history::SOURCE_BROWSED, false, false);
315   EXPECT_TRUE(QueryURL(history_service_.get(), test_url2));
316   EXPECT_EQ(1, query_url_result_.row.visit_count());
317   EXPECT_EQ(0, query_url_result_.row.typed_count());
318   ASSERT_EQ(1U, query_url_result_.visits.size());
319   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
320       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_LINK));
321 
322   // No path.
323   const GURL test_url3("http://intranet_host/");
324   history_service_->AddPage(test_url3, base::Time::Now(), nullptr, 0, GURL(),
325                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
326                             history::SOURCE_BROWSED, false, false);
327   EXPECT_TRUE(QueryURL(history_service_.get(), test_url3));
328   EXPECT_EQ(1, query_url_result_.row.visit_count());
329   EXPECT_EQ(0, query_url_result_.row.typed_count());
330   ASSERT_EQ(1U, query_url_result_.visits.size());
331   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
332       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_LINK));
333 
334   // Different scheme.
335   const GURL test_url4("https://intranet_host/");
336   history_service_->AddPage(test_url4, base::Time::Now(), nullptr, 0, GURL(),
337                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
338                             history::SOURCE_BROWSED, false, false);
339   EXPECT_TRUE(QueryURL(history_service_.get(), test_url4));
340   EXPECT_EQ(1, query_url_result_.row.visit_count());
341   EXPECT_EQ(0, query_url_result_.row.typed_count());
342   ASSERT_EQ(1U, query_url_result_.visits.size());
343   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
344       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_LINK));
345 
346   // Different transition.
347   const GURL test_url5("http://intranet_host/another_path");
348   history_service_->AddPage(
349       test_url5, base::Time::Now(), nullptr, 0, GURL(), history::RedirectList(),
350       ui::PAGE_TRANSITION_AUTO_BOOKMARK, history::SOURCE_BROWSED, false, false);
351   EXPECT_TRUE(QueryURL(history_service_.get(), test_url5));
352   EXPECT_EQ(1, query_url_result_.row.visit_count());
353   EXPECT_EQ(0, query_url_result_.row.typed_count());
354   ASSERT_EQ(1U, query_url_result_.visits.size());
355   EXPECT_TRUE(
356       ui::PageTransitionCoreTypeIs(query_url_result_.visits[0].transition,
357                                    ui::PAGE_TRANSITION_AUTO_BOOKMARK));
358 
359   // Original URL.
360   history_service_->AddPage(test_url, base::Time::Now(), nullptr, 0, GURL(),
361                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
362                             history::SOURCE_BROWSED, false, false);
363   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
364   EXPECT_EQ(2, query_url_result_.row.visit_count());
365   EXPECT_EQ(1, query_url_result_.row.typed_count());
366   ASSERT_EQ(2U, query_url_result_.visits.size());
367   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
368       query_url_result_.visits[1].transition, ui::PAGE_TRANSITION_LINK));
369 
370   // A redirect chain with an intranet URL at the head should be promoted.
371   history::RedirectList redirects1 = {GURL("http://intranet1/path"),
372                                       GURL("http://second1.com/"),
373                                       GURL("http://third1.com/")};
374   history_service_->AddPage(redirects1.back(), base::Time::Now(), nullptr, 0,
375                             GURL(), redirects1, ui::PAGE_TRANSITION_LINK,
376                             history::SOURCE_BROWSED, false, false);
377   EXPECT_TRUE(QueryURL(history_service_.get(), redirects1.front()));
378   EXPECT_EQ(1, query_url_result_.row.visit_count());
379   EXPECT_EQ(1, query_url_result_.row.typed_count());
380   ASSERT_EQ(1U, query_url_result_.visits.size());
381   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
382       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_TYPED));
383 
384   // As should one with an intranet URL at the tail.
385   history::RedirectList redirects2 = {GURL("http://first2.com/"),
386                                       GURL("http://second2.com/"),
387                                       GURL("http://intranet2/path")};
388   history_service_->AddPage(redirects2.back(), base::Time::Now(), nullptr, 0,
389                             GURL(), redirects2, ui::PAGE_TRANSITION_LINK,
390                             history::SOURCE_BROWSED, false, false);
391   EXPECT_TRUE(QueryURL(history_service_.get(), redirects2.back()));
392   EXPECT_EQ(1, query_url_result_.row.visit_count());
393   EXPECT_EQ(0, query_url_result_.row.typed_count());
394   ASSERT_EQ(1U, query_url_result_.visits.size());
395   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
396       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_TYPED));
397 
398   // But not one with an intranet URL in the middle.
399   history::RedirectList redirects3 = {GURL("http://first3.com/"),
400                                       GURL("http://intranet3/path"),
401                                       GURL("http://third3.com/")};
402   history_service_->AddPage(redirects3.back(), base::Time::Now(), nullptr, 0,
403                             GURL(), redirects3, ui::PAGE_TRANSITION_LINK,
404                             history::SOURCE_BROWSED, false, false);
405   EXPECT_TRUE(QueryURL(history_service_.get(), redirects3[1]));
406   EXPECT_EQ(1, query_url_result_.row.visit_count());
407   EXPECT_EQ(0, query_url_result_.row.typed_count());
408   ASSERT_EQ(1U, query_url_result_.visits.size());
409   EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
410       query_url_result_.visits[0].transition, ui::PAGE_TRANSITION_LINK));
411 }
412 
TEST_F(HistoryServiceTest,Typed)413 TEST_F(HistoryServiceTest, Typed) {
414   const ContextID context_id = reinterpret_cast<ContextID>(1);
415 
416   ASSERT_TRUE(history_service_.get());
417 
418   // Add the page once as typed.
419   const GURL test_url("http://www.google.com/");
420   history_service_->AddPage(test_url, base::Time::Now(), context_id, 0, GURL(),
421                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
422                             history::SOURCE_BROWSED, false, false);
423   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
424 
425   // We should have the same typed & visit count.
426   EXPECT_EQ(1, query_url_result_.row.visit_count());
427   EXPECT_EQ(1, query_url_result_.row.typed_count());
428 
429   // Add the page again not typed.
430   history_service_->AddPage(test_url, base::Time::Now(), context_id, 0, GURL(),
431                             history::RedirectList(), ui::PAGE_TRANSITION_LINK,
432                             history::SOURCE_BROWSED, false, false);
433   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
434 
435   // The second time should not have updated the typed count.
436   EXPECT_EQ(2, query_url_result_.row.visit_count());
437   EXPECT_EQ(1, query_url_result_.row.typed_count());
438 
439   // Add the page again as a generated URL.
440   history_service_->AddPage(test_url, base::Time::Now(), context_id, 0, GURL(),
441                             history::RedirectList(),
442                             ui::PAGE_TRANSITION_GENERATED,
443                             history::SOURCE_BROWSED, false, false);
444   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
445 
446   // This should have worked like a link click.
447   EXPECT_EQ(3, query_url_result_.row.visit_count());
448   EXPECT_EQ(1, query_url_result_.row.typed_count());
449 
450   // Add the page again as a reload.
451   history_service_->AddPage(test_url, base::Time::Now(), context_id, 0, GURL(),
452                             history::RedirectList(), ui::PAGE_TRANSITION_RELOAD,
453                             history::SOURCE_BROWSED, false, false);
454   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
455 
456   // This should not have incremented any visit counts.
457   EXPECT_EQ(3, query_url_result_.row.visit_count());
458   EXPECT_EQ(1, query_url_result_.row.typed_count());
459 }
460 
TEST_F(HistoryServiceTest,SetTitle)461 TEST_F(HistoryServiceTest, SetTitle) {
462   ASSERT_TRUE(history_service_.get());
463 
464   // Add a URL.
465   const GURL existing_url("http://www.google.com/");
466   history_service_->AddPage(
467       existing_url, base::Time::Now(), history::SOURCE_BROWSED);
468 
469   // Set some title.
470   const base::string16 existing_title = base::UTF8ToUTF16("Google");
471   history_service_->SetPageTitle(existing_url, existing_title);
472 
473   // Make sure the title got set.
474   EXPECT_TRUE(QueryURL(history_service_.get(), existing_url));
475   EXPECT_EQ(existing_title, query_url_result_.row.title());
476 
477   // set a title on a nonexistent page
478   const GURL nonexistent_url("http://news.google.com/");
479   const base::string16 nonexistent_title = base::UTF8ToUTF16("Google News");
480   history_service_->SetPageTitle(nonexistent_url, nonexistent_title);
481 
482   // Make sure nothing got written.
483   EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url));
484   EXPECT_EQ(base::string16(), query_url_result_.row.title());
485 
486   // TODO(brettw) this should also test redirects, which get the title of the
487   // destination page.
488 }
489 
TEST_F(HistoryServiceTest,MostVisitedURLs)490 TEST_F(HistoryServiceTest, MostVisitedURLs) {
491   ASSERT_TRUE(history_service_.get());
492 
493   const GURL url0("http://www.google.com/url0/");
494   const GURL url1("http://www.google.com/url1/");
495   const GURL url2("http://www.google.com/url2/");
496   const GURL url3("http://www.google.com/url3/");
497   const GURL url4("http://www.google.com/url4/");
498 
499   const ContextID context_id = reinterpret_cast<ContextID>(1);
500 
501   // Add two pages.
502   history_service_->AddPage(url0, base::Time::Now(), context_id, 0, GURL(),
503                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
504                             history::SOURCE_BROWSED, false, false);
505   history_service_->AddPage(url1, base::Time::Now(), context_id, 0, GURL(),
506                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
507                             history::SOURCE_BROWSED, false, false);
508 
509   QueryMostVisitedURLs();
510 
511   EXPECT_EQ(2U, most_visited_urls_.size());
512   EXPECT_EQ(url0, most_visited_urls_[0].url);
513   EXPECT_EQ(url1, most_visited_urls_[1].url);
514 
515   // Add another page.
516   history_service_->AddPage(url2, base::Time::Now(), context_id, 0, GURL(),
517                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
518                             history::SOURCE_BROWSED, false, false);
519 
520   QueryMostVisitedURLs();
521 
522   EXPECT_EQ(3U, most_visited_urls_.size());
523   EXPECT_EQ(url0, most_visited_urls_[0].url);
524   EXPECT_EQ(url1, most_visited_urls_[1].url);
525   EXPECT_EQ(url2, most_visited_urls_[2].url);
526 
527   // Revisit url2, making it the top URL.
528   history_service_->AddPage(url2, base::Time::Now(), context_id, 0, GURL(),
529                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
530                             history::SOURCE_BROWSED, false, false);
531 
532   QueryMostVisitedURLs();
533 
534   EXPECT_EQ(3U, most_visited_urls_.size());
535   EXPECT_EQ(url2, most_visited_urls_[0].url);
536   EXPECT_EQ(url0, most_visited_urls_[1].url);
537   EXPECT_EQ(url1, most_visited_urls_[2].url);
538 
539   // Revisit url1, making it the top URL.
540   history_service_->AddPage(url1, base::Time::Now(), context_id, 0, GURL(),
541                             history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
542                             history::SOURCE_BROWSED, false, false);
543 
544   QueryMostVisitedURLs();
545 
546   EXPECT_EQ(3U, most_visited_urls_.size());
547   EXPECT_EQ(url1, most_visited_urls_[0].url);
548   EXPECT_EQ(url2, most_visited_urls_[1].url);
549   EXPECT_EQ(url0, most_visited_urls_[2].url);
550 
551   // Visit url4 using redirects.
552   history::RedirectList redirects = {url3, url4};
553   history_service_->AddPage(url4, base::Time::Now(), context_id, 0, GURL(),
554                             redirects, ui::PAGE_TRANSITION_TYPED,
555                             history::SOURCE_BROWSED, false, false);
556 
557   QueryMostVisitedURLs();
558 
559   EXPECT_EQ(4U, most_visited_urls_.size());
560   EXPECT_EQ(url1, most_visited_urls_[0].url);
561   EXPECT_EQ(url2, most_visited_urls_[1].url);
562   EXPECT_EQ(url0, most_visited_urls_[2].url);
563   EXPECT_EQ(url3, most_visited_urls_[3].url);
564 }
565 
566 namespace {
567 
568 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
569 // invoke_count is increment. When invoked kWantInvokeCount times, true is
570 // returned from RunOnDBThread which should stop RunOnDBThread from being
571 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
572 // true.
573 class HistoryDBTaskImpl : public HistoryDBTask {
574  public:
575   static const int kWantInvokeCount;
576 
HistoryDBTaskImpl(int * invoke_count,bool * done_invoked)577   HistoryDBTaskImpl(int* invoke_count, bool* done_invoked)
578       : invoke_count_(invoke_count), done_invoked_(done_invoked) {}
579 
RunOnDBThread(HistoryBackend * backend,HistoryDatabase * db)580   bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override {
581     return (++*invoke_count_ == kWantInvokeCount);
582   }
583 
DoneRunOnMainThread()584   void DoneRunOnMainThread() override {
585     *done_invoked_ = true;
586     base::RunLoop::QuitCurrentWhenIdleDeprecated();
587   }
588 
589   int* invoke_count_;
590   bool* done_invoked_;
591 
592  private:
~HistoryDBTaskImpl()593   ~HistoryDBTaskImpl() override {}
594 
595   DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
596 };
597 
598 // static
599 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
600 
601 }  // namespace
602 
TEST_F(HistoryServiceTest,HistoryDBTask)603 TEST_F(HistoryServiceTest, HistoryDBTask) {
604   ASSERT_TRUE(history_service_.get());
605   base::CancelableTaskTracker task_tracker;
606   int invoke_count = 0;
607   bool done_invoked = false;
608   history_service_->ScheduleDBTask(
609       FROM_HERE,
610       std::unique_ptr<history::HistoryDBTask>(
611           new HistoryDBTaskImpl(&invoke_count, &done_invoked)),
612       &task_tracker);
613   // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
614   // it will stop the message loop. If the test hangs here, it means
615   // DoneRunOnMainThread isn't being invoked correctly.
616   base::RunLoop().Run();
617   CleanupHistoryService();
618   // WARNING: history has now been deleted.
619   history_service_.reset();
620   ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, invoke_count);
621   ASSERT_TRUE(done_invoked);
622 }
623 
TEST_F(HistoryServiceTest,HistoryDBTaskCanceled)624 TEST_F(HistoryServiceTest, HistoryDBTaskCanceled) {
625   ASSERT_TRUE(history_service_.get());
626   base::CancelableTaskTracker task_tracker;
627   int invoke_count = 0;
628   bool done_invoked = false;
629   history_service_->ScheduleDBTask(
630       FROM_HERE,
631       std::unique_ptr<history::HistoryDBTask>(
632           new HistoryDBTaskImpl(&invoke_count, &done_invoked)),
633       &task_tracker);
634   task_tracker.TryCancelAll();
635   CleanupHistoryService();
636   // WARNING: history has now been deleted.
637   history_service_.reset();
638   ASSERT_FALSE(done_invoked);
639 }
640 
641 // Helper to add a page at specified point of time.
AddPageAtTime(HistoryService * history,const std::string & url_spec,base::Time time_in_the_past)642 void AddPageAtTime(HistoryService* history,
643                    const std::string& url_spec,
644                    base::Time time_in_the_past) {
645   const GURL url(url_spec);
646   history->AddPage(url, time_in_the_past, nullptr, 0, GURL(),
647                    history::RedirectList(), ui::PAGE_TRANSITION_LINK,
648                    history::SOURCE_BROWSED, false, false);
649 }
650 
AddPageInThePast(HistoryService * history,const std::string & url_spec,int days_back)651 void AddPageInThePast(HistoryService* history,
652                       const std::string& url_spec,
653                       int days_back) {
654   base::Time time_in_the_past =
655       base::Time::Now() - base::TimeDelta::FromDays(days_back);
656   AddPageAtTime(history, url_spec, time_in_the_past);
657 }
658 
659 // Helper to add a page with specified days back in the past.
GetTimeInThePast(base::Time base_time,int days_back,int hours_since_midnight,int minutes=0,int seconds=0)660 base::Time GetTimeInThePast(base::Time base_time,
661                             int days_back,
662                             int hours_since_midnight,
663                             int minutes = 0,
664                             int seconds = 0) {
665   base::Time past_midnight = MidnightNDaysLater(base_time, -days_back);
666 
667   return past_midnight + base::TimeDelta::FromHours(hours_since_midnight) +
668          base::TimeDelta::FromMinutes(minutes) +
669          base::TimeDelta::FromSeconds(seconds);
670 }
671 
672 // Helper to contain a callback and run loop logic.
GetMonthlyHostCountHelper(HistoryService * history,base::CancelableTaskTracker * tracker)673 int GetMonthlyHostCountHelper(HistoryService* history,
674                               base::CancelableTaskTracker* tracker) {
675   base::RunLoop run_loop;
676   int count = 0;
677   history->CountUniqueHostsVisitedLastMonth(
678       base::BindLambdaForTesting([&](HistoryCountResult result) {
679         count = result.count;
680         run_loop.Quit();
681       }),
682       tracker);
683   run_loop.Run();
684   return count;
685 }
686 
GetDomainDiversityHelper(HistoryService * history,base::Time begin_time,base::Time end_time,DomainMetricBitmaskType metric_type_bitmask,base::CancelableTaskTracker * tracker)687 DomainDiversityResults GetDomainDiversityHelper(
688     HistoryService* history,
689     base::Time begin_time,
690     base::Time end_time,
691     DomainMetricBitmaskType metric_type_bitmask,
692     base::CancelableTaskTracker* tracker) {
693   base::RunLoop run_loop;
694   base::TimeDelta dst_rounding_offset = base::TimeDelta::FromHours(4);
695 
696   // Compute the number of days to report metrics for.
697   int number_of_days = 0;
698   if (begin_time < end_time) {
699     number_of_days = (end_time.LocalMidnight() - begin_time.LocalMidnight() +
700                       dst_rounding_offset)
701                          .InDaysFloored();
702   }
703 
704   DomainDiversityResults results;
705   history->GetDomainDiversity(
706       end_time, number_of_days, metric_type_bitmask,
707       base::BindLambdaForTesting([&](DomainDiversityResults result) {
708         results = result;
709         run_loop.Quit();
710       }),
711       tracker);
712   run_loop.Run();
713   return results;
714 }
715 
716 // Test one domain visit metric. A negative value indicates that an invalid
717 // metric is expected.
TestDomainMetric(const base::Optional<DomainMetricCountType> & metric,int expected)718 void TestDomainMetric(const base::Optional<DomainMetricCountType>& metric,
719                       int expected) {
720   if (expected >= 0) {
721     ASSERT_TRUE(metric.has_value());
722     EXPECT_EQ(expected, metric.value().count);
723   } else {
724     EXPECT_FALSE(metric.has_value());
725   }
726 }
727 
728 // Test a set of 1-day, 7-day and 28-day domain visit metrics.
TestDomainMetricSet(const DomainMetricSet & metric_set,int expected_one_day_metric,int expected_seven_day_metric,int expected_twenty_eight_day_metric)729 void TestDomainMetricSet(const DomainMetricSet& metric_set,
730                          int expected_one_day_metric,
731                          int expected_seven_day_metric,
732                          int expected_twenty_eight_day_metric) {
733   TestDomainMetric(metric_set.one_day_metric, expected_one_day_metric);
734   TestDomainMetric(metric_set.seven_day_metric, expected_seven_day_metric);
735   TestDomainMetric(metric_set.twenty_eight_day_metric,
736                    expected_twenty_eight_day_metric);
737 }
738 
739 // Counts hosts visited in the last month.
TEST_F(HistoryServiceTest,CountMonthlyVisitedHosts)740 TEST_F(HistoryServiceTest, CountMonthlyVisitedHosts) {
741   base::HistogramTester histogram_tester;
742   HistoryService* history = history_service_.get();
743   ASSERT_TRUE(history);
744 
745   AddPageInThePast(history, "http://www.google.com/", 0);
746   EXPECT_EQ(1, GetMonthlyHostCountHelper(history, &tracker_));
747 
748   AddPageInThePast(history, "http://www.google.com/foo", 1);
749   AddPageInThePast(history, "https://www.google.com/foo", 5);
750   AddPageInThePast(history, "https://www.gmail.com/foo", 10);
751   // Expect 2 because only host part of URL counts.
752   EXPECT_EQ(2, GetMonthlyHostCountHelper(history, &tracker_));
753 
754   AddPageInThePast(history, "https://www.gmail.com/foo", 31);
755   // Count should not change since URL added is older than a month.
756   EXPECT_EQ(2, GetMonthlyHostCountHelper(history, &tracker_));
757 
758   AddPageInThePast(history, "https://www.yahoo.com/foo", 29);
759   EXPECT_EQ(3, GetMonthlyHostCountHelper(history, &tracker_));
760 
761   // The time required to compute host count is reported on each computation.
762   histogram_tester.ExpectTotalCount("History.DatabaseMonthlyHostCountTime", 4);
763 }
764 
TEST_F(HistoryServiceTest,GetDomainDiversityShortBasetimeRange)765 TEST_F(HistoryServiceTest, GetDomainDiversityShortBasetimeRange) {
766   base::HistogramTester histogram_tester;
767   HistoryService* history = history_service_.get();
768   ASSERT_TRUE(history);
769 
770   base::Time query_time = base::Time::Now();
771 
772   // Make sure |query_time| is at least some time past the midnight so that
773   // some domain visits can be inserted between |query_time| and midnight
774   // for testing.
775   query_time =
776       std::max(query_time.LocalMidnight() + base::TimeDelta::FromMinutes(10),
777                query_time);
778 
779   AddPageAtTime(history, "http://www.google.com/",
780                 GetTimeInThePast(query_time, /*days_back=*/2,
781                                  /*hours_since_midnight=*/12));
782   AddPageAtTime(history, "http://www.gmail.com/",
783                 GetTimeInThePast(query_time, 2, 13));
784   AddPageAtTime(history, "http://www.gmail.com/foo",
785                 GetTimeInThePast(query_time, 2, 14));
786   AddPageAtTime(history, "http://images.google.com/foo",
787                 GetTimeInThePast(query_time, 1, 7));
788 
789   // Domains visited on the query day will not be included in the result.
790   AddPageAtTime(history, "http://www.youtube.com/", query_time.LocalMidnight());
791   AddPageAtTime(history, "http://www.chromium.com/",
792                 query_time.LocalMidnight() + base::TimeDelta::FromMinutes(5));
793   AddPageAtTime(history, "http://www.youtube.com/", query_time);
794 
795   // IP addresses, empty strings, non-TLD's should not be counted
796   // as domains.
797   AddPageAtTime(history, "127.0.0.1", GetTimeInThePast(query_time, 1, 8));
798   AddPageAtTime(history, "", GetTimeInThePast(query_time, 1, 13));
799   AddPageAtTime(history, "http://localhost/",
800                 GetTimeInThePast(query_time, 1, 8));
801   AddPageAtTime(history, "http://ak/", GetTimeInThePast(query_time, 1, 14));
802 
803   // Should return empty result if |begin_time| == |end_time|.
804   DomainDiversityResults res = GetDomainDiversityHelper(
805       history, query_time, query_time,
806       history::kEnableLast1DayMetric | history::kEnableLast7DayMetric |
807           history::kEnableLast28DayMetric,
808       &tracker_);
809   EXPECT_EQ(0u, res.size());
810 
811   // Metrics will be computed for each of the 4 continuous midnights.
812   res = GetDomainDiversityHelper(
813       history, GetTimeInThePast(query_time, 4, 0), query_time,
814       history::kEnableLast1DayMetric | history::kEnableLast7DayMetric |
815           history::kEnableLast28DayMetric,
816       &tracker_);
817 
818   ASSERT_EQ(4u, res.size());
819 
820   TestDomainMetricSet(res[0], 1, 2, 2);
821   TestDomainMetricSet(res[1], 2, 2, 2);
822   TestDomainMetricSet(res[2], 0, 0, 0);
823   TestDomainMetricSet(res[3], 0, 0, 0);
824 }
825 
TEST_F(HistoryServiceTest,GetDomainDiversityLongBasetimeRange)826 TEST_F(HistoryServiceTest, GetDomainDiversityLongBasetimeRange) {
827   base::HistogramTester histogram_tester;
828   HistoryService* history = history_service_.get();
829   ASSERT_TRUE(history);
830 
831   base::Time query_time = base::Time::Now();
832 
833   AddPageAtTime(history, "http://www.google.com/",
834                 GetTimeInThePast(query_time, /*days_back=*/90,
835                                  /*hours_since_midnight=*/6));
836   AddPageAtTime(history, "http://maps.google.com/",
837                 GetTimeInThePast(query_time, 34, 6));
838   AddPageAtTime(history, "http://www.google.com/",
839                 GetTimeInThePast(query_time, 31, 4));
840   AddPageAtTime(history, "https://www.google.co.uk/",
841                 GetTimeInThePast(query_time, 14, 5));
842   AddPageAtTime(history, "http://www.gmail.com/",
843                 GetTimeInThePast(query_time, 10, 13));
844   AddPageAtTime(history, "http://www.chromium.org/foo",
845                 GetTimeInThePast(query_time, 7, 14));
846   AddPageAtTime(history, "https://www.youtube.com/",
847                 GetTimeInThePast(query_time, 2, 12));
848   AddPageAtTime(history, "https://www.youtube.com/foo",
849                 GetTimeInThePast(query_time, 2, 12));
850   AddPageAtTime(history, "https://www.chromium.org/",
851                 GetTimeInThePast(query_time, 1, 13));
852   AddPageAtTime(history, "https://www.google.com/",
853                 GetTimeInThePast(query_time, 1, 13));
854 
855   DomainDiversityResults res = GetDomainDiversityHelper(
856       history, GetTimeInThePast(query_time, 10, 12), query_time,
857       history::kEnableLast1DayMetric | history::kEnableLast7DayMetric |
858           history::kEnableLast28DayMetric,
859       &tracker_);
860   // Only up to seven days will be considered.
861   ASSERT_EQ(7u, res.size());
862 
863   TestDomainMetricSet(res[0], 2, 3, 5);
864   TestDomainMetricSet(res[1], 1, 2, 4);
865   TestDomainMetricSet(res[2], 0, 1, 3);
866   TestDomainMetricSet(res[3], 0, 2, 4);
867   TestDomainMetricSet(res[4], 0, 2, 4);
868   TestDomainMetricSet(res[5], 0, 2, 4);
869   TestDomainMetricSet(res[6], 1, 2, 4);
870 }
871 
TEST_F(HistoryServiceTest,GetDomainDiversityBitmaskTest)872 TEST_F(HistoryServiceTest, GetDomainDiversityBitmaskTest) {
873   base::HistogramTester histogram_tester;
874   HistoryService* history = history_service_.get();
875   ASSERT_TRUE(history);
876 
877   base::Time query_time = base::Time::Now();
878 
879   AddPageAtTime(history, "http://www.google.com/",
880                 GetTimeInThePast(query_time, /*days_back=*/28,
881                                  /*hours_since_midnight=*/6));
882   AddPageAtTime(history, "http://www.youtube.com/",
883                 GetTimeInThePast(query_time, 7, 6));
884   AddPageAtTime(history, "http://www.chromium.com/",
885                 GetTimeInThePast(query_time, 1, 4));
886 
887   DomainDiversityResults res = GetDomainDiversityHelper(
888       history, GetTimeInThePast(query_time, 7, 12), query_time,
889       history::kEnableLast1DayMetric | history::kEnableLast7DayMetric,
890       &tracker_);
891   ASSERT_EQ(7u, res.size());
892 
893   TestDomainMetricSet(res[0], 1, 2, -1);
894   TestDomainMetricSet(res[1], 0, 1, -1);
895   TestDomainMetricSet(res[2], 0, 1, -1);
896   TestDomainMetricSet(res[3], 0, 1, -1);
897   TestDomainMetricSet(res[4], 0, 1, -1);
898   TestDomainMetricSet(res[5], 0, 1, -1);
899   TestDomainMetricSet(res[6], 1, 1, -1);
900 
901   res = GetDomainDiversityHelper(
902       history, GetTimeInThePast(query_time, 6, 12), query_time,
903       history::kEnableLast28DayMetric | history::kEnableLast7DayMetric,
904       &tracker_);
905 
906   ASSERT_EQ(6u, res.size());
907   TestDomainMetricSet(res[0], -1, 2, 3);
908   TestDomainMetricSet(res[1], -1, 1, 2);
909   TestDomainMetricSet(res[2], -1, 1, 2);
910   TestDomainMetricSet(res[3], -1, 1, 2);
911   TestDomainMetricSet(res[4], -1, 1, 2);
912   TestDomainMetricSet(res[5], -1, 1, 2);
913 }
914 }  // namespace history
915