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.
5 // History unit tests come in two flavors:
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.
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
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/attachments/attachment_id.h"
64 #include "sync/api/attachments/attachment_service_proxy_for_test.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/protocol/history_delete_directive_specifics.pb.h"
73 #include "sync/protocol/sync.pb.h"
74 #include "testing/gtest/include/gtest/gtest.h"
75 #include "third_party/skia/include/core/SkBitmap.h"
76 #include "ui/gfx/codec/jpeg_codec.h"
79 using base::TimeDelta;
80 using content::DownloadItem;
83 class HistoryBackendDBTest;
85 // Delegate class for when we create a backend without a HistoryService.
87 // This must be outside the anonymous namespace for the friend statement in
88 // HistoryBackendDBTest to work.
89 class BackendDelegate : public HistoryBackend::Delegate {
91 explicit BackendDelegate(HistoryBackendDBTest* history_test)
92 : history_test_(history_test) {
95 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
96 virtual void SetInMemoryBackend(
97 scoped_ptr<InMemoryHistoryBackend> backend) OVERRIDE;
98 virtual void BroadcastNotifications(
100 scoped_ptr<HistoryDetails> details) OVERRIDE;
101 virtual void DBLoaded() OVERRIDE {}
102 virtual void NotifyVisitDBObserversOnAddVisit(
103 const BriefVisitInfo& info) OVERRIDE {}
105 HistoryBackendDBTest* history_test_;
108 // This must be outside the anonymous namespace for the friend statement in
109 // HistoryBackend to work.
110 class HistoryBackendDBTest : public HistoryUnitTestBase {
112 HistoryBackendDBTest() : db_(NULL) {
115 virtual ~HistoryBackendDBTest() {
119 friend class BackendDelegate;
121 // Creates the HistoryBackend and HistoryDatabase on the current thread,
122 // assigning the values to backend_ and db_.
123 void CreateBackendAndDatabase() {
125 new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
126 backend_->Init(std::string(), false);
127 db_ = backend_->db_.get();
128 DCHECK(in_mem_backend_) << "Mem backend should have been set by "
129 "HistoryBackend::Init";
132 void CreateDBVersion(int version) {
133 base::FilePath data_path;
134 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
135 data_path = data_path.AppendASCII("History");
137 data_path.AppendASCII(base::StringPrintf("history.%d.sql", version));
138 ASSERT_NO_FATAL_FAILURE(
139 ExecuteSQLScript(data_path, history_dir_.Append(
140 chrome::kHistoryFilename)));
144 virtual void SetUp() {
145 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
146 history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBTest");
147 ASSERT_TRUE(base::CreateDirectory(history_dir_));
150 void DeleteBackend() {
151 if (backend_.get()) {
157 virtual void TearDown() {
160 // Make sure we don't have any event pending that could disrupt the next
162 base::MessageLoop::current()->PostTask(FROM_HERE,
163 base::MessageLoop::QuitClosure());
164 base::MessageLoop::current()->Run();
167 bool AddDownload(uint32 id,
168 DownloadItem::DownloadState state,
170 std::vector<GURL> url_chain;
171 url_chain.push_back(GURL("foo-url"));
173 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("current-path")),
174 base::FilePath(FILE_PATH_LITERAL("target-path")),
176 GURL("http://referrer.com/"),
184 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
185 content::DOWNLOAD_INTERRUPT_REASON_NONE,
190 return db_->CreateDownload(download);
193 base::ScopedTempDir temp_dir_;
195 base::MessageLoopForUI message_loop_;
197 // names of the database files
198 base::FilePath history_dir_;
200 // Created via CreateBackendAndDatabase.
201 scoped_refptr<HistoryBackend> backend_;
202 scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
203 HistoryDatabase* db_; // Cached reference to the backend's database.
206 void BackendDelegate::SetInMemoryBackend(
207 scoped_ptr<InMemoryHistoryBackend> backend) {
208 // Save the in-memory backend to the history test object, this happens
209 // synchronously, so we don't have to do anything fancy.
210 history_test_->in_mem_backend_.swap(backend);
213 void BackendDelegate::BroadcastNotifications(
215 scoped_ptr<HistoryDetails> details) {
216 // Currently, just send the notifications directly to the in-memory database.
217 // We may want do do something more fancy in the future.
218 content::Details<HistoryDetails> det(details.get());
219 history_test_->in_mem_backend_->Observe(type,
220 content::Source<HistoryBackendDBTest>(NULL), det);
223 TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
224 CreateBackendAndDatabase();
226 // Initially there should be nothing in the downloads database.
227 std::vector<DownloadRow> downloads;
228 db_->QueryDownloads(&downloads);
229 EXPECT_EQ(0U, downloads.size());
231 // Add a download, test that it was added correctly, remove it, test that it
235 EXPECT_TRUE(AddDownload(id, DownloadItem::COMPLETE, Time()));
236 db_->QueryDownloads(&downloads);
237 EXPECT_EQ(1U, downloads.size());
239 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
240 downloads[0].current_path);
241 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
242 downloads[0].target_path);
243 EXPECT_EQ(1UL, downloads[0].url_chain.size());
244 EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
245 EXPECT_EQ(std::string("http://referrer.com/"),
246 std::string(downloads[0].referrer_url.spec()));
247 EXPECT_EQ(now, downloads[0].start_time);
248 EXPECT_EQ(now, downloads[0].end_time);
249 EXPECT_EQ(0, downloads[0].received_bytes);
250 EXPECT_EQ(512, downloads[0].total_bytes);
251 EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
252 EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
253 downloads[0].danger_type);
254 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
255 downloads[0].interrupt_reason);
256 EXPECT_FALSE(downloads[0].opened);
257 EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
258 EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
260 db_->QueryDownloads(&downloads);
261 EXPECT_EQ(1U, downloads.size());
262 db_->RemoveDownload(id);
263 db_->QueryDownloads(&downloads);
264 EXPECT_EQ(0U, downloads.size());
267 TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
268 // Create the db we want.
269 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
271 // Open the db for manual manipulation.
273 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
275 // Manually insert corrupted rows; there's infrastructure in place now to
276 // make this impossible, at least according to the test above.
277 for (int state = 0; state < 5; ++state) {
278 sql::Statement s(db.GetUniqueStatement(
279 "INSERT INTO downloads (id, full_path, url, start_time, "
280 "received_bytes, total_bytes, state, end_time, opened) VALUES "
281 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
282 s.BindInt64(0, 1 + state);
283 s.BindString(1, "path");
284 s.BindString(2, "url");
285 s.BindInt64(3, base::Time::Now().ToTimeT());
289 s.BindInt64(7, base::Time::Now().ToTimeT());
290 s.BindInt(8, state % 2);
291 ASSERT_TRUE(s.Run());
295 // Re-open the db using the HistoryDatabase, which should migrate from version
296 // 22 to the current version, fixing just the row whose state was 3.
297 // Then close the db so that we can re-open it directly.
298 CreateBackendAndDatabase();
301 // Re-open the db for manual manipulation.
303 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
305 // The version should have been updated.
306 int cur_version = HistoryDatabase::GetCurrentVersion();
307 ASSERT_LT(22, cur_version);
308 sql::Statement s(db.GetUniqueStatement(
309 "SELECT value FROM meta WHERE key = 'version'"));
310 EXPECT_TRUE(s.Step());
311 EXPECT_EQ(cur_version, s.ColumnInt(0));
314 sql::Statement statement(db.GetUniqueStatement(
315 "SELECT id, state, opened "
319 while (statement.Step()) {
320 EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
321 // The only thing that migration should have changed was state from 3 to
323 EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
324 EXPECT_EQ(counter % 2, statement.ColumnInt(2));
327 EXPECT_EQ(5, counter);
332 TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
333 Time now(base::Time::Now());
335 // Create the db we want. The schema didn't change from 22->23, so just
336 // re-use the v22 file.
337 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
339 // Re-open the db for manual manipulation.
341 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
343 // Manually insert some rows.
344 sql::Statement s(db.GetUniqueStatement(
345 "INSERT INTO downloads (id, full_path, url, start_time, "
346 "received_bytes, total_bytes, state, end_time, opened) VALUES "
347 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
351 s.BindInt64(0, ++id);
352 s.BindString(1, std::string());
353 s.BindString(2, "http://whatever.com/index.html");
354 s.BindInt64(3, now.ToTimeT());
358 s.BindInt64(7, now.ToTimeT());
360 ASSERT_TRUE(s.Run());
364 s.BindInt64(0, ++id);
365 s.BindString(1, "/path/to/some/file");
366 s.BindString(2, "http://whatever.com/index1.html");
367 s.BindInt64(3, now.ToTimeT());
371 s.BindInt64(7, now.ToTimeT());
373 ASSERT_TRUE(s.Run());
376 // Re-open the db using the HistoryDatabase, which should migrate from version
377 // 23 to 24, creating the new tables and creating the new path, reason,
378 // and danger columns.
379 CreateBackendAndDatabase();
382 // Re-open the db for manual manipulation.
384 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
386 // The version should have been updated.
387 int cur_version = HistoryDatabase::GetCurrentVersion();
388 ASSERT_LT(23, cur_version);
389 sql::Statement s(db.GetUniqueStatement(
390 "SELECT value FROM meta WHERE key = 'version'"));
391 EXPECT_TRUE(s.Step());
392 EXPECT_EQ(cur_version, s.ColumnInt(0));
395 base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
397 // Confirm downloads table is valid.
398 sql::Statement statement(db.GetUniqueStatement(
399 "SELECT id, interrupt_reason, current_path, target_path, "
400 " danger_type, start_time, end_time "
401 "FROM downloads ORDER BY id"));
402 EXPECT_TRUE(statement.Step());
403 EXPECT_EQ(1, statement.ColumnInt64(0));
404 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
405 statement.ColumnInt(1));
406 EXPECT_EQ("", statement.ColumnString(2));
407 EXPECT_EQ("", statement.ColumnString(3));
408 // Implicit dependence on value of kDangerTypeNotDangerous from
409 // download_database.cc.
410 EXPECT_EQ(0, statement.ColumnInt(4));
411 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
412 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
414 EXPECT_TRUE(statement.Step());
415 EXPECT_EQ(2, statement.ColumnInt64(0));
416 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
417 statement.ColumnInt(1));
418 EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
419 EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
420 EXPECT_EQ(0, statement.ColumnInt(4));
421 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
422 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
424 EXPECT_FALSE(statement.Step());
427 // Confirm downloads_url_chains table is valid.
428 sql::Statement statement(db.GetUniqueStatement(
429 "SELECT id, chain_index, url FROM downloads_url_chains "
430 " ORDER BY id, chain_index"));
431 EXPECT_TRUE(statement.Step());
432 EXPECT_EQ(1, statement.ColumnInt64(0));
433 EXPECT_EQ(0, statement.ColumnInt(1));
434 EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
436 EXPECT_TRUE(statement.Step());
437 EXPECT_EQ(2, statement.ColumnInt64(0));
438 EXPECT_EQ(0, statement.ColumnInt(1));
439 EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
441 EXPECT_FALSE(statement.Step());
446 TEST_F(HistoryBackendDBTest, MigrateReferrer) {
447 Time now(base::Time::Now());
448 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
451 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
452 sql::Statement s(db.GetUniqueStatement(
453 "INSERT INTO downloads (id, full_path, url, start_time, "
454 "received_bytes, total_bytes, state, end_time, opened) VALUES "
455 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
457 s.BindInt64(0, ++db_handle);
458 s.BindString(1, "full_path");
459 s.BindString(2, "http://whatever.com/index.html");
460 s.BindInt64(3, now.ToTimeT());
464 s.BindInt64(7, now.ToTimeT());
466 ASSERT_TRUE(s.Run());
468 // Re-open the db using the HistoryDatabase, which should migrate to version
469 // 26, creating the referrer column.
470 CreateBackendAndDatabase();
473 // Re-open the db for manual manipulation.
475 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
476 // The version should have been updated.
477 int cur_version = HistoryDatabase::GetCurrentVersion();
478 ASSERT_LE(26, cur_version);
480 sql::Statement s(db.GetUniqueStatement(
481 "SELECT value FROM meta WHERE key = 'version'"));
482 EXPECT_TRUE(s.Step());
483 EXPECT_EQ(cur_version, s.ColumnInt(0));
486 sql::Statement s(db.GetUniqueStatement(
487 "SELECT referrer from downloads"));
488 EXPECT_TRUE(s.Step());
489 EXPECT_EQ(std::string(), s.ColumnString(0));
494 TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
495 Time now(base::Time::Now());
496 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
499 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
501 sql::Statement s(db.GetUniqueStatement(
502 "INSERT INTO downloads (id, current_path, target_path, start_time, "
503 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
504 "end_time, opened, referrer) VALUES "
505 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
507 s.BindString(1, "current_path");
508 s.BindString(2, "target_path");
509 s.BindInt64(3, now.ToTimeT());
515 s.BindInt64(9, now.ToTimeT());
517 s.BindString(11, "referrer");
518 ASSERT_TRUE(s.Run());
521 sql::Statement s(db.GetUniqueStatement(
522 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
526 s.BindString(2, "url");
527 ASSERT_TRUE(s.Run());
530 // Re-open the db using the HistoryDatabase, which should migrate to version
531 // 27, creating the by_ext_id and by_ext_name columns.
532 CreateBackendAndDatabase();
535 // Re-open the db for manual manipulation.
537 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
538 // The version should have been updated.
539 int cur_version = HistoryDatabase::GetCurrentVersion();
540 ASSERT_LE(27, cur_version);
542 sql::Statement s(db.GetUniqueStatement(
543 "SELECT value FROM meta WHERE key = 'version'"));
544 EXPECT_TRUE(s.Step());
545 EXPECT_EQ(cur_version, s.ColumnInt(0));
548 sql::Statement s(db.GetUniqueStatement(
549 "SELECT by_ext_id, by_ext_name from downloads"));
550 EXPECT_TRUE(s.Step());
551 EXPECT_EQ(std::string(), s.ColumnString(0));
552 EXPECT_EQ(std::string(), s.ColumnString(1));
557 TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
558 Time now(base::Time::Now());
559 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
562 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
564 sql::Statement s(db.GetUniqueStatement(
565 "INSERT INTO downloads (id, current_path, target_path, start_time, "
566 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
567 "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
568 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
570 s.BindString(1, "current_path");
571 s.BindString(2, "target_path");
572 s.BindInt64(3, now.ToTimeT());
578 s.BindInt64(9, now.ToTimeT());
580 s.BindString(11, "referrer");
581 s.BindString(12, "by extension ID");
582 s.BindString(13, "by extension name");
583 ASSERT_TRUE(s.Run());
586 sql::Statement s(db.GetUniqueStatement(
587 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
591 s.BindString(2, "url");
592 ASSERT_TRUE(s.Run());
595 // Re-open the db using the HistoryDatabase, which should migrate to the
596 // current version, creating the etag and last_modified columns.
597 CreateBackendAndDatabase();
600 // Re-open the db for manual manipulation.
602 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
603 // The version should have been updated.
604 int cur_version = HistoryDatabase::GetCurrentVersion();
605 ASSERT_LE(28, cur_version);
607 sql::Statement s(db.GetUniqueStatement(
608 "SELECT value FROM meta WHERE key = 'version'"));
609 EXPECT_TRUE(s.Step());
610 EXPECT_EQ(cur_version, s.ColumnInt(0));
613 sql::Statement s(db.GetUniqueStatement(
614 "SELECT etag, last_modified from downloads"));
615 EXPECT_TRUE(s.Step());
616 EXPECT_EQ(std::string(), s.ColumnString(0));
617 EXPECT_EQ(std::string(), s.ColumnString(1));
622 TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
624 CreateBackendAndDatabase();
626 base::Time now(base::Time::Now());
628 // Add some downloads.
629 uint32 id1 = 1, id2 = 2, id3 = 3;
630 AddDownload(id1, DownloadItem::COMPLETE, now);
631 AddDownload(id2, DownloadItem::COMPLETE, now + base::TimeDelta::FromDays(2));
632 AddDownload(id3, DownloadItem::COMPLETE, now - base::TimeDelta::FromDays(2));
634 // Confirm that resulted in the correct number of rows in the DB.
638 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
639 sql::Statement statement(db.GetUniqueStatement(
640 "Select Count(*) from downloads"));
641 EXPECT_TRUE(statement.Step());
642 EXPECT_EQ(3, statement.ColumnInt(0));
644 sql::Statement statement1(db.GetUniqueStatement(
645 "Select Count(*) from downloads_url_chains"));
646 EXPECT_TRUE(statement1.Step());
647 EXPECT_EQ(3, statement1.ColumnInt(0));
650 // Delete some rows and make sure the results are still correct.
651 CreateBackendAndDatabase();
652 db_->RemoveDownload(id2);
653 db_->RemoveDownload(id3);
657 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
658 sql::Statement statement(db.GetUniqueStatement(
659 "Select Count(*) from downloads"));
660 EXPECT_TRUE(statement.Step());
661 EXPECT_EQ(1, statement.ColumnInt(0));
663 sql::Statement statement1(db.GetUniqueStatement(
664 "Select Count(*) from downloads_url_chains"));
665 EXPECT_TRUE(statement1.Step());
666 EXPECT_EQ(1, statement1.ColumnInt(0));
670 TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
671 CreateBackendAndDatabase();
672 base::Time now(base::Time::Now());
673 std::vector<GURL> url_chain;
674 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
675 base::FilePath(FILE_PATH_LITERAL("foo-path")),
684 DownloadItem::COMPLETE,
685 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
686 content::DOWNLOAD_INTERRUPT_REASON_NONE,
692 // Creating records without any urls should fail.
693 EXPECT_FALSE(db_->CreateDownload(download));
695 download.url_chain.push_back(GURL("foo-url"));
696 EXPECT_TRUE(db_->CreateDownload(download));
698 // Pretend that the URLs were dropped.
702 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
703 sql::Statement statement(db.GetUniqueStatement(
704 "DELETE FROM downloads_url_chains WHERE id=1"));
705 ASSERT_TRUE(statement.Run());
707 CreateBackendAndDatabase();
708 std::vector<DownloadRow> downloads;
709 db_->QueryDownloads(&downloads);
710 EXPECT_EQ(0U, downloads.size());
712 // QueryDownloads should have nuked the corrupt record.
716 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
718 sql::Statement statement(db.GetUniqueStatement(
719 "SELECT count(*) from downloads"));
720 ASSERT_TRUE(statement.Step());
721 EXPECT_EQ(0, statement.ColumnInt(0));
726 TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
728 CreateBackendAndDatabase();
730 base::Time now(base::Time::Now());
732 // Put an IN_PROGRESS download in the DB.
733 AddDownload(1, DownloadItem::IN_PROGRESS, now);
735 // Confirm that they made it into the DB unchanged.
739 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
740 sql::Statement statement(db.GetUniqueStatement(
741 "Select Count(*) from downloads"));
742 EXPECT_TRUE(statement.Step());
743 EXPECT_EQ(1, statement.ColumnInt(0));
745 sql::Statement statement1(db.GetUniqueStatement(
746 "Select state, interrupt_reason from downloads"));
747 EXPECT_TRUE(statement1.Step());
748 EXPECT_EQ(DownloadDatabase::kStateInProgress, statement1.ColumnInt(0));
749 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, statement1.ColumnInt(1));
750 EXPECT_FALSE(statement1.Step());
753 // Read in the DB through query downloads, then test that the
754 // right transformation was returned.
755 CreateBackendAndDatabase();
756 std::vector<DownloadRow> results;
757 db_->QueryDownloads(&results);
758 ASSERT_EQ(1u, results.size());
759 EXPECT_EQ(content::DownloadItem::INTERRUPTED, results[0].state);
760 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
761 results[0].interrupt_reason);
763 // Allow the update to propagate, shut down the DB, and confirm that
764 // the query updated the on disk database as well.
765 base::MessageLoop::current()->RunUntilIdle();
769 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
770 sql::Statement statement(db.GetUniqueStatement(
771 "Select Count(*) from downloads"));
772 EXPECT_TRUE(statement.Step());
773 EXPECT_EQ(1, statement.ColumnInt(0));
775 sql::Statement statement1(db.GetUniqueStatement(
776 "Select state, interrupt_reason from downloads"));
777 EXPECT_TRUE(statement1.Step());
778 EXPECT_EQ(DownloadDatabase::kStateInterrupted, statement1.ColumnInt(0));
779 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
780 statement1.ColumnInt(1));
781 EXPECT_FALSE(statement1.Step());
785 struct InterruptReasonAssociation {
790 // Test is dependent on interrupt reasons being listed in header file
792 const InterruptReasonAssociation current_reasons[] = {
793 #define INTERRUPT_REASON(a, b) { #a, b },
794 #include "content/public/browser/download_interrupt_reason_values.h"
795 #undef INTERRUPT_REASON
798 // This represents a list of all reasons we've previously used;
799 // Do Not Remove Any Entries From This List.
800 const InterruptReasonAssociation historical_reasons[] = {
802 {"FILE_ACCESS_DENIED", 2},
803 {"FILE_NO_SPACE", 3},
804 {"FILE_NAME_TOO_LONG", 5},
805 {"FILE_TOO_LARGE", 6},
806 {"FILE_VIRUS_INFECTED", 7},
807 {"FILE_TRANSIENT_ERROR", 10},
808 {"FILE_BLOCKED", 11},
809 {"FILE_SECURITY_CHECK_FAILED", 12},
810 {"FILE_TOO_SHORT", 13},
811 {"NETWORK_FAILED", 20},
812 {"NETWORK_TIMEOUT", 21},
813 {"NETWORK_DISCONNECTED", 22},
814 {"NETWORK_SERVER_DOWN", 23},
815 {"NETWORK_INVALID_REQUEST", 24},
816 {"SERVER_FAILED", 30},
817 {"SERVER_NO_RANGE", 31},
818 {"SERVER_PRECONDITION", 32},
819 {"SERVER_BAD_CONTENT", 33},
820 {"USER_CANCELED", 40},
821 {"USER_SHUTDOWN", 41},
825 // Make sure no one has changed a DownloadInterruptReason we've previously
827 TEST_F(HistoryBackendDBTest,
828 ConfirmDownloadInterruptReasonBackwardsCompatible) {
829 // Are there any cases in which a historical number has been repurposed
830 // for an error other than it's original?
831 for (size_t i = 0; i < arraysize(current_reasons); i++) {
832 const InterruptReasonAssociation& cur_reason(current_reasons[i]);
835 for (size_t j = 0; j < arraysize(historical_reasons); ++j) {
836 const InterruptReasonAssociation& hist_reason(historical_reasons[j]);
838 if (hist_reason.value == cur_reason.value) {
839 EXPECT_EQ(cur_reason.name, hist_reason.name)
840 << "Same integer value used for old error \""
842 << "\" as for new error \""
844 << "\"." << std::endl
845 << "**This will cause database conflicts with persisted values**"
847 << "Please assign a new, non-conflicting value for the new error.";
850 if (hist_reason.name == cur_reason.name) {
851 EXPECT_EQ(cur_reason.value, hist_reason.value)
852 << "Same name (\"" << hist_reason.name
853 << "\") maps to a different value historically ("
854 << hist_reason.value << ") and currently ("
855 << cur_reason.value << ")" << std::endl
856 << "This may cause database conflicts with persisted values"
858 << "If this error is the same as the old one, you should"
860 << "use the old value, and if it is different, you should"
862 << "use a new name.";
869 << "Error \"" << cur_reason.name << "\" not found in historical list."
875 // The tracker uses RenderProcessHost pointers for scoping but never
876 // dereferences them. We use ints because it's easier. This function converts
878 static void* MakeFakeHost(int id) {
880 memcpy(&host, &id, sizeof(id));
884 class HistoryTest : public testing::Test {
887 : got_thumbnail_callback_(false),
888 redirect_query_success_(false),
889 query_url_success_(false) {
892 virtual ~HistoryTest() {
895 void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
896 std::vector<PageUsageData*>* data) {
897 page_usage_data_.swap(*data);
898 base::MessageLoop::current()->Quit();
901 void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) {
902 base::MessageLoop::current()->Quit();
905 void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,
906 MostVisitedURLList url_list) {
907 most_visited_urls_.swap(url_list);
908 base::MessageLoop::current()->Quit();
912 friend class BackendDelegate;
915 virtual void SetUp() {
916 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
917 history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
918 ASSERT_TRUE(base::CreateDirectory(history_dir_));
919 history_service_.reset(new HistoryService);
920 if (!history_service_->Init(history_dir_, NULL)) {
921 history_service_.reset();
926 virtual void TearDown() {
927 if (history_service_)
928 CleanupHistoryService();
930 // Make sure we don't have any event pending that could disrupt the next
932 base::MessageLoop::current()->PostTask(FROM_HERE,
933 base::MessageLoop::QuitClosure());
934 base::MessageLoop::current()->Run();
937 void CleanupHistoryService() {
938 DCHECK(history_service_);
940 history_service_->NotifyRenderProcessHostDestruction(0);
941 history_service_->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
942 history_service_->Cleanup();
943 history_service_.reset();
945 // Wait for the backend class to terminate before deleting the files and
946 // moving to the next test. Note: if this never terminates, somebody is
947 // probably leaking a reference to the history backend, so it never calls
949 base::MessageLoop::current()->Run();
952 // Fills the query_url_row_ and query_url_visits_ structures with the
953 // information about the given URL and returns true. If the URL was not
954 // found, this will return false and those structures will not be changed.
955 bool QueryURL(HistoryService* history, const GURL& url) {
956 history_service_->QueryURL(url, true, &consumer_,
957 base::Bind(&HistoryTest::SaveURLAndQuit,
958 base::Unretained(this)));
959 base::MessageLoop::current()->Run(); // Will be exited in SaveURLAndQuit.
960 return query_url_success_;
963 // Callback for HistoryService::QueryURL.
964 void SaveURLAndQuit(HistoryService::Handle handle,
966 const URLRow* url_row,
967 VisitVector* visit_vector) {
968 query_url_success_ = success;
969 if (query_url_success_) {
970 query_url_row_ = *url_row;
971 query_url_visits_.swap(*visit_vector);
973 query_url_row_ = URLRow();
974 query_url_visits_.clear();
976 base::MessageLoop::current()->Quit();
979 // Fills in saved_redirects_ with the redirect information for the given URL,
980 // returning true on success. False means the URL was not found.
981 bool QueryRedirectsFrom(HistoryService* history, const GURL& url) {
982 history_service_->QueryRedirectsFrom(
984 base::Bind(&HistoryTest::OnRedirectQueryComplete,
985 base::Unretained(this)));
986 base::MessageLoop::current()->Run(); // Will be exited in *QueryComplete.
987 return redirect_query_success_;
990 // Callback for QueryRedirects.
991 void OnRedirectQueryComplete(HistoryService::Handle handle,
994 history::RedirectList* redirects) {
995 redirect_query_success_ = success;
996 if (redirect_query_success_)
997 saved_redirects_.swap(*redirects);
999 saved_redirects_.clear();
1000 base::MessageLoop::current()->Quit();
1003 base::ScopedTempDir temp_dir_;
1005 base::MessageLoopForUI message_loop_;
1007 // PageUsageData vector to test segments.
1008 ScopedVector<PageUsageData> page_usage_data_;
1010 MostVisitedURLList most_visited_urls_;
1012 // When non-NULL, this will be deleted on tear down and we will block until
1013 // the backend thread has completed. This allows tests for the history
1014 // service to use this feature, but other tests to ignore this.
1015 scoped_ptr<HistoryService> history_service_;
1017 // names of the database files
1018 base::FilePath history_dir_;
1020 // Set by the thumbnail callback when we get data, you should be sure to
1021 // clear this before issuing a thumbnail request.
1022 bool got_thumbnail_callback_;
1023 std::vector<unsigned char> thumbnail_data_;
1025 // Set by the redirect callback when we get data. You should be sure to
1026 // clear this before issuing a redirect request.
1027 history::RedirectList saved_redirects_;
1028 bool redirect_query_success_;
1030 // For history requests.
1031 CancelableRequestConsumer consumer_;
1033 // For saving URL info after a call to QueryURL
1034 bool query_url_success_;
1035 URLRow query_url_row_;
1036 VisitVector query_url_visits_;
1039 TEST_F(HistoryTest, AddPage) {
1040 ASSERT_TRUE(history_service_.get());
1041 // Add the page once from a child frame.
1042 const GURL test_url("http://www.google.com/");
1043 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1044 history::RedirectList(),
1045 content::PAGE_TRANSITION_MANUAL_SUBFRAME,
1046 history::SOURCE_BROWSED, false);
1047 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1048 EXPECT_EQ(1, query_url_row_.visit_count());
1049 EXPECT_EQ(0, query_url_row_.typed_count());
1050 EXPECT_TRUE(query_url_row_.hidden()); // Hidden because of child frame.
1052 // Add the page once from the main frame (should unhide it).
1053 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1054 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1055 history::SOURCE_BROWSED, false);
1056 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1057 EXPECT_EQ(2, query_url_row_.visit_count()); // Added twice.
1058 EXPECT_EQ(0, query_url_row_.typed_count()); // Never typed.
1059 EXPECT_FALSE(query_url_row_.hidden()); // Because loaded in main frame.
1062 TEST_F(HistoryTest, AddRedirect) {
1063 ASSERT_TRUE(history_service_.get());
1064 const char* first_sequence[] = {
1065 "http://first.page.com/",
1066 "http://second.page.com/"};
1067 int first_count = arraysize(first_sequence);
1068 history::RedirectList first_redirects;
1069 for (int i = 0; i < first_count; i++)
1070 first_redirects.push_back(GURL(first_sequence[i]));
1072 // Add the sequence of pages as a server with no referrer. Note that we need
1073 // to have a non-NULL page ID scope.
1074 history_service_->AddPage(
1075 first_redirects.back(), base::Time::Now(), MakeFakeHost(1),
1076 0, GURL(), first_redirects, content::PAGE_TRANSITION_LINK,
1077 history::SOURCE_BROWSED, true);
1079 // The first page should be added once with a link visit type (because we set
1080 // LINK when we added the original URL, and a referrer of nowhere (0).
1081 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[0]));
1082 EXPECT_EQ(1, query_url_row_.visit_count());
1083 ASSERT_EQ(1U, query_url_visits_.size());
1084 int64 first_visit = query_url_visits_[0].visit_id;
1085 EXPECT_EQ(content::PAGE_TRANSITION_LINK |
1086 content::PAGE_TRANSITION_CHAIN_START,
1087 query_url_visits_[0].transition);
1088 EXPECT_EQ(0, query_url_visits_[0].referring_visit); // No referrer.
1090 // The second page should be a server redirect type with a referrer of the
1092 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[1]));
1093 EXPECT_EQ(1, query_url_row_.visit_count());
1094 ASSERT_EQ(1U, query_url_visits_.size());
1095 int64 second_visit = query_url_visits_[0].visit_id;
1096 EXPECT_EQ(content::PAGE_TRANSITION_SERVER_REDIRECT |
1097 content::PAGE_TRANSITION_CHAIN_END,
1098 query_url_visits_[0].transition);
1099 EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit);
1101 // Check that the redirect finding function successfully reports it.
1102 saved_redirects_.clear();
1103 QueryRedirectsFrom(history_service_.get(), first_redirects[0]);
1104 ASSERT_EQ(1U, saved_redirects_.size());
1105 EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
1107 // Now add a client redirect from that second visit to a third, client
1108 // redirects are tracked by the RenderView prior to updating history,
1109 // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
1110 history::RedirectList second_redirects;
1111 second_redirects.push_back(first_redirects[1]);
1112 second_redirects.push_back(GURL("http://last.page.com/"));
1113 history_service_->AddPage(second_redirects[1], base::Time::Now(),
1114 MakeFakeHost(1), 1, second_redirects[0], second_redirects,
1115 static_cast<content::PageTransition>(
1116 content::PAGE_TRANSITION_LINK |
1117 content::PAGE_TRANSITION_CLIENT_REDIRECT),
1118 history::SOURCE_BROWSED, true);
1120 // The last page (source of the client redirect) should NOT have an
1121 // additional visit added, because it was a client redirect (normally it
1122 // would). We should only have 1 left over from the first sequence.
1123 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[0]));
1124 EXPECT_EQ(1, query_url_row_.visit_count());
1126 // The final page should be set as a client redirect from the previous visit.
1127 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[1]));
1128 EXPECT_EQ(1, query_url_row_.visit_count());
1129 ASSERT_EQ(1U, query_url_visits_.size());
1130 EXPECT_EQ(content::PAGE_TRANSITION_CLIENT_REDIRECT |
1131 content::PAGE_TRANSITION_CHAIN_END,
1132 query_url_visits_[0].transition);
1133 EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit);
1136 TEST_F(HistoryTest, MakeIntranetURLsTyped) {
1137 ASSERT_TRUE(history_service_.get());
1139 // Add a non-typed visit to an intranet URL on an unvisited host. This should
1140 // get promoted to a typed visit.
1141 const GURL test_url("http://intranet_host/path");
1142 history_service_->AddPage(
1143 test_url, base::Time::Now(), NULL, 0, GURL(),
1144 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1145 history::SOURCE_BROWSED, false);
1146 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1147 EXPECT_EQ(1, query_url_row_.visit_count());
1148 EXPECT_EQ(1, query_url_row_.typed_count());
1149 ASSERT_EQ(1U, query_url_visits_.size());
1150 EXPECT_EQ(content::PAGE_TRANSITION_TYPED,
1151 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1153 // Add more visits on the same host. None of these should be promoted since
1154 // there is already a typed visit.
1157 const GURL test_url2("http://intranet_host/different_path");
1158 history_service_->AddPage(
1159 test_url2, base::Time::Now(), NULL, 0, GURL(),
1160 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1161 history::SOURCE_BROWSED, false);
1162 EXPECT_TRUE(QueryURL(history_service_.get(), test_url2));
1163 EXPECT_EQ(1, query_url_row_.visit_count());
1164 EXPECT_EQ(0, query_url_row_.typed_count());
1165 ASSERT_EQ(1U, query_url_visits_.size());
1166 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1167 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1170 const GURL test_url3("http://intranet_host/");
1171 history_service_->AddPage(
1172 test_url3, base::Time::Now(), NULL, 0, GURL(),
1173 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1174 history::SOURCE_BROWSED, false);
1175 EXPECT_TRUE(QueryURL(history_service_.get(), test_url3));
1176 EXPECT_EQ(1, query_url_row_.visit_count());
1177 EXPECT_EQ(0, query_url_row_.typed_count());
1178 ASSERT_EQ(1U, query_url_visits_.size());
1179 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1180 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1182 // Different scheme.
1183 const GURL test_url4("https://intranet_host/");
1184 history_service_->AddPage(
1185 test_url4, base::Time::Now(), NULL, 0, GURL(),
1186 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1187 history::SOURCE_BROWSED, false);
1188 EXPECT_TRUE(QueryURL(history_service_.get(), test_url4));
1189 EXPECT_EQ(1, query_url_row_.visit_count());
1190 EXPECT_EQ(0, query_url_row_.typed_count());
1191 ASSERT_EQ(1U, query_url_visits_.size());
1192 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1193 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1195 // Different transition.
1196 const GURL test_url5("http://intranet_host/another_path");
1197 history_service_->AddPage(
1198 test_url5, base::Time::Now(), NULL, 0, GURL(),
1199 history::RedirectList(),
1200 content::PAGE_TRANSITION_AUTO_BOOKMARK,
1201 history::SOURCE_BROWSED, false);
1202 EXPECT_TRUE(QueryURL(history_service_.get(), test_url5));
1203 EXPECT_EQ(1, query_url_row_.visit_count());
1204 EXPECT_EQ(0, query_url_row_.typed_count());
1205 ASSERT_EQ(1U, query_url_visits_.size());
1206 EXPECT_EQ(content::PAGE_TRANSITION_AUTO_BOOKMARK,
1207 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1210 history_service_->AddPage(
1211 test_url, base::Time::Now(), NULL, 0, GURL(),
1212 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1213 history::SOURCE_BROWSED, false);
1214 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1215 EXPECT_EQ(2, query_url_row_.visit_count());
1216 EXPECT_EQ(1, query_url_row_.typed_count());
1217 ASSERT_EQ(2U, query_url_visits_.size());
1218 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1219 content::PageTransitionStripQualifier(query_url_visits_[1].transition));
1222 TEST_F(HistoryTest, Typed) {
1223 ASSERT_TRUE(history_service_.get());
1225 // Add the page once as typed.
1226 const GURL test_url("http://www.google.com/");
1227 history_service_->AddPage(
1228 test_url, base::Time::Now(), NULL, 0, GURL(),
1229 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1230 history::SOURCE_BROWSED, false);
1231 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1233 // We should have the same typed & visit count.
1234 EXPECT_EQ(1, query_url_row_.visit_count());
1235 EXPECT_EQ(1, query_url_row_.typed_count());
1237 // Add the page again not typed.
1238 history_service_->AddPage(
1239 test_url, base::Time::Now(), NULL, 0, GURL(),
1240 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1241 history::SOURCE_BROWSED, false);
1242 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1244 // The second time should not have updated the typed count.
1245 EXPECT_EQ(2, query_url_row_.visit_count());
1246 EXPECT_EQ(1, query_url_row_.typed_count());
1248 // Add the page again as a generated URL.
1249 history_service_->AddPage(
1250 test_url, base::Time::Now(), NULL, 0, GURL(),
1251 history::RedirectList(), content::PAGE_TRANSITION_GENERATED,
1252 history::SOURCE_BROWSED, false);
1253 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1255 // This should have worked like a link click.
1256 EXPECT_EQ(3, query_url_row_.visit_count());
1257 EXPECT_EQ(1, query_url_row_.typed_count());
1259 // Add the page again as a reload.
1260 history_service_->AddPage(
1261 test_url, base::Time::Now(), NULL, 0, GURL(),
1262 history::RedirectList(), content::PAGE_TRANSITION_RELOAD,
1263 history::SOURCE_BROWSED, false);
1264 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1266 // This should not have incremented any visit counts.
1267 EXPECT_EQ(3, query_url_row_.visit_count());
1268 EXPECT_EQ(1, query_url_row_.typed_count());
1271 TEST_F(HistoryTest, SetTitle) {
1272 ASSERT_TRUE(history_service_.get());
1275 const GURL existing_url("http://www.google.com/");
1276 history_service_->AddPage(
1277 existing_url, base::Time::Now(), history::SOURCE_BROWSED);
1280 const base::string16 existing_title = base::UTF8ToUTF16("Google");
1281 history_service_->SetPageTitle(existing_url, existing_title);
1283 // Make sure the title got set.
1284 EXPECT_TRUE(QueryURL(history_service_.get(), existing_url));
1285 EXPECT_EQ(existing_title, query_url_row_.title());
1287 // set a title on a nonexistent page
1288 const GURL nonexistent_url("http://news.google.com/");
1289 const base::string16 nonexistent_title = base::UTF8ToUTF16("Google News");
1290 history_service_->SetPageTitle(nonexistent_url, nonexistent_title);
1292 // Make sure nothing got written.
1293 EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url));
1294 EXPECT_EQ(base::string16(), query_url_row_.title());
1296 // TODO(brettw) this should also test redirects, which get the title of the
1297 // destination page.
1300 // crbug.com/159387: This test fails when daylight savings time ends.
1301 TEST_F(HistoryTest, DISABLED_Segments) {
1302 ASSERT_TRUE(history_service_.get());
1304 static const void* scope = static_cast<void*>(this);
1307 const GURL existing_url("http://www.google.com/");
1308 history_service_->AddPage(
1309 existing_url, base::Time::Now(), scope, 0, GURL(),
1310 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1311 history::SOURCE_BROWSED, false);
1313 // Make sure a segment was created.
1314 history_service_->QuerySegmentUsageSince(
1315 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1316 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1317 base::Unretained(this)));
1319 // Wait for processing.
1320 base::MessageLoop::current()->Run();
1322 ASSERT_EQ(1U, page_usage_data_.size());
1323 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1324 EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore());
1326 // Add a URL which doesn't create a segment.
1327 const GURL link_url("http://yahoo.com/");
1328 history_service_->AddPage(
1329 link_url, base::Time::Now(), scope, 0, GURL(),
1330 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1331 history::SOURCE_BROWSED, false);
1334 history_service_->QuerySegmentUsageSince(
1335 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1336 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1337 base::Unretained(this)));
1339 // Wait for processing.
1340 base::MessageLoop::current()->Run();
1342 // Make sure we still have one segment.
1343 ASSERT_EQ(1U, page_usage_data_.size());
1344 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1346 // Add a page linked from existing_url.
1347 history_service_->AddPage(
1348 GURL("http://www.google.com/foo"), base::Time::Now(),
1349 scope, 3, existing_url, history::RedirectList(),
1350 content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED,
1354 history_service_->QuerySegmentUsageSince(
1355 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1356 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1357 base::Unretained(this)));
1359 // Wait for processing.
1360 base::MessageLoop::current()->Run();
1362 // Make sure we still have one segment.
1363 ASSERT_EQ(1U, page_usage_data_.size());
1364 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1366 // However, the score should have increased.
1367 EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0);
1370 TEST_F(HistoryTest, MostVisitedURLs) {
1371 ASSERT_TRUE(history_service_.get());
1373 const GURL url0("http://www.google.com/url0/");
1374 const GURL url1("http://www.google.com/url1/");
1375 const GURL url2("http://www.google.com/url2/");
1376 const GURL url3("http://www.google.com/url3/");
1377 const GURL url4("http://www.google.com/url4/");
1379 static const void* scope = static_cast<void*>(this);
1382 history_service_->AddPage(
1383 url0, base::Time::Now(), scope, 0, GURL(),
1384 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1385 history::SOURCE_BROWSED, false);
1386 history_service_->AddPage(
1387 url1, base::Time::Now(), scope, 0, GURL(),
1388 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1389 history::SOURCE_BROWSED, false);
1390 history_service_->QueryMostVisitedURLs(
1393 &HistoryTest::OnMostVisitedURLsAvailable,
1394 base::Unretained(this)));
1395 base::MessageLoop::current()->Run();
1397 EXPECT_EQ(2U, most_visited_urls_.size());
1398 EXPECT_EQ(url0, most_visited_urls_[0].url);
1399 EXPECT_EQ(url1, most_visited_urls_[1].url);
1401 // Add another page.
1402 history_service_->AddPage(
1403 url2, base::Time::Now(), scope, 0, GURL(),
1404 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1405 history::SOURCE_BROWSED, false);
1406 history_service_->QueryMostVisitedURLs(
1409 &HistoryTest::OnMostVisitedURLsAvailable,
1410 base::Unretained(this)));
1411 base::MessageLoop::current()->Run();
1413 EXPECT_EQ(3U, most_visited_urls_.size());
1414 EXPECT_EQ(url0, most_visited_urls_[0].url);
1415 EXPECT_EQ(url1, most_visited_urls_[1].url);
1416 EXPECT_EQ(url2, most_visited_urls_[2].url);
1418 // Revisit url2, making it the top URL.
1419 history_service_->AddPage(
1420 url2, base::Time::Now(), scope, 0, GURL(),
1421 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1422 history::SOURCE_BROWSED, false);
1423 history_service_->QueryMostVisitedURLs(
1426 &HistoryTest::OnMostVisitedURLsAvailable,
1427 base::Unretained(this)));
1428 base::MessageLoop::current()->Run();
1430 EXPECT_EQ(3U, most_visited_urls_.size());
1431 EXPECT_EQ(url2, most_visited_urls_[0].url);
1432 EXPECT_EQ(url0, most_visited_urls_[1].url);
1433 EXPECT_EQ(url1, most_visited_urls_[2].url);
1435 // Revisit url1, making it the top URL.
1436 history_service_->AddPage(
1437 url1, base::Time::Now(), scope, 0, GURL(),
1438 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1439 history::SOURCE_BROWSED, false);
1440 history_service_->QueryMostVisitedURLs(
1443 &HistoryTest::OnMostVisitedURLsAvailable,
1444 base::Unretained(this)));
1445 base::MessageLoop::current()->Run();
1447 EXPECT_EQ(3U, most_visited_urls_.size());
1448 EXPECT_EQ(url1, most_visited_urls_[0].url);
1449 EXPECT_EQ(url2, most_visited_urls_[1].url);
1450 EXPECT_EQ(url0, most_visited_urls_[2].url);
1453 history::RedirectList redirects;
1454 redirects.push_back(url3);
1455 redirects.push_back(url4);
1457 // Visit url4 using redirects.
1458 history_service_->AddPage(
1459 url4, base::Time::Now(), scope, 0, GURL(),
1460 redirects, content::PAGE_TRANSITION_TYPED,
1461 history::SOURCE_BROWSED, false);
1462 history_service_->QueryMostVisitedURLs(
1465 &HistoryTest::OnMostVisitedURLsAvailable,
1466 base::Unretained(this)));
1467 base::MessageLoop::current()->Run();
1469 EXPECT_EQ(4U, most_visited_urls_.size());
1470 EXPECT_EQ(url1, most_visited_urls_[0].url);
1471 EXPECT_EQ(url2, most_visited_urls_[1].url);
1472 EXPECT_EQ(url0, most_visited_urls_[2].url);
1473 EXPECT_EQ(url3, most_visited_urls_[3].url);
1474 EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
1479 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
1480 // invoke_count is increment. When invoked kWantInvokeCount times, true is
1481 // returned from RunOnDBThread which should stop RunOnDBThread from being
1482 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
1484 class HistoryDBTaskImpl : public HistoryDBTask {
1486 static const int kWantInvokeCount;
1488 HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {}
1490 virtual bool RunOnDBThread(HistoryBackend* backend,
1491 HistoryDatabase* db) OVERRIDE {
1492 return (++invoke_count == kWantInvokeCount);
1495 virtual void DoneRunOnMainThread() OVERRIDE {
1496 done_invoked = true;
1497 base::MessageLoop::current()->Quit();
1504 virtual ~HistoryDBTaskImpl() {}
1506 DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
1510 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
1514 TEST_F(HistoryTest, HistoryDBTask) {
1515 ASSERT_TRUE(history_service_.get());
1516 CancelableRequestConsumerT<int, 0> request_consumer;
1517 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1518 history_service_->ScheduleDBTask(task.get(), &request_consumer);
1519 // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
1520 // it will stop the message loop. If the test hangs here, it means
1521 // DoneRunOnMainThread isn't being invoked correctly.
1522 base::MessageLoop::current()->Run();
1523 CleanupHistoryService();
1524 // WARNING: history has now been deleted.
1525 history_service_.reset();
1526 ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count);
1527 ASSERT_TRUE(task->done_invoked);
1530 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
1531 ASSERT_TRUE(history_service_.get());
1532 CancelableRequestConsumerT<int, 0> request_consumer;
1533 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1534 history_service_->ScheduleDBTask(task.get(), &request_consumer);
1535 request_consumer.CancelAllRequests();
1536 CleanupHistoryService();
1537 // WARNING: history has now been deleted.
1538 history_service_.reset();
1539 ASSERT_FALSE(task->done_invoked);
1542 // Create a local delete directive and process it while sync is
1543 // online, and then when offline. The delete directive should be sent to sync,
1544 // no error should be returned for the first time, and an error should be
1545 // returned for the second time.
1546 TEST_F(HistoryTest, ProcessLocalDeleteDirectiveSyncOnline) {
1547 ASSERT_TRUE(history_service_.get());
1549 const GURL test_url("http://www.google.com/");
1550 for (int64 i = 1; i <= 10; ++i) {
1552 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1553 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1554 history::RedirectList(),
1555 content::PAGE_TRANSITION_LINK,
1556 history::SOURCE_BROWSED, false);
1559 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
1560 sync_pb::GlobalIdDirective* global_id_directive =
1561 delete_directive.mutable_global_id_directive();
1562 global_id_directive->add_global_id(
1563 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1))
1564 .ToInternalValue());
1566 syncer::FakeSyncChangeProcessor change_processor;
1569 history_service_->MergeDataAndStartSyncing(
1570 syncer::HISTORY_DELETE_DIRECTIVES,
1571 syncer::SyncDataList(),
1572 scoped_ptr<syncer::SyncChangeProcessor>(
1573 new syncer::SyncChangeProcessorWrapperForTest(
1574 &change_processor)),
1575 scoped_ptr<syncer::SyncErrorFactory>())
1579 syncer::SyncError err =
1580 history_service_->ProcessLocalDeleteDirective(delete_directive);
1581 EXPECT_FALSE(err.IsSet());
1582 EXPECT_EQ(1u, change_processor.changes().size());
1584 history_service_->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES);
1585 err = history_service_->ProcessLocalDeleteDirective(delete_directive);
1586 EXPECT_TRUE(err.IsSet());
1587 EXPECT_EQ(1u, change_processor.changes().size());
1590 // Closure function that runs periodically to check result of delete directive
1591 // processing. Stop when timeout or processing ends indicated by the creation
1593 void CheckDirectiveProcessingResult(
1595 const syncer::FakeSyncChangeProcessor* change_processor,
1596 uint32 num_changes) {
1597 if (base::Time::Now() > timeout ||
1598 change_processor->changes().size() >= num_changes) {
1602 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
1603 base::MessageLoop::current()->PostTask(
1605 base::Bind(&CheckDirectiveProcessingResult, timeout,
1606 change_processor, num_changes));
1609 // Create a delete directive for a few specific history entries,
1610 // including ones that don't exist. The expected entries should be
1612 TEST_F(HistoryTest, ProcessGlobalIdDeleteDirective) {
1613 ASSERT_TRUE(history_service_.get());
1614 const GURL test_url("http://www.google.com/");
1615 for (int64 i = 1; i <= 20; i++) {
1617 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1618 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1619 history::RedirectList(),
1620 content::PAGE_TRANSITION_LINK,
1621 history::SOURCE_BROWSED, false);
1624 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1625 EXPECT_EQ(20, query_url_row_.visit_count());
1627 syncer::SyncDataList directives;
1629 sync_pb::EntitySpecifics entity_specs;
1630 sync_pb::GlobalIdDirective* global_id_directive =
1631 entity_specs.mutable_history_delete_directive()
1632 ->mutable_global_id_directive();
1633 global_id_directive->add_global_id(
1634 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6))
1635 .ToInternalValue());
1636 global_id_directive->set_start_time_usec(3);
1637 global_id_directive->set_end_time_usec(10);
1638 directives.push_back(syncer::SyncData::CreateRemoteData(
1642 syncer::AttachmentIdList(),
1643 syncer::AttachmentServiceProxyForTest::Create()));
1646 global_id_directive->Clear();
1647 global_id_directive->add_global_id(
1648 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17))
1649 .ToInternalValue());
1650 global_id_directive->set_start_time_usec(13);
1651 global_id_directive->set_end_time_usec(19);
1652 directives.push_back(syncer::SyncData::CreateRemoteData(
1656 syncer::AttachmentIdList(),
1657 syncer::AttachmentServiceProxyForTest::Create()));
1659 syncer::FakeSyncChangeProcessor change_processor;
1661 history_service_->MergeDataAndStartSyncing(
1662 syncer::HISTORY_DELETE_DIRECTIVES,
1664 scoped_ptr<syncer::SyncChangeProcessor>(
1665 new syncer::SyncChangeProcessorWrapperForTest(
1666 &change_processor)),
1667 scoped_ptr<syncer::SyncErrorFactory>())
1671 // Inject a task to check status and keep message loop filled before directive
1672 // processing finishes.
1673 base::MessageLoop::current()->PostTask(
1675 base::Bind(&CheckDirectiveProcessingResult,
1676 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1677 &change_processor, 2));
1678 base::MessageLoop::current()->RunUntilIdle();
1679 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1680 ASSERT_EQ(5, query_url_row_.visit_count());
1681 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1682 query_url_visits_[0].visit_time);
1683 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(2),
1684 query_url_visits_[1].visit_time);
1685 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(11),
1686 query_url_visits_[2].visit_time);
1687 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(12),
1688 query_url_visits_[3].visit_time);
1689 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(20),
1690 query_url_visits_[4].visit_time);
1692 // Expect two sync changes for deleting processed directives.
1693 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1694 ASSERT_EQ(2u, sync_changes.size());
1695 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1696 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1697 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1698 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1701 // Create delete directives for time ranges. The expected entries should be
1703 TEST_F(HistoryTest, ProcessTimeRangeDeleteDirective) {
1704 ASSERT_TRUE(history_service_.get());
1705 const GURL test_url("http://www.google.com/");
1706 for (int64 i = 1; i <= 10; ++i) {
1708 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1709 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1710 history::RedirectList(),
1711 content::PAGE_TRANSITION_LINK,
1712 history::SOURCE_BROWSED, false);
1715 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1716 EXPECT_EQ(10, query_url_row_.visit_count());
1718 syncer::SyncDataList directives;
1720 sync_pb::EntitySpecifics entity_specs;
1721 sync_pb::TimeRangeDirective* time_range_directive =
1722 entity_specs.mutable_history_delete_directive()
1723 ->mutable_time_range_directive();
1724 time_range_directive->set_start_time_usec(2);
1725 time_range_directive->set_end_time_usec(5);
1726 directives.push_back(syncer::SyncData::CreateRemoteData(
1730 syncer::AttachmentIdList(),
1731 syncer::AttachmentServiceProxyForTest::Create()));
1734 time_range_directive->Clear();
1735 time_range_directive->set_start_time_usec(8);
1736 time_range_directive->set_end_time_usec(10);
1737 directives.push_back(syncer::SyncData::CreateRemoteData(
1741 syncer::AttachmentIdList(),
1742 syncer::AttachmentServiceProxyForTest::Create()));
1744 syncer::FakeSyncChangeProcessor change_processor;
1746 history_service_->MergeDataAndStartSyncing(
1747 syncer::HISTORY_DELETE_DIRECTIVES,
1749 scoped_ptr<syncer::SyncChangeProcessor>(
1750 new syncer::SyncChangeProcessorWrapperForTest(
1751 &change_processor)),
1752 scoped_ptr<syncer::SyncErrorFactory>())
1756 // Inject a task to check status and keep message loop filled before
1757 // directive processing finishes.
1758 base::MessageLoop::current()->PostTask(
1760 base::Bind(&CheckDirectiveProcessingResult,
1761 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1762 &change_processor, 2));
1763 base::MessageLoop::current()->RunUntilIdle();
1764 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1765 ASSERT_EQ(3, query_url_row_.visit_count());
1766 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1767 query_url_visits_[0].visit_time);
1768 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6),
1769 query_url_visits_[1].visit_time);
1770 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(7),
1771 query_url_visits_[2].visit_time);
1773 // Expect two sync changes for deleting processed directives.
1774 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1775 ASSERT_EQ(2u, sync_changes.size());
1776 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1777 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1778 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1779 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1782 TEST_F(HistoryBackendDBTest, MigratePresentations) {
1783 // Create the db we want. Use 22 since segments didn't change in that time
1785 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
1787 const SegmentID segment_id = 2;
1788 const URLID url_id = 3;
1789 const GURL url("http://www.foo.com");
1790 const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
1791 const base::string16 title(base::ASCIIToUTF16("Title1"));
1792 const Time segment_time(Time::Now());
1795 // Re-open the db for manual manipulation.
1797 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
1799 // Add an entry to urls.
1801 sql::Statement s(db.GetUniqueStatement(
1803 "(id, url, title, last_visit_time) VALUES "
1805 s.BindInt64(0, url_id);
1806 s.BindString(1, url.spec());
1807 s.BindString16(2, title);
1808 s.BindInt64(3, segment_time.ToInternalValue());
1809 ASSERT_TRUE(s.Run());
1812 // Add an entry to segments.
1814 sql::Statement s(db.GetUniqueStatement(
1815 "INSERT INTO segments "
1816 "(id, name, url_id, pres_index) VALUES "
1818 s.BindInt64(0, segment_id);
1819 s.BindString(1, url_name);
1820 s.BindInt64(2, url_id);
1821 s.BindInt(3, 4); // pres_index
1822 ASSERT_TRUE(s.Run());
1825 // And one to segment_usage.
1827 sql::Statement s(db.GetUniqueStatement(
1828 "INSERT INTO segment_usage "
1829 "(id, segment_id, time_slot, visit_count) VALUES "
1831 s.BindInt64(0, 4); // id.
1832 s.BindInt64(1, segment_id);
1833 s.BindInt64(2, segment_time.ToInternalValue());
1834 s.BindInt(3, 5); // visit count.
1835 ASSERT_TRUE(s.Run());
1839 // Re-open the db, triggering migration.
1840 CreateBackendAndDatabase();
1842 std::vector<PageUsageData*> results;
1843 db_->QuerySegmentUsage(segment_time, 10, &results);
1844 ASSERT_EQ(1u, results.size());
1845 EXPECT_EQ(url, results[0]->GetURL());
1846 EXPECT_EQ(segment_id, results[0]->GetID());
1847 EXPECT_EQ(title, results[0]->GetTitle());
1848 STLDeleteElements(&results);
1851 } // namespace history