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