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/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"
80 using base::TimeDelta;
81 using content::DownloadItem;
84 class HistoryBackendDBTest;
86 // Delegate class for when we create a backend without a HistoryService.
88 // This must be outside the anonymous namespace for the friend statement in
89 // HistoryBackendDBTest to work.
90 class BackendDelegate : public HistoryBackend::Delegate {
92 explicit BackendDelegate(HistoryBackendDBTest* history_test)
93 : history_test_(history_test) {
96 void NotifyProfileError(sql::InitStatus init_status) override {}
97 void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) override;
98 void NotifyAddVisit(const BriefVisitInfo& info) override {}
99 void NotifyFaviconChanged(const std::set<GURL>& url) override {}
100 void NotifyURLVisited(ui::PageTransition transition,
102 const RedirectList& redirects,
103 base::Time visit_time) override {}
104 void BroadcastNotifications(int type,
105 scoped_ptr<HistoryDetails> details) override;
106 void DBLoaded() override {}
109 HistoryBackendDBTest* history_test_;
112 // This must be outside the anonymous namespace for the friend statement in
113 // HistoryBackend to work.
114 class HistoryBackendDBTest : public HistoryUnitTestBase {
116 HistoryBackendDBTest() : db_(NULL) {
119 ~HistoryBackendDBTest() override {}
122 friend class BackendDelegate;
124 // Creates the HistoryBackend and HistoryDatabase on the current thread,
125 // assigning the values to backend_ and db_.
126 void CreateBackendAndDatabase() {
128 new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
129 backend_->Init(std::string(), false);
130 db_ = backend_->db_.get();
131 DCHECK(in_mem_backend_) << "Mem backend should have been set by "
132 "HistoryBackend::Init";
135 void CreateDBVersion(int version) {
136 base::FilePath data_path;
137 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
138 data_path = data_path.AppendASCII("History");
140 data_path.AppendASCII(base::StringPrintf("history.%d.sql", version));
141 ASSERT_NO_FATAL_FAILURE(
142 ExecuteSQLScript(data_path, history_dir_.Append(
143 chrome::kHistoryFilename)));
146 void CreateArchivedDB() {
147 base::FilePath data_path;
148 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
149 data_path = data_path.AppendASCII("History");
150 data_path = data_path.AppendASCII("archived_history.4.sql");
151 ASSERT_NO_FATAL_FAILURE(
152 ExecuteSQLScript(data_path, history_dir_.Append(
153 chrome::kArchivedHistoryFilename)));
157 void SetUp() override {
158 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
159 history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBTest");
160 ASSERT_TRUE(base::CreateDirectory(history_dir_));
163 void DeleteBackend() {
164 if (backend_.get()) {
170 void TearDown() override {
173 // Make sure we don't have any event pending that could disrupt the next
175 base::MessageLoop::current()->PostTask(FROM_HERE,
176 base::MessageLoop::QuitClosure());
177 base::MessageLoop::current()->Run();
180 bool AddDownload(uint32 id,
181 DownloadItem::DownloadState state,
183 std::vector<GURL> url_chain;
184 url_chain.push_back(GURL("foo-url"));
186 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("current-path")),
187 base::FilePath(FILE_PATH_LITERAL("target-path")),
189 GURL("http://referrer.com/"),
190 "application/vnd.oasis.opendocument.text",
191 "application/octet-stream",
199 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
200 content::DOWNLOAD_INTERRUPT_REASON_NONE,
205 return db_->CreateDownload(download);
208 base::ScopedTempDir temp_dir_;
210 base::MessageLoopForUI message_loop_;
212 // names of the database files
213 base::FilePath history_dir_;
215 // Created via CreateBackendAndDatabase.
216 scoped_refptr<HistoryBackend> backend_;
217 scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
218 HistoryDatabase* db_; // Cached reference to the backend's database.
221 void BackendDelegate::SetInMemoryBackend(
222 scoped_ptr<InMemoryHistoryBackend> backend) {
223 // Save the in-memory backend to the history test object, this happens
224 // synchronously, so we don't have to do anything fancy.
225 history_test_->in_mem_backend_.swap(backend);
228 void BackendDelegate::BroadcastNotifications(
230 scoped_ptr<HistoryDetails> details) {
231 // Currently, just send the notifications directly to the in-memory database.
232 // We may want do do something more fancy in the future.
233 content::Details<HistoryDetails> det(details.get());
234 history_test_->in_mem_backend_->Observe(type,
235 content::Source<HistoryBackendDBTest>(NULL), det);
238 TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
239 CreateBackendAndDatabase();
241 // Initially there should be nothing in the downloads database.
242 std::vector<DownloadRow> downloads;
243 db_->QueryDownloads(&downloads);
244 EXPECT_EQ(0U, downloads.size());
246 // Add a download, test that it was added correctly, remove it, test that it
250 EXPECT_TRUE(AddDownload(id, DownloadItem::COMPLETE, Time()));
251 db_->QueryDownloads(&downloads);
252 EXPECT_EQ(1U, downloads.size());
254 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
255 downloads[0].current_path);
256 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
257 downloads[0].target_path);
258 EXPECT_EQ(1UL, downloads[0].url_chain.size());
259 EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
260 EXPECT_EQ(std::string("http://referrer.com/"),
261 std::string(downloads[0].referrer_url.spec()));
262 EXPECT_EQ(now, downloads[0].start_time);
263 EXPECT_EQ(now, downloads[0].end_time);
264 EXPECT_EQ(0, downloads[0].received_bytes);
265 EXPECT_EQ(512, downloads[0].total_bytes);
266 EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
267 EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
268 downloads[0].danger_type);
269 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
270 downloads[0].interrupt_reason);
271 EXPECT_FALSE(downloads[0].opened);
272 EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
273 EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
274 EXPECT_EQ("application/vnd.oasis.opendocument.text", downloads[0].mime_type);
275 EXPECT_EQ("application/octet-stream", downloads[0].original_mime_type);
277 db_->QueryDownloads(&downloads);
278 EXPECT_EQ(1U, downloads.size());
279 db_->RemoveDownload(id);
280 db_->QueryDownloads(&downloads);
281 EXPECT_EQ(0U, downloads.size());
284 TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
285 // Create the db we want.
286 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
288 // Open the db for manual manipulation.
290 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
292 // Manually insert corrupted rows; there's infrastructure in place now to
293 // make this impossible, at least according to the test above.
294 for (int state = 0; state < 5; ++state) {
295 sql::Statement s(db.GetUniqueStatement(
296 "INSERT INTO downloads (id, full_path, url, start_time, "
297 "received_bytes, total_bytes, state, end_time, opened) VALUES "
298 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
299 s.BindInt64(0, 1 + state);
300 s.BindString(1, "path");
301 s.BindString(2, "url");
302 s.BindInt64(3, base::Time::Now().ToTimeT());
306 s.BindInt64(7, base::Time::Now().ToTimeT());
307 s.BindInt(8, state % 2);
308 ASSERT_TRUE(s.Run());
312 // Re-open the db using the HistoryDatabase, which should migrate from version
313 // 22 to the current version, fixing just the row whose state was 3.
314 // Then close the db so that we can re-open it directly.
315 CreateBackendAndDatabase();
318 // Re-open the db for manual manipulation.
320 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
322 // The version should have been updated.
323 int cur_version = HistoryDatabase::GetCurrentVersion();
324 ASSERT_LT(22, cur_version);
325 sql::Statement s(db.GetUniqueStatement(
326 "SELECT value FROM meta WHERE key = 'version'"));
327 EXPECT_TRUE(s.Step());
328 EXPECT_EQ(cur_version, s.ColumnInt(0));
331 sql::Statement statement(db.GetUniqueStatement(
332 "SELECT id, state, opened "
336 while (statement.Step()) {
337 EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
338 // The only thing that migration should have changed was state from 3 to
340 EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
341 EXPECT_EQ(counter % 2, statement.ColumnInt(2));
344 EXPECT_EQ(5, counter);
349 TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
350 Time now(base::Time::Now());
352 // Create the db we want. The schema didn't change from 22->23, so just
353 // re-use the v22 file.
354 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
356 // Re-open the db for manual manipulation.
358 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
360 // Manually insert some rows.
361 sql::Statement s(db.GetUniqueStatement(
362 "INSERT INTO downloads (id, full_path, url, start_time, "
363 "received_bytes, total_bytes, state, end_time, opened) VALUES "
364 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
368 s.BindInt64(0, ++id);
369 s.BindString(1, std::string());
370 s.BindString(2, "http://whatever.com/index.html");
371 s.BindInt64(3, now.ToTimeT());
375 s.BindInt64(7, now.ToTimeT());
377 ASSERT_TRUE(s.Run());
381 s.BindInt64(0, ++id);
382 s.BindString(1, "/path/to/some/file");
383 s.BindString(2, "http://whatever.com/index1.html");
384 s.BindInt64(3, now.ToTimeT());
388 s.BindInt64(7, now.ToTimeT());
390 ASSERT_TRUE(s.Run());
393 // Re-open the db using the HistoryDatabase, which should migrate from version
394 // 23 to 24, creating the new tables and creating the new path, reason,
395 // and danger columns.
396 CreateBackendAndDatabase();
399 // Re-open the db for manual manipulation.
401 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
403 // The version should have been updated.
404 int cur_version = HistoryDatabase::GetCurrentVersion();
405 ASSERT_LT(23, cur_version);
406 sql::Statement s(db.GetUniqueStatement(
407 "SELECT value FROM meta WHERE key = 'version'"));
408 EXPECT_TRUE(s.Step());
409 EXPECT_EQ(cur_version, s.ColumnInt(0));
412 base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
414 // Confirm downloads table is valid.
415 sql::Statement statement(db.GetUniqueStatement(
416 "SELECT id, interrupt_reason, current_path, target_path, "
417 " danger_type, start_time, end_time "
418 "FROM downloads ORDER BY id"));
419 EXPECT_TRUE(statement.Step());
420 EXPECT_EQ(1, statement.ColumnInt64(0));
421 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
422 statement.ColumnInt(1));
423 EXPECT_EQ("", statement.ColumnString(2));
424 EXPECT_EQ("", statement.ColumnString(3));
425 // Implicit dependence on value of kDangerTypeNotDangerous from
426 // download_database.cc.
427 EXPECT_EQ(0, statement.ColumnInt(4));
428 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
429 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
431 EXPECT_TRUE(statement.Step());
432 EXPECT_EQ(2, statement.ColumnInt64(0));
433 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
434 statement.ColumnInt(1));
435 EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
436 EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
437 EXPECT_EQ(0, statement.ColumnInt(4));
438 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
439 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
441 EXPECT_FALSE(statement.Step());
444 // Confirm downloads_url_chains table is valid.
445 sql::Statement statement(db.GetUniqueStatement(
446 "SELECT id, chain_index, url FROM downloads_url_chains "
447 " ORDER BY id, chain_index"));
448 EXPECT_TRUE(statement.Step());
449 EXPECT_EQ(1, statement.ColumnInt64(0));
450 EXPECT_EQ(0, statement.ColumnInt(1));
451 EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
453 EXPECT_TRUE(statement.Step());
454 EXPECT_EQ(2, statement.ColumnInt64(0));
455 EXPECT_EQ(0, statement.ColumnInt(1));
456 EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
458 EXPECT_FALSE(statement.Step());
463 TEST_F(HistoryBackendDBTest, MigrateReferrer) {
464 Time now(base::Time::Now());
465 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
468 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
469 sql::Statement s(db.GetUniqueStatement(
470 "INSERT INTO downloads (id, full_path, url, start_time, "
471 "received_bytes, total_bytes, state, end_time, opened) VALUES "
472 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
474 s.BindInt64(0, ++db_handle);
475 s.BindString(1, "full_path");
476 s.BindString(2, "http://whatever.com/index.html");
477 s.BindInt64(3, now.ToTimeT());
481 s.BindInt64(7, now.ToTimeT());
483 ASSERT_TRUE(s.Run());
485 // Re-open the db using the HistoryDatabase, which should migrate to version
486 // 26, creating the referrer column.
487 CreateBackendAndDatabase();
490 // Re-open the db for manual manipulation.
492 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
493 // The version should have been updated.
494 int cur_version = HistoryDatabase::GetCurrentVersion();
495 ASSERT_LE(26, cur_version);
497 sql::Statement s(db.GetUniqueStatement(
498 "SELECT value FROM meta WHERE key = 'version'"));
499 EXPECT_TRUE(s.Step());
500 EXPECT_EQ(cur_version, s.ColumnInt(0));
503 sql::Statement s(db.GetUniqueStatement(
504 "SELECT referrer from downloads"));
505 EXPECT_TRUE(s.Step());
506 EXPECT_EQ(std::string(), s.ColumnString(0));
511 TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
512 Time now(base::Time::Now());
513 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
516 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
518 sql::Statement s(db.GetUniqueStatement(
519 "INSERT INTO downloads (id, current_path, target_path, start_time, "
520 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
521 "end_time, opened, referrer) VALUES "
522 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
524 s.BindString(1, "current_path");
525 s.BindString(2, "target_path");
526 s.BindInt64(3, now.ToTimeT());
532 s.BindInt64(9, now.ToTimeT());
534 s.BindString(11, "referrer");
535 ASSERT_TRUE(s.Run());
538 sql::Statement s(db.GetUniqueStatement(
539 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
543 s.BindString(2, "url");
544 ASSERT_TRUE(s.Run());
547 // Re-open the db using the HistoryDatabase, which should migrate to version
548 // 27, creating the by_ext_id and by_ext_name columns.
549 CreateBackendAndDatabase();
552 // Re-open the db for manual manipulation.
554 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
555 // The version should have been updated.
556 int cur_version = HistoryDatabase::GetCurrentVersion();
557 ASSERT_LE(27, cur_version);
559 sql::Statement s(db.GetUniqueStatement(
560 "SELECT value FROM meta WHERE key = 'version'"));
561 EXPECT_TRUE(s.Step());
562 EXPECT_EQ(cur_version, s.ColumnInt(0));
565 sql::Statement s(db.GetUniqueStatement(
566 "SELECT by_ext_id, by_ext_name from downloads"));
567 EXPECT_TRUE(s.Step());
568 EXPECT_EQ(std::string(), s.ColumnString(0));
569 EXPECT_EQ(std::string(), s.ColumnString(1));
574 TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
575 Time now(base::Time::Now());
576 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
579 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
581 sql::Statement s(db.GetUniqueStatement(
582 "INSERT INTO downloads (id, current_path, target_path, start_time, "
583 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
584 "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
585 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
587 s.BindString(1, "current_path");
588 s.BindString(2, "target_path");
589 s.BindInt64(3, now.ToTimeT());
595 s.BindInt64(9, now.ToTimeT());
597 s.BindString(11, "referrer");
598 s.BindString(12, "by extension ID");
599 s.BindString(13, "by extension name");
600 ASSERT_TRUE(s.Run());
603 sql::Statement s(db.GetUniqueStatement(
604 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
608 s.BindString(2, "url");
609 ASSERT_TRUE(s.Run());
612 // Re-open the db using the HistoryDatabase, which should migrate to the
613 // current version, creating the etag and last_modified columns.
614 CreateBackendAndDatabase();
617 // Re-open the db for manual manipulation.
619 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
620 // The version should have been updated.
621 int cur_version = HistoryDatabase::GetCurrentVersion();
622 ASSERT_LE(28, cur_version);
624 sql::Statement s(db.GetUniqueStatement(
625 "SELECT value FROM meta WHERE key = 'version'"));
626 EXPECT_TRUE(s.Step());
627 EXPECT_EQ(cur_version, s.ColumnInt(0));
630 sql::Statement s(db.GetUniqueStatement(
631 "SELECT etag, last_modified from downloads"));
632 EXPECT_TRUE(s.Step());
633 EXPECT_EQ(std::string(), s.ColumnString(0));
634 EXPECT_EQ(std::string(), s.ColumnString(1));
639 TEST_F(HistoryBackendDBTest, PurgeArchivedDatabase) {
640 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
641 ASSERT_NO_FATAL_FAILURE(CreateArchivedDB());
643 ASSERT_TRUE(base::PathExists(
644 history_dir_.Append(chrome::kArchivedHistoryFilename)));
646 CreateBackendAndDatabase();
649 // We do not retain expired history entries in an archived database as of M37.
650 // Verify that any legacy archived database is deleted on start-up.
651 ASSERT_FALSE(base::PathExists(
652 history_dir_.Append(chrome::kArchivedHistoryFilename)));
655 TEST_F(HistoryBackendDBTest, MigrateDownloadMimeType) {
656 Time now(base::Time::Now());
657 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(28));
660 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
662 sql::Statement s(db.GetUniqueStatement(
663 "INSERT INTO downloads (id, current_path, target_path, start_time, "
664 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
665 "end_time, opened, referrer, by_ext_id, by_ext_name, etag, "
666 "last_modified) VALUES "
667 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
669 s.BindString(1, "current_path");
670 s.BindString(2, "target_path");
671 s.BindInt64(3, now.ToTimeT());
677 s.BindInt64(9, now.ToTimeT());
679 s.BindString(11, "referrer");
680 s.BindString(12, "by extension ID");
681 s.BindString(13, "by extension name");
682 s.BindString(14, "etag");
683 s.BindInt64(15, now.ToTimeT());
684 ASSERT_TRUE(s.Run());
687 sql::Statement s(db.GetUniqueStatement(
688 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
692 s.BindString(2, "url");
693 ASSERT_TRUE(s.Run());
696 // Re-open the db using the HistoryDatabase, which should migrate to the
697 // current version, creating themime_type abd original_mime_type columns.
698 CreateBackendAndDatabase();
701 // Re-open the db for manual manipulation.
703 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
704 // The version should have been updated.
705 int cur_version = HistoryDatabase::GetCurrentVersion();
706 ASSERT_LE(29, cur_version);
708 sql::Statement s(db.GetUniqueStatement(
709 "SELECT value FROM meta WHERE key = 'version'"));
710 EXPECT_TRUE(s.Step());
711 EXPECT_EQ(cur_version, s.ColumnInt(0));
714 sql::Statement s(db.GetUniqueStatement(
715 "SELECT mime_type, original_mime_type from downloads"));
716 EXPECT_TRUE(s.Step());
717 EXPECT_EQ(std::string(), s.ColumnString(0));
718 EXPECT_EQ(std::string(), s.ColumnString(1));
723 TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
725 CreateBackendAndDatabase();
727 base::Time now(base::Time::Now());
729 // Add some downloads.
730 uint32 id1 = 1, id2 = 2, id3 = 3;
731 AddDownload(id1, DownloadItem::COMPLETE, now);
732 AddDownload(id2, DownloadItem::COMPLETE, now + base::TimeDelta::FromDays(2));
733 AddDownload(id3, DownloadItem::COMPLETE, now - base::TimeDelta::FromDays(2));
735 // Confirm that resulted in the correct number of rows in the DB.
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(3, statement.ColumnInt(0));
745 sql::Statement statement1(db.GetUniqueStatement(
746 "Select Count(*) from downloads_url_chains"));
747 EXPECT_TRUE(statement1.Step());
748 EXPECT_EQ(3, statement1.ColumnInt(0));
751 // Delete some rows and make sure the results are still correct.
752 CreateBackendAndDatabase();
753 db_->RemoveDownload(id2);
754 db_->RemoveDownload(id3);
758 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
759 sql::Statement statement(db.GetUniqueStatement(
760 "Select Count(*) from downloads"));
761 EXPECT_TRUE(statement.Step());
762 EXPECT_EQ(1, statement.ColumnInt(0));
764 sql::Statement statement1(db.GetUniqueStatement(
765 "Select Count(*) from downloads_url_chains"));
766 EXPECT_TRUE(statement1.Step());
767 EXPECT_EQ(1, statement1.ColumnInt(0));
771 TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
772 CreateBackendAndDatabase();
773 base::Time now(base::Time::Now());
774 std::vector<GURL> url_chain;
775 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
776 base::FilePath(FILE_PATH_LITERAL("foo-path")),
779 "application/octet-stream",
780 "application/octet-stream",
787 DownloadItem::COMPLETE,
788 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
789 content::DOWNLOAD_INTERRUPT_REASON_NONE,
795 // Creating records without any urls should fail.
796 EXPECT_FALSE(db_->CreateDownload(download));
798 download.url_chain.push_back(GURL("foo-url"));
799 EXPECT_TRUE(db_->CreateDownload(download));
801 // Pretend that the URLs were dropped.
805 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
806 sql::Statement statement(db.GetUniqueStatement(
807 "DELETE FROM downloads_url_chains WHERE id=1"));
808 ASSERT_TRUE(statement.Run());
810 CreateBackendAndDatabase();
811 std::vector<DownloadRow> downloads;
812 db_->QueryDownloads(&downloads);
813 EXPECT_EQ(0U, downloads.size());
815 // QueryDownloads should have nuked the corrupt record.
819 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
821 sql::Statement statement(db.GetUniqueStatement(
822 "SELECT count(*) from downloads"));
823 ASSERT_TRUE(statement.Step());
824 EXPECT_EQ(0, statement.ColumnInt(0));
829 TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
831 CreateBackendAndDatabase();
833 base::Time now(base::Time::Now());
835 // Put an IN_PROGRESS download in the DB.
836 AddDownload(1, DownloadItem::IN_PROGRESS, now);
838 // Confirm that they made it into the DB unchanged.
842 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
843 sql::Statement statement(db.GetUniqueStatement(
844 "Select Count(*) from downloads"));
845 EXPECT_TRUE(statement.Step());
846 EXPECT_EQ(1, statement.ColumnInt(0));
848 sql::Statement statement1(db.GetUniqueStatement(
849 "Select state, interrupt_reason from downloads"));
850 EXPECT_TRUE(statement1.Step());
851 EXPECT_EQ(DownloadDatabase::kStateInProgress, statement1.ColumnInt(0));
852 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, statement1.ColumnInt(1));
853 EXPECT_FALSE(statement1.Step());
856 // Read in the DB through query downloads, then test that the
857 // right transformation was returned.
858 CreateBackendAndDatabase();
859 std::vector<DownloadRow> results;
860 db_->QueryDownloads(&results);
861 ASSERT_EQ(1u, results.size());
862 EXPECT_EQ(content::DownloadItem::INTERRUPTED, results[0].state);
863 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
864 results[0].interrupt_reason);
866 // Allow the update to propagate, shut down the DB, and confirm that
867 // the query updated the on disk database as well.
868 base::MessageLoop::current()->RunUntilIdle();
872 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
873 sql::Statement statement(db.GetUniqueStatement(
874 "Select Count(*) from downloads"));
875 EXPECT_TRUE(statement.Step());
876 EXPECT_EQ(1, statement.ColumnInt(0));
878 sql::Statement statement1(db.GetUniqueStatement(
879 "Select state, interrupt_reason from downloads"));
880 EXPECT_TRUE(statement1.Step());
881 EXPECT_EQ(DownloadDatabase::kStateInterrupted, statement1.ColumnInt(0));
882 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
883 statement1.ColumnInt(1));
884 EXPECT_FALSE(statement1.Step());
888 struct InterruptReasonAssociation {
893 // Test is dependent on interrupt reasons being listed in header file
895 const InterruptReasonAssociation current_reasons[] = {
896 #define INTERRUPT_REASON(a, b) { #a, b },
897 #include "content/public/browser/download_interrupt_reason_values.h"
898 #undef INTERRUPT_REASON
901 // This represents a list of all reasons we've previously used;
902 // Do Not Remove Any Entries From This List.
903 const InterruptReasonAssociation historical_reasons[] = {
905 {"FILE_ACCESS_DENIED", 2},
906 {"FILE_NO_SPACE", 3},
907 {"FILE_NAME_TOO_LONG", 5},
908 {"FILE_TOO_LARGE", 6},
909 {"FILE_VIRUS_INFECTED", 7},
910 {"FILE_TRANSIENT_ERROR", 10},
911 {"FILE_BLOCKED", 11},
912 {"FILE_SECURITY_CHECK_FAILED", 12},
913 {"FILE_TOO_SHORT", 13},
914 {"NETWORK_FAILED", 20},
915 {"NETWORK_TIMEOUT", 21},
916 {"NETWORK_DISCONNECTED", 22},
917 {"NETWORK_SERVER_DOWN", 23},
918 {"NETWORK_INVALID_REQUEST", 24},
919 {"SERVER_FAILED", 30},
920 {"SERVER_NO_RANGE", 31},
921 {"SERVER_PRECONDITION", 32},
922 {"SERVER_BAD_CONTENT", 33},
923 {"SERVER_UNAUTHORIZED", 34},
924 {"SERVER_CERT_PROBLEM", 35},
925 {"USER_CANCELED", 40},
926 {"USER_SHUTDOWN", 41},
930 // Make sure no one has changed a DownloadInterruptReason we've previously
932 TEST_F(HistoryBackendDBTest,
933 ConfirmDownloadInterruptReasonBackwardsCompatible) {
934 // Are there any cases in which a historical number has been repurposed
935 // for an error other than it's original?
936 for (size_t i = 0; i < arraysize(current_reasons); i++) {
937 const InterruptReasonAssociation& cur_reason(current_reasons[i]);
940 for (size_t j = 0; j < arraysize(historical_reasons); ++j) {
941 const InterruptReasonAssociation& hist_reason(historical_reasons[j]);
943 if (hist_reason.value == cur_reason.value) {
944 EXPECT_EQ(cur_reason.name, hist_reason.name)
945 << "Same integer value used for old error \""
947 << "\" as for new error \""
949 << "\"." << std::endl
950 << "**This will cause database conflicts with persisted values**"
952 << "Please assign a new, non-conflicting value for the new error.";
955 if (hist_reason.name == cur_reason.name) {
956 EXPECT_EQ(cur_reason.value, hist_reason.value)
957 << "Same name (\"" << hist_reason.name
958 << "\") maps to a different value historically ("
959 << hist_reason.value << ") and currently ("
960 << cur_reason.value << ")" << std::endl
961 << "This may cause database conflicts with persisted values"
963 << "If this error is the same as the old one, you should"
965 << "use the old value, and if it is different, you should"
967 << "use a new name.";
974 << "Error \"" << cur_reason.name << "\" not found in historical list."
980 class HistoryTest : public testing::Test {
983 : got_thumbnail_callback_(false),
984 query_url_success_(false) {
987 ~HistoryTest() override {}
989 void OnMostVisitedURLsAvailable(const MostVisitedURLList* url_list) {
990 most_visited_urls_ = *url_list;
991 base::MessageLoop::current()->Quit();
995 friend class BackendDelegate;
998 void SetUp() override {
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();
1009 void TearDown() override {
1010 if (history_service_)
1011 CleanupHistoryService();
1013 // Make sure we don't have any event pending that could disrupt the next
1015 base::MessageLoop::current()->PostTask(FROM_HERE,
1016 base::MessageLoop::QuitClosure());
1017 base::MessageLoop::current()->Run();
1020 void CleanupHistoryService() {
1021 DCHECK(history_service_);
1023 history_service_->ClearCachedDataForContextID(0);
1024 history_service_->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
1025 history_service_->Cleanup();
1026 history_service_.reset();
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();
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(
1042 base::Bind(&HistoryTest::SaveURLAndQuit, base::Unretained(this)),
1044 base::MessageLoop::current()->Run(); // Will be exited in SaveURLAndQuit.
1045 return query_url_success_;
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;
1057 query_url_row_ = URLRow();
1058 query_url_visits_.clear();
1060 base::MessageLoop::current()->Quit();
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(
1068 base::Bind(&HistoryTest::OnRedirectQueryComplete,
1069 base::Unretained(this)),
1071 base::MessageLoop::current()->Run(); // Will be exited in *QueryComplete.
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());
1081 base::MessageLoop::current()->Quit();
1084 base::ScopedTempDir temp_dir_;
1086 base::MessageLoopForUI message_loop_;
1088 MostVisitedURLList most_visited_urls_;
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_;
1095 // names of the database files
1096 base::FilePath history_dir_;
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_;
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_;
1107 // For history requests.
1108 base::CancelableTaskTracker tracker_;
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_;
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.
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.
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]));
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);
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.
1167 // The second page should be a server redirect type with a referrer of the
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);
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]);
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);
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());
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);
1214 TEST_F(HistoryTest, MakeIntranetURLsTyped) {
1215 ASSERT_TRUE(history_service_.get());
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));
1231 // Add more visits on the same host. None of these should be promoted since
1232 // there is already a typed visit.
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));
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));
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));
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));
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));
1300 TEST_F(HistoryTest, Typed) {
1301 const ContextID context_id = reinterpret_cast<ContextID>(1);
1303 ASSERT_TRUE(history_service_.get());
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));
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());
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));
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());
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));
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());
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));
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());
1351 TEST_F(HistoryTest, SetTitle) {
1352 ASSERT_TRUE(history_service_.get());
1355 const GURL existing_url("http://www.google.com/");
1356 history_service_->AddPage(
1357 existing_url, base::Time::Now(), history::SOURCE_BROWSED);
1360 const base::string16 existing_title = base::UTF8ToUTF16("Google");
1361 history_service_->SetPageTitle(existing_url, existing_title);
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());
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);
1372 // Make sure nothing got written.
1373 EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url));
1374 EXPECT_EQ(base::string16(), query_url_row_.title());
1376 // TODO(brettw) this should also test redirects, which get the title of the
1377 // destination page.
1380 TEST_F(HistoryTest, MostVisitedURLs) {
1381 ASSERT_TRUE(history_service_.get());
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/");
1389 const ContextID context_id = reinterpret_cast<ContextID>(1);
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(
1403 base::Bind(&HistoryTest::OnMostVisitedURLsAvailable,
1404 base::Unretained(this)),
1406 base::MessageLoop::current()->Run();
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);
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(
1420 base::Bind(&HistoryTest::OnMostVisitedURLsAvailable,
1421 base::Unretained(this)),
1423 base::MessageLoop::current()->Run();
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);
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(
1438 base::Bind(&HistoryTest::OnMostVisitedURLsAvailable,
1439 base::Unretained(this)),
1441 base::MessageLoop::current()->Run();
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);
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(
1456 base::Bind(&HistoryTest::OnMostVisitedURLsAvailable,
1457 base::Unretained(this)),
1459 base::MessageLoop::current()->Run();
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);
1467 history::RedirectList redirects;
1468 redirects.push_back(url3);
1469 redirects.push_back(url4);
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(
1479 base::Bind(&HistoryTest::OnMostVisitedURLsAvailable,
1480 base::Unretained(this)),
1482 base::MessageLoop::current()->Run();
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());
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
1499 class HistoryDBTaskImpl : public HistoryDBTask {
1501 static const int kWantInvokeCount;
1503 HistoryDBTaskImpl(int* invoke_count, bool* done_invoked)
1504 : invoke_count_(invoke_count), done_invoked_(done_invoked) {}
1506 bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override {
1507 return (++*invoke_count_ == kWantInvokeCount);
1510 void DoneRunOnMainThread() override {
1511 *done_invoked_ = true;
1512 base::MessageLoop::current()->Quit();
1516 bool* done_invoked_;
1519 ~HistoryDBTaskImpl() override {}
1521 DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
1525 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
1529 TEST_F(HistoryTest, HistoryDBTask) {
1530 ASSERT_TRUE(history_service_.get());
1531 base::CancelableTaskTracker task_tracker;
1532 int invoke_count = 0;
1533 bool done_invoked = false;
1534 history_service_->ScheduleDBTask(
1535 scoped_ptr<history::HistoryDBTask>(
1536 new HistoryDBTaskImpl(&invoke_count, &done_invoked)),
1538 // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
1539 // it will stop the message loop. If the test hangs here, it means
1540 // DoneRunOnMainThread isn't being invoked correctly.
1541 base::MessageLoop::current()->Run();
1542 CleanupHistoryService();
1543 // WARNING: history has now been deleted.
1544 history_service_.reset();
1545 ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, invoke_count);
1546 ASSERT_TRUE(done_invoked);
1549 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
1550 ASSERT_TRUE(history_service_.get());
1551 base::CancelableTaskTracker task_tracker;
1552 int invoke_count = 0;
1553 bool done_invoked = false;
1554 history_service_->ScheduleDBTask(
1555 scoped_ptr<history::HistoryDBTask>(
1556 new HistoryDBTaskImpl(&invoke_count, &done_invoked)),
1558 task_tracker.TryCancelAll();
1559 CleanupHistoryService();
1560 // WARNING: history has now been deleted.
1561 history_service_.reset();
1562 ASSERT_FALSE(done_invoked);
1565 // Create a local delete directive and process it while sync is
1566 // online, and then when offline. The delete directive should be sent to sync,
1567 // no error should be returned for the first time, and an error should be
1568 // returned for the second time.
1569 TEST_F(HistoryTest, ProcessLocalDeleteDirectiveSyncOnline) {
1570 ASSERT_TRUE(history_service_.get());
1572 const GURL test_url("http://www.google.com/");
1573 for (int64 i = 1; i <= 10; ++i) {
1575 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1576 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1577 history::RedirectList(),
1578 ui::PAGE_TRANSITION_LINK,
1579 history::SOURCE_BROWSED, false);
1582 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
1583 sync_pb::GlobalIdDirective* global_id_directive =
1584 delete_directive.mutable_global_id_directive();
1585 global_id_directive->add_global_id(
1586 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1))
1587 .ToInternalValue());
1589 syncer::FakeSyncChangeProcessor change_processor;
1592 history_service_->MergeDataAndStartSyncing(
1593 syncer::HISTORY_DELETE_DIRECTIVES,
1594 syncer::SyncDataList(),
1595 scoped_ptr<syncer::SyncChangeProcessor>(
1596 new syncer::SyncChangeProcessorWrapperForTest(
1597 &change_processor)),
1598 scoped_ptr<syncer::SyncErrorFactory>())
1602 syncer::SyncError err =
1603 history_service_->ProcessLocalDeleteDirective(delete_directive);
1604 EXPECT_FALSE(err.IsSet());
1605 EXPECT_EQ(1u, change_processor.changes().size());
1607 history_service_->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES);
1608 err = history_service_->ProcessLocalDeleteDirective(delete_directive);
1609 EXPECT_TRUE(err.IsSet());
1610 EXPECT_EQ(1u, change_processor.changes().size());
1613 // Closure function that runs periodically to check result of delete directive
1614 // processing. Stop when timeout or processing ends indicated by the creation
1616 void CheckDirectiveProcessingResult(
1618 const syncer::FakeSyncChangeProcessor* change_processor,
1619 uint32 num_changes) {
1620 if (base::Time::Now() > timeout ||
1621 change_processor->changes().size() >= num_changes) {
1625 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
1626 base::MessageLoop::current()->PostTask(
1628 base::Bind(&CheckDirectiveProcessingResult, timeout,
1629 change_processor, num_changes));
1632 // Create a delete directive for a few specific history entries,
1633 // including ones that don't exist. The expected entries should be
1635 TEST_F(HistoryTest, ProcessGlobalIdDeleteDirective) {
1636 ASSERT_TRUE(history_service_.get());
1637 const GURL test_url("http://www.google.com/");
1638 for (int64 i = 1; i <= 20; i++) {
1640 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1641 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1642 history::RedirectList(),
1643 ui::PAGE_TRANSITION_LINK,
1644 history::SOURCE_BROWSED, false);
1647 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1648 EXPECT_EQ(20, query_url_row_.visit_count());
1650 syncer::SyncDataList directives;
1652 sync_pb::EntitySpecifics entity_specs;
1653 sync_pb::GlobalIdDirective* global_id_directive =
1654 entity_specs.mutable_history_delete_directive()
1655 ->mutable_global_id_directive();
1656 global_id_directive->add_global_id(
1657 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6))
1658 .ToInternalValue());
1659 global_id_directive->set_start_time_usec(3);
1660 global_id_directive->set_end_time_usec(10);
1661 directives.push_back(syncer::SyncData::CreateRemoteData(
1665 syncer::AttachmentIdList(),
1666 syncer::AttachmentServiceProxyForTest::Create()));
1669 global_id_directive->Clear();
1670 global_id_directive->add_global_id(
1671 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17))
1672 .ToInternalValue());
1673 global_id_directive->set_start_time_usec(13);
1674 global_id_directive->set_end_time_usec(19);
1675 directives.push_back(syncer::SyncData::CreateRemoteData(
1679 syncer::AttachmentIdList(),
1680 syncer::AttachmentServiceProxyForTest::Create()));
1682 syncer::FakeSyncChangeProcessor change_processor;
1684 history_service_->MergeDataAndStartSyncing(
1685 syncer::HISTORY_DELETE_DIRECTIVES,
1687 scoped_ptr<syncer::SyncChangeProcessor>(
1688 new syncer::SyncChangeProcessorWrapperForTest(
1689 &change_processor)),
1690 scoped_ptr<syncer::SyncErrorFactory>())
1694 // Inject a task to check status and keep message loop filled before directive
1695 // processing finishes.
1696 base::MessageLoop::current()->PostTask(
1698 base::Bind(&CheckDirectiveProcessingResult,
1699 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1700 &change_processor, 2));
1701 base::MessageLoop::current()->RunUntilIdle();
1702 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1703 ASSERT_EQ(5, query_url_row_.visit_count());
1704 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1705 query_url_visits_[0].visit_time);
1706 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(2),
1707 query_url_visits_[1].visit_time);
1708 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(11),
1709 query_url_visits_[2].visit_time);
1710 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(12),
1711 query_url_visits_[3].visit_time);
1712 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(20),
1713 query_url_visits_[4].visit_time);
1715 // Expect two sync changes for deleting processed directives.
1716 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1717 ASSERT_EQ(2u, sync_changes.size());
1718 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1719 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1720 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1721 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1724 // Create delete directives for time ranges. The expected entries should be
1726 TEST_F(HistoryTest, ProcessTimeRangeDeleteDirective) {
1727 ASSERT_TRUE(history_service_.get());
1728 const GURL test_url("http://www.google.com/");
1729 for (int64 i = 1; i <= 10; ++i) {
1731 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1732 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1733 history::RedirectList(),
1734 ui::PAGE_TRANSITION_LINK,
1735 history::SOURCE_BROWSED, false);
1738 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1739 EXPECT_EQ(10, query_url_row_.visit_count());
1741 syncer::SyncDataList directives;
1743 sync_pb::EntitySpecifics entity_specs;
1744 sync_pb::TimeRangeDirective* time_range_directive =
1745 entity_specs.mutable_history_delete_directive()
1746 ->mutable_time_range_directive();
1747 time_range_directive->set_start_time_usec(2);
1748 time_range_directive->set_end_time_usec(5);
1749 directives.push_back(syncer::SyncData::CreateRemoteData(
1753 syncer::AttachmentIdList(),
1754 syncer::AttachmentServiceProxyForTest::Create()));
1757 time_range_directive->Clear();
1758 time_range_directive->set_start_time_usec(8);
1759 time_range_directive->set_end_time_usec(10);
1760 directives.push_back(syncer::SyncData::CreateRemoteData(
1764 syncer::AttachmentIdList(),
1765 syncer::AttachmentServiceProxyForTest::Create()));
1767 syncer::FakeSyncChangeProcessor change_processor;
1769 history_service_->MergeDataAndStartSyncing(
1770 syncer::HISTORY_DELETE_DIRECTIVES,
1772 scoped_ptr<syncer::SyncChangeProcessor>(
1773 new syncer::SyncChangeProcessorWrapperForTest(
1774 &change_processor)),
1775 scoped_ptr<syncer::SyncErrorFactory>())
1779 // Inject a task to check status and keep message loop filled before
1780 // directive processing finishes.
1781 base::MessageLoop::current()->PostTask(
1783 base::Bind(&CheckDirectiveProcessingResult,
1784 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1785 &change_processor, 2));
1786 base::MessageLoop::current()->RunUntilIdle();
1787 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1788 ASSERT_EQ(3, query_url_row_.visit_count());
1789 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1790 query_url_visits_[0].visit_time);
1791 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6),
1792 query_url_visits_[1].visit_time);
1793 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(7),
1794 query_url_visits_[2].visit_time);
1796 // Expect two sync changes for deleting processed directives.
1797 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1798 ASSERT_EQ(2u, sync_changes.size());
1799 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1800 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1801 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1802 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1805 TEST_F(HistoryBackendDBTest, MigratePresentations) {
1806 // Create the db we want. Use 22 since segments didn't change in that time
1808 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
1810 const SegmentID segment_id = 2;
1811 const URLID url_id = 3;
1812 const GURL url("http://www.foo.com");
1813 const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
1814 const base::string16 title(base::ASCIIToUTF16("Title1"));
1815 const Time segment_time(Time::Now());
1818 // Re-open the db for manual manipulation.
1820 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
1822 // Add an entry to urls.
1824 sql::Statement s(db.GetUniqueStatement(
1826 "(id, url, title, last_visit_time) VALUES "
1828 s.BindInt64(0, url_id);
1829 s.BindString(1, url.spec());
1830 s.BindString16(2, title);
1831 s.BindInt64(3, segment_time.ToInternalValue());
1832 ASSERT_TRUE(s.Run());
1835 // Add an entry to segments.
1837 sql::Statement s(db.GetUniqueStatement(
1838 "INSERT INTO segments "
1839 "(id, name, url_id, pres_index) VALUES "
1841 s.BindInt64(0, segment_id);
1842 s.BindString(1, url_name);
1843 s.BindInt64(2, url_id);
1844 s.BindInt(3, 4); // pres_index
1845 ASSERT_TRUE(s.Run());
1848 // And one to segment_usage.
1850 sql::Statement s(db.GetUniqueStatement(
1851 "INSERT INTO segment_usage "
1852 "(id, segment_id, time_slot, visit_count) VALUES "
1854 s.BindInt64(0, 4); // id.
1855 s.BindInt64(1, segment_id);
1856 s.BindInt64(2, segment_time.ToInternalValue());
1857 s.BindInt(3, 5); // visit count.
1858 ASSERT_TRUE(s.Run());
1862 // Re-open the db, triggering migration.
1863 CreateBackendAndDatabase();
1865 std::vector<PageUsageData*> results;
1866 db_->QuerySegmentUsage(segment_time, 10, &results);
1867 ASSERT_EQ(1u, results.size());
1868 EXPECT_EQ(url, results[0]->GetURL());
1869 EXPECT_EQ(segment_id, results[0]->GetID());
1870 EXPECT_EQ(title, results[0]->GetTitle());
1871 STLDeleteElements(&results);
1874 } // namespace history