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/file_path.h"
6#include "base/files/scoped_temp_dir.h"
7#include "base/path_service.h"
8#include "base/strings/string_util.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/history/url_database.h"
11#include "sql/connection.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14using base::Time;
15using base::TimeDelta;
16
17namespace history {
18
19namespace {
20
21bool IsURLRowEqual(const URLRow& a,
22                   const URLRow& b) {
23  // TODO(brettw) when the database stores an actual Time value rather than
24  // a time_t, do a reaul comparison. Instead, we have to do a more rough
25  // comparison since the conversion reduces the precision.
26  return a.title() == b.title() &&
27      a.visit_count() == b.visit_count() &&
28      a.typed_count() == b.typed_count() &&
29      a.last_visit() - b.last_visit() <= TimeDelta::FromSeconds(1) &&
30      a.hidden() == b.hidden();
31}
32
33}  // namespace
34
35class URLDatabaseTest : public testing::Test,
36                        public URLDatabase {
37 public:
38  URLDatabaseTest() {
39  }
40
41 protected:
42  // Provided for URL/VisitDatabase.
43  virtual sql::Connection& GetDB() OVERRIDE {
44    return db_;
45  }
46
47 private:
48  // Test setup.
49  virtual void SetUp() {
50    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
51    base::FilePath db_file = temp_dir_.path().AppendASCII("URLTest.db");
52
53    EXPECT_TRUE(db_.Open(db_file));
54
55    // Initialize the tables for this test.
56    CreateURLTable(false);
57    CreateMainURLIndex();
58    InitKeywordSearchTermsTable();
59    CreateKeywordSearchTermsIndices();
60  }
61  virtual void TearDown() {
62    db_.Close();
63  }
64
65  base::ScopedTempDir temp_dir_;
66  sql::Connection db_;
67};
68
69// Test add and query for the URL table in the HistoryDatabase.
70TEST_F(URLDatabaseTest, AddURL) {
71  // First, add two URLs.
72  const GURL url1("http://www.google.com/");
73  URLRow url_info1(url1);
74  url_info1.set_title(UTF8ToUTF16("Google"));
75  url_info1.set_visit_count(4);
76  url_info1.set_typed_count(2);
77  url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
78  url_info1.set_hidden(false);
79  EXPECT_TRUE(AddURL(url_info1));
80
81  const GURL url2("http://mail.google.com/");
82  URLRow url_info2(url2);
83  url_info2.set_title(UTF8ToUTF16("Google Mail"));
84  url_info2.set_visit_count(3);
85  url_info2.set_typed_count(0);
86  url_info2.set_last_visit(Time::Now() - TimeDelta::FromDays(2));
87  url_info2.set_hidden(true);
88  EXPECT_TRUE(AddURL(url_info2));
89
90  // Query both of them.
91  URLRow info;
92  EXPECT_TRUE(GetRowForURL(url1, &info));
93  EXPECT_TRUE(IsURLRowEqual(url_info1, info));
94  URLID id2 = GetRowForURL(url2, &info);
95  EXPECT_TRUE(id2);
96  EXPECT_TRUE(IsURLRowEqual(url_info2, info));
97
98  // Update the second.
99  url_info2.set_title(UTF8ToUTF16("Google Mail Too"));
100  url_info2.set_visit_count(4);
101  url_info2.set_typed_count(1);
102  url_info2.set_typed_count(91011);
103  url_info2.set_hidden(false);
104  EXPECT_TRUE(UpdateURLRow(id2, url_info2));
105
106  // Make sure it got updated.
107  URLRow info2;
108  EXPECT_TRUE(GetRowForURL(url2, &info2));
109  EXPECT_TRUE(IsURLRowEqual(url_info2, info2));
110
111  // Query a nonexistent URL.
112  EXPECT_EQ(0, GetRowForURL(GURL("http://news.google.com/"), &info));
113
114  // Delete all urls in the domain.
115  // TODO(acw): test the new url based delete domain
116  // EXPECT_TRUE(db.DeleteDomain(kDomainID));
117
118  // Make sure the urls have been properly removed.
119  // TODO(acw): commented out because remove no longer works.
120  // EXPECT_TRUE(db.GetURLInfo(url1, NULL) == NULL);
121  // EXPECT_TRUE(db.GetURLInfo(url2, NULL) == NULL);
122}
123
124// Tests adding, querying and deleting keyword visits.
125TEST_F(URLDatabaseTest, KeywordSearchTermVisit) {
126  URLRow url_info1(GURL("http://www.google.com/"));
127  url_info1.set_title(UTF8ToUTF16("Google"));
128  url_info1.set_visit_count(4);
129  url_info1.set_typed_count(2);
130  url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
131  url_info1.set_hidden(false);
132  URLID url_id = AddURL(url_info1);
133  ASSERT_NE(0, url_id);
134
135  // Add a keyword visit.
136  TemplateURLID keyword_id = 100;
137  base::string16 keyword = UTF8ToUTF16("visit");
138  ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id, keyword_id, keyword));
139
140  // Make sure we get it back.
141  std::vector<KeywordSearchTermVisit> matches;
142  GetMostRecentKeywordSearchTerms(keyword_id, keyword, 10, &matches);
143  ASSERT_EQ(1U, matches.size());
144  ASSERT_EQ(keyword, matches[0].term);
145
146  KeywordSearchTermRow keyword_search_term_row;
147  ASSERT_TRUE(GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
148  EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
149  EXPECT_EQ(url_id, keyword_search_term_row.url_id);
150  EXPECT_EQ(keyword, keyword_search_term_row.term);
151
152  // Delete the keyword visit.
153  DeleteAllSearchTermsForKeyword(keyword_id);
154
155  // Make sure we don't get it back when querying.
156  matches.clear();
157  GetMostRecentKeywordSearchTerms(keyword_id, keyword, 10, &matches);
158  ASSERT_EQ(0U, matches.size());
159
160  ASSERT_FALSE(GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
161}
162
163// Make sure deleting a URL also deletes a keyword visit.
164TEST_F(URLDatabaseTest, DeleteURLDeletesKeywordSearchTermVisit) {
165  URLRow url_info1(GURL("http://www.google.com/"));
166  url_info1.set_title(UTF8ToUTF16("Google"));
167  url_info1.set_visit_count(4);
168  url_info1.set_typed_count(2);
169  url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
170  url_info1.set_hidden(false);
171  URLID url_id = AddURL(url_info1);
172  ASSERT_NE(0, url_id);
173
174  // Add a keyword visit.
175  ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id, 1, UTF8ToUTF16("visit")));
176
177  // Delete the url.
178  ASSERT_TRUE(DeleteURLRow(url_id));
179
180  // Make sure the keyword visit was deleted.
181  std::vector<KeywordSearchTermVisit> matches;
182  GetMostRecentKeywordSearchTerms(1, UTF8ToUTF16("visit"), 10, &matches);
183  ASSERT_EQ(0U, matches.size());
184}
185
186TEST_F(URLDatabaseTest, EnumeratorForSignificant) {
187  std::set<std::string> good_urls;
188  // Add URLs which do and don't meet the criteria.
189  URLRow url_no_match(GURL("http://www.url_no_match.com/"));
190  EXPECT_TRUE(AddURL(url_no_match));
191
192  std::string url_string2("http://www.url_match_visit_count.com/");
193  good_urls.insert("http://www.url_match_visit_count.com/");
194  URLRow url_match_visit_count(GURL("http://www.url_match_visit_count.com/"));
195  url_match_visit_count.set_visit_count(kLowQualityMatchVisitLimit);
196  EXPECT_TRUE(AddURL(url_match_visit_count));
197
198  good_urls.insert("http://www.url_match_typed_count.com/");
199  URLRow url_match_typed_count(GURL("http://www.url_match_typed_count.com/"));
200  url_match_typed_count.set_typed_count(kLowQualityMatchTypedLimit);
201  EXPECT_TRUE(AddURL(url_match_typed_count));
202
203  good_urls.insert("http://www.url_match_last_visit.com/");
204  URLRow url_match_last_visit(GURL("http://www.url_match_last_visit.com/"));
205  url_match_last_visit.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
206  EXPECT_TRUE(AddURL(url_match_last_visit));
207
208  URLRow url_no_match_last_visit(GURL(
209      "http://www.url_no_match_last_visit.com/"));
210  url_no_match_last_visit.set_last_visit(Time::Now() -
211      TimeDelta::FromDays(kLowQualityMatchAgeLimitInDays + 1));
212  EXPECT_TRUE(AddURL(url_no_match_last_visit));
213
214  URLDatabase::URLEnumerator history_enum;
215  EXPECT_TRUE(InitURLEnumeratorForSignificant(&history_enum));
216  URLRow row;
217  int row_count = 0;
218  for (; history_enum.GetNextURL(&row); ++row_count)
219    EXPECT_EQ(1U, good_urls.count(row.url().spec()));
220  EXPECT_EQ(3, row_count);
221}
222
223// Test GetKeywordSearchTermRows and DeleteSearchTerm
224TEST_F(URLDatabaseTest, GetAndDeleteKeywordSearchTermByTerm) {
225  URLRow url_info1(GURL("http://www.google.com/"));
226  url_info1.set_title(UTF8ToUTF16("Google"));
227  url_info1.set_visit_count(4);
228  url_info1.set_typed_count(2);
229  url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
230  url_info1.set_hidden(false);
231  URLID url_id1 = AddURL(url_info1);
232  ASSERT_NE(0, url_id1);
233
234  // Add a keyword visit.
235  TemplateURLID keyword_id = 100;
236  base::string16 keyword = UTF8ToUTF16("visit");
237  ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id1, keyword_id, keyword));
238
239  URLRow url_info2(GURL("https://www.google.com/"));
240  url_info2.set_title(UTF8ToUTF16("Google"));
241  url_info2.set_visit_count(4);
242  url_info2.set_typed_count(2);
243  url_info2.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
244  url_info2.set_hidden(false);
245  URLID url_id2 = AddURL(url_info2);
246  ASSERT_NE(0, url_id2);
247  // Add the same keyword for url_info2.
248  ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id2, keyword_id, keyword));
249
250  // Add another URL for different keyword.
251  URLRow url_info3(GURL("https://www.google.com/search"));
252  url_info3.set_title(UTF8ToUTF16("Google"));
253  url_info3.set_visit_count(4);
254  url_info3.set_typed_count(2);
255  url_info3.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
256  url_info3.set_hidden(false);
257  URLID url_id3 = AddURL(url_info3);
258  ASSERT_NE(0, url_id3);
259  base::string16 keyword2 = UTF8ToUTF16("Search");
260
261  ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id3, keyword_id, keyword2));
262
263  // We should get 2 rows for |keyword|.
264  std::vector<KeywordSearchTermRow> rows;
265  ASSERT_TRUE(GetKeywordSearchTermRows(keyword, &rows));
266  ASSERT_EQ(2u, rows.size());
267  if (rows[0].url_id == url_id1) {
268    EXPECT_EQ(keyword, rows[0].term);
269    EXPECT_EQ(keyword, rows[1].term);
270    EXPECT_EQ(url_id2, rows[1].url_id);
271  } else {
272    EXPECT_EQ(keyword, rows[0].term);
273    EXPECT_EQ(url_id1, rows[1].url_id);
274    EXPECT_EQ(keyword, rows[1].term);
275    EXPECT_EQ(url_id2, rows[0].url_id);
276  }
277
278  // We should get 1 row for |keyword2|.
279  rows.clear();
280  ASSERT_TRUE(GetKeywordSearchTermRows(keyword2, &rows));
281  ASSERT_EQ(1u, rows.size());
282  EXPECT_EQ(keyword2, rows[0].term);
283  EXPECT_EQ(url_id3, rows[0].url_id);
284
285  // Delete all rows have keyword.
286  ASSERT_TRUE(DeleteKeywordSearchTerm(keyword));
287  rows.clear();
288  // We should still find keyword2.
289  ASSERT_TRUE(GetKeywordSearchTermRows(keyword2, &rows));
290  ASSERT_EQ(1u, rows.size());
291  EXPECT_EQ(keyword2, rows[0].term);
292  EXPECT_EQ(url_id3, rows[0].url_id);
293  rows.clear();
294  // No row for keyword.
295  ASSERT_TRUE(GetKeywordSearchTermRows(keyword, &rows));
296  EXPECT_TRUE(rows.empty());
297}
298
299}  // namespace history
300