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