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 #include "base/files/scoped_temp_dir.h"
      6 #include "base/format_macros.h"
      7 #include "base/path_service.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/time/time.h"
     11 #include "chrome/browser/history/shortcuts_database.h"
     12 #include "chrome/common/chrome_constants.h"
     13 #include "chrome/common/chrome_paths.h"
     14 #include "chrome/test/base/testing_profile.h"
     15 #include "components/omnibox/autocomplete_match_type.h"
     16 #include "sql/statement.h"
     17 #include "sql/test/test_helpers.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 #include "ui/base/page_transition_types.h"
     20 
     21 using base::ASCIIToUTF16;
     22 
     23 // Helpers --------------------------------------------------------------------
     24 
     25 namespace {
     26 
     27 struct ShortcutsDatabaseTestInfo {
     28   std::string guid;
     29   std::string text;
     30   std::string fill_into_edit;
     31   std::string destination_url;
     32   std::string contents;
     33   std::string contents_class;
     34   std::string description;
     35   std::string description_class;
     36   ui::PageTransition transition;
     37   AutocompleteMatchType::Type type;
     38   std::string keyword;
     39   int days_from_now;
     40   int number_of_hits;
     41 } shortcut_test_db[] = {
     42   { "BD85DBA2-8C29-49F9-84AE-48E1E90880DF", "goog", "www.google.com",
     43     "http://www.google.com/", "Google", "0,1,4,0", "Google", "0,1",
     44     ui::PAGE_TRANSITION_GENERATED, AutocompleteMatchType::SEARCH_HISTORY,
     45     "google.com", 1, 100, },
     46   { "BD85DBA2-8C29-49F9-84AE-48E1E90880E0", "slash", "slashdot.org",
     47     "http://slashdot.org/", "slashdot.org", "0,1",
     48     "Slashdot - News for nerds, stuff that matters", "0,0",
     49     ui::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_URL, "", 0,
     50     100},
     51   { "BD85DBA2-8C29-49F9-84AE-48E1E90880E1", "news", "slashdot.org",
     52     "http://slashdot.org/", "slashdot.org", "0,1",
     53     "Slashdot - News for nerds, stuff that matters", "0,0",
     54     ui::PAGE_TRANSITION_LINK, AutocompleteMatchType::HISTORY_TITLE, "", 0,
     55     5},
     56 };
     57 
     58 typedef testing::Test ShortcutsDatabaseMigrationTest;
     59 
     60 // Checks that the database at |db| has the version 2 columns iff |is_v2|.
     61 void CheckV2ColumnExistence(const base::FilePath& db_path, bool is_v2) {
     62   sql::Connection connection;
     63   ASSERT_TRUE(connection.Open(db_path));
     64   EXPECT_EQ(is_v2, connection.DoesColumnExist("omni_box_shortcuts",
     65                                               "fill_into_edit"));
     66   EXPECT_EQ(is_v2, connection.DoesColumnExist("omni_box_shortcuts",
     67                                               "transition"));
     68   EXPECT_EQ(is_v2, connection.DoesColumnExist("omni_box_shortcuts", "type"));
     69   EXPECT_EQ(is_v2, connection.DoesColumnExist("omni_box_shortcuts", "keyword"));
     70 }
     71 
     72 }  // namespace
     73 
     74 namespace history {
     75 
     76 
     77 // ShortcutsDatabaseTest ------------------------------------------------------
     78 
     79 class ShortcutsDatabaseTest : public testing::Test {
     80  public:
     81   virtual void SetUp();
     82   virtual void TearDown();
     83 
     84   void ClearDB();
     85   size_t CountRecords() const;
     86 
     87   ShortcutsDatabase::Shortcut ShortcutFromTestInfo(
     88       const ShortcutsDatabaseTestInfo& info);
     89 
     90   void AddAll();
     91 
     92   scoped_ptr<TestingProfile> profile_;
     93   scoped_refptr<ShortcutsDatabase> db_;
     94 };
     95 
     96 void ShortcutsDatabaseTest::SetUp() {
     97   profile_.reset(new TestingProfile());
     98   db_ = new ShortcutsDatabase(
     99       profile_->GetPath().Append(chrome::kShortcutsDatabaseName));
    100   ASSERT_TRUE(db_->Init());
    101   ClearDB();
    102 }
    103 
    104 void ShortcutsDatabaseTest::TearDown() {
    105   db_ = NULL;
    106 }
    107 
    108 void ShortcutsDatabaseTest::ClearDB() {
    109   sql::Statement
    110       s(db_->db_.GetUniqueStatement("DELETE FROM omni_box_shortcuts"));
    111   EXPECT_TRUE(s.Run());
    112 }
    113 
    114 size_t ShortcutsDatabaseTest::CountRecords() const {
    115   sql::Statement s(db_->db_.GetUniqueStatement(
    116       "SELECT count(*) FROM omni_box_shortcuts"));
    117   EXPECT_TRUE(s.Step());
    118   return static_cast<size_t>(s.ColumnInt(0));
    119 }
    120 
    121 ShortcutsDatabase::Shortcut ShortcutsDatabaseTest::ShortcutFromTestInfo(
    122     const ShortcutsDatabaseTestInfo& info) {
    123   return ShortcutsDatabase::Shortcut(
    124       info.guid, ASCIIToUTF16(info.text),
    125       ShortcutsDatabase::Shortcut::MatchCore(
    126           ASCIIToUTF16(info.fill_into_edit), GURL(info.destination_url),
    127           ASCIIToUTF16(info.contents), info.contents_class,
    128           ASCIIToUTF16(info.description), info.description_class,
    129           info.transition, info.type, ASCIIToUTF16(info.keyword)),
    130       base::Time::Now() - base::TimeDelta::FromDays(info.days_from_now),
    131       info.number_of_hits);
    132 }
    133 
    134 void ShortcutsDatabaseTest::AddAll() {
    135   ClearDB();
    136   for (size_t i = 0; i < arraysize(shortcut_test_db); ++i)
    137     db_->AddShortcut(ShortcutFromTestInfo(shortcut_test_db[i]));
    138   EXPECT_EQ(arraysize(shortcut_test_db), CountRecords());
    139 }
    140 
    141 
    142 // Actual tests ---------------------------------------------------------------
    143 
    144 TEST_F(ShortcutsDatabaseTest, AddShortcut) {
    145   ClearDB();
    146   EXPECT_EQ(0U, CountRecords());
    147   EXPECT_TRUE(db_->AddShortcut(ShortcutFromTestInfo(shortcut_test_db[0])));
    148   EXPECT_EQ(1U, CountRecords());
    149   EXPECT_TRUE(db_->AddShortcut(ShortcutFromTestInfo(shortcut_test_db[1])));
    150   EXPECT_EQ(2U, CountRecords());
    151   EXPECT_TRUE(db_->AddShortcut(ShortcutFromTestInfo(shortcut_test_db[2])));
    152   EXPECT_EQ(3U, CountRecords());
    153 }
    154 
    155 TEST_F(ShortcutsDatabaseTest, UpdateShortcut) {
    156   AddAll();
    157   ShortcutsDatabase::Shortcut shortcut(
    158       ShortcutFromTestInfo(shortcut_test_db[1]));
    159   shortcut.match_core.contents = ASCIIToUTF16("gro.todhsals");
    160   EXPECT_TRUE(db_->UpdateShortcut(shortcut));
    161   ShortcutsDatabase::GuidToShortcutMap shortcuts;
    162   db_->LoadShortcuts(&shortcuts);
    163   ShortcutsDatabase::GuidToShortcutMap::const_iterator it(
    164       shortcuts.find(shortcut.id));
    165   EXPECT_TRUE(it != shortcuts.end());
    166   EXPECT_TRUE(it->second.match_core.contents == shortcut.match_core.contents);
    167 }
    168 
    169 TEST_F(ShortcutsDatabaseTest, DeleteShortcutsWithIds) {
    170   AddAll();
    171   std::vector<std::string> shortcut_ids;
    172   shortcut_ids.push_back(shortcut_test_db[0].guid);
    173   shortcut_ids.push_back(shortcut_test_db[2].guid);
    174   EXPECT_TRUE(db_->DeleteShortcutsWithIDs(shortcut_ids));
    175   EXPECT_EQ(arraysize(shortcut_test_db) - 2, CountRecords());
    176 
    177   ShortcutsDatabase::GuidToShortcutMap shortcuts;
    178   db_->LoadShortcuts(&shortcuts);
    179 
    180   ShortcutsDatabase::GuidToShortcutMap::iterator it =
    181       shortcuts.find(shortcut_test_db[0].guid);
    182   EXPECT_TRUE(it == shortcuts.end());
    183 
    184   it = shortcuts.find(shortcut_test_db[1].guid);
    185   EXPECT_TRUE(it != shortcuts.end());
    186 
    187   it = shortcuts.find(shortcut_test_db[2].guid);
    188   EXPECT_TRUE(it == shortcuts.end());
    189 }
    190 
    191 TEST_F(ShortcutsDatabaseTest, DeleteShortcutsWithURL) {
    192   AddAll();
    193 
    194   EXPECT_TRUE(db_->DeleteShortcutsWithURL("http://slashdot.org/"));
    195   EXPECT_EQ(arraysize(shortcut_test_db) - 2, CountRecords());
    196 
    197   ShortcutsDatabase::GuidToShortcutMap shortcuts;
    198   db_->LoadShortcuts(&shortcuts);
    199 
    200   ShortcutsDatabase::GuidToShortcutMap::iterator it =
    201       shortcuts.find(shortcut_test_db[0].guid);
    202   EXPECT_TRUE(it != shortcuts.end());
    203 
    204   it = shortcuts.find(shortcut_test_db[1].guid);
    205   EXPECT_TRUE(it == shortcuts.end());
    206 
    207   it = shortcuts.find(shortcut_test_db[2].guid);
    208   EXPECT_TRUE(it == shortcuts.end());
    209 }
    210 
    211 
    212 TEST_F(ShortcutsDatabaseTest, DeleteAllShortcuts) {
    213   AddAll();
    214   ShortcutsDatabase::GuidToShortcutMap shortcuts;
    215   db_->LoadShortcuts(&shortcuts);
    216   EXPECT_EQ(arraysize(shortcut_test_db), shortcuts.size());
    217   EXPECT_TRUE(db_->DeleteAllShortcuts());
    218   db_->LoadShortcuts(&shortcuts);
    219   EXPECT_EQ(0U, shortcuts.size());
    220 }
    221 
    222 TEST(ShortcutsDatabaseMigrationTest, MigrateTableAddFillIntoEdit) {
    223   // Use the pre-v0 test file to create a test database in a temp dir.
    224   base::FilePath sql_path;
    225   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
    226   sql_path = sql_path.AppendASCII("History").AppendASCII(
    227 #if defined(OS_ANDROID)
    228       "Shortcuts.v1.sql");
    229 #else
    230       "Shortcuts.no_fill_into_edit.sql");
    231 #endif
    232   base::ScopedTempDir temp_dir;
    233   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    234   base::FilePath db_path(temp_dir.path().AppendASCII("TestShortcuts1.db"));
    235   ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, sql_path));
    236 
    237   CheckV2ColumnExistence(db_path, false);
    238 
    239   // Create a ShortcutsDatabase from the test database, which will migrate the
    240   // test database to the current version.
    241   {
    242     scoped_refptr<ShortcutsDatabase> db(new ShortcutsDatabase(db_path));
    243     db->Init();
    244   }
    245 
    246   CheckV2ColumnExistence(db_path, true);
    247 
    248   // Check the values in each of the new columns.
    249   sql::Connection connection;
    250   ASSERT_TRUE(connection.Open(db_path));
    251   sql::Statement statement(connection.GetUniqueStatement(
    252       "SELECT fill_into_edit, url, transition, type, keyword "
    253       "FROM omni_box_shortcuts"));
    254   ASSERT_TRUE(statement.is_valid());
    255   while (statement.Step()) {
    256     // |fill_into_edit| should have been copied from the |url|.
    257     EXPECT_EQ(statement.ColumnString(1), statement.ColumnString(0));
    258 
    259     // The other three columns have default values.
    260     EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
    261               ui::PageTransitionFromInt(statement.ColumnInt(2)));
    262     EXPECT_EQ(AutocompleteMatchType::HISTORY_TITLE,
    263               static_cast<AutocompleteMatchType::Type>(statement.ColumnInt(3)));
    264     EXPECT_TRUE(statement.ColumnString(4).empty());
    265   }
    266   EXPECT_TRUE(statement.Succeeded());
    267 #if !defined(OS_WIN)
    268   EXPECT_TRUE(temp_dir.Delete());
    269 #endif
    270 }
    271 
    272 TEST(ShortcutsDatabaseMigrationTest, MigrateV0ToV1) {
    273   // Use the v0 test file to create a test database in a temp dir.
    274   base::FilePath sql_path;
    275   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
    276   sql_path = sql_path.AppendASCII("History").AppendASCII("Shortcuts.v0.sql");
    277   base::ScopedTempDir temp_dir;
    278   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    279   base::FilePath db_path(temp_dir.path().AppendASCII("TestShortcuts2.db"));
    280   ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, sql_path));
    281 
    282   // Create a ShortcutsDatabase from the test database, which will migrate the
    283   // test database to the current version.
    284   {
    285     scoped_refptr<ShortcutsDatabase> db(new ShortcutsDatabase(db_path));
    286     db->Init();
    287   }
    288 
    289   // Check that all the old type values got converted to new values.
    290   sql::Connection connection;
    291   ASSERT_TRUE(connection.Open(db_path));
    292   sql::Statement statement(connection.GetUniqueStatement(
    293       "SELECT count(1) FROM omni_box_shortcuts WHERE type in (9, 10, 11, 12)"));
    294   ASSERT_TRUE(statement.is_valid());
    295   while (statement.Step())
    296     EXPECT_EQ(0, statement.ColumnInt(0));
    297   EXPECT_TRUE(statement.Succeeded());
    298 #if !defined(OS_WIN)
    299   EXPECT_TRUE(temp_dir.Delete());
    300 #endif
    301 }
    302 
    303 }  // namespace history
    304