1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// 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 <time.h> 21 22#include <algorithm> 23#include <string> 24 25#include "app/sql/connection.h" 26#include "app/sql/statement.h" 27#include "base/basictypes.h" 28#include "base/callback.h" 29#include "base/command_line.h" 30#include "base/file_path.h" 31#include "base/file_util.h" 32#include "base/memory/scoped_temp_dir.h" 33#include "base/memory/scoped_vector.h" 34#include "base/message_loop.h" 35#include "base/path_service.h" 36#include "base/string_util.h" 37#include "base/task.h" 38#include "base/utf_string_conversions.h" 39#include "chrome/browser/download/download_item.h" 40#include "chrome/browser/history/download_create_info.h" 41#include "chrome/browser/history/history.h" 42#include "chrome/browser/history/history_backend.h" 43#include "chrome/browser/history/history_database.h" 44#include "chrome/browser/history/history_notifications.h" 45#include "chrome/browser/history/in_memory_database.h" 46#include "chrome/browser/history/in_memory_history_backend.h" 47#include "chrome/browser/history/page_usage_data.h" 48#include "chrome/common/chrome_paths.h" 49#include "chrome/common/thumbnail_score.h" 50#include "chrome/tools/profiles/thumbnail-inl.h" 51#include "content/common/notification_details.h" 52#include "content/common/notification_source.h" 53#include "testing/gtest/include/gtest/gtest.h" 54#include "third_party/skia/include/core/SkBitmap.h" 55#include "ui/gfx/codec/jpeg_codec.h" 56 57using base::Time; 58using base::TimeDelta; 59 60namespace history { 61class HistoryTest; 62} 63 64// Specialize RunnableMethodTraits for HistoryTest so we can create callbacks. 65// None of these callbacks can outlast the test, so there is not need to retain 66// the HistoryTest object. 67DISABLE_RUNNABLE_METHOD_REFCOUNT(history::HistoryTest); 68 69namespace history { 70 71namespace { 72 73// The tracker uses RenderProcessHost pointers for scoping but never 74// dereferences them. We use ints because it's easier. This function converts 75// between the two. 76static void* MakeFakeHost(int id) { 77 void* host = 0; 78 memcpy(&host, &id, sizeof(id)); 79 return host; 80} 81 82} // namespace 83 84// Delegate class for when we create a backend without a HistoryService. 85class BackendDelegate : public HistoryBackend::Delegate { 86 public: 87 explicit BackendDelegate(HistoryTest* history_test) 88 : history_test_(history_test) { 89 } 90 91 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {} 92 virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE; 93 virtual void BroadcastNotifications(NotificationType type, 94 HistoryDetails* details) OVERRIDE; 95 virtual void DBLoaded() OVERRIDE {} 96 virtual void StartTopSitesMigration() OVERRIDE {} 97 private: 98 HistoryTest* history_test_; 99}; 100 101// This must be outside the anonymous namespace for the friend statement in 102// HistoryBackend to work. 103class HistoryTest : public testing::Test { 104 public: 105 HistoryTest() 106 : history_service_(NULL), 107 got_thumbnail_callback_(false), 108 redirect_query_success_(false), 109 query_url_success_(false), 110 db_(NULL) { 111 } 112 ~HistoryTest() { 113 } 114 115 // Creates the HistoryBackend and HistoryDatabase on the current thread, 116 // assigning the values to backend_ and db_. 117 void CreateBackendAndDatabase() { 118 backend_ = 119 new HistoryBackend(history_dir_, new BackendDelegate(this), NULL); 120 backend_->Init(std::string(), false); 121 db_ = backend_->db_.get(); 122 DCHECK(in_mem_backend_.get()) << "Mem backend should have been set by " 123 "HistoryBackend::Init"; 124 } 125 126 void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle, 127 std::vector<PageUsageData*>* data) { 128 page_usage_data_->swap(*data); 129 MessageLoop::current()->Quit(); 130 } 131 132 void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) { 133 MessageLoop::current()->Quit(); 134 } 135 136 void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle, 137 MostVisitedURLList url_list) { 138 most_visited_urls_.swap(url_list); 139 MessageLoop::current()->Quit(); 140 } 141 142 protected: 143 friend class BackendDelegate; 144 145 // testing::Test 146 virtual void SetUp() { 147 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 148 history_dir_ = temp_dir_.path().AppendASCII("HistoryTest"); 149 ASSERT_TRUE(file_util::CreateDirectory(history_dir_)); 150 } 151 152 void DeleteBackend() { 153 if (backend_) { 154 backend_->Closing(); 155 backend_ = NULL; 156 } 157 } 158 159 virtual void TearDown() { 160 DeleteBackend(); 161 162 if (history_service_) 163 CleanupHistoryService(); 164 165 // Make sure we don't have any event pending that could disrupt the next 166 // test. 167 MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); 168 MessageLoop::current()->Run(); 169 } 170 171 void CleanupHistoryService() { 172 DCHECK(history_service_.get()); 173 174 history_service_->NotifyRenderProcessHostDestruction(0); 175 history_service_->SetOnBackendDestroyTask(new MessageLoop::QuitTask); 176 history_service_->Cleanup(); 177 history_service_ = NULL; 178 179 // Wait for the backend class to terminate before deleting the files and 180 // moving to the next test. Note: if this never terminates, somebody is 181 // probably leaking a reference to the history backend, so it never calls 182 // our destroy task. 183 MessageLoop::current()->Run(); 184 } 185 186 int64 AddDownload(int32 state, const Time& time) { 187 DownloadCreateInfo download(FilePath(FILE_PATH_LITERAL("foo-path")), 188 GURL("foo-url"), time, 0, 512, state, 0, false); 189 return db_->CreateDownload(download); 190 } 191 192 // Fills the query_url_row_ and query_url_visits_ structures with the 193 // information about the given URL and returns true. If the URL was not 194 // found, this will return false and those structures will not be changed. 195 bool QueryURL(HistoryService* history, const GURL& url) { 196 history->QueryURL(url, true, &consumer_, 197 NewCallback(this, &HistoryTest::SaveURLAndQuit)); 198 MessageLoop::current()->Run(); // Will be exited in SaveURLAndQuit. 199 return query_url_success_; 200 } 201 202 // Callback for HistoryService::QueryURL. 203 void SaveURLAndQuit(HistoryService::Handle handle, 204 bool success, 205 const URLRow* url_row, 206 VisitVector* visit_vector) { 207 query_url_success_ = success; 208 if (query_url_success_) { 209 query_url_row_ = *url_row; 210 query_url_visits_.swap(*visit_vector); 211 } else { 212 query_url_row_ = URLRow(); 213 query_url_visits_.clear(); 214 } 215 MessageLoop::current()->Quit(); 216 } 217 218 // Fills in saved_redirects_ with the redirect information for the given URL, 219 // returning true on success. False means the URL was not found. 220 bool QueryRedirectsFrom(HistoryService* history, const GURL& url) { 221 history->QueryRedirectsFrom(url, &consumer_, 222 NewCallback(this, &HistoryTest::OnRedirectQueryComplete)); 223 MessageLoop::current()->Run(); // Will be exited in *QueryComplete. 224 return redirect_query_success_; 225 } 226 227 // Callback for QueryRedirects. 228 void OnRedirectQueryComplete(HistoryService::Handle handle, 229 GURL url, 230 bool success, 231 history::RedirectList* redirects) { 232 redirect_query_success_ = success; 233 if (redirect_query_success_) 234 saved_redirects_.swap(*redirects); 235 else 236 saved_redirects_.clear(); 237 MessageLoop::current()->Quit(); 238 } 239 240 ScopedTempDir temp_dir_; 241 242 MessageLoopForUI message_loop_; 243 244 // PageUsageData vector to test segments. 245 ScopedVector<PageUsageData> page_usage_data_; 246 247 MostVisitedURLList most_visited_urls_; 248 249 // When non-NULL, this will be deleted on tear down and we will block until 250 // the backend thread has completed. This allows tests for the history 251 // service to use this feature, but other tests to ignore this. 252 scoped_refptr<HistoryService> history_service_; 253 254 // names of the database files 255 FilePath history_dir_; 256 257 // Set by the thumbnail callback when we get data, you should be sure to 258 // clear this before issuing a thumbnail request. 259 bool got_thumbnail_callback_; 260 std::vector<unsigned char> thumbnail_data_; 261 262 // Set by the redirect callback when we get data. You should be sure to 263 // clear this before issuing a redirect request. 264 history::RedirectList saved_redirects_; 265 bool redirect_query_success_; 266 267 // For history requests. 268 CancelableRequestConsumer consumer_; 269 270 // For saving URL info after a call to QueryURL 271 bool query_url_success_; 272 URLRow query_url_row_; 273 VisitVector query_url_visits_; 274 275 // Created via CreateBackendAndDatabase. 276 scoped_refptr<HistoryBackend> backend_; 277 scoped_ptr<InMemoryHistoryBackend> in_mem_backend_; 278 HistoryDatabase* db_; // Cached reference to the backend's database. 279}; 280 281void BackendDelegate::SetInMemoryBackend(InMemoryHistoryBackend* backend) { 282 // Save the in-memory backend to the history test object, this happens 283 // synchronously, so we don't have to do anything fancy. 284 history_test_->in_mem_backend_.reset(backend); 285} 286 287void BackendDelegate::BroadcastNotifications(NotificationType type, 288 HistoryDetails* details) { 289 // Currently, just send the notifications directly to the in-memory database. 290 // We may want do do something more fancy in the future. 291 Details<HistoryDetails> det(details); 292 history_test_->in_mem_backend_->Observe(type, 293 Source<HistoryTest>(NULL), det); 294 295 // The backend passes ownership of the details pointer to us. 296 delete details; 297} 298 299TEST_F(HistoryTest, ClearBrowsingData_Downloads) { 300 CreateBackendAndDatabase(); 301 302 Time now = Time::Now(); 303 TimeDelta one_day = TimeDelta::FromDays(1); 304 Time month_ago = now - TimeDelta::FromDays(30); 305 306 // Initially there should be nothing in the downloads database. 307 std::vector<DownloadCreateInfo> downloads; 308 db_->QueryDownloads(&downloads); 309 EXPECT_EQ(0U, downloads.size()); 310 311 // Keep track of these as we need to update them later during the test. 312 DownloadID in_progress, removing; 313 314 // Create one with a 0 time. 315 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, Time())); 316 // Create one for now and +/- 1 day. 317 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now - one_day)); 318 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now)); 319 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now + one_day)); 320 // Try the other four states. 321 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, month_ago)); 322 EXPECT_NE(0, in_progress = AddDownload(DownloadItem::IN_PROGRESS, month_ago)); 323 EXPECT_NE(0, AddDownload(DownloadItem::CANCELLED, month_ago)); 324 EXPECT_NE(0, AddDownload(DownloadItem::INTERRUPTED, month_ago)); 325 EXPECT_NE(0, removing = AddDownload(DownloadItem::REMOVING, month_ago)); 326 327 // Test to see if inserts worked. 328 db_->QueryDownloads(&downloads); 329 EXPECT_EQ(9U, downloads.size()); 330 331 // Try removing from current timestamp. This should delete the one in the 332 // future and one very recent one. 333 db_->RemoveDownloadsBetween(now, Time()); 334 db_->QueryDownloads(&downloads); 335 EXPECT_EQ(7U, downloads.size()); 336 337 // Try removing from two months ago. This should not delete items that are 338 // 'in progress' or in 'removing' state. 339 db_->RemoveDownloadsBetween(now - TimeDelta::FromDays(60), Time()); 340 db_->QueryDownloads(&downloads); 341 EXPECT_EQ(3U, downloads.size()); 342 343 // Download manager converts to TimeT, which is lossy, so we do the same 344 // for comparison. 345 Time month_ago_lossy = Time::FromTimeT(month_ago.ToTimeT()); 346 347 // Make sure the right values remain. 348 EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state); 349 EXPECT_EQ(0, downloads[0].start_time.ToInternalValue()); 350 EXPECT_EQ(DownloadItem::IN_PROGRESS, downloads[1].state); 351 EXPECT_EQ(month_ago_lossy.ToInternalValue(), 352 downloads[1].start_time.ToInternalValue()); 353 EXPECT_EQ(DownloadItem::REMOVING, downloads[2].state); 354 EXPECT_EQ(month_ago_lossy.ToInternalValue(), 355 downloads[2].start_time.ToInternalValue()); 356 357 // Change state so we can delete the downloads. 358 EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::COMPLETE, in_progress)); 359 EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::CANCELLED, removing)); 360 361 // Try removing from Time=0. This should delete all. 362 db_->RemoveDownloadsBetween(Time(), Time()); 363 db_->QueryDownloads(&downloads); 364 EXPECT_EQ(0U, downloads.size()); 365 366 // Check removal of downloads stuck in IN_PROGRESS state. 367 EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, month_ago)); 368 EXPECT_NE(0, AddDownload(DownloadItem::IN_PROGRESS, month_ago)); 369 db_->QueryDownloads(&downloads); 370 EXPECT_EQ(2U, downloads.size()); 371 db_->RemoveDownloadsBetween(Time(), Time()); 372 db_->QueryDownloads(&downloads); 373 // IN_PROGRESS download should remain. It it indicated as "Canceled" 374 EXPECT_EQ(1U, downloads.size()); 375 db_->CleanUpInProgressEntries(); 376 db_->QueryDownloads(&downloads); 377 EXPECT_EQ(1U, downloads.size()); 378 db_->RemoveDownloadsBetween(Time(), Time()); 379 db_->QueryDownloads(&downloads); 380 EXPECT_EQ(0U, downloads.size()); 381} 382 383TEST_F(HistoryTest, AddPage) { 384 scoped_refptr<HistoryService> history(new HistoryService); 385 history_service_ = history; 386 ASSERT_TRUE(history->Init(history_dir_, NULL)); 387 388 // Add the page once from a child frame. 389 const GURL test_url("http://www.google.com/"); 390 history->AddPage(test_url, NULL, 0, GURL(), 391 PageTransition::MANUAL_SUBFRAME, 392 history::RedirectList(), 393 history::SOURCE_BROWSED, false); 394 EXPECT_TRUE(QueryURL(history, test_url)); 395 EXPECT_EQ(1, query_url_row_.visit_count()); 396 EXPECT_EQ(0, query_url_row_.typed_count()); 397 EXPECT_TRUE(query_url_row_.hidden()); // Hidden because of child frame. 398 399 // Add the page once from the main frame (should unhide it). 400 history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK, 401 history::RedirectList(), 402 history::SOURCE_BROWSED, false); 403 EXPECT_TRUE(QueryURL(history, test_url)); 404 EXPECT_EQ(2, query_url_row_.visit_count()); // Added twice. 405 EXPECT_EQ(0, query_url_row_.typed_count()); // Never typed. 406 EXPECT_FALSE(query_url_row_.hidden()); // Because loaded in main frame. 407} 408 409TEST_F(HistoryTest, AddPageSameTimes) { 410 scoped_refptr<HistoryService> history(new HistoryService); 411 history_service_ = history; 412 ASSERT_TRUE(history->Init(history_dir_, NULL)); 413 414 Time now = Time::Now(); 415 const GURL test_urls[] = { 416 GURL("http://timer.first.page/"), 417 GURL("http://timer.second.page/"), 418 GURL("http://timer.third.page/"), 419 }; 420 421 // Make sure that two pages added at the same time with no intervening 422 // additions have different timestamps. 423 history->AddPage(test_urls[0], now, NULL, 0, GURL(), 424 PageTransition::LINK, 425 history::RedirectList(), 426 history::SOURCE_BROWSED, false); 427 EXPECT_TRUE(QueryURL(history, test_urls[0])); 428 EXPECT_EQ(1, query_url_row_.visit_count()); 429 EXPECT_TRUE(now == query_url_row_.last_visit()); // gtest doesn't like Time 430 431 history->AddPage(test_urls[1], now, NULL, 0, GURL(), 432 PageTransition::LINK, 433 history::RedirectList(), 434 history::SOURCE_BROWSED, false); 435 EXPECT_TRUE(QueryURL(history, test_urls[1])); 436 EXPECT_EQ(1, query_url_row_.visit_count()); 437 EXPECT_TRUE(now + TimeDelta::FromMicroseconds(1) == 438 query_url_row_.last_visit()); 439 440 // Make sure the next page, at a different time, is also correct. 441 history->AddPage(test_urls[2], now + TimeDelta::FromMinutes(1), 442 NULL, 0, GURL(), 443 PageTransition::LINK, 444 history::RedirectList(), 445 history::SOURCE_BROWSED, false); 446 EXPECT_TRUE(QueryURL(history, test_urls[2])); 447 EXPECT_EQ(1, query_url_row_.visit_count()); 448 EXPECT_TRUE(now + TimeDelta::FromMinutes(1) == 449 query_url_row_.last_visit()); 450} 451 452TEST_F(HistoryTest, AddRedirect) { 453 scoped_refptr<HistoryService> history(new HistoryService); 454 history_service_ = history; 455 ASSERT_TRUE(history->Init(history_dir_, NULL)); 456 457 const char* first_sequence[] = { 458 "http://first.page/", 459 "http://second.page/"}; 460 int first_count = arraysize(first_sequence); 461 history::RedirectList first_redirects; 462 for (int i = 0; i < first_count; i++) 463 first_redirects.push_back(GURL(first_sequence[i])); 464 465 // Add the sequence of pages as a server with no referrer. Note that we need 466 // to have a non-NULL page ID scope. 467 history->AddPage(first_redirects.back(), MakeFakeHost(1), 0, GURL(), 468 PageTransition::LINK, first_redirects, 469 history::SOURCE_BROWSED, true); 470 471 // The first page should be added once with a link visit type (because we set 472 // LINK when we added the original URL, and a referrer of nowhere (0). 473 EXPECT_TRUE(QueryURL(history, first_redirects[0])); 474 EXPECT_EQ(1, query_url_row_.visit_count()); 475 ASSERT_EQ(1U, query_url_visits_.size()); 476 int64 first_visit = query_url_visits_[0].visit_id; 477 EXPECT_EQ(PageTransition::LINK | 478 PageTransition::CHAIN_START, query_url_visits_[0].transition); 479 EXPECT_EQ(0, query_url_visits_[0].referring_visit); // No referrer. 480 481 // The second page should be a server redirect type with a referrer of the 482 // first page. 483 EXPECT_TRUE(QueryURL(history, first_redirects[1])); 484 EXPECT_EQ(1, query_url_row_.visit_count()); 485 ASSERT_EQ(1U, query_url_visits_.size()); 486 int64 second_visit = query_url_visits_[0].visit_id; 487 EXPECT_EQ(PageTransition::SERVER_REDIRECT | 488 PageTransition::CHAIN_END, query_url_visits_[0].transition); 489 EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit); 490 491 // Check that the redirect finding function successfully reports it. 492 saved_redirects_.clear(); 493 QueryRedirectsFrom(history, first_redirects[0]); 494 ASSERT_EQ(1U, saved_redirects_.size()); 495 EXPECT_EQ(first_redirects[1], saved_redirects_[0]); 496 497 // Now add a client redirect from that second visit to a third, client 498 // redirects are tracked by the RenderView prior to updating history, 499 // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior. 500 history::RedirectList second_redirects; 501 second_redirects.push_back(first_redirects[1]); 502 second_redirects.push_back(GURL("http://last.page/")); 503 history->AddPage(second_redirects[1], MakeFakeHost(1), 1, 504 second_redirects[0], 505 static_cast<PageTransition::Type>(PageTransition::LINK | 506 PageTransition::CLIENT_REDIRECT), 507 second_redirects, history::SOURCE_BROWSED, true); 508 509 // The last page (source of the client redirect) should NOT have an 510 // additional visit added, because it was a client redirect (normally it 511 // would). We should only have 1 left over from the first sequence. 512 EXPECT_TRUE(QueryURL(history, second_redirects[0])); 513 EXPECT_EQ(1, query_url_row_.visit_count()); 514 515 // The final page should be set as a client redirect from the previous visit. 516 EXPECT_TRUE(QueryURL(history, second_redirects[1])); 517 EXPECT_EQ(1, query_url_row_.visit_count()); 518 ASSERT_EQ(1U, query_url_visits_.size()); 519 EXPECT_EQ(PageTransition::CLIENT_REDIRECT | 520 PageTransition::CHAIN_END, query_url_visits_[0].transition); 521 EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit); 522} 523 524TEST_F(HistoryTest, Typed) { 525 scoped_refptr<HistoryService> history(new HistoryService); 526 history_service_ = history; 527 ASSERT_TRUE(history->Init(history_dir_, NULL)); 528 529 // Add the page once as typed. 530 const GURL test_url("http://www.google.com/"); 531 history->AddPage(test_url, NULL, 0, GURL(), PageTransition::TYPED, 532 history::RedirectList(), 533 history::SOURCE_BROWSED, false); 534 EXPECT_TRUE(QueryURL(history, test_url)); 535 536 // We should have the same typed & visit count. 537 EXPECT_EQ(1, query_url_row_.visit_count()); 538 EXPECT_EQ(1, query_url_row_.typed_count()); 539 540 // Add the page again not typed. 541 history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK, 542 history::RedirectList(), 543 history::SOURCE_BROWSED, false); 544 EXPECT_TRUE(QueryURL(history, test_url)); 545 546 // The second time should not have updated the typed count. 547 EXPECT_EQ(2, query_url_row_.visit_count()); 548 EXPECT_EQ(1, query_url_row_.typed_count()); 549 550 // Add the page again as a generated URL. 551 history->AddPage(test_url, NULL, 0, GURL(), 552 PageTransition::GENERATED, history::RedirectList(), 553 history::SOURCE_BROWSED, false); 554 EXPECT_TRUE(QueryURL(history, test_url)); 555 556 // This should have worked like a link click. 557 EXPECT_EQ(3, query_url_row_.visit_count()); 558 EXPECT_EQ(1, query_url_row_.typed_count()); 559 560 // Add the page again as a reload. 561 history->AddPage(test_url, NULL, 0, GURL(), 562 PageTransition::RELOAD, history::RedirectList(), 563 history::SOURCE_BROWSED, false); 564 EXPECT_TRUE(QueryURL(history, test_url)); 565 566 // This should not have incremented any visit counts. 567 EXPECT_EQ(3, query_url_row_.visit_count()); 568 EXPECT_EQ(1, query_url_row_.typed_count()); 569} 570 571TEST_F(HistoryTest, SetTitle) { 572 scoped_refptr<HistoryService> history(new HistoryService); 573 history_service_ = history; 574 ASSERT_TRUE(history->Init(history_dir_, NULL)); 575 576 // Add a URL. 577 const GURL existing_url("http://www.google.com/"); 578 history->AddPage(existing_url, history::SOURCE_BROWSED); 579 580 // Set some title. 581 const string16 existing_title = UTF8ToUTF16("Google"); 582 history->SetPageTitle(existing_url, existing_title); 583 584 // Make sure the title got set. 585 EXPECT_TRUE(QueryURL(history, existing_url)); 586 EXPECT_EQ(existing_title, query_url_row_.title()); 587 588 // set a title on a nonexistent page 589 const GURL nonexistent_url("http://news.google.com/"); 590 const string16 nonexistent_title = UTF8ToUTF16("Google News"); 591 history->SetPageTitle(nonexistent_url, nonexistent_title); 592 593 // Make sure nothing got written. 594 EXPECT_FALSE(QueryURL(history, nonexistent_url)); 595 EXPECT_EQ(string16(), query_url_row_.title()); 596 597 // TODO(brettw) this should also test redirects, which get the title of the 598 // destination page. 599} 600 601TEST_F(HistoryTest, Segments) { 602 scoped_refptr<HistoryService> history(new HistoryService); 603 history_service_ = history; 604 605 ASSERT_TRUE(history->Init(history_dir_, NULL)); 606 607 static const void* scope = static_cast<void*>(this); 608 609 // Add a URL. 610 const GURL existing_url("http://www.google.com/"); 611 history->AddPage(existing_url, scope, 0, GURL(), 612 PageTransition::TYPED, history::RedirectList(), 613 history::SOURCE_BROWSED, false); 614 615 // Make sure a segment was created. 616 history->QuerySegmentUsageSince( 617 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 618 NewCallback(static_cast<HistoryTest*>(this), 619 &HistoryTest::OnSegmentUsageAvailable)); 620 621 // Wait for processing. 622 MessageLoop::current()->Run(); 623 624 ASSERT_EQ(1U, page_usage_data_->size()); 625 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 626 EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore()); 627 628 // Add a URL which doesn't create a segment. 629 const GURL link_url("http://yahoo.com/"); 630 history->AddPage(link_url, scope, 0, GURL(), 631 PageTransition::LINK, history::RedirectList(), 632 history::SOURCE_BROWSED, false); 633 634 // Query again 635 history->QuerySegmentUsageSince( 636 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 637 NewCallback(static_cast<HistoryTest*>(this), 638 &HistoryTest::OnSegmentUsageAvailable)); 639 640 // Wait for processing. 641 MessageLoop::current()->Run(); 642 643 // Make sure we still have one segment. 644 ASSERT_EQ(1U, page_usage_data_->size()); 645 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 646 647 // Add a page linked from existing_url. 648 history->AddPage(GURL("http://www.google.com/foo"), scope, 3, existing_url, 649 PageTransition::LINK, history::RedirectList(), 650 history::SOURCE_BROWSED, false); 651 652 // Query again 653 history->QuerySegmentUsageSince( 654 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 655 NewCallback(static_cast<HistoryTest*>(this), 656 &HistoryTest::OnSegmentUsageAvailable)); 657 658 // Wait for processing. 659 MessageLoop::current()->Run(); 660 661 // Make sure we still have one segment. 662 ASSERT_EQ(1U, page_usage_data_->size()); 663 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 664 665 // However, the score should have increased. 666 EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0); 667} 668 669TEST_F(HistoryTest, MostVisitedURLs) { 670 scoped_refptr<HistoryService> history(new HistoryService); 671 history_service_ = history; 672 ASSERT_TRUE(history->Init(history_dir_, NULL)); 673 674 const GURL url0("http://www.google.com/url0/"); 675 const GURL url1("http://www.google.com/url1/"); 676 const GURL url2("http://www.google.com/url2/"); 677 const GURL url3("http://www.google.com/url3/"); 678 const GURL url4("http://www.google.com/url4/"); 679 680 static const void* scope = static_cast<void*>(this); 681 682 // Add two pages. 683 history->AddPage(url0, scope, 0, GURL(), 684 PageTransition::TYPED, history::RedirectList(), 685 history::SOURCE_BROWSED, false); 686 history->AddPage(url1, scope, 0, GURL(), 687 PageTransition::TYPED, history::RedirectList(), 688 history::SOURCE_BROWSED, false); 689 history->QueryMostVisitedURLs(20, 90, &consumer_, 690 NewCallback(static_cast<HistoryTest*>(this), 691 &HistoryTest::OnMostVisitedURLsAvailable)); 692 MessageLoop::current()->Run(); 693 694 EXPECT_EQ(2U, most_visited_urls_.size()); 695 EXPECT_EQ(url0, most_visited_urls_[0].url); 696 EXPECT_EQ(url1, most_visited_urls_[1].url); 697 698 // Add another page. 699 history->AddPage(url2, scope, 0, GURL(), 700 PageTransition::TYPED, history::RedirectList(), 701 history::SOURCE_BROWSED, false); 702 history->QueryMostVisitedURLs(20, 90, &consumer_, 703 NewCallback(static_cast<HistoryTest*>(this), 704 &HistoryTest::OnMostVisitedURLsAvailable)); 705 MessageLoop::current()->Run(); 706 707 EXPECT_EQ(3U, most_visited_urls_.size()); 708 EXPECT_EQ(url0, most_visited_urls_[0].url); 709 EXPECT_EQ(url1, most_visited_urls_[1].url); 710 EXPECT_EQ(url2, most_visited_urls_[2].url); 711 712 // Revisit url2, making it the top URL. 713 history->AddPage(url2, scope, 0, GURL(), 714 PageTransition::TYPED, history::RedirectList(), 715 history::SOURCE_BROWSED, false); 716 history->QueryMostVisitedURLs(20, 90, &consumer_, 717 NewCallback(static_cast<HistoryTest*>(this), 718 &HistoryTest::OnMostVisitedURLsAvailable)); 719 MessageLoop::current()->Run(); 720 721 EXPECT_EQ(3U, most_visited_urls_.size()); 722 EXPECT_EQ(url2, most_visited_urls_[0].url); 723 EXPECT_EQ(url0, most_visited_urls_[1].url); 724 EXPECT_EQ(url1, most_visited_urls_[2].url); 725 726 // Revisit url1, making it the top URL. 727 history->AddPage(url1, scope, 0, GURL(), 728 PageTransition::TYPED, history::RedirectList(), 729 history::SOURCE_BROWSED, false); 730 history->QueryMostVisitedURLs(20, 90, &consumer_, 731 NewCallback(static_cast<HistoryTest*>(this), 732 &HistoryTest::OnMostVisitedURLsAvailable)); 733 MessageLoop::current()->Run(); 734 735 EXPECT_EQ(3U, most_visited_urls_.size()); 736 EXPECT_EQ(url1, most_visited_urls_[0].url); 737 EXPECT_EQ(url2, most_visited_urls_[1].url); 738 EXPECT_EQ(url0, most_visited_urls_[2].url); 739 740 // Redirects 741 history::RedirectList redirects; 742 redirects.push_back(url3); 743 redirects.push_back(url4); 744 745 // Visit url4 using redirects. 746 history->AddPage(url4, scope, 0, GURL(), 747 PageTransition::TYPED, redirects, 748 history::SOURCE_BROWSED, false); 749 history->QueryMostVisitedURLs(20, 90, &consumer_, 750 NewCallback(static_cast<HistoryTest*>(this), 751 &HistoryTest::OnMostVisitedURLsAvailable)); 752 MessageLoop::current()->Run(); 753 754 EXPECT_EQ(4U, most_visited_urls_.size()); 755 EXPECT_EQ(url1, most_visited_urls_[0].url); 756 EXPECT_EQ(url2, most_visited_urls_[1].url); 757 EXPECT_EQ(url0, most_visited_urls_[2].url); 758 EXPECT_EQ(url3, most_visited_urls_[3].url); 759 EXPECT_EQ(2U, most_visited_urls_[3].redirects.size()); 760} 761 762// The version of the history database should be current in the "typical 763// history" example file or it will be imported on startup, throwing off timing 764// measurements. 765// 766// See test/data/profiles/typical_history/README.txt for instructions on 767// how to up the version. 768TEST(HistoryProfileTest, TypicalProfileVersion) { 769 FilePath file; 770 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file)); 771 file = file.AppendASCII("profiles"); 772 file = file.AppendASCII("typical_history"); 773 file = file.AppendASCII("Default"); 774 file = file.AppendASCII("History"); 775 776 int cur_version = HistoryDatabase::GetCurrentVersion(); 777 778 sql::Connection db; 779 ASSERT_TRUE(db.Open(file)); 780 781 { 782 sql::Statement s(db.GetUniqueStatement( 783 "SELECT value FROM meta WHERE key = 'version'")); 784 EXPECT_TRUE(s.Step()); 785 int file_version = s.ColumnInt(0); 786 EXPECT_EQ(cur_version, file_version); 787 } 788} 789 790namespace { 791 792// A HistoryDBTask implementation. Each time RunOnDBThread is invoked 793// invoke_count is increment. When invoked kWantInvokeCount times, true is 794// returned from RunOnDBThread which should stop RunOnDBThread from being 795// invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to 796// true. 797class HistoryDBTaskImpl : public HistoryDBTask { 798 public: 799 static const int kWantInvokeCount; 800 801 HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {} 802 803 virtual bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) { 804 return (++invoke_count == kWantInvokeCount); 805 } 806 807 virtual void DoneRunOnMainThread() { 808 done_invoked = true; 809 MessageLoop::current()->Quit(); 810 } 811 812 int invoke_count; 813 bool done_invoked; 814 815 private: 816 virtual ~HistoryDBTaskImpl() {} 817 818 DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl); 819}; 820 821// static 822const int HistoryDBTaskImpl::kWantInvokeCount = 2; 823 824} // namespace 825 826TEST_F(HistoryTest, HistoryDBTask) { 827 CancelableRequestConsumerT<int, 0> request_consumer; 828 HistoryService* history = new HistoryService(); 829 ASSERT_TRUE(history->Init(history_dir_, NULL)); 830 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl()); 831 history_service_ = history; 832 history->ScheduleDBTask(task.get(), &request_consumer); 833 // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs, 834 // it will stop the message loop. If the test hangs here, it means 835 // DoneRunOnMainThread isn't being invoked correctly. 836 MessageLoop::current()->Run(); 837 CleanupHistoryService(); 838 // WARNING: history has now been deleted. 839 history = NULL; 840 ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count); 841 ASSERT_TRUE(task->done_invoked); 842} 843 844TEST_F(HistoryTest, HistoryDBTaskCanceled) { 845 CancelableRequestConsumerT<int, 0> request_consumer; 846 HistoryService* history = new HistoryService(); 847 ASSERT_TRUE(history->Init(history_dir_, NULL)); 848 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl()); 849 history_service_ = history; 850 history->ScheduleDBTask(task.get(), &request_consumer); 851 request_consumer.CancelAllRequests(); 852 CleanupHistoryService(); 853 // WARNING: history has now been deleted. 854 history = NULL; 855 ASSERT_FALSE(task->done_invoked); 856} 857 858} // namespace history 859