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 <string>
6#include <vector>
7
8#include "base/files/scoped_temp_dir.h"
9#include "base/path_service.h"
10#include "base/stl_util.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "components/search_engines/keyword_table.h"
15#include "components/search_engines/template_url_data.h"
16#include "components/webdata/common/web_database.h"
17#include "sql/statement.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using base::ASCIIToUTF16;
21using base::Time;
22using base::TimeDelta;
23
24class KeywordTableTest : public testing::Test {
25 public:
26  KeywordTableTest() {}
27  virtual ~KeywordTableTest() {}
28
29 protected:
30  virtual void SetUp() {
31    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
32    file_ = temp_dir_.path().AppendASCII("TestWebDatabase");
33
34    table_.reset(new KeywordTable);
35    db_.reset(new WebDatabase);
36    db_->AddTable(table_.get());
37    ASSERT_EQ(sql::INIT_OK, db_->Init(file_));
38  }
39
40  void AddKeyword(const TemplateURLData& keyword) const {
41    EXPECT_TRUE(table_->AddKeyword(keyword));
42  }
43
44  TemplateURLData CreateAndAddKeyword() const {
45    TemplateURLData keyword;
46    keyword.short_name = ASCIIToUTF16("short_name");
47    keyword.SetKeyword(ASCIIToUTF16("keyword"));
48    keyword.SetURL("http://url/");
49    keyword.suggestions_url = "url2";
50    keyword.instant_url = "http://instant/";
51    keyword.image_url = "http://image-search-url/";
52    keyword.new_tab_url = "http://new-tab-url/";
53    keyword.search_url_post_params = "ie=utf-8,oe=utf-8";
54    keyword.image_url_post_params = "name=1,value=2";
55    keyword.favicon_url = GURL("http://favicon.url/");
56    keyword.originating_url = GURL("http://google.com/");
57    keyword.show_in_default_list = true;
58    keyword.safe_for_autoreplace = true;
59    keyword.input_encodings.push_back("UTF-8");
60    keyword.input_encodings.push_back("UTF-16");
61    keyword.id = 1;
62    keyword.date_created = base::Time::UnixEpoch();
63    keyword.last_modified = base::Time::UnixEpoch();
64    keyword.created_by_policy = true;
65    keyword.usage_count = 32;
66    keyword.prepopulate_id = 10;
67    keyword.sync_guid = "1234-5678-90AB-CDEF";
68    keyword.alternate_urls.push_back("a_url1");
69    keyword.alternate_urls.push_back("a_url2");
70    keyword.search_terms_replacement_key = "espv";
71    AddKeyword(keyword);
72    return keyword;
73  }
74
75  void RemoveKeyword(TemplateURLID id) const {
76    EXPECT_TRUE(table_->RemoveKeyword(id));
77  }
78
79  void UpdateKeyword(const TemplateURLData& keyword) const {
80    EXPECT_TRUE(table_->UpdateKeyword(keyword));
81  }
82
83  KeywordTable::Keywords GetKeywords() const {
84    KeywordTable::Keywords keywords;
85    EXPECT_TRUE(table_->GetKeywords(&keywords));
86    return keywords;
87  }
88
89  void KeywordMiscTest() const {
90    EXPECT_EQ(kInvalidTemplateURLID, table_->GetDefaultSearchProviderID());
91    EXPECT_EQ(0, table_->GetBuiltinKeywordVersion());
92
93    EXPECT_TRUE(table_->SetDefaultSearchProviderID(10));
94    EXPECT_TRUE(table_->SetBuiltinKeywordVersion(11));
95
96    EXPECT_EQ(10, table_->GetDefaultSearchProviderID());
97    EXPECT_EQ(11, table_->GetBuiltinKeywordVersion());
98  }
99
100  void GetStatement(const char* sql, sql::Statement* statement) const {
101    statement->Assign(table_->db_->GetUniqueStatement(sql));
102  }
103
104 private:
105  base::FilePath file_;
106  base::ScopedTempDir temp_dir_;
107  scoped_ptr<KeywordTable> table_;
108  scoped_ptr<WebDatabase> db_;
109
110  DISALLOW_COPY_AND_ASSIGN(KeywordTableTest);
111};
112
113
114TEST_F(KeywordTableTest, Keywords) {
115  TemplateURLData keyword(CreateAndAddKeyword());
116
117  KeywordTable::Keywords keywords(GetKeywords());
118  EXPECT_EQ(1U, keywords.size());
119  const TemplateURLData& restored_keyword = keywords.front();
120
121  EXPECT_EQ(keyword.short_name, restored_keyword.short_name);
122  EXPECT_EQ(keyword.keyword(), restored_keyword.keyword());
123  EXPECT_EQ(keyword.url(), restored_keyword.url());
124  EXPECT_EQ(keyword.suggestions_url, restored_keyword.suggestions_url);
125  EXPECT_EQ(keyword.instant_url, restored_keyword.instant_url);
126  EXPECT_EQ(keyword.favicon_url, restored_keyword.favicon_url);
127  EXPECT_EQ(keyword.originating_url, restored_keyword.originating_url);
128  EXPECT_EQ(keyword.show_in_default_list,
129            restored_keyword.show_in_default_list);
130  EXPECT_EQ(keyword.safe_for_autoreplace,
131            restored_keyword.safe_for_autoreplace);
132  EXPECT_EQ(keyword.input_encodings, restored_keyword.input_encodings);
133  EXPECT_EQ(keyword.id, restored_keyword.id);
134  // The database stores time only at the resolution of a second.
135  EXPECT_EQ(keyword.date_created.ToTimeT(),
136            restored_keyword.date_created.ToTimeT());
137  EXPECT_EQ(keyword.last_modified.ToTimeT(),
138            restored_keyword.last_modified.ToTimeT());
139  EXPECT_EQ(keyword.created_by_policy, restored_keyword.created_by_policy);
140  EXPECT_EQ(keyword.usage_count, restored_keyword.usage_count);
141  EXPECT_EQ(keyword.prepopulate_id, restored_keyword.prepopulate_id);
142
143  RemoveKeyword(restored_keyword.id);
144
145  EXPECT_EQ(0U, GetKeywords().size());
146}
147
148TEST_F(KeywordTableTest, KeywordMisc) {
149  KeywordMiscTest();
150}
151
152TEST_F(KeywordTableTest, UpdateKeyword) {
153  TemplateURLData keyword(CreateAndAddKeyword());
154
155  keyword.SetKeyword(ASCIIToUTF16("url"));
156  keyword.instant_url = "http://instant2/";
157  keyword.originating_url = GURL("http://originating.url/");
158  keyword.input_encodings.push_back("Shift_JIS");
159  keyword.prepopulate_id = 5;
160  UpdateKeyword(keyword);
161
162  KeywordTable::Keywords keywords(GetKeywords());
163  EXPECT_EQ(1U, keywords.size());
164  const TemplateURLData& restored_keyword = keywords.front();
165
166  EXPECT_EQ(keyword.short_name, restored_keyword.short_name);
167  EXPECT_EQ(keyword.keyword(), restored_keyword.keyword());
168  EXPECT_EQ(keyword.suggestions_url, restored_keyword.suggestions_url);
169  EXPECT_EQ(keyword.instant_url, restored_keyword.instant_url);
170  EXPECT_EQ(keyword.favicon_url, restored_keyword.favicon_url);
171  EXPECT_EQ(keyword.originating_url, restored_keyword.originating_url);
172  EXPECT_EQ(keyword.show_in_default_list,
173            restored_keyword.show_in_default_list);
174  EXPECT_EQ(keyword.safe_for_autoreplace,
175            restored_keyword.safe_for_autoreplace);
176  EXPECT_EQ(keyword.input_encodings, restored_keyword.input_encodings);
177  EXPECT_EQ(keyword.id, restored_keyword.id);
178  EXPECT_EQ(keyword.prepopulate_id, restored_keyword.prepopulate_id);
179}
180
181TEST_F(KeywordTableTest, KeywordWithNoFavicon) {
182  TemplateURLData keyword;
183  keyword.short_name = ASCIIToUTF16("short_name");
184  keyword.SetKeyword(ASCIIToUTF16("keyword"));
185  keyword.SetURL("http://url/");
186  keyword.safe_for_autoreplace = true;
187  keyword.id = -100;
188  AddKeyword(keyword);
189
190  KeywordTable::Keywords keywords(GetKeywords());
191  EXPECT_EQ(1U, keywords.size());
192  const TemplateURLData& restored_keyword = keywords.front();
193
194  EXPECT_EQ(keyword.short_name, restored_keyword.short_name);
195  EXPECT_EQ(keyword.keyword(), restored_keyword.keyword());
196  EXPECT_EQ(keyword.favicon_url, restored_keyword.favicon_url);
197  EXPECT_EQ(keyword.safe_for_autoreplace,
198            restored_keyword.safe_for_autoreplace);
199  EXPECT_EQ(keyword.id, restored_keyword.id);
200}
201
202TEST_F(KeywordTableTest, SanitizeURLs) {
203  TemplateURLData keyword;
204  keyword.short_name = ASCIIToUTF16("legit");
205  keyword.SetKeyword(ASCIIToUTF16("legit"));
206  keyword.SetURL("http://url/");
207  keyword.id = 1000;
208  AddKeyword(keyword);
209
210  keyword.short_name = ASCIIToUTF16("bogus");
211  keyword.SetKeyword(ASCIIToUTF16("bogus"));
212  keyword.id = 2000;
213  AddKeyword(keyword);
214
215  EXPECT_EQ(2U, GetKeywords().size());
216
217  // Erase the URL field for the second keyword to simulate having bogus data
218  // previously saved into the database.
219  sql::Statement s;
220  GetStatement("UPDATE keywords SET url=? WHERE id=?", &s);
221  s.BindString16(0, base::string16());
222  s.BindInt64(1, 2000);
223  EXPECT_TRUE(s.Run());
224
225  // GetKeywords() should erase the entry with the empty URL field.
226  EXPECT_EQ(1U, GetKeywords().size());
227}
228