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