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