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 <set>
      6 #include <vector>
      7 
      8 #include "base/files/file_path.h"
      9 #include "base/files/scoped_temp_dir.h"
     10 #include "base/path_service.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/time/time.h"
     13 #include "chrome/browser/history/url_database.h"
     14 #include "chrome/browser/history/visit_database.h"
     15 #include "sql/connection.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 #include "testing/platform_test.h"
     18 
     19 using base::Time;
     20 using base::TimeDelta;
     21 
     22 namespace history {
     23 
     24 namespace {
     25 
     26 bool IsVisitInfoEqual(const VisitRow& a,
     27                       const VisitRow& b) {
     28   return a.visit_id == b.visit_id &&
     29          a.url_id == b.url_id &&
     30          a.visit_time == b.visit_time &&
     31          a.referring_visit == b.referring_visit &&
     32          a.transition == b.transition;
     33 }
     34 
     35 }  // namespace
     36 
     37 class VisitDatabaseTest : public PlatformTest,
     38                           public URLDatabase,
     39                           public VisitDatabase {
     40  public:
     41   VisitDatabaseTest() {
     42   }
     43 
     44  private:
     45   // Test setup.
     46   virtual void SetUp() {
     47     PlatformTest::SetUp();
     48     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     49     base::FilePath db_file = temp_dir_.path().AppendASCII("VisitTest.db");
     50 
     51     EXPECT_TRUE(db_.Open(db_file));
     52 
     53     // Initialize the tables for this test.
     54     CreateURLTable(false);
     55     CreateMainURLIndex();
     56     InitVisitTable();
     57   }
     58   virtual void TearDown() {
     59     db_.Close();
     60     PlatformTest::TearDown();
     61   }
     62 
     63   // Provided for URL/VisitDatabase.
     64   virtual sql::Connection& GetDB() OVERRIDE {
     65     return db_;
     66   }
     67 
     68   base::ScopedTempDir temp_dir_;
     69   sql::Connection db_;
     70 };
     71 
     72 TEST_F(VisitDatabaseTest, Add) {
     73   // Add one visit.
     74   VisitRow visit_info1(1, Time::Now(), 0, content::PAGE_TRANSITION_LINK, 0);
     75   EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED));
     76 
     77   // Add second visit for the same page.
     78   VisitRow visit_info2(visit_info1.url_id,
     79       visit_info1.visit_time + TimeDelta::FromSeconds(1), 1,
     80       content::PAGE_TRANSITION_TYPED, 0);
     81   EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED));
     82 
     83   // Add third visit for a different page.
     84   VisitRow visit_info3(2,
     85       visit_info1.visit_time + TimeDelta::FromSeconds(2), 0,
     86       content::PAGE_TRANSITION_LINK, 0);
     87   EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED));
     88 
     89   // Query the first two.
     90   std::vector<VisitRow> matches;
     91   EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches));
     92   EXPECT_EQ(static_cast<size_t>(2), matches.size());
     93 
     94   // Make sure we got both (order in result set is visit time).
     95   EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) &&
     96               IsVisitInfoEqual(matches[1], visit_info2));
     97 }
     98 
     99 TEST_F(VisitDatabaseTest, Delete) {
    100   // Add three visits that form a chain of navigation, and then delete the
    101   // middle one. We should be left with the outer two visits, and the chain
    102   // should link them.
    103   static const int kTime1 = 1000;
    104   VisitRow visit_info1(1, Time::FromInternalValue(kTime1), 0,
    105                        content::PAGE_TRANSITION_LINK, 0);
    106   EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED));
    107 
    108   static const int kTime2 = kTime1 + 1;
    109   VisitRow visit_info2(1, Time::FromInternalValue(kTime2),
    110                        visit_info1.visit_id, content::PAGE_TRANSITION_LINK, 0);
    111   EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED));
    112 
    113   static const int kTime3 = kTime2 + 1;
    114   VisitRow visit_info3(1, Time::FromInternalValue(kTime3),
    115                        visit_info2.visit_id, content::PAGE_TRANSITION_LINK, 0);
    116   EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED));
    117 
    118   // First make sure all the visits are there.
    119   std::vector<VisitRow> matches;
    120   EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches));
    121   EXPECT_EQ(static_cast<size_t>(3), matches.size());
    122   EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) &&
    123               IsVisitInfoEqual(matches[1], visit_info2) &&
    124               IsVisitInfoEqual(matches[2], visit_info3));
    125 
    126   // Delete the middle one.
    127   DeleteVisit(visit_info2);
    128 
    129   // The outer two should be left, and the last one should have the first as
    130   // the referrer.
    131   visit_info3.referring_visit = visit_info1.visit_id;
    132   matches.clear();
    133   EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches));
    134   EXPECT_EQ(static_cast<size_t>(2), matches.size());
    135   EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) &&
    136               IsVisitInfoEqual(matches[1], visit_info3));
    137 }
    138 
    139 TEST_F(VisitDatabaseTest, Update) {
    140   // Make something in the database.
    141   VisitRow original(1, Time::Now(), 23, content::PageTransitionFromInt(0), 19);
    142   AddVisit(&original, SOURCE_BROWSED);
    143 
    144   // Mutate that row.
    145   VisitRow modification(original);
    146   modification.url_id = 2;
    147   modification.transition = content::PAGE_TRANSITION_TYPED;
    148   modification.visit_time = Time::Now() + TimeDelta::FromDays(1);
    149   modification.referring_visit = 9292;
    150   UpdateVisitRow(modification);
    151 
    152   // Check that the mutated version was written.
    153   VisitRow final;
    154   GetRowForVisit(original.visit_id, &final);
    155   EXPECT_TRUE(IsVisitInfoEqual(modification, final));
    156 }
    157 
    158 // TODO(brettw) write test for GetMostRecentVisitForURL!
    159 
    160 namespace {
    161 
    162 std::vector<VisitRow> GetTestVisitRows() {
    163   // Tests can be sensitive to the local timezone, so use a local time as the
    164   // basis for all visit times.
    165   base::Time base_time = Time::UnixEpoch().LocalMidnight();
    166 
    167   // Add one visit.
    168   VisitRow visit_info1(1, base_time + TimeDelta::FromMinutes(1), 0,
    169       static_cast<content::PageTransition>(
    170           content::PAGE_TRANSITION_LINK |
    171           content::PAGE_TRANSITION_CHAIN_START |
    172           content::PAGE_TRANSITION_CHAIN_END),
    173       0);
    174   visit_info1.visit_id = 1;
    175 
    176   // Add second visit for the same page.
    177   VisitRow visit_info2(visit_info1.url_id,
    178       visit_info1.visit_time + TimeDelta::FromSeconds(1), 1,
    179       static_cast<content::PageTransition>(
    180           content::PAGE_TRANSITION_TYPED |
    181           content::PAGE_TRANSITION_CHAIN_START |
    182           content::PAGE_TRANSITION_CHAIN_END),
    183       0);
    184   visit_info2.visit_id = 2;
    185 
    186   // Add third visit for a different page.
    187   VisitRow visit_info3(2,
    188       visit_info1.visit_time + TimeDelta::FromSeconds(2), 0,
    189       static_cast<content::PageTransition>(
    190           content::PAGE_TRANSITION_LINK |
    191           content::PAGE_TRANSITION_CHAIN_START),
    192       0);
    193   visit_info3.visit_id = 3;
    194 
    195   // Add a redirect visit from the last page.
    196   VisitRow visit_info4(3,
    197       visit_info1.visit_time + TimeDelta::FromSeconds(3), visit_info3.visit_id,
    198       static_cast<content::PageTransition>(
    199           content::PAGE_TRANSITION_SERVER_REDIRECT |
    200           content::PAGE_TRANSITION_CHAIN_END),
    201       0);
    202   visit_info4.visit_id = 4;
    203 
    204   // Add a subframe visit.
    205   VisitRow visit_info5(4,
    206       visit_info1.visit_time + TimeDelta::FromSeconds(4), visit_info4.visit_id,
    207       static_cast<content::PageTransition>(
    208           content::PAGE_TRANSITION_AUTO_SUBFRAME |
    209           content::PAGE_TRANSITION_CHAIN_START |
    210           content::PAGE_TRANSITION_CHAIN_END),
    211       0);
    212   visit_info5.visit_id = 5;
    213 
    214   // Add third visit for the same URL as visit 1 and 2, but exactly a day
    215   // later than visit 2.
    216   VisitRow visit_info6(visit_info1.url_id,
    217       visit_info2.visit_time + TimeDelta::FromDays(1), 1,
    218       static_cast<content::PageTransition>(
    219           content::PAGE_TRANSITION_TYPED |
    220           content::PAGE_TRANSITION_CHAIN_START |
    221           content::PAGE_TRANSITION_CHAIN_END),
    222       0);
    223   visit_info6.visit_id = 6;
    224 
    225   std::vector<VisitRow> test_visit_rows;
    226   test_visit_rows.push_back(visit_info1);
    227   test_visit_rows.push_back(visit_info2);
    228   test_visit_rows.push_back(visit_info3);
    229   test_visit_rows.push_back(visit_info4);
    230   test_visit_rows.push_back(visit_info5);
    231   test_visit_rows.push_back(visit_info6);
    232   return test_visit_rows;
    233 }
    234 
    235 }  // namespace
    236 
    237 TEST_F(VisitDatabaseTest, GetVisitsForTimes) {
    238   std::vector<VisitRow> test_visit_rows = GetTestVisitRows();
    239 
    240   for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    241     EXPECT_TRUE(AddVisit(&test_visit_rows[i], SOURCE_BROWSED));
    242   }
    243 
    244   // Query the visits for all our times.  We should get all visits.
    245   {
    246     std::vector<base::Time> times;
    247     for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    248       times.push_back(test_visit_rows[i].visit_time);
    249     }
    250     VisitVector results;
    251     GetVisitsForTimes(times, &results);
    252     EXPECT_EQ(test_visit_rows.size(), results.size());
    253   }
    254 
    255   // Query the visits for a single time.
    256   for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    257     std::vector<base::Time> times;
    258     times.push_back(test_visit_rows[i].visit_time);
    259     VisitVector results;
    260     GetVisitsForTimes(times, &results);
    261     ASSERT_EQ(static_cast<size_t>(1), results.size());
    262     EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[i]));
    263   }
    264 }
    265 
    266 TEST_F(VisitDatabaseTest, GetAllVisitsInRange) {
    267   std::vector<VisitRow> test_visit_rows = GetTestVisitRows();
    268 
    269   for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    270     EXPECT_TRUE(AddVisit(&test_visit_rows[i], SOURCE_BROWSED));
    271   }
    272 
    273   // Query the visits for all time.  We should get all visits.
    274   VisitVector results;
    275   GetAllVisitsInRange(Time(), Time(), 0, &results);
    276   ASSERT_EQ(test_visit_rows.size(), results.size());
    277   for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    278     EXPECT_TRUE(IsVisitInfoEqual(results[i], test_visit_rows[i]));
    279   }
    280 
    281   // Query a time range and make sure beginning is inclusive and ending is
    282   // exclusive.
    283   GetAllVisitsInRange(test_visit_rows[1].visit_time,
    284                       test_visit_rows[3].visit_time, 0,
    285                       &results);
    286   ASSERT_EQ(static_cast<size_t>(2), results.size());
    287   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[1]));
    288   EXPECT_TRUE(IsVisitInfoEqual(results[1], test_visit_rows[2]));
    289 
    290   // Query for a max count and make sure we get only that number.
    291   GetAllVisitsInRange(Time(), Time(), 1, &results);
    292   ASSERT_EQ(static_cast<size_t>(1), results.size());
    293   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[0]));
    294 }
    295 
    296 TEST_F(VisitDatabaseTest, GetVisibleVisitsInRange) {
    297   std::vector<VisitRow> test_visit_rows = GetTestVisitRows();
    298 
    299   for (size_t i = 0; i < test_visit_rows.size(); ++i) {
    300     EXPECT_TRUE(AddVisit(&test_visit_rows[i], SOURCE_BROWSED));
    301   }
    302 
    303   // Query the visits for all time.  We should not get the first or the second
    304   // visit (duplicates of the sixth) or the redirect or subframe visits.
    305   VisitVector results;
    306   QueryOptions options;
    307   GetVisibleVisitsInRange(options, &results);
    308   ASSERT_EQ(static_cast<size_t>(2), results.size());
    309   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
    310   EXPECT_TRUE(IsVisitInfoEqual(results[1], test_visit_rows[3]));
    311 
    312   // Now try with only per-day de-duping -- the second visit should appear,
    313   // since it's a duplicate of visit6 but on a different day.
    314   options.duplicate_policy = QueryOptions::REMOVE_DUPLICATES_PER_DAY;
    315   GetVisibleVisitsInRange(options, &results);
    316   ASSERT_EQ(static_cast<size_t>(3), results.size());
    317   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
    318   EXPECT_TRUE(IsVisitInfoEqual(results[1], test_visit_rows[3]));
    319   EXPECT_TRUE(IsVisitInfoEqual(results[2], test_visit_rows[1]));
    320 
    321   // Now try without de-duping, expect to see all visible visits.
    322   options.duplicate_policy = QueryOptions::KEEP_ALL_DUPLICATES;
    323   GetVisibleVisitsInRange(options, &results);
    324   ASSERT_EQ(static_cast<size_t>(4), results.size());
    325   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
    326   EXPECT_TRUE(IsVisitInfoEqual(results[1], test_visit_rows[3]));
    327   EXPECT_TRUE(IsVisitInfoEqual(results[2], test_visit_rows[1]));
    328   EXPECT_TRUE(IsVisitInfoEqual(results[3], test_visit_rows[0]));
    329 
    330   // Set the end time to exclude the second visit. The first visit should be
    331   // returned. Even though the second is a more recent visit, it's not in the
    332   // query range.
    333   options.end_time = test_visit_rows[1].visit_time;
    334   GetVisibleVisitsInRange(options, &results);
    335   ASSERT_EQ(static_cast<size_t>(1), results.size());
    336   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[0]));
    337 
    338   options = QueryOptions();  // Reset to options to default.
    339 
    340   // Query for a max count and make sure we get only that number.
    341   options.max_count = 1;
    342   GetVisibleVisitsInRange(options, &results);
    343   ASSERT_EQ(static_cast<size_t>(1), results.size());
    344   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[5]));
    345 
    346   // Query a time range and make sure beginning is inclusive and ending is
    347   // exclusive.
    348   options.begin_time = test_visit_rows[1].visit_time;
    349   options.end_time = test_visit_rows[3].visit_time;
    350   options.max_count = 0;
    351   GetVisibleVisitsInRange(options, &results);
    352   ASSERT_EQ(static_cast<size_t>(1), results.size());
    353   EXPECT_TRUE(IsVisitInfoEqual(results[0], test_visit_rows[1]));
    354 }
    355 
    356 TEST_F(VisitDatabaseTest, VisitSource) {
    357   // Add visits.
    358   VisitRow visit_info1(111, Time::Now(), 0, content::PAGE_TRANSITION_LINK, 0);
    359   ASSERT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED));
    360 
    361   VisitRow visit_info2(112, Time::Now(), 1, content::PAGE_TRANSITION_TYPED, 0);
    362   ASSERT_TRUE(AddVisit(&visit_info2, SOURCE_SYNCED));
    363 
    364   VisitRow visit_info3(113, Time::Now(), 0, content::PAGE_TRANSITION_TYPED, 0);
    365   ASSERT_TRUE(AddVisit(&visit_info3, SOURCE_EXTENSION));
    366 
    367   // Query each visit.
    368   std::vector<VisitRow> matches;
    369   ASSERT_TRUE(GetVisitsForURL(111, &matches));
    370   ASSERT_EQ(1U, matches.size());
    371   VisitSourceMap sources;
    372   GetVisitsSource(matches, &sources);
    373   EXPECT_EQ(0U, sources.size());
    374 
    375   ASSERT_TRUE(GetVisitsForURL(112, &matches));
    376   ASSERT_EQ(1U, matches.size());
    377   GetVisitsSource(matches, &sources);
    378   ASSERT_EQ(1U, sources.size());
    379   EXPECT_EQ(SOURCE_SYNCED, sources[matches[0].visit_id]);
    380 
    381   ASSERT_TRUE(GetVisitsForURL(113, &matches));
    382   ASSERT_EQ(1U, matches.size());
    383   GetVisitsSource(matches, &sources);
    384   ASSERT_EQ(1U, sources.size());
    385   EXPECT_EQ(SOURCE_EXTENSION, sources[matches[0].visit_id]);
    386 }
    387 
    388 }  // namespace history
    389