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