1 // Copyright (c) 2011 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 "app/sql/connection.h" 6 #include "base/file_path.h" 7 #include "base/file_util.h" 8 #include "base/path_service.h" 9 #include "base/string_util.h" 10 #include "base/memory/scoped_temp_dir.h" 11 #include "chrome/browser/history/url_database.h" 12 #include "chrome/browser/history/visit_database.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/platform_test.h" 15 16 using base::Time; 17 using base::TimeDelta; 18 19 namespace history { 20 21 namespace { 22 23 bool IsVisitInfoEqual(const VisitRow& a, 24 const VisitRow& b) { 25 return a.visit_id == b.visit_id && 26 a.url_id == b.url_id && 27 a.visit_time == b.visit_time && 28 a.referring_visit == b.referring_visit && 29 a.transition == b.transition && 30 a.is_indexed == b.is_indexed; 31 } 32 33 } // namespace 34 35 class VisitDatabaseTest : public PlatformTest, 36 public URLDatabase, 37 public VisitDatabase { 38 public: 39 VisitDatabaseTest() { 40 } 41 42 private: 43 // Test setup. 44 void SetUp() { 45 PlatformTest::SetUp(); 46 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 47 FilePath db_file = temp_dir_.path().AppendASCII("VisitTest.db"); 48 49 EXPECT_TRUE(db_.Open(db_file)); 50 51 // Initialize the tables for this test. 52 CreateURLTable(false); 53 CreateMainURLIndex(); 54 InitVisitTable(); 55 } 56 void TearDown() { 57 db_.Close(); 58 PlatformTest::TearDown(); 59 } 60 61 // Provided for URL/VisitDatabase. 62 virtual sql::Connection& GetDB() { 63 return db_; 64 } 65 66 ScopedTempDir temp_dir_; 67 sql::Connection db_; 68 }; 69 70 TEST_F(VisitDatabaseTest, Add) { 71 // Add one visit. 72 VisitRow visit_info1(1, Time::Now(), 0, PageTransition::LINK, 0); 73 EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); 74 75 // Add second visit for the same page. 76 VisitRow visit_info2(visit_info1.url_id, 77 visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, 78 PageTransition::TYPED, 0); 79 EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED)); 80 81 // Add third visit for a different page. 82 VisitRow visit_info3(2, 83 visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, 84 PageTransition::LINK, 0); 85 EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED)); 86 87 // Query the first two. 88 std::vector<VisitRow> matches; 89 EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches)); 90 EXPECT_EQ(static_cast<size_t>(2), matches.size()); 91 92 // Make sure we got both (order in result set is visit time). 93 EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) && 94 IsVisitInfoEqual(matches[1], visit_info2)); 95 } 96 97 TEST_F(VisitDatabaseTest, Delete) { 98 // Add three visits that form a chain of navigation, and then delete the 99 // middle one. We should be left with the outer two visits, and the chain 100 // should link them. 101 static const int kTime1 = 1000; 102 VisitRow visit_info1(1, Time::FromInternalValue(kTime1), 0, 103 PageTransition::LINK, 0); 104 EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); 105 106 static const int kTime2 = kTime1 + 1; 107 VisitRow visit_info2(1, Time::FromInternalValue(kTime2), 108 visit_info1.visit_id, PageTransition::LINK, 0); 109 EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED)); 110 111 static const int kTime3 = kTime2 + 1; 112 VisitRow visit_info3(1, Time::FromInternalValue(kTime3), 113 visit_info2.visit_id, PageTransition::LINK, 0); 114 EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED)); 115 116 // First make sure all the visits are there. 117 std::vector<VisitRow> matches; 118 EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches)); 119 EXPECT_EQ(static_cast<size_t>(3), matches.size()); 120 EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) && 121 IsVisitInfoEqual(matches[1], visit_info2) && 122 IsVisitInfoEqual(matches[2], visit_info3)); 123 124 // Delete the middle one. 125 DeleteVisit(visit_info2); 126 127 // The outer two should be left, and the last one should have the first as 128 // the referrer. 129 visit_info3.referring_visit = visit_info1.visit_id; 130 matches.clear(); 131 EXPECT_TRUE(GetVisitsForURL(visit_info1.url_id, &matches)); 132 EXPECT_EQ(static_cast<size_t>(2), matches.size()); 133 EXPECT_TRUE(IsVisitInfoEqual(matches[0], visit_info1) && 134 IsVisitInfoEqual(matches[1], visit_info3)); 135 } 136 137 TEST_F(VisitDatabaseTest, Update) { 138 // Make something in the database. 139 VisitRow original(1, Time::Now(), 23, 22, 19); 140 AddVisit(&original, SOURCE_BROWSED); 141 142 // Mutate that row. 143 VisitRow modification(original); 144 modification.url_id = 2; 145 modification.transition = PageTransition::TYPED; 146 modification.visit_time = Time::Now() + TimeDelta::FromDays(1); 147 modification.referring_visit = 9292; 148 modification.is_indexed = true; 149 UpdateVisitRow(modification); 150 151 // Check that the mutated version was written. 152 VisitRow final; 153 GetRowForVisit(original.visit_id, &final); 154 EXPECT_TRUE(IsVisitInfoEqual(modification, final)); 155 } 156 157 // TODO(brettw) write test for GetMostRecentVisitForURL! 158 159 TEST_F(VisitDatabaseTest, GetVisibleVisitsInRange) { 160 // Add one visit. 161 VisitRow visit_info1(1, Time::Now(), 0, 162 static_cast<PageTransition::Type>(PageTransition::LINK | 163 PageTransition::CHAIN_START | 164 PageTransition::CHAIN_END), 165 0); 166 visit_info1.visit_id = 1; 167 EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); 168 169 // Add second visit for the same page. 170 VisitRow visit_info2(visit_info1.url_id, 171 visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, 172 static_cast<PageTransition::Type>(PageTransition::TYPED | 173 PageTransition::CHAIN_START | 174 PageTransition::CHAIN_END), 175 0); 176 visit_info2.visit_id = 2; 177 EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED)); 178 179 // Add third visit for a different page. 180 VisitRow visit_info3(2, 181 visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, 182 static_cast<PageTransition::Type>(PageTransition::LINK | 183 PageTransition::CHAIN_START), 184 0); 185 visit_info3.visit_id = 3; 186 EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED)); 187 188 // Add a redirect visit from the last page. 189 VisitRow visit_info4(3, 190 visit_info1.visit_time + TimeDelta::FromSeconds(3), visit_info3.visit_id, 191 static_cast<PageTransition::Type>(PageTransition::SERVER_REDIRECT | 192 PageTransition::CHAIN_END), 193 0); 194 visit_info4.visit_id = 4; 195 EXPECT_TRUE(AddVisit(&visit_info4, SOURCE_BROWSED)); 196 197 // Add a subframe visit. 198 VisitRow visit_info5(4, 199 visit_info1.visit_time + TimeDelta::FromSeconds(4), visit_info4.visit_id, 200 static_cast<PageTransition::Type>(PageTransition::AUTO_SUBFRAME | 201 PageTransition::CHAIN_START | 202 PageTransition::CHAIN_END), 203 0); 204 visit_info5.visit_id = 5; 205 EXPECT_TRUE(AddVisit(&visit_info5, SOURCE_BROWSED)); 206 207 // Query the visits for all time, we should not get the first (duplicate of 208 // the second) or the redirect or subframe visits. 209 VisitVector results; 210 GetVisibleVisitsInRange(Time(), Time(), 0, &results); 211 ASSERT_EQ(static_cast<size_t>(2), results.size()); 212 EXPECT_TRUE(IsVisitInfoEqual(results[0], visit_info4) && 213 IsVisitInfoEqual(results[1], visit_info2)); 214 215 // Query a time range and make sure beginning is inclusive and ending is 216 // exclusive. 217 GetVisibleVisitsInRange(visit_info2.visit_time, visit_info4.visit_time, 0, 218 &results); 219 ASSERT_EQ(static_cast<size_t>(1), results.size()); 220 EXPECT_TRUE(IsVisitInfoEqual(results[0], visit_info2)); 221 222 // Query for a max count and make sure we get only that number. 223 GetVisibleVisitsInRange(Time(), Time(), 1, &results); 224 ASSERT_EQ(static_cast<size_t>(1), results.size()); 225 EXPECT_TRUE(IsVisitInfoEqual(results[0], visit_info4)); 226 } 227 228 TEST_F(VisitDatabaseTest, VisitSource) { 229 // Add visits. 230 VisitRow visit_info1(111, Time::Now(), 0, PageTransition::LINK, 0); 231 ASSERT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); 232 233 VisitRow visit_info2(112, Time::Now(), 1, PageTransition::TYPED, 0); 234 ASSERT_TRUE(AddVisit(&visit_info2, SOURCE_SYNCED)); 235 236 VisitRow visit_info3(113, Time::Now(), 0, PageTransition::TYPED, 0); 237 ASSERT_TRUE(AddVisit(&visit_info3, SOURCE_EXTENSION)); 238 239 // Query each visit. 240 std::vector<VisitRow> matches; 241 ASSERT_TRUE(GetVisitsForURL(111, &matches)); 242 ASSERT_EQ(1U, matches.size()); 243 VisitSourceMap sources; 244 GetVisitsSource(matches, &sources); 245 EXPECT_EQ(0U, sources.size()); 246 247 ASSERT_TRUE(GetVisitsForURL(112, &matches)); 248 ASSERT_EQ(1U, matches.size()); 249 GetVisitsSource(matches, &sources); 250 ASSERT_EQ(1U, sources.size()); 251 EXPECT_EQ(SOURCE_SYNCED, sources[matches[0].visit_id]); 252 253 ASSERT_TRUE(GetVisitsForURL(113, &matches)); 254 ASSERT_EQ(1U, matches.size()); 255 GetVisitsSource(matches, &sources); 256 ASSERT_EQ(1U, sources.size()); 257 EXPECT_EQ(SOURCE_EXTENSION, sources[matches[0].visit_id]); 258 } 259 260 } // namespace history 261