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/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_database.h"
53 #include "chrome/browser/history/in_memory_history_backend.h"
54 #include "chrome/browser/history/page_usage_data.h"
55 #include "chrome/common/chrome_constants.h"
56 #include "chrome/common/chrome_paths.h"
57 #include "chrome/tools/profiles/thumbnail-inl.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/attachments/attachment_service_proxy_for_test.h"
66 #include "sync/api/fake_sync_change_processor.h"
67 #include "sync/api/sync_change.h"
68 #include "sync/api/sync_change_processor.h"
69 #include "sync/api/sync_change_processor_wrapper_for_test.h"
70 #include "sync/api/sync_error.h"
71 #include "sync/api/sync_error_factory.h"
72 #include "sync/api/sync_merge_result.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 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
97 virtual void SetInMemoryBackend(
98 scoped_ptr<InMemoryHistoryBackend> backend) OVERRIDE;
99 virtual void BroadcastNotifications(
101 scoped_ptr<HistoryDetails> details) OVERRIDE;
102 virtual void DBLoaded() OVERRIDE {}
103 virtual void NotifyVisitDBObserversOnAddVisit(
104 const BriefVisitInfo& info) OVERRIDE {}
106 HistoryBackendDBTest* history_test_;
109 // This must be outside the anonymous namespace for the friend statement in
110 // HistoryBackend to work.
111 class HistoryBackendDBTest : public HistoryUnitTestBase {
113 HistoryBackendDBTest() : db_(NULL) {
116 virtual ~HistoryBackendDBTest() {
120 friend class BackendDelegate;
122 // Creates the HistoryBackend and HistoryDatabase on the current thread,
123 // assigning the values to backend_ and db_.
124 void CreateBackendAndDatabase() {
126 new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
127 backend_->Init(std::string(), false);
128 db_ = backend_->db_.get();
129 DCHECK(in_mem_backend_) << "Mem backend should have been set by "
130 "HistoryBackend::Init";
133 void CreateDBVersion(int version) {
134 base::FilePath data_path;
135 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
136 data_path = data_path.AppendASCII("History");
138 data_path.AppendASCII(base::StringPrintf("history.%d.sql", version));
139 ASSERT_NO_FATAL_FAILURE(
140 ExecuteSQLScript(data_path, history_dir_.Append(
141 chrome::kHistoryFilename)));
144 void CreateArchivedDB() {
145 base::FilePath data_path;
146 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
147 data_path = data_path.AppendASCII("History");
148 data_path = data_path.AppendASCII("archived_history.4.sql");
149 ASSERT_NO_FATAL_FAILURE(
150 ExecuteSQLScript(data_path, history_dir_.Append(
151 chrome::kArchivedHistoryFilename)));
155 virtual void SetUp() {
156 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
157 history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBTest");
158 ASSERT_TRUE(base::CreateDirectory(history_dir_));
161 void DeleteBackend() {
162 if (backend_.get()) {
168 virtual void TearDown() {
171 // Make sure we don't have any event pending that could disrupt the next
173 base::MessageLoop::current()->PostTask(FROM_HERE,
174 base::MessageLoop::QuitClosure());
175 base::MessageLoop::current()->Run();
178 bool AddDownload(uint32 id,
179 DownloadItem::DownloadState state,
181 std::vector<GURL> url_chain;
182 url_chain.push_back(GURL("foo-url"));
184 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("current-path")),
185 base::FilePath(FILE_PATH_LITERAL("target-path")),
187 GURL("http://referrer.com/"),
188 "application/vnd.oasis.opendocument.text",
189 "application/octet-stream",
197 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
198 content::DOWNLOAD_INTERRUPT_REASON_NONE,
203 return db_->CreateDownload(download);
206 base::ScopedTempDir temp_dir_;
208 base::MessageLoopForUI message_loop_;
210 // names of the database files
211 base::FilePath history_dir_;
213 // Created via CreateBackendAndDatabase.
214 scoped_refptr<HistoryBackend> backend_;
215 scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
216 HistoryDatabase* db_; // Cached reference to the backend's database.
219 void BackendDelegate::SetInMemoryBackend(
220 scoped_ptr<InMemoryHistoryBackend> backend) {
221 // Save the in-memory backend to the history test object, this happens
222 // synchronously, so we don't have to do anything fancy.
223 history_test_->in_mem_backend_.swap(backend);
226 void BackendDelegate::BroadcastNotifications(
228 scoped_ptr<HistoryDetails> details) {
229 // Currently, just send the notifications directly to the in-memory database.
230 // We may want do do something more fancy in the future.
231 content::Details<HistoryDetails> det(details.get());
232 history_test_->in_mem_backend_->Observe(type,
233 content::Source<HistoryBackendDBTest>(NULL), det);
236 TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
237 CreateBackendAndDatabase();
239 // Initially there should be nothing in the downloads database.
240 std::vector<DownloadRow> downloads;
241 db_->QueryDownloads(&downloads);
242 EXPECT_EQ(0U, downloads.size());
244 // Add a download, test that it was added correctly, remove it, test that it
248 EXPECT_TRUE(AddDownload(id, DownloadItem::COMPLETE, Time()));
249 db_->QueryDownloads(&downloads);
250 EXPECT_EQ(1U, downloads.size());
252 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
253 downloads[0].current_path);
254 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
255 downloads[0].target_path);
256 EXPECT_EQ(1UL, downloads[0].url_chain.size());
257 EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
258 EXPECT_EQ(std::string("http://referrer.com/"),
259 std::string(downloads[0].referrer_url.spec()));
260 EXPECT_EQ(now, downloads[0].start_time);
261 EXPECT_EQ(now, downloads[0].end_time);
262 EXPECT_EQ(0, downloads[0].received_bytes);
263 EXPECT_EQ(512, downloads[0].total_bytes);
264 EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
265 EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
266 downloads[0].danger_type);
267 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
268 downloads[0].interrupt_reason);
269 EXPECT_FALSE(downloads[0].opened);
270 EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
271 EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
272 EXPECT_EQ("application/vnd.oasis.opendocument.text", downloads[0].mime_type);
273 EXPECT_EQ("application/octet-stream", downloads[0].original_mime_type);
275 db_->QueryDownloads(&downloads);
276 EXPECT_EQ(1U, downloads.size());
277 db_->RemoveDownload(id);
278 db_->QueryDownloads(&downloads);
279 EXPECT_EQ(0U, downloads.size());
282 TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
283 // Create the db we want.
284 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
286 // Open the db for manual manipulation.
288 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
290 // Manually insert corrupted rows; there's infrastructure in place now to
291 // make this impossible, at least according to the test above.
292 for (int state = 0; state < 5; ++state) {
293 sql::Statement s(db.GetUniqueStatement(
294 "INSERT INTO downloads (id, full_path, url, start_time, "
295 "received_bytes, total_bytes, state, end_time, opened) VALUES "
296 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
297 s.BindInt64(0, 1 + state);
298 s.BindString(1, "path");
299 s.BindString(2, "url");
300 s.BindInt64(3, base::Time::Now().ToTimeT());
304 s.BindInt64(7, base::Time::Now().ToTimeT());
305 s.BindInt(8, state % 2);
306 ASSERT_TRUE(s.Run());
310 // Re-open the db using the HistoryDatabase, which should migrate from version
311 // 22 to the current version, fixing just the row whose state was 3.
312 // Then close the db so that we can re-open it directly.
313 CreateBackendAndDatabase();
316 // Re-open the db for manual manipulation.
318 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
320 // The version should have been updated.
321 int cur_version = HistoryDatabase::GetCurrentVersion();
322 ASSERT_LT(22, cur_version);
323 sql::Statement s(db.GetUniqueStatement(
324 "SELECT value FROM meta WHERE key = 'version'"));
325 EXPECT_TRUE(s.Step());
326 EXPECT_EQ(cur_version, s.ColumnInt(0));
329 sql::Statement statement(db.GetUniqueStatement(
330 "SELECT id, state, opened "
334 while (statement.Step()) {
335 EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
336 // The only thing that migration should have changed was state from 3 to
338 EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
339 EXPECT_EQ(counter % 2, statement.ColumnInt(2));
342 EXPECT_EQ(5, counter);
347 TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
348 Time now(base::Time::Now());
350 // Create the db we want. The schema didn't change from 22->23, so just
351 // re-use the v22 file.
352 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
354 // Re-open the db for manual manipulation.
356 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
358 // Manually insert some rows.
359 sql::Statement s(db.GetUniqueStatement(
360 "INSERT INTO downloads (id, full_path, url, start_time, "
361 "received_bytes, total_bytes, state, end_time, opened) VALUES "
362 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
366 s.BindInt64(0, ++id);
367 s.BindString(1, std::string());
368 s.BindString(2, "http://whatever.com/index.html");
369 s.BindInt64(3, now.ToTimeT());
373 s.BindInt64(7, now.ToTimeT());
375 ASSERT_TRUE(s.Run());
379 s.BindInt64(0, ++id);
380 s.BindString(1, "/path/to/some/file");
381 s.BindString(2, "http://whatever.com/index1.html");
382 s.BindInt64(3, now.ToTimeT());
386 s.BindInt64(7, now.ToTimeT());
388 ASSERT_TRUE(s.Run());
391 // Re-open the db using the HistoryDatabase, which should migrate from version
392 // 23 to 24, creating the new tables and creating the new path, reason,
393 // and danger columns.
394 CreateBackendAndDatabase();
397 // Re-open the db for manual manipulation.
399 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
401 // The version should have been updated.
402 int cur_version = HistoryDatabase::GetCurrentVersion();
403 ASSERT_LT(23, cur_version);
404 sql::Statement s(db.GetUniqueStatement(
405 "SELECT value FROM meta WHERE key = 'version'"));
406 EXPECT_TRUE(s.Step());
407 EXPECT_EQ(cur_version, s.ColumnInt(0));
410 base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
412 // Confirm downloads table is valid.
413 sql::Statement statement(db.GetUniqueStatement(
414 "SELECT id, interrupt_reason, current_path, target_path, "
415 " danger_type, start_time, end_time "
416 "FROM downloads ORDER BY id"));
417 EXPECT_TRUE(statement.Step());
418 EXPECT_EQ(1, statement.ColumnInt64(0));
419 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
420 statement.ColumnInt(1));
421 EXPECT_EQ("", statement.ColumnString(2));
422 EXPECT_EQ("", statement.ColumnString(3));
423 // Implicit dependence on value of kDangerTypeNotDangerous from
424 // download_database.cc.
425 EXPECT_EQ(0, statement.ColumnInt(4));
426 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
427 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
429 EXPECT_TRUE(statement.Step());
430 EXPECT_EQ(2, statement.ColumnInt64(0));
431 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
432 statement.ColumnInt(1));
433 EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
434 EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
435 EXPECT_EQ(0, statement.ColumnInt(4));
436 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
437 EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
439 EXPECT_FALSE(statement.Step());
442 // Confirm downloads_url_chains table is valid.
443 sql::Statement statement(db.GetUniqueStatement(
444 "SELECT id, chain_index, url FROM downloads_url_chains "
445 " ORDER BY id, chain_index"));
446 EXPECT_TRUE(statement.Step());
447 EXPECT_EQ(1, statement.ColumnInt64(0));
448 EXPECT_EQ(0, statement.ColumnInt(1));
449 EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
451 EXPECT_TRUE(statement.Step());
452 EXPECT_EQ(2, statement.ColumnInt64(0));
453 EXPECT_EQ(0, statement.ColumnInt(1));
454 EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
456 EXPECT_FALSE(statement.Step());
461 TEST_F(HistoryBackendDBTest, MigrateReferrer) {
462 Time now(base::Time::Now());
463 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
466 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
467 sql::Statement s(db.GetUniqueStatement(
468 "INSERT INTO downloads (id, full_path, url, start_time, "
469 "received_bytes, total_bytes, state, end_time, opened) VALUES "
470 "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
472 s.BindInt64(0, ++db_handle);
473 s.BindString(1, "full_path");
474 s.BindString(2, "http://whatever.com/index.html");
475 s.BindInt64(3, now.ToTimeT());
479 s.BindInt64(7, now.ToTimeT());
481 ASSERT_TRUE(s.Run());
483 // Re-open the db using the HistoryDatabase, which should migrate to version
484 // 26, creating the referrer column.
485 CreateBackendAndDatabase();
488 // Re-open the db for manual manipulation.
490 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
491 // The version should have been updated.
492 int cur_version = HistoryDatabase::GetCurrentVersion();
493 ASSERT_LE(26, cur_version);
495 sql::Statement s(db.GetUniqueStatement(
496 "SELECT value FROM meta WHERE key = 'version'"));
497 EXPECT_TRUE(s.Step());
498 EXPECT_EQ(cur_version, s.ColumnInt(0));
501 sql::Statement s(db.GetUniqueStatement(
502 "SELECT referrer from downloads"));
503 EXPECT_TRUE(s.Step());
504 EXPECT_EQ(std::string(), s.ColumnString(0));
509 TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
510 Time now(base::Time::Now());
511 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
514 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
516 sql::Statement s(db.GetUniqueStatement(
517 "INSERT INTO downloads (id, current_path, target_path, start_time, "
518 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
519 "end_time, opened, referrer) VALUES "
520 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
522 s.BindString(1, "current_path");
523 s.BindString(2, "target_path");
524 s.BindInt64(3, now.ToTimeT());
530 s.BindInt64(9, now.ToTimeT());
532 s.BindString(11, "referrer");
533 ASSERT_TRUE(s.Run());
536 sql::Statement s(db.GetUniqueStatement(
537 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
541 s.BindString(2, "url");
542 ASSERT_TRUE(s.Run());
545 // Re-open the db using the HistoryDatabase, which should migrate to version
546 // 27, creating the by_ext_id and by_ext_name columns.
547 CreateBackendAndDatabase();
550 // Re-open the db for manual manipulation.
552 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
553 // The version should have been updated.
554 int cur_version = HistoryDatabase::GetCurrentVersion();
555 ASSERT_LE(27, cur_version);
557 sql::Statement s(db.GetUniqueStatement(
558 "SELECT value FROM meta WHERE key = 'version'"));
559 EXPECT_TRUE(s.Step());
560 EXPECT_EQ(cur_version, s.ColumnInt(0));
563 sql::Statement s(db.GetUniqueStatement(
564 "SELECT by_ext_id, by_ext_name from downloads"));
565 EXPECT_TRUE(s.Step());
566 EXPECT_EQ(std::string(), s.ColumnString(0));
567 EXPECT_EQ(std::string(), s.ColumnString(1));
572 TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
573 Time now(base::Time::Now());
574 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
577 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
579 sql::Statement s(db.GetUniqueStatement(
580 "INSERT INTO downloads (id, current_path, target_path, start_time, "
581 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
582 "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
583 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
585 s.BindString(1, "current_path");
586 s.BindString(2, "target_path");
587 s.BindInt64(3, now.ToTimeT());
593 s.BindInt64(9, now.ToTimeT());
595 s.BindString(11, "referrer");
596 s.BindString(12, "by extension ID");
597 s.BindString(13, "by extension name");
598 ASSERT_TRUE(s.Run());
601 sql::Statement s(db.GetUniqueStatement(
602 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
606 s.BindString(2, "url");
607 ASSERT_TRUE(s.Run());
610 // Re-open the db using the HistoryDatabase, which should migrate to the
611 // current version, creating the etag and last_modified columns.
612 CreateBackendAndDatabase();
615 // Re-open the db for manual manipulation.
617 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
618 // The version should have been updated.
619 int cur_version = HistoryDatabase::GetCurrentVersion();
620 ASSERT_LE(28, cur_version);
622 sql::Statement s(db.GetUniqueStatement(
623 "SELECT value FROM meta WHERE key = 'version'"));
624 EXPECT_TRUE(s.Step());
625 EXPECT_EQ(cur_version, s.ColumnInt(0));
628 sql::Statement s(db.GetUniqueStatement(
629 "SELECT etag, last_modified from downloads"));
630 EXPECT_TRUE(s.Step());
631 EXPECT_EQ(std::string(), s.ColumnString(0));
632 EXPECT_EQ(std::string(), s.ColumnString(1));
637 TEST_F(HistoryBackendDBTest, PurgeArchivedDatabase) {
638 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
639 ASSERT_NO_FATAL_FAILURE(CreateArchivedDB());
641 ASSERT_TRUE(base::PathExists(
642 history_dir_.Append(chrome::kArchivedHistoryFilename)));
644 CreateBackendAndDatabase();
647 // We do not retain expired history entries in an archived database as of M37.
648 // Verify that any legacy archived database is deleted on start-up.
649 ASSERT_FALSE(base::PathExists(
650 history_dir_.Append(chrome::kArchivedHistoryFilename)));
653 TEST_F(HistoryBackendDBTest, MigrateDownloadMimeType) {
654 Time now(base::Time::Now());
655 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(28));
658 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
660 sql::Statement s(db.GetUniqueStatement(
661 "INSERT INTO downloads (id, current_path, target_path, start_time, "
662 "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
663 "end_time, opened, referrer, by_ext_id, by_ext_name, etag, "
664 "last_modified) VALUES "
665 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
667 s.BindString(1, "current_path");
668 s.BindString(2, "target_path");
669 s.BindInt64(3, now.ToTimeT());
675 s.BindInt64(9, now.ToTimeT());
677 s.BindString(11, "referrer");
678 s.BindString(12, "by extension ID");
679 s.BindString(13, "by extension name");
680 s.BindString(14, "etag");
681 s.BindInt64(15, now.ToTimeT());
682 ASSERT_TRUE(s.Run());
685 sql::Statement s(db.GetUniqueStatement(
686 "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
690 s.BindString(2, "url");
691 ASSERT_TRUE(s.Run());
694 // Re-open the db using the HistoryDatabase, which should migrate to the
695 // current version, creating themime_type abd original_mime_type columns.
696 CreateBackendAndDatabase();
699 // Re-open the db for manual manipulation.
701 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
702 // The version should have been updated.
703 int cur_version = HistoryDatabase::GetCurrentVersion();
704 ASSERT_LE(29, cur_version);
706 sql::Statement s(db.GetUniqueStatement(
707 "SELECT value FROM meta WHERE key = 'version'"));
708 EXPECT_TRUE(s.Step());
709 EXPECT_EQ(cur_version, s.ColumnInt(0));
712 sql::Statement s(db.GetUniqueStatement(
713 "SELECT mime_type, original_mime_type from downloads"));
714 EXPECT_TRUE(s.Step());
715 EXPECT_EQ(std::string(), s.ColumnString(0));
716 EXPECT_EQ(std::string(), s.ColumnString(1));
721 TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
723 CreateBackendAndDatabase();
725 base::Time now(base::Time::Now());
727 // Add some downloads.
728 uint32 id1 = 1, id2 = 2, id3 = 3;
729 AddDownload(id1, DownloadItem::COMPLETE, now);
730 AddDownload(id2, DownloadItem::COMPLETE, now + base::TimeDelta::FromDays(2));
731 AddDownload(id3, DownloadItem::COMPLETE, now - base::TimeDelta::FromDays(2));
733 // Confirm that resulted in the correct number of rows in the DB.
737 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
738 sql::Statement statement(db.GetUniqueStatement(
739 "Select Count(*) from downloads"));
740 EXPECT_TRUE(statement.Step());
741 EXPECT_EQ(3, statement.ColumnInt(0));
743 sql::Statement statement1(db.GetUniqueStatement(
744 "Select Count(*) from downloads_url_chains"));
745 EXPECT_TRUE(statement1.Step());
746 EXPECT_EQ(3, statement1.ColumnInt(0));
749 // Delete some rows and make sure the results are still correct.
750 CreateBackendAndDatabase();
751 db_->RemoveDownload(id2);
752 db_->RemoveDownload(id3);
756 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
757 sql::Statement statement(db.GetUniqueStatement(
758 "Select Count(*) from downloads"));
759 EXPECT_TRUE(statement.Step());
760 EXPECT_EQ(1, statement.ColumnInt(0));
762 sql::Statement statement1(db.GetUniqueStatement(
763 "Select Count(*) from downloads_url_chains"));
764 EXPECT_TRUE(statement1.Step());
765 EXPECT_EQ(1, statement1.ColumnInt(0));
769 TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
770 CreateBackendAndDatabase();
771 base::Time now(base::Time::Now());
772 std::vector<GURL> url_chain;
773 DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
774 base::FilePath(FILE_PATH_LITERAL("foo-path")),
777 "application/octet-stream",
778 "application/octet-stream",
785 DownloadItem::COMPLETE,
786 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
787 content::DOWNLOAD_INTERRUPT_REASON_NONE,
793 // Creating records without any urls should fail.
794 EXPECT_FALSE(db_->CreateDownload(download));
796 download.url_chain.push_back(GURL("foo-url"));
797 EXPECT_TRUE(db_->CreateDownload(download));
799 // Pretend that the URLs were dropped.
803 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
804 sql::Statement statement(db.GetUniqueStatement(
805 "DELETE FROM downloads_url_chains WHERE id=1"));
806 ASSERT_TRUE(statement.Run());
808 CreateBackendAndDatabase();
809 std::vector<DownloadRow> downloads;
810 db_->QueryDownloads(&downloads);
811 EXPECT_EQ(0U, downloads.size());
813 // QueryDownloads should have nuked the corrupt record.
817 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
819 sql::Statement statement(db.GetUniqueStatement(
820 "SELECT count(*) from downloads"));
821 ASSERT_TRUE(statement.Step());
822 EXPECT_EQ(0, statement.ColumnInt(0));
827 TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
829 CreateBackendAndDatabase();
831 base::Time now(base::Time::Now());
833 // Put an IN_PROGRESS download in the DB.
834 AddDownload(1, DownloadItem::IN_PROGRESS, now);
836 // Confirm that they made it into the DB unchanged.
840 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
841 sql::Statement statement(db.GetUniqueStatement(
842 "Select Count(*) from downloads"));
843 EXPECT_TRUE(statement.Step());
844 EXPECT_EQ(1, statement.ColumnInt(0));
846 sql::Statement statement1(db.GetUniqueStatement(
847 "Select state, interrupt_reason from downloads"));
848 EXPECT_TRUE(statement1.Step());
849 EXPECT_EQ(DownloadDatabase::kStateInProgress, statement1.ColumnInt(0));
850 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, statement1.ColumnInt(1));
851 EXPECT_FALSE(statement1.Step());
854 // Read in the DB through query downloads, then test that the
855 // right transformation was returned.
856 CreateBackendAndDatabase();
857 std::vector<DownloadRow> results;
858 db_->QueryDownloads(&results);
859 ASSERT_EQ(1u, results.size());
860 EXPECT_EQ(content::DownloadItem::INTERRUPTED, results[0].state);
861 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
862 results[0].interrupt_reason);
864 // Allow the update to propagate, shut down the DB, and confirm that
865 // the query updated the on disk database as well.
866 base::MessageLoop::current()->RunUntilIdle();
870 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
871 sql::Statement statement(db.GetUniqueStatement(
872 "Select Count(*) from downloads"));
873 EXPECT_TRUE(statement.Step());
874 EXPECT_EQ(1, statement.ColumnInt(0));
876 sql::Statement statement1(db.GetUniqueStatement(
877 "Select state, interrupt_reason from downloads"));
878 EXPECT_TRUE(statement1.Step());
879 EXPECT_EQ(DownloadDatabase::kStateInterrupted, statement1.ColumnInt(0));
880 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
881 statement1.ColumnInt(1));
882 EXPECT_FALSE(statement1.Step());
886 struct InterruptReasonAssociation {
891 // Test is dependent on interrupt reasons being listed in header file
893 const InterruptReasonAssociation current_reasons[] = {
894 #define INTERRUPT_REASON(a, b) { #a, b },
895 #include "content/public/browser/download_interrupt_reason_values.h"
896 #undef INTERRUPT_REASON
899 // This represents a list of all reasons we've previously used;
900 // Do Not Remove Any Entries From This List.
901 const InterruptReasonAssociation historical_reasons[] = {
903 {"FILE_ACCESS_DENIED", 2},
904 {"FILE_NO_SPACE", 3},
905 {"FILE_NAME_TOO_LONG", 5},
906 {"FILE_TOO_LARGE", 6},
907 {"FILE_VIRUS_INFECTED", 7},
908 {"FILE_TRANSIENT_ERROR", 10},
909 {"FILE_BLOCKED", 11},
910 {"FILE_SECURITY_CHECK_FAILED", 12},
911 {"FILE_TOO_SHORT", 13},
912 {"NETWORK_FAILED", 20},
913 {"NETWORK_TIMEOUT", 21},
914 {"NETWORK_DISCONNECTED", 22},
915 {"NETWORK_SERVER_DOWN", 23},
916 {"NETWORK_INVALID_REQUEST", 24},
917 {"SERVER_FAILED", 30},
918 {"SERVER_NO_RANGE", 31},
919 {"SERVER_PRECONDITION", 32},
920 {"SERVER_BAD_CONTENT", 33},
921 {"USER_CANCELED", 40},
922 {"USER_SHUTDOWN", 41},
926 // Make sure no one has changed a DownloadInterruptReason we've previously
928 TEST_F(HistoryBackendDBTest,
929 ConfirmDownloadInterruptReasonBackwardsCompatible) {
930 // Are there any cases in which a historical number has been repurposed
931 // for an error other than it's original?
932 for (size_t i = 0; i < arraysize(current_reasons); i++) {
933 const InterruptReasonAssociation& cur_reason(current_reasons[i]);
936 for (size_t j = 0; j < arraysize(historical_reasons); ++j) {
937 const InterruptReasonAssociation& hist_reason(historical_reasons[j]);
939 if (hist_reason.value == cur_reason.value) {
940 EXPECT_EQ(cur_reason.name, hist_reason.name)
941 << "Same integer value used for old error \""
943 << "\" as for new error \""
945 << "\"." << std::endl
946 << "**This will cause database conflicts with persisted values**"
948 << "Please assign a new, non-conflicting value for the new error.";
951 if (hist_reason.name == cur_reason.name) {
952 EXPECT_EQ(cur_reason.value, hist_reason.value)
953 << "Same name (\"" << hist_reason.name
954 << "\") maps to a different value historically ("
955 << hist_reason.value << ") and currently ("
956 << cur_reason.value << ")" << std::endl
957 << "This may cause database conflicts with persisted values"
959 << "If this error is the same as the old one, you should"
961 << "use the old value, and if it is different, you should"
963 << "use a new name.";
970 << "Error \"" << cur_reason.name << "\" not found in historical list."
976 class HistoryTest : public testing::Test {
979 : got_thumbnail_callback_(false),
980 redirect_query_success_(false),
981 query_url_success_(false) {
984 virtual ~HistoryTest() {
987 void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
988 std::vector<PageUsageData*>* data) {
989 page_usage_data_.swap(*data);
990 base::MessageLoop::current()->Quit();
993 void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) {
994 base::MessageLoop::current()->Quit();
997 void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,
998 MostVisitedURLList url_list) {
999 most_visited_urls_.swap(url_list);
1000 base::MessageLoop::current()->Quit();
1004 friend class BackendDelegate;
1007 virtual void SetUp() {
1008 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1009 history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
1010 ASSERT_TRUE(base::CreateDirectory(history_dir_));
1011 history_service_.reset(new HistoryService);
1012 if (!history_service_->Init(history_dir_)) {
1013 history_service_.reset();
1018 virtual void TearDown() {
1019 if (history_service_)
1020 CleanupHistoryService();
1022 // Make sure we don't have any event pending that could disrupt the next
1024 base::MessageLoop::current()->PostTask(FROM_HERE,
1025 base::MessageLoop::QuitClosure());
1026 base::MessageLoop::current()->Run();
1029 void CleanupHistoryService() {
1030 DCHECK(history_service_);
1032 history_service_->ClearCachedDataForContextID(0);
1033 history_service_->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
1034 history_service_->Cleanup();
1035 history_service_.reset();
1037 // Wait for the backend class to terminate before deleting the files and
1038 // moving to the next test. Note: if this never terminates, somebody is
1039 // probably leaking a reference to the history backend, so it never calls
1040 // our destroy task.
1041 base::MessageLoop::current()->Run();
1044 // Fills the query_url_row_ and query_url_visits_ structures with the
1045 // information about the given URL and returns true. If the URL was not
1046 // found, this will return false and those structures will not be changed.
1047 bool QueryURL(HistoryService* history, const GURL& url) {
1048 history_service_->QueryURL(
1051 base::Bind(&HistoryTest::SaveURLAndQuit, base::Unretained(this)),
1053 base::MessageLoop::current()->Run(); // Will be exited in SaveURLAndQuit.
1054 return query_url_success_;
1057 // Callback for HistoryService::QueryURL.
1058 void SaveURLAndQuit(bool success,
1059 const URLRow& url_row,
1060 const VisitVector& visits) {
1061 query_url_success_ = success;
1062 if (query_url_success_) {
1063 query_url_row_ = url_row;
1064 query_url_visits_ = visits;
1066 query_url_row_ = URLRow();
1067 query_url_visits_.clear();
1069 base::MessageLoop::current()->Quit();
1072 // Fills in saved_redirects_ with the redirect information for the given URL,
1073 // returning true on success. False means the URL was not found.
1074 bool QueryRedirectsFrom(HistoryService* history, const GURL& url) {
1075 history_service_->QueryRedirectsFrom(
1077 base::Bind(&HistoryTest::OnRedirectQueryComplete,
1078 base::Unretained(this)));
1079 base::MessageLoop::current()->Run(); // Will be exited in *QueryComplete.
1080 return redirect_query_success_;
1083 // Callback for QueryRedirects.
1084 void OnRedirectQueryComplete(HistoryService::Handle handle,
1087 history::RedirectList* redirects) {
1088 redirect_query_success_ = success;
1089 if (redirect_query_success_)
1090 saved_redirects_.swap(*redirects);
1092 saved_redirects_.clear();
1093 base::MessageLoop::current()->Quit();
1096 base::ScopedTempDir temp_dir_;
1098 base::MessageLoopForUI message_loop_;
1100 // PageUsageData vector to test segments.
1101 ScopedVector<PageUsageData> page_usage_data_;
1103 MostVisitedURLList most_visited_urls_;
1105 // When non-NULL, this will be deleted on tear down and we will block until
1106 // the backend thread has completed. This allows tests for the history
1107 // service to use this feature, but other tests to ignore this.
1108 scoped_ptr<HistoryService> history_service_;
1110 // names of the database files
1111 base::FilePath history_dir_;
1113 // Set by the thumbnail callback when we get data, you should be sure to
1114 // clear this before issuing a thumbnail request.
1115 bool got_thumbnail_callback_;
1116 std::vector<unsigned char> thumbnail_data_;
1118 // Set by the redirect callback when we get data. You should be sure to
1119 // clear this before issuing a redirect request.
1120 history::RedirectList saved_redirects_;
1121 bool redirect_query_success_;
1123 // For history requests.
1124 base::CancelableTaskTracker tracker_;
1125 CancelableRequestConsumer consumer_;
1127 // For saving URL info after a call to QueryURL
1128 bool query_url_success_;
1129 URLRow query_url_row_;
1130 VisitVector query_url_visits_;
1133 TEST_F(HistoryTest, AddPage) {
1134 ASSERT_TRUE(history_service_.get());
1135 // Add the page once from a child frame.
1136 const GURL test_url("http://www.google.com/");
1137 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1138 history::RedirectList(),
1139 content::PAGE_TRANSITION_MANUAL_SUBFRAME,
1140 history::SOURCE_BROWSED, false);
1141 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1142 EXPECT_EQ(1, query_url_row_.visit_count());
1143 EXPECT_EQ(0, query_url_row_.typed_count());
1144 EXPECT_TRUE(query_url_row_.hidden()); // Hidden because of child frame.
1146 // Add the page once from the main frame (should unhide it).
1147 history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1148 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1149 history::SOURCE_BROWSED, false);
1150 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1151 EXPECT_EQ(2, query_url_row_.visit_count()); // Added twice.
1152 EXPECT_EQ(0, query_url_row_.typed_count()); // Never typed.
1153 EXPECT_FALSE(query_url_row_.hidden()); // Because loaded in main frame.
1156 TEST_F(HistoryTest, AddRedirect) {
1157 ASSERT_TRUE(history_service_.get());
1158 const char* first_sequence[] = {
1159 "http://first.page.com/",
1160 "http://second.page.com/"};
1161 int first_count = arraysize(first_sequence);
1162 history::RedirectList first_redirects;
1163 for (int i = 0; i < first_count; i++)
1164 first_redirects.push_back(GURL(first_sequence[i]));
1166 // Add the sequence of pages as a server with no referrer. Note that we need
1167 // to have a non-NULL page ID scope.
1168 history_service_->AddPage(
1169 first_redirects.back(), base::Time::Now(),
1170 reinterpret_cast<ContextID>(1), 0, GURL(), first_redirects,
1171 content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED, true);
1173 // The first page should be added once with a link visit type (because we set
1174 // LINK when we added the original URL, and a referrer of nowhere (0).
1175 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[0]));
1176 EXPECT_EQ(1, query_url_row_.visit_count());
1177 ASSERT_EQ(1U, query_url_visits_.size());
1178 int64 first_visit = query_url_visits_[0].visit_id;
1179 EXPECT_EQ(content::PAGE_TRANSITION_LINK |
1180 content::PAGE_TRANSITION_CHAIN_START,
1181 query_url_visits_[0].transition);
1182 EXPECT_EQ(0, query_url_visits_[0].referring_visit); // No referrer.
1184 // The second page should be a server redirect type with a referrer of the
1186 EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[1]));
1187 EXPECT_EQ(1, query_url_row_.visit_count());
1188 ASSERT_EQ(1U, query_url_visits_.size());
1189 int64 second_visit = query_url_visits_[0].visit_id;
1190 EXPECT_EQ(content::PAGE_TRANSITION_SERVER_REDIRECT |
1191 content::PAGE_TRANSITION_CHAIN_END,
1192 query_url_visits_[0].transition);
1193 EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit);
1195 // Check that the redirect finding function successfully reports it.
1196 saved_redirects_.clear();
1197 QueryRedirectsFrom(history_service_.get(), first_redirects[0]);
1198 ASSERT_EQ(1U, saved_redirects_.size());
1199 EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
1201 // Now add a client redirect from that second visit to a third, client
1202 // redirects are tracked by the RenderView prior to updating history,
1203 // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
1204 history::RedirectList second_redirects;
1205 second_redirects.push_back(first_redirects[1]);
1206 second_redirects.push_back(GURL("http://last.page.com/"));
1207 history_service_->AddPage(second_redirects[1], base::Time::Now(),
1208 reinterpret_cast<ContextID>(1), 1,
1209 second_redirects[0], second_redirects,
1210 static_cast<content::PageTransition>(
1211 content::PAGE_TRANSITION_LINK |
1212 content::PAGE_TRANSITION_CLIENT_REDIRECT),
1213 history::SOURCE_BROWSED, true);
1215 // The last page (source of the client redirect) should NOT have an
1216 // additional visit added, because it was a client redirect (normally it
1217 // would). We should only have 1 left over from the first sequence.
1218 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[0]));
1219 EXPECT_EQ(1, query_url_row_.visit_count());
1221 // The final page should be set as a client redirect from the previous visit.
1222 EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[1]));
1223 EXPECT_EQ(1, query_url_row_.visit_count());
1224 ASSERT_EQ(1U, query_url_visits_.size());
1225 EXPECT_EQ(content::PAGE_TRANSITION_CLIENT_REDIRECT |
1226 content::PAGE_TRANSITION_CHAIN_END,
1227 query_url_visits_[0].transition);
1228 EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit);
1231 TEST_F(HistoryTest, MakeIntranetURLsTyped) {
1232 ASSERT_TRUE(history_service_.get());
1234 // Add a non-typed visit to an intranet URL on an unvisited host. This should
1235 // get promoted to a typed visit.
1236 const GURL test_url("http://intranet_host/path");
1237 history_service_->AddPage(
1238 test_url, base::Time::Now(), NULL, 0, GURL(),
1239 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1240 history::SOURCE_BROWSED, false);
1241 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1242 EXPECT_EQ(1, query_url_row_.visit_count());
1243 EXPECT_EQ(1, query_url_row_.typed_count());
1244 ASSERT_EQ(1U, query_url_visits_.size());
1245 EXPECT_EQ(content::PAGE_TRANSITION_TYPED,
1246 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1248 // Add more visits on the same host. None of these should be promoted since
1249 // there is already a typed visit.
1252 const GURL test_url2("http://intranet_host/different_path");
1253 history_service_->AddPage(
1254 test_url2, base::Time::Now(), NULL, 0, GURL(),
1255 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1256 history::SOURCE_BROWSED, false);
1257 EXPECT_TRUE(QueryURL(history_service_.get(), test_url2));
1258 EXPECT_EQ(1, query_url_row_.visit_count());
1259 EXPECT_EQ(0, query_url_row_.typed_count());
1260 ASSERT_EQ(1U, query_url_visits_.size());
1261 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1262 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1265 const GURL test_url3("http://intranet_host/");
1266 history_service_->AddPage(
1267 test_url3, base::Time::Now(), NULL, 0, GURL(),
1268 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1269 history::SOURCE_BROWSED, false);
1270 EXPECT_TRUE(QueryURL(history_service_.get(), test_url3));
1271 EXPECT_EQ(1, query_url_row_.visit_count());
1272 EXPECT_EQ(0, query_url_row_.typed_count());
1273 ASSERT_EQ(1U, query_url_visits_.size());
1274 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1275 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1277 // Different scheme.
1278 const GURL test_url4("https://intranet_host/");
1279 history_service_->AddPage(
1280 test_url4, base::Time::Now(), NULL, 0, GURL(),
1281 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1282 history::SOURCE_BROWSED, false);
1283 EXPECT_TRUE(QueryURL(history_service_.get(), test_url4));
1284 EXPECT_EQ(1, query_url_row_.visit_count());
1285 EXPECT_EQ(0, query_url_row_.typed_count());
1286 ASSERT_EQ(1U, query_url_visits_.size());
1287 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1288 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1290 // Different transition.
1291 const GURL test_url5("http://intranet_host/another_path");
1292 history_service_->AddPage(
1293 test_url5, base::Time::Now(), NULL, 0, GURL(),
1294 history::RedirectList(),
1295 content::PAGE_TRANSITION_AUTO_BOOKMARK,
1296 history::SOURCE_BROWSED, false);
1297 EXPECT_TRUE(QueryURL(history_service_.get(), test_url5));
1298 EXPECT_EQ(1, query_url_row_.visit_count());
1299 EXPECT_EQ(0, query_url_row_.typed_count());
1300 ASSERT_EQ(1U, query_url_visits_.size());
1301 EXPECT_EQ(content::PAGE_TRANSITION_AUTO_BOOKMARK,
1302 content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1305 history_service_->AddPage(
1306 test_url, base::Time::Now(), NULL, 0, GURL(),
1307 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1308 history::SOURCE_BROWSED, false);
1309 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1310 EXPECT_EQ(2, query_url_row_.visit_count());
1311 EXPECT_EQ(1, query_url_row_.typed_count());
1312 ASSERT_EQ(2U, query_url_visits_.size());
1313 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1314 content::PageTransitionStripQualifier(query_url_visits_[1].transition));
1317 TEST_F(HistoryTest, Typed) {
1318 ASSERT_TRUE(history_service_.get());
1320 // Add the page once as typed.
1321 const GURL test_url("http://www.google.com/");
1322 history_service_->AddPage(
1323 test_url, base::Time::Now(), NULL, 0, GURL(),
1324 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1325 history::SOURCE_BROWSED, false);
1326 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1328 // We should have the same typed & visit count.
1329 EXPECT_EQ(1, query_url_row_.visit_count());
1330 EXPECT_EQ(1, query_url_row_.typed_count());
1332 // Add the page again not typed.
1333 history_service_->AddPage(
1334 test_url, base::Time::Now(), NULL, 0, GURL(),
1335 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1336 history::SOURCE_BROWSED, false);
1337 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1339 // The second time should not have updated the typed count.
1340 EXPECT_EQ(2, query_url_row_.visit_count());
1341 EXPECT_EQ(1, query_url_row_.typed_count());
1343 // Add the page again as a generated URL.
1344 history_service_->AddPage(
1345 test_url, base::Time::Now(), NULL, 0, GURL(),
1346 history::RedirectList(), content::PAGE_TRANSITION_GENERATED,
1347 history::SOURCE_BROWSED, false);
1348 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1350 // This should have worked like a link click.
1351 EXPECT_EQ(3, query_url_row_.visit_count());
1352 EXPECT_EQ(1, query_url_row_.typed_count());
1354 // Add the page again as a reload.
1355 history_service_->AddPage(
1356 test_url, base::Time::Now(), NULL, 0, GURL(),
1357 history::RedirectList(), content::PAGE_TRANSITION_RELOAD,
1358 history::SOURCE_BROWSED, false);
1359 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1361 // This should not have incremented any visit counts.
1362 EXPECT_EQ(3, query_url_row_.visit_count());
1363 EXPECT_EQ(1, query_url_row_.typed_count());
1366 TEST_F(HistoryTest, SetTitle) {
1367 ASSERT_TRUE(history_service_.get());
1370 const GURL existing_url("http://www.google.com/");
1371 history_service_->AddPage(
1372 existing_url, base::Time::Now(), history::SOURCE_BROWSED);
1375 const base::string16 existing_title = base::UTF8ToUTF16("Google");
1376 history_service_->SetPageTitle(existing_url, existing_title);
1378 // Make sure the title got set.
1379 EXPECT_TRUE(QueryURL(history_service_.get(), existing_url));
1380 EXPECT_EQ(existing_title, query_url_row_.title());
1382 // set a title on a nonexistent page
1383 const GURL nonexistent_url("http://news.google.com/");
1384 const base::string16 nonexistent_title = base::UTF8ToUTF16("Google News");
1385 history_service_->SetPageTitle(nonexistent_url, nonexistent_title);
1387 // Make sure nothing got written.
1388 EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url));
1389 EXPECT_EQ(base::string16(), query_url_row_.title());
1391 // TODO(brettw) this should also test redirects, which get the title of the
1392 // destination page.
1395 // crbug.com/159387: This test fails when daylight savings time ends.
1396 TEST_F(HistoryTest, DISABLED_Segments) {
1397 ASSERT_TRUE(history_service_.get());
1399 static ContextID context_id = static_cast<ContextID>(this);
1402 const GURL existing_url("http://www.google.com/");
1403 history_service_->AddPage(
1404 existing_url, base::Time::Now(), context_id, 0, GURL(),
1405 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1406 history::SOURCE_BROWSED, false);
1408 // Make sure a segment was created.
1409 history_service_->QuerySegmentUsageSince(
1410 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1411 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1412 base::Unretained(this)));
1414 // Wait for processing.
1415 base::MessageLoop::current()->Run();
1417 ASSERT_EQ(1U, page_usage_data_.size());
1418 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1419 EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore());
1421 // Add a URL which doesn't create a segment.
1422 const GURL link_url("http://yahoo.com/");
1423 history_service_->AddPage(
1424 link_url, base::Time::Now(), context_id, 0, GURL(),
1425 history::RedirectList(), content::PAGE_TRANSITION_LINK,
1426 history::SOURCE_BROWSED, false);
1429 history_service_->QuerySegmentUsageSince(
1430 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1431 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1432 base::Unretained(this)));
1434 // Wait for processing.
1435 base::MessageLoop::current()->Run();
1437 // Make sure we still have one segment.
1438 ASSERT_EQ(1U, page_usage_data_.size());
1439 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1441 // Add a page linked from existing_url.
1442 history_service_->AddPage(
1443 GURL("http://www.google.com/foo"), base::Time::Now(),
1444 context_id, 3, existing_url, history::RedirectList(),
1445 content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED,
1449 history_service_->QuerySegmentUsageSince(
1450 &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1451 base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1452 base::Unretained(this)));
1454 // Wait for processing.
1455 base::MessageLoop::current()->Run();
1457 // Make sure we still have one segment.
1458 ASSERT_EQ(1U, page_usage_data_.size());
1459 EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1461 // However, the score should have increased.
1462 EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0);
1465 TEST_F(HistoryTest, MostVisitedURLs) {
1466 ASSERT_TRUE(history_service_.get());
1468 const GURL url0("http://www.google.com/url0/");
1469 const GURL url1("http://www.google.com/url1/");
1470 const GURL url2("http://www.google.com/url2/");
1471 const GURL url3("http://www.google.com/url3/");
1472 const GURL url4("http://www.google.com/url4/");
1474 static ContextID context_id = static_cast<ContextID>(this);
1477 history_service_->AddPage(
1478 url0, base::Time::Now(), context_id, 0, GURL(),
1479 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1480 history::SOURCE_BROWSED, false);
1481 history_service_->AddPage(
1482 url1, base::Time::Now(), context_id, 0, GURL(),
1483 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1484 history::SOURCE_BROWSED, false);
1485 history_service_->QueryMostVisitedURLs(
1488 &HistoryTest::OnMostVisitedURLsAvailable,
1489 base::Unretained(this)));
1490 base::MessageLoop::current()->Run();
1492 EXPECT_EQ(2U, most_visited_urls_.size());
1493 EXPECT_EQ(url0, most_visited_urls_[0].url);
1494 EXPECT_EQ(url1, most_visited_urls_[1].url);
1496 // Add another page.
1497 history_service_->AddPage(
1498 url2, base::Time::Now(), context_id, 0, GURL(),
1499 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1500 history::SOURCE_BROWSED, false);
1501 history_service_->QueryMostVisitedURLs(
1504 &HistoryTest::OnMostVisitedURLsAvailable,
1505 base::Unretained(this)));
1506 base::MessageLoop::current()->Run();
1508 EXPECT_EQ(3U, most_visited_urls_.size());
1509 EXPECT_EQ(url0, most_visited_urls_[0].url);
1510 EXPECT_EQ(url1, most_visited_urls_[1].url);
1511 EXPECT_EQ(url2, most_visited_urls_[2].url);
1513 // Revisit url2, making it the top URL.
1514 history_service_->AddPage(
1515 url2, base::Time::Now(), context_id, 0, GURL(),
1516 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1517 history::SOURCE_BROWSED, false);
1518 history_service_->QueryMostVisitedURLs(
1521 &HistoryTest::OnMostVisitedURLsAvailable,
1522 base::Unretained(this)));
1523 base::MessageLoop::current()->Run();
1525 EXPECT_EQ(3U, most_visited_urls_.size());
1526 EXPECT_EQ(url2, most_visited_urls_[0].url);
1527 EXPECT_EQ(url0, most_visited_urls_[1].url);
1528 EXPECT_EQ(url1, most_visited_urls_[2].url);
1530 // Revisit url1, making it the top URL.
1531 history_service_->AddPage(
1532 url1, base::Time::Now(), context_id, 0, GURL(),
1533 history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1534 history::SOURCE_BROWSED, false);
1535 history_service_->QueryMostVisitedURLs(
1538 &HistoryTest::OnMostVisitedURLsAvailable,
1539 base::Unretained(this)));
1540 base::MessageLoop::current()->Run();
1542 EXPECT_EQ(3U, most_visited_urls_.size());
1543 EXPECT_EQ(url1, most_visited_urls_[0].url);
1544 EXPECT_EQ(url2, most_visited_urls_[1].url);
1545 EXPECT_EQ(url0, most_visited_urls_[2].url);
1548 history::RedirectList redirects;
1549 redirects.push_back(url3);
1550 redirects.push_back(url4);
1552 // Visit url4 using redirects.
1553 history_service_->AddPage(
1554 url4, base::Time::Now(), context_id, 0, GURL(),
1555 redirects, content::PAGE_TRANSITION_TYPED,
1556 history::SOURCE_BROWSED, false);
1557 history_service_->QueryMostVisitedURLs(
1560 &HistoryTest::OnMostVisitedURLsAvailable,
1561 base::Unretained(this)));
1562 base::MessageLoop::current()->Run();
1564 EXPECT_EQ(4U, most_visited_urls_.size());
1565 EXPECT_EQ(url1, most_visited_urls_[0].url);
1566 EXPECT_EQ(url2, most_visited_urls_[1].url);
1567 EXPECT_EQ(url0, most_visited_urls_[2].url);
1568 EXPECT_EQ(url3, most_visited_urls_[3].url);
1569 EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
1574 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
1575 // invoke_count is increment. When invoked kWantInvokeCount times, true is
1576 // returned from RunOnDBThread which should stop RunOnDBThread from being
1577 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
1579 class HistoryDBTaskImpl : public HistoryDBTask {
1581 static const int kWantInvokeCount;
1583 HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {}
1585 virtual bool RunOnDBThread(HistoryBackend* backend,
1586 HistoryDatabase* db) OVERRIDE {
1587 return (++invoke_count == kWantInvokeCount);
1590 virtual void DoneRunOnMainThread() OVERRIDE {
1591 done_invoked = true;
1592 base::MessageLoop::current()->Quit();
1599 virtual ~HistoryDBTaskImpl() {}
1601 DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
1605 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
1609 TEST_F(HistoryTest, HistoryDBTask) {
1610 ASSERT_TRUE(history_service_.get());
1611 CancelableRequestConsumerT<int, 0> request_consumer;
1612 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1613 history_service_->ScheduleDBTask(task.get(), &request_consumer);
1614 // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
1615 // it will stop the message loop. If the test hangs here, it means
1616 // DoneRunOnMainThread isn't being invoked correctly.
1617 base::MessageLoop::current()->Run();
1618 CleanupHistoryService();
1619 // WARNING: history has now been deleted.
1620 history_service_.reset();
1621 ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count);
1622 ASSERT_TRUE(task->done_invoked);
1625 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
1626 ASSERT_TRUE(history_service_.get());
1627 CancelableRequestConsumerT<int, 0> request_consumer;
1628 scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1629 history_service_->ScheduleDBTask(task.get(), &request_consumer);
1630 request_consumer.CancelAllRequests();
1631 CleanupHistoryService();
1632 // WARNING: history has now been deleted.
1633 history_service_.reset();
1634 ASSERT_FALSE(task->done_invoked);
1637 // Create a local delete directive and process it while sync is
1638 // online, and then when offline. The delete directive should be sent to sync,
1639 // no error should be returned for the first time, and an error should be
1640 // returned for the second time.
1641 TEST_F(HistoryTest, ProcessLocalDeleteDirectiveSyncOnline) {
1642 ASSERT_TRUE(history_service_.get());
1644 const GURL test_url("http://www.google.com/");
1645 for (int64 i = 1; i <= 10; ++i) {
1647 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1648 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1649 history::RedirectList(),
1650 content::PAGE_TRANSITION_LINK,
1651 history::SOURCE_BROWSED, false);
1654 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
1655 sync_pb::GlobalIdDirective* global_id_directive =
1656 delete_directive.mutable_global_id_directive();
1657 global_id_directive->add_global_id(
1658 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1))
1659 .ToInternalValue());
1661 syncer::FakeSyncChangeProcessor change_processor;
1664 history_service_->MergeDataAndStartSyncing(
1665 syncer::HISTORY_DELETE_DIRECTIVES,
1666 syncer::SyncDataList(),
1667 scoped_ptr<syncer::SyncChangeProcessor>(
1668 new syncer::SyncChangeProcessorWrapperForTest(
1669 &change_processor)),
1670 scoped_ptr<syncer::SyncErrorFactory>())
1674 syncer::SyncError err =
1675 history_service_->ProcessLocalDeleteDirective(delete_directive);
1676 EXPECT_FALSE(err.IsSet());
1677 EXPECT_EQ(1u, change_processor.changes().size());
1679 history_service_->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES);
1680 err = history_service_->ProcessLocalDeleteDirective(delete_directive);
1681 EXPECT_TRUE(err.IsSet());
1682 EXPECT_EQ(1u, change_processor.changes().size());
1685 // Closure function that runs periodically to check result of delete directive
1686 // processing. Stop when timeout or processing ends indicated by the creation
1688 void CheckDirectiveProcessingResult(
1690 const syncer::FakeSyncChangeProcessor* change_processor,
1691 uint32 num_changes) {
1692 if (base::Time::Now() > timeout ||
1693 change_processor->changes().size() >= num_changes) {
1697 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
1698 base::MessageLoop::current()->PostTask(
1700 base::Bind(&CheckDirectiveProcessingResult, timeout,
1701 change_processor, num_changes));
1704 // Create a delete directive for a few specific history entries,
1705 // including ones that don't exist. The expected entries should be
1707 TEST_F(HistoryTest, ProcessGlobalIdDeleteDirective) {
1708 ASSERT_TRUE(history_service_.get());
1709 const GURL test_url("http://www.google.com/");
1710 for (int64 i = 1; i <= 20; i++) {
1712 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1713 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1714 history::RedirectList(),
1715 content::PAGE_TRANSITION_LINK,
1716 history::SOURCE_BROWSED, false);
1719 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1720 EXPECT_EQ(20, query_url_row_.visit_count());
1722 syncer::SyncDataList directives;
1724 sync_pb::EntitySpecifics entity_specs;
1725 sync_pb::GlobalIdDirective* global_id_directive =
1726 entity_specs.mutable_history_delete_directive()
1727 ->mutable_global_id_directive();
1728 global_id_directive->add_global_id(
1729 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6))
1730 .ToInternalValue());
1731 global_id_directive->set_start_time_usec(3);
1732 global_id_directive->set_end_time_usec(10);
1733 directives.push_back(syncer::SyncData::CreateRemoteData(
1737 syncer::AttachmentIdList(),
1738 syncer::AttachmentServiceProxyForTest::Create()));
1741 global_id_directive->Clear();
1742 global_id_directive->add_global_id(
1743 (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17))
1744 .ToInternalValue());
1745 global_id_directive->set_start_time_usec(13);
1746 global_id_directive->set_end_time_usec(19);
1747 directives.push_back(syncer::SyncData::CreateRemoteData(
1751 syncer::AttachmentIdList(),
1752 syncer::AttachmentServiceProxyForTest::Create()));
1754 syncer::FakeSyncChangeProcessor change_processor;
1756 history_service_->MergeDataAndStartSyncing(
1757 syncer::HISTORY_DELETE_DIRECTIVES,
1759 scoped_ptr<syncer::SyncChangeProcessor>(
1760 new syncer::SyncChangeProcessorWrapperForTest(
1761 &change_processor)),
1762 scoped_ptr<syncer::SyncErrorFactory>())
1766 // Inject a task to check status and keep message loop filled before directive
1767 // processing finishes.
1768 base::MessageLoop::current()->PostTask(
1770 base::Bind(&CheckDirectiveProcessingResult,
1771 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1772 &change_processor, 2));
1773 base::MessageLoop::current()->RunUntilIdle();
1774 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1775 ASSERT_EQ(5, query_url_row_.visit_count());
1776 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1777 query_url_visits_[0].visit_time);
1778 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(2),
1779 query_url_visits_[1].visit_time);
1780 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(11),
1781 query_url_visits_[2].visit_time);
1782 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(12),
1783 query_url_visits_[3].visit_time);
1784 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(20),
1785 query_url_visits_[4].visit_time);
1787 // Expect two sync changes for deleting processed directives.
1788 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1789 ASSERT_EQ(2u, sync_changes.size());
1790 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1791 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1792 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1793 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1796 // Create delete directives for time ranges. The expected entries should be
1798 TEST_F(HistoryTest, ProcessTimeRangeDeleteDirective) {
1799 ASSERT_TRUE(history_service_.get());
1800 const GURL test_url("http://www.google.com/");
1801 for (int64 i = 1; i <= 10; ++i) {
1803 base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1804 history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1805 history::RedirectList(),
1806 content::PAGE_TRANSITION_LINK,
1807 history::SOURCE_BROWSED, false);
1810 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1811 EXPECT_EQ(10, query_url_row_.visit_count());
1813 syncer::SyncDataList directives;
1815 sync_pb::EntitySpecifics entity_specs;
1816 sync_pb::TimeRangeDirective* time_range_directive =
1817 entity_specs.mutable_history_delete_directive()
1818 ->mutable_time_range_directive();
1819 time_range_directive->set_start_time_usec(2);
1820 time_range_directive->set_end_time_usec(5);
1821 directives.push_back(syncer::SyncData::CreateRemoteData(
1825 syncer::AttachmentIdList(),
1826 syncer::AttachmentServiceProxyForTest::Create()));
1829 time_range_directive->Clear();
1830 time_range_directive->set_start_time_usec(8);
1831 time_range_directive->set_end_time_usec(10);
1832 directives.push_back(syncer::SyncData::CreateRemoteData(
1836 syncer::AttachmentIdList(),
1837 syncer::AttachmentServiceProxyForTest::Create()));
1839 syncer::FakeSyncChangeProcessor change_processor;
1841 history_service_->MergeDataAndStartSyncing(
1842 syncer::HISTORY_DELETE_DIRECTIVES,
1844 scoped_ptr<syncer::SyncChangeProcessor>(
1845 new syncer::SyncChangeProcessorWrapperForTest(
1846 &change_processor)),
1847 scoped_ptr<syncer::SyncErrorFactory>())
1851 // Inject a task to check status and keep message loop filled before
1852 // directive processing finishes.
1853 base::MessageLoop::current()->PostTask(
1855 base::Bind(&CheckDirectiveProcessingResult,
1856 base::Time::Now() + base::TimeDelta::FromSeconds(10),
1857 &change_processor, 2));
1858 base::MessageLoop::current()->RunUntilIdle();
1859 EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1860 ASSERT_EQ(3, query_url_row_.visit_count());
1861 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1862 query_url_visits_[0].visit_time);
1863 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6),
1864 query_url_visits_[1].visit_time);
1865 EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(7),
1866 query_url_visits_[2].visit_time);
1868 // Expect two sync changes for deleting processed directives.
1869 const syncer::SyncChangeList& sync_changes = change_processor.changes();
1870 ASSERT_EQ(2u, sync_changes.size());
1871 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1872 EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1873 EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1874 EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1877 TEST_F(HistoryBackendDBTest, MigratePresentations) {
1878 // Create the db we want. Use 22 since segments didn't change in that time
1880 ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
1882 const SegmentID segment_id = 2;
1883 const URLID url_id = 3;
1884 const GURL url("http://www.foo.com");
1885 const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
1886 const base::string16 title(base::ASCIIToUTF16("Title1"));
1887 const Time segment_time(Time::Now());
1890 // Re-open the db for manual manipulation.
1892 ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
1894 // Add an entry to urls.
1896 sql::Statement s(db.GetUniqueStatement(
1898 "(id, url, title, last_visit_time) VALUES "
1900 s.BindInt64(0, url_id);
1901 s.BindString(1, url.spec());
1902 s.BindString16(2, title);
1903 s.BindInt64(3, segment_time.ToInternalValue());
1904 ASSERT_TRUE(s.Run());
1907 // Add an entry to segments.
1909 sql::Statement s(db.GetUniqueStatement(
1910 "INSERT INTO segments "
1911 "(id, name, url_id, pres_index) VALUES "
1913 s.BindInt64(0, segment_id);
1914 s.BindString(1, url_name);
1915 s.BindInt64(2, url_id);
1916 s.BindInt(3, 4); // pres_index
1917 ASSERT_TRUE(s.Run());
1920 // And one to segment_usage.
1922 sql::Statement s(db.GetUniqueStatement(
1923 "INSERT INTO segment_usage "
1924 "(id, segment_id, time_slot, visit_count) VALUES "
1926 s.BindInt64(0, 4); // id.
1927 s.BindInt64(1, segment_id);
1928 s.BindInt64(2, segment_time.ToInternalValue());
1929 s.BindInt(3, 5); // visit count.
1930 ASSERT_TRUE(s.Run());
1934 // Re-open the db, triggering migration.
1935 CreateBackendAndDatabase();
1937 std::vector<PageUsageData*> results;
1938 db_->QuerySegmentUsage(segment_time, 10, &results);
1939 ASSERT_EQ(1u, results.size());
1940 EXPECT_EQ(url, results[0]->GetURL());
1941 EXPECT_EQ(segment_id, results[0]->GetID());
1942 EXPECT_EQ(title, results[0]->GetTitle());
1943 STLDeleteElements(&results);
1946 } // namespace history