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
IsVisitInfoEqual(const VisitRow & a,const VisitRow & b)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:
VisitDatabaseTest()39 VisitDatabaseTest() {
40 }
41
42 private:
43 // Test setup.
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 }
TearDown()56 void TearDown() {
57 db_.Close();
58 PlatformTest::TearDown();
59 }
60
61 // Provided for URL/VisitDatabase.
GetDB()62 virtual sql::Connection& GetDB() {
63 return db_;
64 }
65
66 ScopedTempDir temp_dir_;
67 sql::Connection db_;
68 };
69
TEST_F(VisitDatabaseTest,Add)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
TEST_F(VisitDatabaseTest,Delete)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
TEST_F(VisitDatabaseTest,Update)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
TEST_F(VisitDatabaseTest,GetVisibleVisitsInRange)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
TEST_F(VisitDatabaseTest,VisitSource)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