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