history_unittest.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 <time.h> 21 22#include <algorithm> 23#include <string> 24 25#include "base/basictypes.h" 26#include "base/bind.h" 27#include "base/bind_helpers.h" 28#include "base/callback.h" 29#include "base/command_line.h" 30#include "base/compiler_specific.h" 31#include "base/file_util.h" 32#include "base/files/file_path.h" 33#include "base/files/scoped_temp_dir.h" 34#include "base/logging.h" 35#include "base/memory/scoped_ptr.h" 36#include "base/memory/scoped_vector.h" 37#include "base/message_loop.h" 38#include "base/path_service.h" 39#include "base/string_util.h" 40#include "base/stringprintf.h" 41#include "base/threading/platform_thread.h" 42#include "base/time.h" 43#include "base/utf_string_conversions.h" 44#include "chrome/browser/history/download_row.h" 45#include "chrome/browser/history/history_backend.h" 46#include "chrome/browser/history/history_database.h" 47#include "chrome/browser/history/history_db_task.h" 48#include "chrome/browser/history/history_notifications.h" 49#include "chrome/browser/history/history_service.h" 50#include "chrome/browser/history/history_unittest_base.h" 51#include "chrome/browser/history/in_memory_database.h" 52#include "chrome/browser/history/in_memory_history_backend.h" 53#include "chrome/browser/history/page_usage_data.h" 54#include "chrome/common/chrome_constants.h" 55#include "chrome/common/chrome_paths.h" 56#include "chrome/common/thumbnail_score.h" 57#include "chrome/tools/profiles/thumbnail-inl.h" 58#include "content/public/browser/download_item.h" 59#include "content/public/browser/notification_details.h" 60#include "content/public/browser/notification_source.h" 61#include "sql/connection.h" 62#include "sql/statement.h" 63#include "sync/api/sync_change.h" 64#include "sync/api/sync_change_processor.h" 65#include "sync/api/sync_error.h" 66#include "sync/api/sync_error_factory.h" 67#include "sync/api/sync_merge_result.h" 68#include "sync/protocol/history_delete_directive_specifics.pb.h" 69#include "sync/protocol/sync.pb.h" 70#include "testing/gtest/include/gtest/gtest.h" 71#include "third_party/skia/include/core/SkBitmap.h" 72#include "ui/gfx/codec/jpeg_codec.h" 73 74using base::Time; 75using base::TimeDelta; 76using content::DownloadItem; 77 78namespace history { 79class HistoryBackendDBTest; 80 81// Delegate class for when we create a backend without a HistoryService. 82// 83// This must be outside the anonymous namespace for the friend statement in 84// HistoryBackendDBTest to work. 85class BackendDelegate : public HistoryBackend::Delegate { 86 public: 87 explicit BackendDelegate(HistoryBackendDBTest* history_test) 88 : history_test_(history_test) { 89 } 90 91 virtual void NotifyProfileError(int backend_id, 92 sql::InitStatus init_status) OVERRIDE {} 93 virtual void SetInMemoryBackend(int backend_id, 94 InMemoryHistoryBackend* backend) OVERRIDE; 95 virtual void BroadcastNotifications(int type, 96 HistoryDetails* details) OVERRIDE; 97 virtual void DBLoaded(int backend_id) OVERRIDE {} 98 virtual void StartTopSitesMigration(int backend_id) OVERRIDE {} 99 virtual void NotifyVisitDBObserversOnAddVisit( 100 const BriefVisitInfo& info) OVERRIDE {} 101 private: 102 HistoryBackendDBTest* history_test_; 103}; 104 105// This must be outside the anonymous namespace for the friend statement in 106// HistoryBackend to work. 107class HistoryBackendDBTest : public HistoryUnitTestBase { 108 public: 109 HistoryBackendDBTest() : db_(NULL) { 110 } 111 112 virtual ~HistoryBackendDBTest() { 113 } 114 115 protected: 116 friend class BackendDelegate; 117 118 // Creates the HistoryBackend and HistoryDatabase on the current thread, 119 // assigning the values to backend_ and db_. 120 void CreateBackendAndDatabase() { 121 backend_ = new HistoryBackend(history_dir_, 0, new BackendDelegate(this), 122 NULL); 123 backend_->Init(std::string(), false); 124 db_ = backend_->db_.get(); 125 DCHECK(in_mem_backend_) << "Mem backend should have been set by " 126 "HistoryBackend::Init"; 127 } 128 129 void CreateDBVersion(int version) { 130 base::FilePath data_path; 131 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 132 data_path = data_path.AppendASCII("History"); 133 data_path = 134 data_path.AppendASCII(base::StringPrintf("history.%d.sql", version)); 135 ASSERT_NO_FATAL_FAILURE( 136 ExecuteSQLScript(data_path, history_dir_.Append( 137 chrome::kHistoryFilename))); 138 } 139 140 // testing::Test 141 virtual void SetUp() { 142 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 143 history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBTest"); 144 ASSERT_TRUE(file_util::CreateDirectory(history_dir_)); 145 } 146 147 void DeleteBackend() { 148 if (backend_) { 149 backend_->Closing(); 150 backend_ = NULL; 151 } 152 } 153 154 virtual void TearDown() { 155 DeleteBackend(); 156 157 // Make sure we don't have any event pending that could disrupt the next 158 // test. 159 MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); 160 MessageLoop::current()->Run(); 161 } 162 163 int64 AddDownload(DownloadItem::DownloadState state, const Time& time) { 164 std::vector<GURL> url_chain; 165 url_chain.push_back(GURL("foo-url")); 166 167 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")), 168 base::FilePath(FILE_PATH_LITERAL("foo-path")), 169 url_chain, 170 GURL(std::string()), 171 time, 172 time, 173 0, 174 512, 175 state, 176 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, 177 content::DOWNLOAD_INTERRUPT_REASON_NONE, 178 0, 179 0); 180 return db_->CreateDownload(download); 181 } 182 183 base::ScopedTempDir temp_dir_; 184 185 MessageLoopForUI message_loop_; 186 187 // names of the database files 188 base::FilePath history_dir_; 189 190 // Created via CreateBackendAndDatabase. 191 scoped_refptr<HistoryBackend> backend_; 192 scoped_ptr<InMemoryHistoryBackend> in_mem_backend_; 193 HistoryDatabase* db_; // Cached reference to the backend's database. 194}; 195 196void BackendDelegate::SetInMemoryBackend(int backend_id, 197 InMemoryHistoryBackend* backend) { 198 // Save the in-memory backend to the history test object, this happens 199 // synchronously, so we don't have to do anything fancy. 200 history_test_->in_mem_backend_.reset(backend); 201} 202 203void BackendDelegate::BroadcastNotifications(int type, 204 HistoryDetails* details) { 205 // Currently, just send the notifications directly to the in-memory database. 206 // We may want do do something more fancy in the future. 207 content::Details<HistoryDetails> det(details); 208 history_test_->in_mem_backend_->Observe(type, 209 content::Source<HistoryBackendDBTest>(NULL), det); 210 211 // The backend passes ownership of the details pointer to us. 212 delete details; 213} 214 215TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) { 216 CreateBackendAndDatabase(); 217 218 // Initially there should be nothing in the downloads database. 219 std::vector<DownloadRow> downloads; 220 db_->QueryDownloads(&downloads); 221 EXPECT_EQ(0U, downloads.size()); 222 223 // Add a download, test that it was added, remove it, test that it was 224 // removed. 225 DownloadID handle; 226 EXPECT_NE(0, handle = AddDownload(DownloadItem::COMPLETE, Time())); 227 db_->QueryDownloads(&downloads); 228 EXPECT_EQ(1U, downloads.size()); 229 db_->RemoveDownload(handle); 230 db_->QueryDownloads(&downloads); 231 EXPECT_EQ(0U, downloads.size()); 232} 233 234TEST_F(HistoryBackendDBTest, MigrateDownloadsState) { 235 // Create the db we want. 236 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22)); 237 { 238 // Open the db for manual manipulation. 239 sql::Connection db; 240 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 241 242 // Manually insert corrupted rows; there's infrastructure in place now to 243 // make this impossible, at least according to the test above. 244 for (int state = 0; state < 5; ++state) { 245 sql::Statement s(db.GetUniqueStatement( 246 "INSERT INTO downloads (id, full_path, url, start_time, " 247 "received_bytes, total_bytes, state, end_time, opened) VALUES " 248 "(?, ?, ?, ?, ?, ?, ?, ?, ?)")); 249 s.BindInt64(0, 1 + state); 250 s.BindString(1, "path"); 251 s.BindString(2, "url"); 252 s.BindInt64(3, base::Time::Now().ToTimeT()); 253 s.BindInt64(4, 100); 254 s.BindInt64(5, 100); 255 s.BindInt(6, state); 256 s.BindInt64(7, base::Time::Now().ToTimeT()); 257 s.BindInt(8, state % 2); 258 ASSERT_TRUE(s.Run()); 259 } 260 } 261 262 // Re-open the db using the HistoryDatabase, which should migrate from version 263 // 22 to the current version, fixing just the row whose state was 3. 264 // Then close the db so that we can re-open it directly. 265 CreateBackendAndDatabase(); 266 DeleteBackend(); 267 { 268 // Re-open the db for manual manipulation. 269 sql::Connection db; 270 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 271 { 272 // The version should have been updated. 273 int cur_version = HistoryDatabase::GetCurrentVersion(); 274 ASSERT_LT(22, cur_version); 275 sql::Statement s(db.GetUniqueStatement( 276 "SELECT value FROM meta WHERE key = 'version'")); 277 EXPECT_TRUE(s.Step()); 278 EXPECT_EQ(cur_version, s.ColumnInt(0)); 279 } 280 { 281 sql::Statement statement(db.GetUniqueStatement( 282 "SELECT id, state, opened " 283 "FROM downloads " 284 "ORDER BY id")); 285 int counter = 0; 286 while (statement.Step()) { 287 EXPECT_EQ(1 + counter, statement.ColumnInt64(0)); 288 // The only thing that migration should have changed was state from 3 to 289 // 4. 290 EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1)); 291 EXPECT_EQ(counter % 2, statement.ColumnInt(2)); 292 ++counter; 293 } 294 EXPECT_EQ(5, counter); 295 } 296 } 297} 298 299TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) { 300 Time now(base::Time::Now()); 301 302 // Create the db we want. The schema didn't change from 22->23, so just 303 // re-use the v22 file. 304 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22)); 305 { 306 // Re-open the db for manual manipulation. 307 sql::Connection db; 308 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 309 310 // Manually insert some rows. 311 sql::Statement s(db.GetUniqueStatement( 312 "INSERT INTO downloads (id, full_path, url, start_time, " 313 "received_bytes, total_bytes, state, end_time, opened) VALUES " 314 "(?, ?, ?, ?, ?, ?, ?, ?, ?)")); 315 316 int64 db_handle = 0; 317 // Null path. 318 s.BindInt64(0, ++db_handle); 319 s.BindString(1, std::string()); 320 s.BindString(2, "http://whatever.com/index.html"); 321 s.BindInt64(3, now.ToTimeT()); 322 s.BindInt64(4, 100); 323 s.BindInt64(5, 100); 324 s.BindInt(6, 1); 325 s.BindInt64(7, now.ToTimeT()); 326 s.BindInt(8, 1); 327 ASSERT_TRUE(s.Run()); 328 s.Reset(true); 329 330 // Non-null path. 331 s.BindInt64(0, ++db_handle); 332 s.BindString(1, "/path/to/some/file"); 333 s.BindString(2, "http://whatever.com/index1.html"); 334 s.BindInt64(3, now.ToTimeT()); 335 s.BindInt64(4, 100); 336 s.BindInt64(5, 100); 337 s.BindInt(6, 1); 338 s.BindInt64(7, now.ToTimeT()); 339 s.BindInt(8, 1); 340 ASSERT_TRUE(s.Run()); 341 } 342 343 // Re-open the db using the HistoryDatabase, which should migrate from version 344 // 23 to 24, creating the new tables and creating the new path, reason, 345 // and danger columns. 346 CreateBackendAndDatabase(); 347 DeleteBackend(); 348 { 349 // Re-open the db for manual manipulation. 350 sql::Connection db; 351 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 352 { 353 // The version should have been updated. 354 int cur_version = HistoryDatabase::GetCurrentVersion(); 355 ASSERT_LT(23, cur_version); 356 sql::Statement s(db.GetUniqueStatement( 357 "SELECT value FROM meta WHERE key = 'version'")); 358 EXPECT_TRUE(s.Step()); 359 EXPECT_EQ(cur_version, s.ColumnInt(0)); 360 } 361 { 362 base::Time nowish(base::Time::FromTimeT(now.ToTimeT())); 363 364 // Confirm downloads table is valid. 365 sql::Statement statement(db.GetUniqueStatement( 366 "SELECT id, interrupt_reason, current_path, target_path, " 367 " danger_type, start_time, end_time " 368 "FROM downloads ORDER BY id")); 369 EXPECT_TRUE(statement.Step()); 370 EXPECT_EQ(1, statement.ColumnInt64(0)); 371 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, 372 statement.ColumnInt(1)); 373 EXPECT_EQ("", statement.ColumnString(2)); 374 EXPECT_EQ("", statement.ColumnString(3)); 375 // Implicit dependence on value of kDangerTypeNotDangerous from 376 // download_database.cc. 377 EXPECT_EQ(0, statement.ColumnInt(4)); 378 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5)); 379 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6)); 380 381 EXPECT_TRUE(statement.Step()); 382 EXPECT_EQ(2, statement.ColumnInt64(0)); 383 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, 384 statement.ColumnInt(1)); 385 EXPECT_EQ("/path/to/some/file", statement.ColumnString(2)); 386 EXPECT_EQ("/path/to/some/file", statement.ColumnString(3)); 387 EXPECT_EQ(0, statement.ColumnInt(4)); 388 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5)); 389 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6)); 390 391 EXPECT_FALSE(statement.Step()); 392 } 393 { 394 // Confirm downloads_url_chains table is valid. 395 sql::Statement statement(db.GetUniqueStatement( 396 "SELECT id, chain_index, url FROM downloads_url_chains " 397 " ORDER BY id, chain_index")); 398 EXPECT_TRUE(statement.Step()); 399 EXPECT_EQ(1, statement.ColumnInt64(0)); 400 EXPECT_EQ(0, statement.ColumnInt(1)); 401 EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2)); 402 403 EXPECT_TRUE(statement.Step()); 404 EXPECT_EQ(2, statement.ColumnInt64(0)); 405 EXPECT_EQ(0, statement.ColumnInt(1)); 406 EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2)); 407 408 EXPECT_FALSE(statement.Step()); 409 } 410 } 411} 412 413TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) { 414 // Create the DB. 415 CreateBackendAndDatabase(); 416 417 base::Time now(base::Time::Now()); 418 419 // Add some downloads. 420 AddDownload(DownloadItem::COMPLETE, now); 421 int64 did2 = AddDownload(DownloadItem::COMPLETE, now + 422 base::TimeDelta::FromDays(2)); 423 int64 did3 = AddDownload(DownloadItem::COMPLETE, now - 424 base::TimeDelta::FromDays(2)); 425 426 // Confirm that resulted in the correct number of rows in the DB. 427 DeleteBackend(); 428 { 429 sql::Connection db; 430 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 431 sql::Statement statement(db.GetUniqueStatement( 432 "Select Count(*) from downloads")); 433 EXPECT_TRUE(statement.Step()); 434 EXPECT_EQ(3, statement.ColumnInt(0)); 435 436 sql::Statement statement1(db.GetUniqueStatement( 437 "Select Count(*) from downloads_url_chains")); 438 EXPECT_TRUE(statement1.Step()); 439 EXPECT_EQ(3, statement1.ColumnInt(0)); 440 } 441 442 // Delete some rows and make sure the results are still correct. 443 CreateBackendAndDatabase(); 444 db_->RemoveDownload(did2); 445 db_->RemoveDownload(did3); 446 DeleteBackend(); 447 { 448 sql::Connection db; 449 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 450 sql::Statement statement(db.GetUniqueStatement( 451 "Select Count(*) from downloads")); 452 EXPECT_TRUE(statement.Step()); 453 EXPECT_EQ(1, statement.ColumnInt(0)); 454 455 sql::Statement statement1(db.GetUniqueStatement( 456 "Select Count(*) from downloads_url_chains")); 457 EXPECT_TRUE(statement1.Step()); 458 EXPECT_EQ(1, statement1.ColumnInt(0)); 459 } 460} 461 462TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) { 463 CreateBackendAndDatabase(); 464 base::Time now(base::Time::Now()); 465 std::vector<GURL> url_chain; 466 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")), 467 base::FilePath(FILE_PATH_LITERAL("foo-path")), 468 url_chain, 469 GURL(std::string()), 470 now, 471 now, 472 0, 473 512, 474 DownloadItem::COMPLETE, 475 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, 476 content::DOWNLOAD_INTERRUPT_REASON_NONE, 477 0, 478 0); 479 480 // Creating records without any urls should fail. 481 EXPECT_EQ(DownloadDatabase::kUninitializedHandle, 482 db_->CreateDownload(download)); 483 484 download.url_chain.push_back(GURL("foo-url")); 485 EXPECT_EQ(1, db_->CreateDownload(download)); 486 487 // Pretend that the URLs were dropped. 488 DeleteBackend(); 489 { 490 sql::Connection db; 491 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 492 sql::Statement statement(db.GetUniqueStatement( 493 "DELETE FROM downloads_url_chains WHERE id=1")); 494 ASSERT_TRUE(statement.Run()); 495 } 496 CreateBackendAndDatabase(); 497 std::vector<DownloadRow> downloads; 498 db_->QueryDownloads(&downloads); 499 EXPECT_EQ(0U, downloads.size()); 500 501 // QueryDownloads should have nuked the corrupt record. 502 DeleteBackend(); 503 { 504 sql::Connection db; 505 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 506 { 507 sql::Statement statement(db.GetUniqueStatement( 508 "SELECT count(*) from downloads")); 509 ASSERT_TRUE(statement.Step()); 510 EXPECT_EQ(0, statement.ColumnInt(0)); 511 } 512 } 513} 514 515TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) { 516 // Create the DB. 517 CreateBackendAndDatabase(); 518 519 base::Time now(base::Time::Now()); 520 521 // Put an IN_PROGRESS download in the DB. 522 AddDownload(DownloadItem::IN_PROGRESS, now); 523 524 // Confirm that they made it into the DB unchanged. 525 DeleteBackend(); 526 { 527 sql::Connection db; 528 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 529 sql::Statement statement(db.GetUniqueStatement( 530 "Select Count(*) from downloads")); 531 EXPECT_TRUE(statement.Step()); 532 EXPECT_EQ(1, statement.ColumnInt(0)); 533 534 sql::Statement statement1(db.GetUniqueStatement( 535 "Select state, interrupt_reason from downloads")); 536 EXPECT_TRUE(statement1.Step()); 537 EXPECT_EQ(DownloadDatabase::kStateInProgress, statement1.ColumnInt(0)); 538 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, statement1.ColumnInt(1)); 539 EXPECT_FALSE(statement1.Step()); 540 } 541 542 // Read in the DB through query downloads, then test that the 543 // right transformation was returned. 544 CreateBackendAndDatabase(); 545 std::vector<DownloadRow> results; 546 db_->QueryDownloads(&results); 547 ASSERT_EQ(1u, results.size()); 548 EXPECT_EQ(content::DownloadItem::INTERRUPTED, results[0].state); 549 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH, 550 results[0].interrupt_reason); 551 552 // Allow the update to propagate, shut down the DB, and confirm that 553 // the query updated the on disk database as well. 554 MessageLoop::current()->RunUntilIdle(); 555 DeleteBackend(); 556 { 557 sql::Connection db; 558 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 559 sql::Statement statement(db.GetUniqueStatement( 560 "Select Count(*) from downloads")); 561 EXPECT_TRUE(statement.Step()); 562 EXPECT_EQ(1, statement.ColumnInt(0)); 563 564 sql::Statement statement1(db.GetUniqueStatement( 565 "Select state, interrupt_reason from downloads")); 566 EXPECT_TRUE(statement1.Step()); 567 EXPECT_EQ(DownloadDatabase::kStateInterrupted, statement1.ColumnInt(0)); 568 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH, 569 statement1.ColumnInt(1)); 570 EXPECT_FALSE(statement1.Step()); 571 } 572} 573 574struct InterruptReasonAssociation { 575 std::string name; 576 int value; 577}; 578 579// Test is dependent on interrupt reasons being listed in header file 580// in order. 581const InterruptReasonAssociation current_reasons[] = { 582#define INTERRUPT_REASON(a, b) { #a, b }, 583#include "content/public/browser/download_interrupt_reason_values.h" 584#undef INTERRUPT_REASON 585}; 586 587// This represents a list of all reasons we've previously used; 588// Do Not Remove Any Entries From This List. 589const InterruptReasonAssociation historical_reasons[] = { 590 {"FILE_FAILED", 1}, 591 {"FILE_ACCESS_DENIED", 2}, 592 {"FILE_NO_SPACE", 3}, 593 {"FILE_NAME_TOO_LONG", 5}, 594 {"FILE_TOO_LARGE", 6}, 595 {"FILE_VIRUS_INFECTED", 7}, 596 {"FILE_TRANSIENT_ERROR", 10}, 597 {"FILE_BLOCKED", 11}, 598 {"FILE_SECURITY_CHECK_FAILED", 12}, 599 {"FILE_TOO_SHORT", 13}, 600 {"NETWORK_FAILED", 20}, 601 {"NETWORK_TIMEOUT", 21}, 602 {"NETWORK_DISCONNECTED", 22}, 603 {"NETWORK_SERVER_DOWN", 23}, 604 {"SERVER_FAILED", 30}, 605 {"SERVER_NO_RANGE", 31}, 606 {"SERVER_PRECONDITION", 32}, 607 {"SERVER_BAD_CONTENT", 33}, 608 {"USER_CANCELED", 40}, 609 {"USER_SHUTDOWN", 41}, 610 {"CRASH", 50}, 611}; 612 613// Make sure no one has changed a DownloadInterruptReason we've previously 614// persisted. 615TEST_F(HistoryBackendDBTest, 616 ConfirmDownloadInterruptReasonBackwardsCompatible) { 617 // Are there any cases in which a historical number has been repurposed 618 // for an error other than it's original? 619 for (size_t i = 0; i < arraysize(current_reasons); i++) { 620 const InterruptReasonAssociation& cur_reason(current_reasons[i]); 621 bool found = false; 622 623 for (size_t j = 0; j < arraysize(historical_reasons); ++j) { 624 const InterruptReasonAssociation& hist_reason(historical_reasons[j]); 625 626 if (hist_reason.value == cur_reason.value) { 627 EXPECT_EQ(cur_reason.name, hist_reason.name) 628 << "Same integer value used for old error \"" 629 << hist_reason.name 630 << "\" as for new error \"" 631 << cur_reason.name 632 << "\"." << std::endl 633 << "**This will cause database conflicts with persisted values**" 634 << std::endl 635 << "Please assign a new, non-conflicting value for the new error."; 636 } 637 638 if (hist_reason.name == cur_reason.name) { 639 EXPECT_EQ(cur_reason.value, hist_reason.value) 640 << "Same name (\"" << hist_reason.name 641 << "\") maps to a different value historically (" 642 << hist_reason.value << ") and currently (" 643 << cur_reason.value << ")" << std::endl 644 << "This may cause database conflicts with persisted values" 645 << std::endl 646 << "If this error is the same as the old one, you should" 647 << std::endl 648 << "use the old value, and if it is different, you should" 649 << std::endl 650 << "use a new name."; 651 652 found = true; 653 } 654 } 655 656 EXPECT_TRUE(found) 657 << "Error \"" << cur_reason.name << "\" not found in historical list." 658 << std::endl 659 << "Please add it."; 660 } 661} 662 663// The tracker uses RenderProcessHost pointers for scoping but never 664// dereferences them. We use ints because it's easier. This function converts 665// between the two. 666static void* MakeFakeHost(int id) { 667 void* host = 0; 668 memcpy(&host, &id, sizeof(id)); 669 return host; 670} 671 672class HistoryTest : public testing::Test { 673 public: 674 HistoryTest() 675 : got_thumbnail_callback_(false), 676 redirect_query_success_(false), 677 query_url_success_(false) { 678 } 679 680 virtual ~HistoryTest() { 681 } 682 683 void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle, 684 std::vector<PageUsageData*>* data) { 685 page_usage_data_.swap(*data); 686 MessageLoop::current()->Quit(); 687 } 688 689 void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) { 690 MessageLoop::current()->Quit(); 691 } 692 693 void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle, 694 MostVisitedURLList url_list) { 695 most_visited_urls_.swap(url_list); 696 MessageLoop::current()->Quit(); 697 } 698 699 protected: 700 friend class BackendDelegate; 701 702 // testing::Test 703 virtual void SetUp() { 704 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 705 history_dir_ = temp_dir_.path().AppendASCII("HistoryTest"); 706 ASSERT_TRUE(file_util::CreateDirectory(history_dir_)); 707 history_service_.reset(new HistoryService); 708 if (!history_service_->Init(history_dir_, NULL)) { 709 history_service_.reset(); 710 ADD_FAILURE(); 711 } 712 } 713 714 virtual void TearDown() { 715 if (history_service_) 716 CleanupHistoryService(); 717 718 // Make sure we don't have any event pending that could disrupt the next 719 // test. 720 MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); 721 MessageLoop::current()->Run(); 722 } 723 724 void CleanupHistoryService() { 725 DCHECK(history_service_); 726 727 history_service_->NotifyRenderProcessHostDestruction(0); 728 history_service_->SetOnBackendDestroyTask(MessageLoop::QuitClosure()); 729 history_service_->Cleanup(); 730 history_service_.reset(); 731 732 // Wait for the backend class to terminate before deleting the files and 733 // moving to the next test. Note: if this never terminates, somebody is 734 // probably leaking a reference to the history backend, so it never calls 735 // our destroy task. 736 MessageLoop::current()->Run(); 737 } 738 739 // Fills the query_url_row_ and query_url_visits_ structures with the 740 // information about the given URL and returns true. If the URL was not 741 // found, this will return false and those structures will not be changed. 742 bool QueryURL(HistoryService* history, const GURL& url) { 743 history_service_->QueryURL(url, true, &consumer_, 744 base::Bind(&HistoryTest::SaveURLAndQuit, 745 base::Unretained(this))); 746 MessageLoop::current()->Run(); // Will be exited in SaveURLAndQuit. 747 return query_url_success_; 748 } 749 750 // Callback for HistoryService::QueryURL. 751 void SaveURLAndQuit(HistoryService::Handle handle, 752 bool success, 753 const URLRow* url_row, 754 VisitVector* visit_vector) { 755 query_url_success_ = success; 756 if (query_url_success_) { 757 query_url_row_ = *url_row; 758 query_url_visits_.swap(*visit_vector); 759 } else { 760 query_url_row_ = URLRow(); 761 query_url_visits_.clear(); 762 } 763 MessageLoop::current()->Quit(); 764 } 765 766 // Fills in saved_redirects_ with the redirect information for the given URL, 767 // returning true on success. False means the URL was not found. 768 bool QueryRedirectsFrom(HistoryService* history, const GURL& url) { 769 history_service_->QueryRedirectsFrom( 770 url, &consumer_, 771 base::Bind(&HistoryTest::OnRedirectQueryComplete, 772 base::Unretained(this))); 773 MessageLoop::current()->Run(); // Will be exited in *QueryComplete. 774 return redirect_query_success_; 775 } 776 777 // Callback for QueryRedirects. 778 void OnRedirectQueryComplete(HistoryService::Handle handle, 779 GURL url, 780 bool success, 781 history::RedirectList* redirects) { 782 redirect_query_success_ = success; 783 if (redirect_query_success_) 784 saved_redirects_.swap(*redirects); 785 else 786 saved_redirects_.clear(); 787 MessageLoop::current()->Quit(); 788 } 789 790 base::ScopedTempDir temp_dir_; 791 792 MessageLoopForUI message_loop_; 793 794 // PageUsageData vector to test segments. 795 ScopedVector<PageUsageData> page_usage_data_; 796 797 MostVisitedURLList most_visited_urls_; 798 799 // When non-NULL, this will be deleted on tear down and we will block until 800 // the backend thread has completed. This allows tests for the history 801 // service to use this feature, but other tests to ignore this. 802 scoped_ptr<HistoryService> history_service_; 803 804 // names of the database files 805 base::FilePath history_dir_; 806 807 // Set by the thumbnail callback when we get data, you should be sure to 808 // clear this before issuing a thumbnail request. 809 bool got_thumbnail_callback_; 810 std::vector<unsigned char> thumbnail_data_; 811 812 // Set by the redirect callback when we get data. You should be sure to 813 // clear this before issuing a redirect request. 814 history::RedirectList saved_redirects_; 815 bool redirect_query_success_; 816 817 // For history requests. 818 CancelableRequestConsumer consumer_; 819 820 // For saving URL info after a call to QueryURL 821 bool query_url_success_; 822 URLRow query_url_row_; 823 VisitVector query_url_visits_; 824}; 825 826TEST_F(HistoryTest, AddPage) { 827 ASSERT_TRUE(history_service_.get()); 828 // Add the page once from a child frame. 829 const GURL test_url("http://www.google.com/"); 830 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(), 831 history::RedirectList(), 832 content::PAGE_TRANSITION_MANUAL_SUBFRAME, 833 history::SOURCE_BROWSED, false); 834 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 835 EXPECT_EQ(1, query_url_row_.visit_count()); 836 EXPECT_EQ(0, query_url_row_.typed_count()); 837 EXPECT_TRUE(query_url_row_.hidden()); // Hidden because of child frame. 838 839 // Add the page once from the main frame (should unhide it). 840 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(), 841 history::RedirectList(), content::PAGE_TRANSITION_LINK, 842 history::SOURCE_BROWSED, false); 843 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 844 EXPECT_EQ(2, query_url_row_.visit_count()); // Added twice. 845 EXPECT_EQ(0, query_url_row_.typed_count()); // Never typed. 846 EXPECT_FALSE(query_url_row_.hidden()); // Because loaded in main frame. 847} 848 849TEST_F(HistoryTest, AddRedirect) { 850 ASSERT_TRUE(history_service_.get()); 851 const char* first_sequence[] = { 852 "http://first.page.com/", 853 "http://second.page.com/"}; 854 int first_count = arraysize(first_sequence); 855 history::RedirectList first_redirects; 856 for (int i = 0; i < first_count; i++) 857 first_redirects.push_back(GURL(first_sequence[i])); 858 859 // Add the sequence of pages as a server with no referrer. Note that we need 860 // to have a non-NULL page ID scope. 861 history_service_->AddPage( 862 first_redirects.back(), base::Time::Now(), MakeFakeHost(1), 863 0, GURL(), first_redirects, content::PAGE_TRANSITION_LINK, 864 history::SOURCE_BROWSED, true); 865 866 // The first page should be added once with a link visit type (because we set 867 // LINK when we added the original URL, and a referrer of nowhere (0). 868 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[0])); 869 EXPECT_EQ(1, query_url_row_.visit_count()); 870 ASSERT_EQ(1U, query_url_visits_.size()); 871 int64 first_visit = query_url_visits_[0].visit_id; 872 EXPECT_EQ(content::PAGE_TRANSITION_LINK | 873 content::PAGE_TRANSITION_CHAIN_START, 874 query_url_visits_[0].transition); 875 EXPECT_EQ(0, query_url_visits_[0].referring_visit); // No referrer. 876 877 // The second page should be a server redirect type with a referrer of the 878 // first page. 879 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[1])); 880 EXPECT_EQ(1, query_url_row_.visit_count()); 881 ASSERT_EQ(1U, query_url_visits_.size()); 882 int64 second_visit = query_url_visits_[0].visit_id; 883 EXPECT_EQ(content::PAGE_TRANSITION_SERVER_REDIRECT | 884 content::PAGE_TRANSITION_CHAIN_END, 885 query_url_visits_[0].transition); 886 EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit); 887 888 // Check that the redirect finding function successfully reports it. 889 saved_redirects_.clear(); 890 QueryRedirectsFrom(history_service_.get(), first_redirects[0]); 891 ASSERT_EQ(1U, saved_redirects_.size()); 892 EXPECT_EQ(first_redirects[1], saved_redirects_[0]); 893 894 // Now add a client redirect from that second visit to a third, client 895 // redirects are tracked by the RenderView prior to updating history, 896 // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior. 897 history::RedirectList second_redirects; 898 second_redirects.push_back(first_redirects[1]); 899 second_redirects.push_back(GURL("http://last.page.com/")); 900 history_service_->AddPage(second_redirects[1], base::Time::Now(), 901 MakeFakeHost(1), 1, second_redirects[0], second_redirects, 902 static_cast<content::PageTransition>( 903 content::PAGE_TRANSITION_LINK | 904 content::PAGE_TRANSITION_CLIENT_REDIRECT), 905 history::SOURCE_BROWSED, true); 906 907 // The last page (source of the client redirect) should NOT have an 908 // additional visit added, because it was a client redirect (normally it 909 // would). We should only have 1 left over from the first sequence. 910 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[0])); 911 EXPECT_EQ(1, query_url_row_.visit_count()); 912 913 // The final page should be set as a client redirect from the previous visit. 914 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[1])); 915 EXPECT_EQ(1, query_url_row_.visit_count()); 916 ASSERT_EQ(1U, query_url_visits_.size()); 917 EXPECT_EQ(content::PAGE_TRANSITION_CLIENT_REDIRECT | 918 content::PAGE_TRANSITION_CHAIN_END, 919 query_url_visits_[0].transition); 920 EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit); 921} 922 923TEST_F(HistoryTest, MakeIntranetURLsTyped) { 924 ASSERT_TRUE(history_service_.get()); 925 926 // Add a non-typed visit to an intranet URL on an unvisited host. This should 927 // get promoted to a typed visit. 928 const GURL test_url("http://intranet_host/path"); 929 history_service_->AddPage( 930 test_url, base::Time::Now(), NULL, 0, GURL(), 931 history::RedirectList(), content::PAGE_TRANSITION_LINK, 932 history::SOURCE_BROWSED, false); 933 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 934 EXPECT_EQ(1, query_url_row_.visit_count()); 935 EXPECT_EQ(1, query_url_row_.typed_count()); 936 ASSERT_EQ(1U, query_url_visits_.size()); 937 EXPECT_EQ(content::PAGE_TRANSITION_TYPED, 938 content::PageTransitionStripQualifier(query_url_visits_[0].transition)); 939 940 // Add more visits on the same host. None of these should be promoted since 941 // there is already a typed visit. 942 943 // Different path. 944 const GURL test_url2("http://intranet_host/different_path"); 945 history_service_->AddPage( 946 test_url2, base::Time::Now(), NULL, 0, GURL(), 947 history::RedirectList(), content::PAGE_TRANSITION_LINK, 948 history::SOURCE_BROWSED, false); 949 EXPECT_TRUE(QueryURL(history_service_.get(), test_url2)); 950 EXPECT_EQ(1, query_url_row_.visit_count()); 951 EXPECT_EQ(0, query_url_row_.typed_count()); 952 ASSERT_EQ(1U, query_url_visits_.size()); 953 EXPECT_EQ(content::PAGE_TRANSITION_LINK, 954 content::PageTransitionStripQualifier(query_url_visits_[0].transition)); 955 956 // No path. 957 const GURL test_url3("http://intranet_host/"); 958 history_service_->AddPage( 959 test_url3, base::Time::Now(), NULL, 0, GURL(), 960 history::RedirectList(), content::PAGE_TRANSITION_LINK, 961 history::SOURCE_BROWSED, false); 962 EXPECT_TRUE(QueryURL(history_service_.get(), test_url3)); 963 EXPECT_EQ(1, query_url_row_.visit_count()); 964 EXPECT_EQ(0, query_url_row_.typed_count()); 965 ASSERT_EQ(1U, query_url_visits_.size()); 966 EXPECT_EQ(content::PAGE_TRANSITION_LINK, 967 content::PageTransitionStripQualifier(query_url_visits_[0].transition)); 968 969 // Different scheme. 970 const GURL test_url4("https://intranet_host/"); 971 history_service_->AddPage( 972 test_url4, base::Time::Now(), NULL, 0, GURL(), 973 history::RedirectList(), content::PAGE_TRANSITION_LINK, 974 history::SOURCE_BROWSED, false); 975 EXPECT_TRUE(QueryURL(history_service_.get(), test_url4)); 976 EXPECT_EQ(1, query_url_row_.visit_count()); 977 EXPECT_EQ(0, query_url_row_.typed_count()); 978 ASSERT_EQ(1U, query_url_visits_.size()); 979 EXPECT_EQ(content::PAGE_TRANSITION_LINK, 980 content::PageTransitionStripQualifier(query_url_visits_[0].transition)); 981 982 // Different transition. 983 const GURL test_url5("http://intranet_host/another_path"); 984 history_service_->AddPage( 985 test_url5, base::Time::Now(), NULL, 0, GURL(), 986 history::RedirectList(), 987 content::PAGE_TRANSITION_AUTO_BOOKMARK, 988 history::SOURCE_BROWSED, false); 989 EXPECT_TRUE(QueryURL(history_service_.get(), test_url5)); 990 EXPECT_EQ(1, query_url_row_.visit_count()); 991 EXPECT_EQ(0, query_url_row_.typed_count()); 992 ASSERT_EQ(1U, query_url_visits_.size()); 993 EXPECT_EQ(content::PAGE_TRANSITION_AUTO_BOOKMARK, 994 content::PageTransitionStripQualifier(query_url_visits_[0].transition)); 995 996 // Original URL. 997 history_service_->AddPage( 998 test_url, base::Time::Now(), NULL, 0, GURL(), 999 history::RedirectList(), content::PAGE_TRANSITION_LINK, 1000 history::SOURCE_BROWSED, false); 1001 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1002 EXPECT_EQ(2, query_url_row_.visit_count()); 1003 EXPECT_EQ(1, query_url_row_.typed_count()); 1004 ASSERT_EQ(2U, query_url_visits_.size()); 1005 EXPECT_EQ(content::PAGE_TRANSITION_LINK, 1006 content::PageTransitionStripQualifier(query_url_visits_[1].transition)); 1007} 1008 1009TEST_F(HistoryTest, Typed) { 1010 ASSERT_TRUE(history_service_.get()); 1011 1012 // Add the page once as typed. 1013 const GURL test_url("http://www.google.com/"); 1014 history_service_->AddPage( 1015 test_url, base::Time::Now(), NULL, 0, GURL(), 1016 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1017 history::SOURCE_BROWSED, false); 1018 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1019 1020 // We should have the same typed & visit count. 1021 EXPECT_EQ(1, query_url_row_.visit_count()); 1022 EXPECT_EQ(1, query_url_row_.typed_count()); 1023 1024 // Add the page again not typed. 1025 history_service_->AddPage( 1026 test_url, base::Time::Now(), NULL, 0, GURL(), 1027 history::RedirectList(), content::PAGE_TRANSITION_LINK, 1028 history::SOURCE_BROWSED, false); 1029 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1030 1031 // The second time should not have updated the typed count. 1032 EXPECT_EQ(2, query_url_row_.visit_count()); 1033 EXPECT_EQ(1, query_url_row_.typed_count()); 1034 1035 // Add the page again as a generated URL. 1036 history_service_->AddPage( 1037 test_url, base::Time::Now(), NULL, 0, GURL(), 1038 history::RedirectList(), content::PAGE_TRANSITION_GENERATED, 1039 history::SOURCE_BROWSED, false); 1040 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1041 1042 // This should have worked like a link click. 1043 EXPECT_EQ(3, query_url_row_.visit_count()); 1044 EXPECT_EQ(1, query_url_row_.typed_count()); 1045 1046 // Add the page again as a reload. 1047 history_service_->AddPage( 1048 test_url, base::Time::Now(), NULL, 0, GURL(), 1049 history::RedirectList(), content::PAGE_TRANSITION_RELOAD, 1050 history::SOURCE_BROWSED, false); 1051 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1052 1053 // This should not have incremented any visit counts. 1054 EXPECT_EQ(3, query_url_row_.visit_count()); 1055 EXPECT_EQ(1, query_url_row_.typed_count()); 1056} 1057 1058TEST_F(HistoryTest, SetTitle) { 1059 ASSERT_TRUE(history_service_.get()); 1060 1061 // Add a URL. 1062 const GURL existing_url("http://www.google.com/"); 1063 history_service_->AddPage( 1064 existing_url, base::Time::Now(), history::SOURCE_BROWSED); 1065 1066 // Set some title. 1067 const string16 existing_title = UTF8ToUTF16("Google"); 1068 history_service_->SetPageTitle(existing_url, existing_title); 1069 1070 // Make sure the title got set. 1071 EXPECT_TRUE(QueryURL(history_service_.get(), existing_url)); 1072 EXPECT_EQ(existing_title, query_url_row_.title()); 1073 1074 // set a title on a nonexistent page 1075 const GURL nonexistent_url("http://news.google.com/"); 1076 const string16 nonexistent_title = UTF8ToUTF16("Google News"); 1077 history_service_->SetPageTitle(nonexistent_url, nonexistent_title); 1078 1079 // Make sure nothing got written. 1080 EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url)); 1081 EXPECT_EQ(string16(), query_url_row_.title()); 1082 1083 // TODO(brettw) this should also test redirects, which get the title of the 1084 // destination page. 1085} 1086 1087// crbug.com/159387: This test fails when daylight savings time ends. 1088TEST_F(HistoryTest, DISABLED_Segments) { 1089 ASSERT_TRUE(history_service_.get()); 1090 1091 static const void* scope = static_cast<void*>(this); 1092 1093 // Add a URL. 1094 const GURL existing_url("http://www.google.com/"); 1095 history_service_->AddPage( 1096 existing_url, base::Time::Now(), scope, 0, GURL(), 1097 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1098 history::SOURCE_BROWSED, false); 1099 1100 // Make sure a segment was created. 1101 history_service_->QuerySegmentUsageSince( 1102 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 1103 base::Bind(&HistoryTest::OnSegmentUsageAvailable, 1104 base::Unretained(this))); 1105 1106 // Wait for processing. 1107 MessageLoop::current()->Run(); 1108 1109 ASSERT_EQ(1U, page_usage_data_.size()); 1110 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 1111 EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore()); 1112 1113 // Add a URL which doesn't create a segment. 1114 const GURL link_url("http://yahoo.com/"); 1115 history_service_->AddPage( 1116 link_url, base::Time::Now(), scope, 0, GURL(), 1117 history::RedirectList(), content::PAGE_TRANSITION_LINK, 1118 history::SOURCE_BROWSED, false); 1119 1120 // Query again 1121 history_service_->QuerySegmentUsageSince( 1122 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 1123 base::Bind(&HistoryTest::OnSegmentUsageAvailable, 1124 base::Unretained(this))); 1125 1126 // Wait for processing. 1127 MessageLoop::current()->Run(); 1128 1129 // Make sure we still have one segment. 1130 ASSERT_EQ(1U, page_usage_data_.size()); 1131 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 1132 1133 // Add a page linked from existing_url. 1134 history_service_->AddPage( 1135 GURL("http://www.google.com/foo"), base::Time::Now(), 1136 scope, 3, existing_url, history::RedirectList(), 1137 content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED, 1138 false); 1139 1140 // Query again 1141 history_service_->QuerySegmentUsageSince( 1142 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10, 1143 base::Bind(&HistoryTest::OnSegmentUsageAvailable, 1144 base::Unretained(this))); 1145 1146 // Wait for processing. 1147 MessageLoop::current()->Run(); 1148 1149 // Make sure we still have one segment. 1150 ASSERT_EQ(1U, page_usage_data_.size()); 1151 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url); 1152 1153 // However, the score should have increased. 1154 EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0); 1155} 1156 1157TEST_F(HistoryTest, MostVisitedURLs) { 1158 ASSERT_TRUE(history_service_.get()); 1159 1160 const GURL url0("http://www.google.com/url0/"); 1161 const GURL url1("http://www.google.com/url1/"); 1162 const GURL url2("http://www.google.com/url2/"); 1163 const GURL url3("http://www.google.com/url3/"); 1164 const GURL url4("http://www.google.com/url4/"); 1165 1166 static const void* scope = static_cast<void*>(this); 1167 1168 // Add two pages. 1169 history_service_->AddPage( 1170 url0, base::Time::Now(), scope, 0, GURL(), 1171 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1172 history::SOURCE_BROWSED, false); 1173 history_service_->AddPage( 1174 url1, base::Time::Now(), scope, 0, GURL(), 1175 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1176 history::SOURCE_BROWSED, false); 1177 history_service_->QueryMostVisitedURLs( 1178 20, 90, &consumer_, 1179 base::Bind( 1180 &HistoryTest::OnMostVisitedURLsAvailable, 1181 base::Unretained(this))); 1182 MessageLoop::current()->Run(); 1183 1184 EXPECT_EQ(2U, most_visited_urls_.size()); 1185 EXPECT_EQ(url0, most_visited_urls_[0].url); 1186 EXPECT_EQ(url1, most_visited_urls_[1].url); 1187 1188 // Add another page. 1189 history_service_->AddPage( 1190 url2, base::Time::Now(), scope, 0, GURL(), 1191 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1192 history::SOURCE_BROWSED, false); 1193 history_service_->QueryMostVisitedURLs( 1194 20, 90, &consumer_, 1195 base::Bind( 1196 &HistoryTest::OnMostVisitedURLsAvailable, 1197 base::Unretained(this))); 1198 MessageLoop::current()->Run(); 1199 1200 EXPECT_EQ(3U, most_visited_urls_.size()); 1201 EXPECT_EQ(url0, most_visited_urls_[0].url); 1202 EXPECT_EQ(url1, most_visited_urls_[1].url); 1203 EXPECT_EQ(url2, most_visited_urls_[2].url); 1204 1205 // Revisit url2, making it the top URL. 1206 history_service_->AddPage( 1207 url2, base::Time::Now(), scope, 0, GURL(), 1208 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1209 history::SOURCE_BROWSED, false); 1210 history_service_->QueryMostVisitedURLs( 1211 20, 90, &consumer_, 1212 base::Bind( 1213 &HistoryTest::OnMostVisitedURLsAvailable, 1214 base::Unretained(this))); 1215 MessageLoop::current()->Run(); 1216 1217 EXPECT_EQ(3U, most_visited_urls_.size()); 1218 EXPECT_EQ(url2, most_visited_urls_[0].url); 1219 EXPECT_EQ(url0, most_visited_urls_[1].url); 1220 EXPECT_EQ(url1, most_visited_urls_[2].url); 1221 1222 // Revisit url1, making it the top URL. 1223 history_service_->AddPage( 1224 url1, base::Time::Now(), scope, 0, GURL(), 1225 history::RedirectList(), content::PAGE_TRANSITION_TYPED, 1226 history::SOURCE_BROWSED, false); 1227 history_service_->QueryMostVisitedURLs( 1228 20, 90, &consumer_, 1229 base::Bind( 1230 &HistoryTest::OnMostVisitedURLsAvailable, 1231 base::Unretained(this))); 1232 MessageLoop::current()->Run(); 1233 1234 EXPECT_EQ(3U, most_visited_urls_.size()); 1235 EXPECT_EQ(url1, most_visited_urls_[0].url); 1236 EXPECT_EQ(url2, most_visited_urls_[1].url); 1237 EXPECT_EQ(url0, most_visited_urls_[2].url); 1238 1239 // Redirects 1240 history::RedirectList redirects; 1241 redirects.push_back(url3); 1242 redirects.push_back(url4); 1243 1244 // Visit url4 using redirects. 1245 history_service_->AddPage( 1246 url4, base::Time::Now(), scope, 0, GURL(), 1247 redirects, content::PAGE_TRANSITION_TYPED, 1248 history::SOURCE_BROWSED, false); 1249 history_service_->QueryMostVisitedURLs( 1250 20, 90, &consumer_, 1251 base::Bind( 1252 &HistoryTest::OnMostVisitedURLsAvailable, 1253 base::Unretained(this))); 1254 MessageLoop::current()->Run(); 1255 1256 EXPECT_EQ(4U, most_visited_urls_.size()); 1257 EXPECT_EQ(url1, most_visited_urls_[0].url); 1258 EXPECT_EQ(url2, most_visited_urls_[1].url); 1259 EXPECT_EQ(url0, most_visited_urls_[2].url); 1260 EXPECT_EQ(url3, most_visited_urls_[3].url); 1261 EXPECT_EQ(2U, most_visited_urls_[3].redirects.size()); 1262} 1263 1264// The version of the history database should be current in the "typical 1265// history" example file or it will be imported on startup, throwing off timing 1266// measurements. 1267// 1268// See test/data/profiles/profile_with_default_theme/README.txt for 1269// instructions on how to up the version. 1270TEST(HistoryProfileTest, TypicalProfileVersion) { 1271 base::FilePath file; 1272 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file)); 1273 file = file.AppendASCII("profiles"); 1274 file = file.AppendASCII("profile_with_default_theme"); 1275 file = file.AppendASCII("Default"); 1276 file = file.AppendASCII("History"); 1277 1278 int cur_version = HistoryDatabase::GetCurrentVersion(); 1279 1280 sql::Connection db; 1281 ASSERT_TRUE(db.Open(file)); 1282 1283 { 1284 sql::Statement s(db.GetUniqueStatement( 1285 "SELECT value FROM meta WHERE key = 'version'")); 1286 EXPECT_TRUE(s.Step()); 1287 int file_version = s.ColumnInt(0); 1288 EXPECT_EQ(cur_version, file_version); 1289 } 1290} 1291 1292namespace { 1293 1294// A HistoryDBTask implementation. Each time RunOnDBThread is invoked 1295// invoke_count is increment. When invoked kWantInvokeCount times, true is 1296// returned from RunOnDBThread which should stop RunOnDBThread from being 1297// invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to 1298// true. 1299class HistoryDBTaskImpl : public HistoryDBTask { 1300 public: 1301 static const int kWantInvokeCount; 1302 1303 HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {} 1304 1305 virtual bool RunOnDBThread(HistoryBackend* backend, 1306 HistoryDatabase* db) OVERRIDE { 1307 return (++invoke_count == kWantInvokeCount); 1308 } 1309 1310 virtual void DoneRunOnMainThread() OVERRIDE { 1311 done_invoked = true; 1312 MessageLoop::current()->Quit(); 1313 } 1314 1315 int invoke_count; 1316 bool done_invoked; 1317 1318 private: 1319 virtual ~HistoryDBTaskImpl() {} 1320 1321 DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl); 1322}; 1323 1324// static 1325const int HistoryDBTaskImpl::kWantInvokeCount = 2; 1326 1327} // namespace 1328 1329TEST_F(HistoryTest, HistoryDBTask) { 1330 ASSERT_TRUE(history_service_.get()); 1331 CancelableRequestConsumerT<int, 0> request_consumer; 1332 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl()); 1333 history_service_->ScheduleDBTask(task.get(), &request_consumer); 1334 // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs, 1335 // it will stop the message loop. If the test hangs here, it means 1336 // DoneRunOnMainThread isn't being invoked correctly. 1337 MessageLoop::current()->Run(); 1338 CleanupHistoryService(); 1339 // WARNING: history has now been deleted. 1340 history_service_.reset(); 1341 ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count); 1342 ASSERT_TRUE(task->done_invoked); 1343} 1344 1345TEST_F(HistoryTest, HistoryDBTaskCanceled) { 1346 ASSERT_TRUE(history_service_.get()); 1347 CancelableRequestConsumerT<int, 0> request_consumer; 1348 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl()); 1349 history_service_->ScheduleDBTask(task.get(), &request_consumer); 1350 request_consumer.CancelAllRequests(); 1351 CleanupHistoryService(); 1352 // WARNING: history has now been deleted. 1353 history_service_.reset(); 1354 ASSERT_FALSE(task->done_invoked); 1355} 1356 1357// Dummy SyncChangeProcessor used to help review what SyncChanges are pushed 1358// back up to Sync. 1359// 1360// TODO(akalin): Unify all the various test implementations of 1361// syncer::SyncChangeProcessor. 1362class TestChangeProcessor : public syncer::SyncChangeProcessor { 1363 public: 1364 TestChangeProcessor() {} 1365 virtual ~TestChangeProcessor() {} 1366 1367 virtual syncer::SyncError ProcessSyncChanges( 1368 const tracked_objects::Location& from_here, 1369 const syncer::SyncChangeList& change_list) OVERRIDE { 1370 changes_.insert(changes_.end(), change_list.begin(), change_list.end()); 1371 return syncer::SyncError(); 1372 } 1373 1374 const syncer::SyncChangeList& GetChanges() const { 1375 return changes_; 1376 } 1377 1378 private: 1379 syncer::SyncChangeList changes_; 1380 1381 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor); 1382}; 1383 1384// SyncChangeProcessor implementation that delegates to another one. 1385// This is necessary since most things expect a 1386// scoped_ptr<SyncChangeProcessor>. 1387// 1388// TODO(akalin): Unify this too. 1389class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { 1390 public: 1391 explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) 1392 : recipient_(recipient) { 1393 DCHECK(recipient_); 1394 } 1395 1396 virtual ~SyncChangeProcessorDelegate() {} 1397 1398 // syncer::SyncChangeProcessor implementation. 1399 virtual syncer::SyncError ProcessSyncChanges( 1400 const tracked_objects::Location& from_here, 1401 const syncer::SyncChangeList& change_list) OVERRIDE { 1402 return recipient_->ProcessSyncChanges(from_here, change_list); 1403 } 1404 1405 private: 1406 // The recipient of all sync changes. 1407 syncer::SyncChangeProcessor* const recipient_; 1408 1409 DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); 1410}; 1411 1412// Create a local delete directive and process it while sync is 1413// online, and then when offline. The delete directive should be sent to sync, 1414// no error should be returned for the first time, and an error should be 1415// returned for the second time. 1416TEST_F(HistoryTest, ProcessLocalDeleteDirectiveSyncOnline) { 1417 ASSERT_TRUE(history_service_.get()); 1418 1419 const GURL test_url("http://www.google.com/"); 1420 for (int64 i = 1; i <= 10; ++i) { 1421 base::Time t = 1422 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i); 1423 history_service_->AddPage(test_url, t, NULL, 0, GURL(), 1424 history::RedirectList(), 1425 content::PAGE_TRANSITION_LINK, 1426 history::SOURCE_BROWSED, false); 1427 } 1428 1429 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive; 1430 sync_pb::GlobalIdDirective* global_id_directive = 1431 delete_directive.mutable_global_id_directive(); 1432 global_id_directive->add_global_id( 1433 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1)) 1434 .ToInternalValue()); 1435 1436 TestChangeProcessor change_processor; 1437 1438 EXPECT_FALSE( 1439 history_service_->MergeDataAndStartSyncing( 1440 syncer::HISTORY_DELETE_DIRECTIVES, 1441 syncer::SyncDataList(), 1442 scoped_ptr<syncer::SyncChangeProcessor>( 1443 new SyncChangeProcessorDelegate(&change_processor)), 1444 scoped_ptr<syncer::SyncErrorFactory>()).error().IsSet()); 1445 1446 syncer::SyncError err = 1447 history_service_->ProcessLocalDeleteDirective(delete_directive); 1448 EXPECT_FALSE(err.IsSet()); 1449 EXPECT_EQ(1u, change_processor.GetChanges().size()); 1450 1451 history_service_->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES); 1452 err = history_service_->ProcessLocalDeleteDirective(delete_directive); 1453 EXPECT_TRUE(err.IsSet()); 1454 EXPECT_EQ(1u, change_processor.GetChanges().size()); 1455} 1456 1457// Closure function that runs periodically to check result of delete directive 1458// processing. Stop when timeout or processing ends indicated by the creation 1459// of sync changes. 1460void CheckDirectiveProcessingResult( 1461 Time timeout, const TestChangeProcessor* change_processor, 1462 uint32 num_changes) { 1463 if (base::Time::Now() > timeout || 1464 change_processor->GetChanges().size() >= num_changes) { 1465 return; 1466 } 1467 1468 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 1469 MessageLoop::current()->PostTask( 1470 FROM_HERE, 1471 base::Bind(&CheckDirectiveProcessingResult, timeout, 1472 change_processor, num_changes)); 1473} 1474 1475// Create a delete directive for a few specific history entries, 1476// including ones that don't exist. The expected entries should be 1477// deleted. 1478TEST_F(HistoryTest, ProcessGlobalIdDeleteDirective) { 1479 ASSERT_TRUE(history_service_.get()); 1480 const GURL test_url("http://www.google.com/"); 1481 for (int64 i = 1; i <= 20; i++) { 1482 base::Time t = 1483 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i); 1484 history_service_->AddPage(test_url, t, NULL, 0, GURL(), 1485 history::RedirectList(), 1486 content::PAGE_TRANSITION_LINK, 1487 history::SOURCE_BROWSED, false); 1488 } 1489 1490 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1491 EXPECT_EQ(20, query_url_row_.visit_count()); 1492 1493 syncer::SyncDataList directives; 1494 // 1st directive. 1495 sync_pb::EntitySpecifics entity_specs; 1496 sync_pb::GlobalIdDirective* global_id_directive = 1497 entity_specs.mutable_history_delete_directive() 1498 ->mutable_global_id_directive(); 1499 global_id_directive->add_global_id( 1500 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6)) 1501 .ToInternalValue()); 1502 global_id_directive->set_start_time_usec(3); 1503 global_id_directive->set_end_time_usec(10); 1504 directives.push_back( 1505 syncer::SyncData::CreateRemoteData(1, entity_specs)); 1506 1507 // 2nd directive. 1508 global_id_directive->Clear(); 1509 global_id_directive->add_global_id( 1510 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17)) 1511 .ToInternalValue()); 1512 global_id_directive->set_start_time_usec(13); 1513 global_id_directive->set_end_time_usec(19); 1514 directives.push_back( 1515 syncer::SyncData::CreateRemoteData(2, entity_specs)); 1516 1517 TestChangeProcessor change_processor; 1518 EXPECT_FALSE( 1519 history_service_->MergeDataAndStartSyncing( 1520 syncer::HISTORY_DELETE_DIRECTIVES, 1521 directives, 1522 scoped_ptr<syncer::SyncChangeProcessor>( 1523 new SyncChangeProcessorDelegate(&change_processor)), 1524 scoped_ptr<syncer::SyncErrorFactory>()).error().IsSet()); 1525 1526 // Inject a task to check status and keep message loop filled before directive 1527 // processing finishes. 1528 MessageLoop::current()->PostTask( 1529 FROM_HERE, 1530 base::Bind(&CheckDirectiveProcessingResult, 1531 base::Time::Now() + base::TimeDelta::FromSeconds(10), 1532 &change_processor, 2)); 1533 MessageLoop::current()->RunUntilIdle(); 1534 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1535 ASSERT_EQ(5, query_url_row_.visit_count()); 1536 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1), 1537 query_url_visits_[0].visit_time); 1538 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(2), 1539 query_url_visits_[1].visit_time); 1540 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(11), 1541 query_url_visits_[2].visit_time); 1542 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(12), 1543 query_url_visits_[3].visit_time); 1544 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(20), 1545 query_url_visits_[4].visit_time); 1546 1547 // Expect two sync changes for deleting processed directives. 1548 const syncer::SyncChangeList& sync_changes = change_processor.GetChanges(); 1549 ASSERT_EQ(2u, sync_changes.size()); 1550 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type()); 1551 EXPECT_EQ(1, sync_changes[0].sync_data().GetRemoteId()); 1552 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type()); 1553 EXPECT_EQ(2, sync_changes[1].sync_data().GetRemoteId()); 1554} 1555 1556// Create delete directives for time ranges. The expected entries should be 1557// deleted. 1558TEST_F(HistoryTest, ProcessTimeRangeDeleteDirective) { 1559 ASSERT_TRUE(history_service_.get()); 1560 const GURL test_url("http://www.google.com/"); 1561 for (int64 i = 1; i <= 10; ++i) { 1562 base::Time t = 1563 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i); 1564 history_service_->AddPage(test_url, t, NULL, 0, GURL(), 1565 history::RedirectList(), 1566 content::PAGE_TRANSITION_LINK, 1567 history::SOURCE_BROWSED, false); 1568 } 1569 1570 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1571 EXPECT_EQ(10, query_url_row_.visit_count()); 1572 1573 syncer::SyncDataList directives; 1574 // 1st directive. 1575 sync_pb::EntitySpecifics entity_specs; 1576 sync_pb::TimeRangeDirective* time_range_directive = 1577 entity_specs.mutable_history_delete_directive() 1578 ->mutable_time_range_directive(); 1579 time_range_directive->set_start_time_usec(2); 1580 time_range_directive->set_end_time_usec(5); 1581 directives.push_back(syncer::SyncData::CreateRemoteData(1, entity_specs)); 1582 1583 // 2nd directive. 1584 time_range_directive->Clear(); 1585 time_range_directive->set_start_time_usec(8); 1586 time_range_directive->set_end_time_usec(10); 1587 directives.push_back(syncer::SyncData::CreateRemoteData(2, entity_specs)); 1588 1589 TestChangeProcessor change_processor; 1590 EXPECT_FALSE( 1591 history_service_->MergeDataAndStartSyncing( 1592 syncer::HISTORY_DELETE_DIRECTIVES, 1593 directives, 1594 scoped_ptr<syncer::SyncChangeProcessor>( 1595 new SyncChangeProcessorDelegate(&change_processor)), 1596 scoped_ptr<syncer::SyncErrorFactory>()).error().IsSet()); 1597 1598 // Inject a task to check status and keep message loop filled before 1599 // directive processing finishes. 1600 MessageLoop::current()->PostTask( 1601 FROM_HERE, 1602 base::Bind(&CheckDirectiveProcessingResult, 1603 base::Time::Now() + base::TimeDelta::FromSeconds(10), 1604 &change_processor, 2)); 1605 MessageLoop::current()->RunUntilIdle(); 1606 EXPECT_TRUE(QueryURL(history_service_.get(), test_url)); 1607 ASSERT_EQ(3, query_url_row_.visit_count()); 1608 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1), 1609 query_url_visits_[0].visit_time); 1610 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6), 1611 query_url_visits_[1].visit_time); 1612 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(7), 1613 query_url_visits_[2].visit_time); 1614 1615 // Expect two sync changes for deleting processed directives. 1616 const syncer::SyncChangeList& sync_changes = change_processor.GetChanges(); 1617 ASSERT_EQ(2u, sync_changes.size()); 1618 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type()); 1619 EXPECT_EQ(1, sync_changes[0].sync_data().GetRemoteId()); 1620 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type()); 1621 EXPECT_EQ(2, sync_changes[1].sync_data().GetRemoteId()); 1622} 1623 1624TEST_F(HistoryBackendDBTest, MigratePresentations) { 1625 // Create the db we want. Use 22 since segments didn't change in that time 1626 // frame. 1627 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22)); 1628 1629 const SegmentID segment_id = 2; 1630 const URLID url_id = 3; 1631 const GURL url("http://www.foo.com"); 1632 const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url)); 1633 const string16 title(ASCIIToUTF16("Title1")); 1634 const Time segment_time(Time::Now()); 1635 1636 { 1637 // Re-open the db for manual manipulation. 1638 sql::Connection db; 1639 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); 1640 1641 // Add an entry to urls. 1642 { 1643 sql::Statement s(db.GetUniqueStatement( 1644 "INSERT INTO urls " 1645 "(id, url, title, last_visit_time) VALUES " 1646 "(?, ?, ?, ?)")); 1647 s.BindInt64(0, url_id); 1648 s.BindString(1, url.spec()); 1649 s.BindString16(2, title); 1650 s.BindInt64(3, segment_time.ToInternalValue()); 1651 ASSERT_TRUE(s.Run()); 1652 } 1653 1654 // Add an entry to segments. 1655 { 1656 sql::Statement s(db.GetUniqueStatement( 1657 "INSERT INTO segments " 1658 "(id, name, url_id, pres_index) VALUES " 1659 "(?, ?, ?, ?)")); 1660 s.BindInt64(0, segment_id); 1661 s.BindString(1, url_name); 1662 s.BindInt64(2, url_id); 1663 s.BindInt(3, 4); // pres_index 1664 ASSERT_TRUE(s.Run()); 1665 } 1666 1667 // And one to segment_usage. 1668 { 1669 sql::Statement s(db.GetUniqueStatement( 1670 "INSERT INTO segment_usage " 1671 "(id, segment_id, time_slot, visit_count) VALUES " 1672 "(?, ?, ?, ?)")); 1673 s.BindInt64(0, 4); // id. 1674 s.BindInt64(1, segment_id); 1675 s.BindInt64(2, segment_time.ToInternalValue()); 1676 s.BindInt(3, 5); // visit count. 1677 ASSERT_TRUE(s.Run()); 1678 } 1679 } 1680 1681 // Re-open the db, triggering migration. 1682 CreateBackendAndDatabase(); 1683 1684 std::vector<PageUsageData*> results; 1685 db_->QuerySegmentUsage(segment_time, 10, &results); 1686 ASSERT_EQ(1u, results.size()); 1687 EXPECT_EQ(url, results[0]->GetURL()); 1688 EXPECT_EQ(segment_id, results[0]->GetID()); 1689 EXPECT_EQ(title, results[0]->GetTitle()); 1690 STLDeleteElements(&results); 1691} 1692 1693} // namespace history 1694