template_url_model_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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/callback.h"
6#include "base/scoped_vector.h"
7#include "base/string_split.h"
8#include "base/string_util.h"
9#include "base/ref_counted.h"
10#include "base/thread.h"
11#include "chrome/browser/chrome_thread.h"
12#include "chrome/browser/history/history.h"
13#include "chrome/browser/history/history_notifications.h"
14#include "chrome/browser/search_engines/search_host_to_urls_map.h"
15#include "chrome/browser/search_engines/search_terms_data.h"
16#include "chrome/browser/search_engines/template_url.h"
17#include "chrome/browser/search_engines/template_url_model.h"
18#include "chrome/browser/search_engines/template_url_model_test_util.h"
19#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
20#include "chrome/browser/webdata/web_database.h"
21#include "chrome/common/pref_names.h"
22#include "chrome/test/testing_pref_service.h"
23#include "chrome/test/testing_profile.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26using base::Time;
27using base::TimeDelta;
28
29#if defined(OS_LINUX)
30// Timed out on Chromium Linux.  http://crbug.com/53607
31#define MAYBE_Load DISABLED_Load
32#else
33#define MAYBE_Load Load
34#endif
35
36// Test the GenerateSearchURL on a thread or the main thread.
37class TestGenerateSearchURL :
38    public base::RefCountedThreadSafe<TestGenerateSearchURL> {
39 public:
40  explicit TestGenerateSearchURL(SearchTermsData* search_terms_data)
41      : search_terms_data_(search_terms_data),
42        passed_(false) {
43  }
44
45  // Run the test cases for GenerateSearchURL.
46  void RunTest();
47
48  // Did the test pass?
49  bool passed() const { return passed_; }
50
51 private:
52  friend class base::RefCountedThreadSafe<TestGenerateSearchURL>;
53  ~TestGenerateSearchURL() {}
54
55  SearchTermsData* search_terms_data_;
56  bool passed_;
57
58  DISALLOW_COPY_AND_ASSIGN(TestGenerateSearchURL);
59};
60
61// Simple implementation of SearchTermsData.
62class TestSearchTermsData : public SearchTermsData {
63 public:
64  explicit TestSearchTermsData(const char* google_base_url)
65      : google_base_url_(google_base_url)  {
66  }
67
68  virtual std::string GoogleBaseURLValue() const {
69    return google_base_url_;
70  }
71
72  virtual std::string GetApplicationLocale() const {
73    return "yy";
74  }
75
76#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
77  // Returns the value for the Chrome Omnibox rlz.
78  virtual std::wstring GetRlzParameterValue() const {
79    return std::wstring();
80  }
81#endif
82
83 private:
84  std::string google_base_url_;
85
86  DISALLOW_COPY_AND_ASSIGN(TestSearchTermsData);
87};
88
89// Create an URL that appears to have been prepopulated, but won't be in the
90// current data. The caller owns the returned TemplateURL*.
91static TemplateURL* CreatePreloadedTemplateURL() {
92  TemplateURL* t_url = new TemplateURL();
93  t_url->SetURL("http://www.unittest.com/", 0, 0);
94  t_url->set_keyword(L"unittest");
95  t_url->set_short_name(L"unittest");
96  t_url->set_safe_for_autoreplace(true);
97  GURL favicon_url("http://favicon.url");
98  t_url->SetFavIconURL(favicon_url);
99  t_url->set_date_created(Time::FromTimeT(100));
100  t_url->set_prepopulate_id(999999);
101  return t_url;
102}
103
104class TemplateURLModelTest : public testing::Test {
105 public:
106  TemplateURLModelTest() {}
107
108  virtual void SetUp() {
109    test_util_.SetUp();
110  }
111
112  virtual void TearDown() {
113    test_util_.TearDown();
114  }
115
116  TemplateURL* AddKeywordWithDate(const std::wstring& keyword,
117                                  bool autogenerate_keyword,
118                                  const std::string& url,
119                                  const std::wstring& short_name,
120                                  bool safe_for_autoreplace,
121                                  Time created_date) {
122    TemplateURL* template_url = new TemplateURL();
123    template_url->SetURL(url, 0, 0);
124    template_url->set_keyword(keyword);
125    template_url->set_autogenerate_keyword(autogenerate_keyword);
126    template_url->set_short_name(short_name);
127    template_url->set_date_created(created_date);
128    template_url->set_safe_for_autoreplace(safe_for_autoreplace);
129    model()->Add(template_url);
130    EXPECT_NE(0, template_url->id());
131    return template_url;
132  }
133
134  // Verifies the two TemplateURLs are equal.
135  void AssertEquals(const TemplateURL& expected, const TemplateURL& actual) {
136    ASSERT_EQ(expected.url()->url(), actual.url()->url());
137    ASSERT_EQ(expected.keyword(), actual.keyword());
138    ASSERT_EQ(expected.short_name(), actual.short_name());
139    ASSERT_TRUE(expected.GetFavIconURL() == actual.GetFavIconURL());
140    ASSERT_EQ(expected.id(), actual.id());
141    ASSERT_EQ(expected.safe_for_autoreplace(), actual.safe_for_autoreplace());
142    ASSERT_EQ(expected.show_in_default_list(), actual.show_in_default_list());
143    ASSERT_TRUE(expected.date_created() == actual.date_created());
144  }
145
146  // Creates a TemplateURL with the same prepopluated id as a real prepopulated
147  // item. The input number determines which prepopulated item. The caller is
148  // responsible for owning the returned TemplateURL*.
149  TemplateURL* CreateReplaceablePreloadedTemplateURL(
150      size_t index_offset_from_default,
151      std::wstring* prepopulated_display_url);
152
153  // Verifies the behavior of when a preloaded url later gets changed.
154  // Since the input is the offset from the default, when one passes in
155  // 0, it tests the default. Passing in a number > 0 will verify what
156  // happens when a preloaded url that is not the default gets updated.
157  void TestLoadUpdatingPreloadedURL(size_t index_offset_from_default);
158
159  // Helper methods to make calling TemplateURLModelTestUtil methods less
160  // visually noisy in the test code.
161  void VerifyObserverCount(int expected_changed_count) {
162    test_util_.VerifyObserverCount(expected_changed_count);
163  }
164  void BlockTillServiceProcessesRequests() {
165    test_util_.BlockTillServiceProcessesRequests();
166  }
167  void VerifyLoad() { test_util_.VerifyLoad(); }
168  void ChangeModelToLoadState() { test_util_.ChangeModelToLoadState(); }
169  void ResetModel(bool verify_load) { test_util_.ResetModel(verify_load); }
170  std::wstring GetAndClearSearchTerm() {
171    return test_util_.GetAndClearSearchTerm();
172  }
173  void SetGoogleBaseURL(const std::string& base_url) const {
174    test_util_.SetGoogleBaseURL(base_url);
175  }
176  WebDataService* GetWebDataService() { return test_util_.GetWebDataService(); }
177  TemplateURLModel* model() { return test_util_.model(); }
178  TestingProfile* profile() { return test_util_.profile(); }
179
180 protected:
181  TemplateURLModelTestUtil test_util_;
182
183  DISALLOW_COPY_AND_ASSIGN(TemplateURLModelTest);
184};
185
186void TestGenerateSearchURL::RunTest() {
187  struct GenerateSearchURLCase {
188    const char* test_name;
189    const char* url;
190    const char* expected;
191  } generate_url_cases[] = {
192    { "empty TemplateURLRef", NULL, "" },
193    { "invalid URL", "foo{searchTerms}", "" },
194    { "URL with no replacements", "http://foo/", "http://foo/" },
195    { "basic functionality", "http://foo/{searchTerms}",
196      "http://foo/blah.blah.blah.blah.blah" }
197  };
198
199  // Don't use ASSERT/EXPECT since this is run on a thread in one test
200  // and those macros aren't meant for threads at this time according to
201  // gtest documentation.
202  bool everything_passed = true;
203  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(generate_url_cases); ++i) {
204    TemplateURL t_url;
205    if (generate_url_cases[i].url)
206      t_url.SetURL(generate_url_cases[i].url, 0, 0);
207
208    std::string result = search_terms_data_ ?
209        TemplateURLModel::GenerateSearchURLUsingTermsData(
210            &t_url, *search_terms_data_).spec() :
211        TemplateURLModel::GenerateSearchURL(&t_url).spec();
212    if (strcmp(generate_url_cases[i].expected, result.c_str())) {
213      LOG(ERROR) << generate_url_cases[i].test_name << " failed. Expected " <<
214          generate_url_cases[i].expected << " Actual " << result;
215
216      everything_passed = false;
217    }
218  }
219  passed_ = everything_passed;
220}
221
222TemplateURL* TemplateURLModelTest::CreateReplaceablePreloadedTemplateURL(
223    size_t index_offset_from_default,
224    std::wstring* prepopulated_display_url) {
225  TemplateURL* t_url = CreatePreloadedTemplateURL();
226  ScopedVector<TemplateURL> prepopulated_urls;
227  size_t default_search_provider_index = 0;
228  TemplateURLPrepopulateData::GetPrepopulatedEngines(
229      profile()->GetPrefs(),
230      &prepopulated_urls.get(),
231      &default_search_provider_index);
232  EXPECT_LT(index_offset_from_default, prepopulated_urls.size());
233  size_t prepopulated_index =
234      (default_search_provider_index + index_offset_from_default) %
235      prepopulated_urls.size();
236  t_url->set_prepopulate_id(
237      prepopulated_urls[prepopulated_index]->prepopulate_id());
238  *prepopulated_display_url =
239      prepopulated_urls[prepopulated_index]->url()->DisplayURL();
240  return t_url;
241}
242
243void TemplateURLModelTest::TestLoadUpdatingPreloadedURL(
244    size_t index_offset_from_default) {
245  std::wstring prepopulated_url;
246  TemplateURL* t_url = CreateReplaceablePreloadedTemplateURL(
247      index_offset_from_default, &prepopulated_url);
248  t_url->set_safe_for_autoreplace(false);
249
250  std::wstring original_url = t_url->url()->DisplayURL();
251  ASSERT_STRNE(prepopulated_url.c_str(), original_url.c_str());
252
253  // Then add it to the model and save it all.
254  ChangeModelToLoadState();
255  model()->Add(t_url);
256  const TemplateURL* keyword_url =
257      model()->GetTemplateURLForKeyword(L"unittest");
258  ASSERT_EQ(t_url, keyword_url);
259  ASSERT_STREQ(original_url.c_str(), keyword_url->url()->DisplayURL().c_str());
260  BlockTillServiceProcessesRequests();
261
262  // Now reload the model and verify that the merge updates the url.
263  ResetModel(true);
264  keyword_url = model()->GetTemplateURLForKeyword(L"unittest");
265  ASSERT_TRUE(keyword_url != NULL);
266  ASSERT_STREQ(prepopulated_url.c_str(),
267               keyword_url->url()->DisplayURL().c_str());
268
269  // Wait for any saves to finish.
270  BlockTillServiceProcessesRequests();
271
272  // Reload the model to verify that change was saved correctly.
273  ResetModel(true);
274  keyword_url = model()->GetTemplateURLForKeyword(L"unittest");
275  ASSERT_TRUE(keyword_url != NULL);
276  ASSERT_STREQ(prepopulated_url.c_str(),
277               keyword_url->url()->DisplayURL().c_str());
278}
279
280TEST_F(TemplateURLModelTest, MAYBE_Load) {
281  VerifyLoad();
282}
283
284TEST_F(TemplateURLModelTest, AddUpdateRemove) {
285  // Add a new TemplateURL.
286  VerifyLoad();
287  const size_t initial_count = model()->GetTemplateURLs().size();
288
289  TemplateURL* t_url = new TemplateURL();
290  t_url->SetURL("http://www.google.com/foo/bar", 0, 0);
291  t_url->set_keyword(L"keyword");
292  t_url->set_short_name(L"google");
293  GURL favicon_url("http://favicon.url");
294  t_url->SetFavIconURL(favicon_url);
295  t_url->set_date_created(Time::FromTimeT(100));
296  t_url->set_safe_for_autoreplace(true);
297  model()->Add(t_url);
298  ASSERT_TRUE(model()->CanReplaceKeyword(L"keyword", GURL(), NULL));
299  VerifyObserverCount(1);
300  BlockTillServiceProcessesRequests();
301  // We need to clone as model takes ownership of TemplateURL and will
302  // delete it.
303  TemplateURL cloned_url(*t_url);
304  ASSERT_EQ(1 + initial_count, model()->GetTemplateURLs().size());
305  ASSERT_TRUE(model()->GetTemplateURLForKeyword(t_url->keyword()) == t_url);
306  ASSERT_TRUE(t_url->date_created() == cloned_url.date_created());
307
308  // Reload the model to verify it was actually saved to the database.
309  ResetModel(true);
310  ASSERT_EQ(1 + initial_count, model()->GetTemplateURLs().size());
311  const TemplateURL* loaded_url = model()->GetTemplateURLForKeyword(L"keyword");
312  ASSERT_TRUE(loaded_url != NULL);
313  AssertEquals(cloned_url, *loaded_url);
314  ASSERT_TRUE(model()->CanReplaceKeyword(L"keyword", GURL(), NULL));
315
316  // Mutate an element and verify it succeeded.
317  model()->ResetTemplateURL(loaded_url, L"a", L"b", "c");
318  ASSERT_EQ(L"a", loaded_url->short_name());
319  ASSERT_EQ(L"b", loaded_url->keyword());
320  ASSERT_EQ("c", loaded_url->url()->url());
321  ASSERT_FALSE(loaded_url->safe_for_autoreplace());
322  ASSERT_TRUE(model()->CanReplaceKeyword(L"keyword", GURL(), NULL));
323  ASSERT_FALSE(model()->CanReplaceKeyword(L"b", GURL(), NULL));
324  cloned_url = *loaded_url;
325  BlockTillServiceProcessesRequests();
326  ResetModel(true);
327  ASSERT_EQ(1 + initial_count, model()->GetTemplateURLs().size());
328  loaded_url = model()->GetTemplateURLForKeyword(L"b");
329  ASSERT_TRUE(loaded_url != NULL);
330  AssertEquals(cloned_url, *loaded_url);
331
332  // Remove an element and verify it succeeded.
333  model()->Remove(loaded_url);
334  VerifyObserverCount(1);
335  ResetModel(true);
336  ASSERT_EQ(initial_count, model()->GetTemplateURLs().size());
337  EXPECT_TRUE(model()->GetTemplateURLForKeyword(L"b") == NULL);
338}
339
340TEST_F(TemplateURLModelTest, GenerateKeyword) {
341  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL(), true));
342  // Shouldn't generate keywords for https.
343  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("https://blah"), true));
344  ASSERT_EQ(L"foo", TemplateURLModel::GenerateKeyword(GURL("http://foo"),
345                                                      true));
346  // www. should be stripped.
347  ASSERT_EQ(L"foo", TemplateURLModel::GenerateKeyword(GURL("http://www.foo"),
348                                                      true));
349  // Shouldn't generate keywords with paths, if autodetected.
350  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("http://blah/foo"),
351                                                   true));
352  ASSERT_EQ(L"blah", TemplateURLModel::GenerateKeyword(GURL("http://blah/foo"),
353                                                       false));
354  // FTP shouldn't generate a keyword.
355  ASSERT_EQ(L"", TemplateURLModel::GenerateKeyword(GURL("ftp://blah/"), true));
356  // Make sure we don't get a trailing /
357  ASSERT_EQ(L"blah", TemplateURLModel::GenerateKeyword(GURL("http://blah/"),
358                                                       true));
359}
360
361TEST_F(TemplateURLModelTest, GenerateSearchURL) {
362  scoped_refptr<TestGenerateSearchURL> test_generate_search_url(
363      new TestGenerateSearchURL(NULL));
364  test_generate_search_url->RunTest();
365  EXPECT_TRUE(test_generate_search_url->passed());
366}
367
368TEST_F(TemplateURLModelTest, GenerateSearchURLUsingTermsData) {
369  // Run the test for GenerateSearchURLUsingTermsData on the "IO" thread and
370  // wait for it to finish.
371  TestSearchTermsData search_terms_data("http://google.com/");
372  scoped_refptr<TestGenerateSearchURL> test_generate_search_url(
373      new TestGenerateSearchURL(&search_terms_data));
374
375  ChromeThread io_thread(ChromeThread::IO);
376  io_thread.Start();
377  io_thread.message_loop()->PostTask(
378      FROM_HERE,
379      NewRunnableMethod(test_generate_search_url.get(),
380                        &TestGenerateSearchURL::RunTest));
381  test_util_.BlockTillIOThreadProcessesRequests();
382  EXPECT_TRUE(test_generate_search_url->passed());
383  io_thread.Stop();
384}
385
386TEST_F(TemplateURLModelTest, ClearBrowsingData_Keywords) {
387  Time now = Time::Now();
388  TimeDelta one_day = TimeDelta::FromDays(1);
389  Time month_ago = now - TimeDelta::FromDays(30);
390
391  // Nothing has been added.
392  EXPECT_EQ(0U, model()->GetTemplateURLs().size());
393
394  // Create one with a 0 time.
395  AddKeywordWithDate(L"key1", false, "http://foo1", L"name1", true, Time());
396  // Create one for now and +/- 1 day.
397  AddKeywordWithDate(L"key2", false, "http://foo2", L"name2", true,
398                     now - one_day);
399  AddKeywordWithDate(L"key3", false, "http://foo3", L"name3", true, now);
400  AddKeywordWithDate(L"key4", false, "http://foo4", L"name4", true,
401                     now + one_day);
402  // Try the other three states.
403  AddKeywordWithDate(L"key5", false, "http://foo5", L"name5", false, now);
404  AddKeywordWithDate(L"key6", false, "http://foo6", L"name6", false,
405                     month_ago);
406
407  // We just added a few items, validate them.
408  EXPECT_EQ(6U, model()->GetTemplateURLs().size());
409
410  // Try removing from current timestamp. This should delete the one in the
411  // future and one very recent one.
412  model()->RemoveAutoGeneratedSince(now);
413  EXPECT_EQ(4U, model()->GetTemplateURLs().size());
414
415  // Try removing from two months ago. This should only delete items that are
416  // auto-generated.
417  model()->RemoveAutoGeneratedSince(now - TimeDelta::FromDays(60));
418  EXPECT_EQ(3U, model()->GetTemplateURLs().size());
419
420  // Make sure the right values remain.
421  EXPECT_EQ(L"key1", model()->GetTemplateURLs()[0]->keyword());
422  EXPECT_TRUE(model()->GetTemplateURLs()[0]->safe_for_autoreplace());
423  EXPECT_EQ(0U,
424            model()->GetTemplateURLs()[0]->date_created().ToInternalValue());
425
426  EXPECT_EQ(L"key5", model()->GetTemplateURLs()[1]->keyword());
427  EXPECT_FALSE(model()->GetTemplateURLs()[1]->safe_for_autoreplace());
428  EXPECT_EQ(now.ToInternalValue(),
429            model()->GetTemplateURLs()[1]->date_created().ToInternalValue());
430
431  EXPECT_EQ(L"key6", model()->GetTemplateURLs()[2]->keyword());
432  EXPECT_FALSE(model()->GetTemplateURLs()[2]->safe_for_autoreplace());
433  EXPECT_EQ(month_ago.ToInternalValue(),
434            model()->GetTemplateURLs()[2]->date_created().ToInternalValue());
435
436  // Try removing from Time=0. This should delete one more.
437  model()->RemoveAutoGeneratedSince(Time());
438  EXPECT_EQ(2U, model()->GetTemplateURLs().size());
439}
440
441TEST_F(TemplateURLModelTest, Reset) {
442  // Add a new TemplateURL.
443  VerifyLoad();
444  const size_t initial_count = model()->GetTemplateURLs().size();
445  TemplateURL* t_url = new TemplateURL();
446  t_url->SetURL("http://www.google.com/foo/bar", 0, 0);
447  t_url->set_keyword(L"keyword");
448  t_url->set_short_name(L"google");
449  GURL favicon_url("http://favicon.url");
450  t_url->SetFavIconURL(favicon_url);
451  t_url->set_date_created(Time::FromTimeT(100));
452  model()->Add(t_url);
453
454  VerifyObserverCount(1);
455  BlockTillServiceProcessesRequests();
456
457  // Reset the short name, keyword, url and make sure it takes.
458  const std::wstring new_short_name(L"a");
459  const std::wstring new_keyword(L"b");
460  const std::string new_url("c");
461  model()->ResetTemplateURL(t_url, new_short_name, new_keyword, new_url);
462  ASSERT_EQ(new_short_name, t_url->short_name());
463  ASSERT_EQ(new_keyword, t_url->keyword());
464  ASSERT_EQ(new_url, t_url->url()->url());
465
466  // Make sure the mappings in the model were updated.
467  ASSERT_TRUE(model()->GetTemplateURLForKeyword(new_keyword) == t_url);
468  ASSERT_TRUE(model()->GetTemplateURLForKeyword(L"keyword") == NULL);
469
470  TemplateURL last_url = *t_url;
471
472  // Reload the model from the database and make sure the change took.
473  ResetModel(true);
474  t_url = NULL;
475  EXPECT_EQ(initial_count + 1, model()->GetTemplateURLs().size());
476  const TemplateURL* read_url = model()->GetTemplateURLForKeyword(new_keyword);
477  ASSERT_TRUE(read_url);
478  AssertEquals(last_url, *read_url);
479}
480
481TEST_F(TemplateURLModelTest, DefaultSearchProvider) {
482  // Add a new TemplateURL.
483  VerifyLoad();
484  const size_t initial_count = model()->GetTemplateURLs().size();
485  TemplateURL* t_url = AddKeywordWithDate(L"key1", false, "http://foo1",
486                                          L"name1", true, Time());
487
488  test_util_.ResetObserverCount();
489  model()->SetDefaultSearchProvider(t_url);
490
491  ASSERT_EQ(t_url, model()->GetDefaultSearchProvider());
492
493  ASSERT_TRUE(t_url->safe_for_autoreplace());
494  ASSERT_TRUE(t_url->show_in_default_list());
495
496  // Setting the default search provider should have caused notification.
497  VerifyObserverCount(1);
498
499  BlockTillServiceProcessesRequests();
500
501  TemplateURL cloned_url = *t_url;
502
503  ResetModel(true);
504  t_url = NULL;
505
506  // Make sure when we reload we get a default search provider.
507  EXPECT_EQ(1 + initial_count, model()->GetTemplateURLs().size());
508  ASSERT_TRUE(model()->GetDefaultSearchProvider());
509  AssertEquals(cloned_url, *model()->GetDefaultSearchProvider());
510}
511
512TEST_F(TemplateURLModelTest, TemplateURLWithNoKeyword) {
513  VerifyLoad();
514
515  const size_t initial_count = model()->GetTemplateURLs().size();
516
517  AddKeywordWithDate(std::wstring(), false, "http://foo1", L"name1", true,
518                     Time());
519
520  // We just added a few items, validate them.
521  ASSERT_EQ(initial_count + 1, model()->GetTemplateURLs().size());
522
523  // Reload the model from the database and make sure we get the url back.
524  ResetModel(true);
525
526  ASSERT_EQ(1 + initial_count, model()->GetTemplateURLs().size());
527
528  bool found_keyword = false;
529  for (size_t i = 0; i < initial_count + 1; ++i) {
530    if (model()->GetTemplateURLs()[i]->keyword().empty()) {
531      found_keyword = true;
532      break;
533    }
534  }
535  ASSERT_TRUE(found_keyword);
536}
537
538TEST_F(TemplateURLModelTest, CantReplaceWithSameKeyword) {
539  ChangeModelToLoadState();
540  ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL(), NULL));
541  TemplateURL* t_url = AddKeywordWithDate(L"foo", false, "http://foo1",
542                                          L"name1", true, Time());
543
544  // Can still replace, newly added template url is marked safe to replace.
545  ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL("http://foo2"), NULL));
546
547  // ResetTemplateURL marks the TemplateURL as unsafe to replace, so it should
548  // no longer be replaceable.
549  model()->ResetTemplateURL(t_url, t_url->short_name(), t_url->keyword(),
550                           t_url->url()->url());
551
552  ASSERT_FALSE(model()->CanReplaceKeyword(L"foo", GURL("http://foo2"), NULL));
553}
554
555TEST_F(TemplateURLModelTest, CantReplaceWithSameHosts) {
556  ChangeModelToLoadState();
557  ASSERT_TRUE(model()->CanReplaceKeyword(L"foo", GURL("http://foo.com"), NULL));
558  TemplateURL* t_url = AddKeywordWithDate(L"foo", false, "http://foo.com",
559                                          L"name1", true, Time());
560
561  // Can still replace, newly added template url is marked safe to replace.
562  ASSERT_TRUE(model()->CanReplaceKeyword(L"bar", GURL("http://foo.com"), NULL));
563
564  // ResetTemplateURL marks the TemplateURL as unsafe to replace, so it should
565  // no longer be replaceable.
566  model()->ResetTemplateURL(t_url, t_url->short_name(), t_url->keyword(),
567                           t_url->url()->url());
568
569  ASSERT_FALSE(model()->CanReplaceKeyword(L"bar",
570                                          GURL("http://foo.com"), NULL));
571}
572
573TEST_F(TemplateURLModelTest, HasDefaultSearchProvider) {
574  // We should have a default search provider even if we haven't loaded.
575  ASSERT_TRUE(model()->GetDefaultSearchProvider());
576
577  // Now force the model to load and make sure we still have a default.
578  VerifyLoad();
579
580  ASSERT_TRUE(model()->GetDefaultSearchProvider());
581}
582
583TEST_F(TemplateURLModelTest, DefaultSearchProviderLoadedFromPrefs) {
584  VerifyLoad();
585
586  TemplateURL* template_url = new TemplateURL();
587  template_url->SetURL("http://url", 0, 0);
588  template_url->SetSuggestionsURL("http://url2", 0, 0);
589  template_url->set_short_name(L"a");
590  template_url->set_safe_for_autoreplace(true);
591  template_url->set_date_created(Time::FromTimeT(100));
592
593  model()->Add(template_url);
594
595  const TemplateURLID id = template_url->id();
596
597  model()->SetDefaultSearchProvider(template_url);
598
599  BlockTillServiceProcessesRequests();
600
601  TemplateURL first_default_search_provider = *template_url;
602
603  template_url = NULL;
604
605  // Reset the model and don't load it. The template url we set as the default
606  // should be pulled from prefs now.
607  ResetModel(false);
608
609  // NOTE: This doesn't use AssertEquals as only a subset of the TemplateURLs
610  // value are persisted to prefs.
611  const TemplateURL* default_turl = model()->GetDefaultSearchProvider();
612  ASSERT_TRUE(default_turl);
613  ASSERT_TRUE(default_turl->url());
614  ASSERT_EQ("http://url", default_turl->url()->url());
615  ASSERT_TRUE(default_turl->suggestions_url());
616  ASSERT_EQ("http://url2", default_turl->suggestions_url()->url());
617  ASSERT_EQ(L"a", default_turl->short_name());
618  ASSERT_EQ(id, default_turl->id());
619
620  // Now do a load and make sure the default search provider really takes.
621  VerifyLoad();
622
623  ASSERT_TRUE(model()->GetDefaultSearchProvider());
624  AssertEquals(first_default_search_provider,
625               *model()->GetDefaultSearchProvider());
626}
627
628TEST_F(TemplateURLModelTest, BuildQueryTerms) {
629  struct TestData {
630    const std::string url;
631    const bool result;
632    // Keys and values are a semicolon separated list of expected values in the
633    // map.
634    const std::string keys;
635    const std::string values;
636  } data[] = {
637    // No query should return false.
638    { "http://blah/", false, "", "" },
639
640    // Query with empty key should return false.
641    { "http://blah/foo?=y", false, "", "" },
642
643    // Query with key occurring multiple times should return false.
644    { "http://blah/foo?x=y&x=z", false, "", "" },
645
646    { "http://blah/foo?x=y", true, "x", "y" },
647    { "http://blah/foo?x=y&y=z", true, "x;y", "y;z" },
648
649    // Key occurring multiple times should get an empty string.
650    { "http://blah/foo?x=y&x=z&y=z", true, "x;y", ";z" },
651  };
652
653  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
654    TemplateURLModel::QueryTerms terms;
655    ASSERT_EQ(data[i].result,
656              TemplateURLModel::BuildQueryTerms(GURL(data[i].url), &terms));
657    if (data[i].result) {
658      std::vector<std::string> keys;
659      std::vector<std::string> values;
660      SplitString(data[i].keys, ';', &keys);
661      SplitString(data[i].values, ';', &values);
662      ASSERT_TRUE(keys.size() == values.size());
663      ASSERT_EQ(keys.size(), terms.size());
664      for (size_t j = 0; j < keys.size(); ++j) {
665        TemplateURLModel::QueryTerms::iterator term_iterator =
666            terms.find(keys[j]);
667        ASSERT_TRUE(term_iterator != terms.end());
668        ASSERT_EQ(values[j], term_iterator->second);
669      }
670    }
671  }
672}
673
674TEST_F(TemplateURLModelTest, UpdateKeywordSearchTermsForURL) {
675  struct TestData {
676    const std::string url;
677    const std::wstring term;
678  } data[] = {
679    { "http://foo/", L"" },
680    { "http://foo/foo?q=xx", L"" },
681    { "http://x/bar?q=xx", L"" },
682    { "http://x/foo?y=xx", L"" },
683    { "http://x/foo?q=xx", L"xx" },
684    { "http://x/foo?a=b&q=xx", L"xx" },
685    { "http://x/foo?q=b&q=xx", L"" },
686  };
687
688  ChangeModelToLoadState();
689  AddKeywordWithDate(L"x", false, "http://x/foo?q={searchTerms}", L"name",
690                     false, Time());
691
692  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
693    history::URLVisitedDetails details;
694    details.row = history::URLRow(GURL(data[i].url));
695    details.transition = 0;
696    model()->UpdateKeywordSearchTermsForURL(details);
697    EXPECT_EQ(data[i].term, GetAndClearSearchTerm());
698  }
699}
700
701TEST_F(TemplateURLModelTest, DontUpdateKeywordSearchForNonReplaceable) {
702  struct TestData {
703    const std::string url;
704  } data[] = {
705    { "http://foo/" },
706    { "http://x/bar?q=xx" },
707    { "http://x/foo?y=xx" },
708  };
709
710  ChangeModelToLoadState();
711  AddKeywordWithDate(L"x", false, "http://x/foo", L"name", false, Time());
712
713  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
714    history::URLVisitedDetails details;
715    details.row = history::URLRow(GURL(data[i].url));
716    details.transition = 0;
717    model()->UpdateKeywordSearchTermsForURL(details);
718    ASSERT_EQ(std::wstring(), GetAndClearSearchTerm());
719  }
720}
721
722TEST_F(TemplateURLModelTest, ChangeGoogleBaseValue) {
723  // NOTE: Do not do a VerifyLoad() here as it will load the prepopulate data,
724  // which also has a {google:baseURL} keyword in it, which will confuse this
725  // test.
726  ChangeModelToLoadState();
727  SetGoogleBaseURL("http://google.com/");
728  const TemplateURL* t_url = AddKeywordWithDate(std::wstring(), true,
729      "{google:baseURL}?q={searchTerms}", L"name", false, Time());
730  ASSERT_EQ(t_url, model()->GetTemplateURLForHost("google.com"));
731  EXPECT_EQ("google.com", t_url->url()->GetHost());
732  EXPECT_EQ(L"google.com", t_url->keyword());
733
734  // Change the Google base url.
735  test_util_.ResetObserverCount();
736  SetGoogleBaseURL("http://foo.com/");
737  VerifyObserverCount(1);
738
739  // Make sure the host->TemplateURL map was updated appropriately.
740  ASSERT_EQ(t_url, model()->GetTemplateURLForHost("foo.com"));
741  EXPECT_TRUE(model()->GetTemplateURLForHost("google.com") == NULL);
742  EXPECT_EQ("foo.com", t_url->url()->GetHost());
743  EXPECT_EQ(L"foo.com", t_url->keyword());
744  EXPECT_EQ("http://foo.com/?q=x", t_url->url()->ReplaceSearchTerms(*t_url,
745      L"x", TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()));
746}
747
748struct QueryHistoryCallbackImpl {
749  QueryHistoryCallbackImpl() : success(false) {}
750
751  void Callback(HistoryService::Handle handle,
752                bool success, const history::URLRow* row,
753                history::VisitVector* visits) {
754    this->success = success;
755    if (row)
756      this->row = *row;
757    if (visits)
758      this->visits = *visits;
759  }
760
761  bool success;
762  history::URLRow row;
763  history::VisitVector visits;
764};
765
766// Make sure TemplateURLModel generates a KEYWORD_GENERATED visit for
767// KEYWORD visits.
768TEST_F(TemplateURLModelTest, GenerateVisitOnKeyword) {
769  VerifyLoad();
770  profile()->CreateHistoryService(true, false);
771
772  // Create a keyword.
773  TemplateURL* t_url = AddKeywordWithDate(
774      L"keyword", false, "http://foo.com/foo?query={searchTerms}",
775      L"keyword", true, base::Time::Now());
776
777  // Add a visit that matches the url of the keyword.
778  HistoryService* history =
779      profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
780  history->AddPage(
781      GURL(t_url->url()->ReplaceSearchTerms(*t_url, L"blah", 0,
782                                            std::wstring())),
783      NULL, 0, GURL(), PageTransition::KEYWORD, history::RedirectList(),
784      history::SOURCE_BROWSED, false);
785
786  // Wait for history to finish processing the request.
787  profile()->BlockUntilHistoryProcessesPendingRequests();
788
789  // Query history for the generated url.
790  CancelableRequestConsumer consumer;
791  QueryHistoryCallbackImpl callback;
792  history->QueryURL(GURL("http://keyword"), true, &consumer,
793      NewCallback(&callback, &QueryHistoryCallbackImpl::Callback));
794
795  // Wait for the request to be processed.
796  profile()->BlockUntilHistoryProcessesPendingRequests();
797
798  // And make sure the url and visit were added.
799  EXPECT_TRUE(callback.success);
800  EXPECT_NE(0, callback.row.id());
801  ASSERT_EQ(1U, callback.visits.size());
802  EXPECT_EQ(PageTransition::KEYWORD_GENERATED,
803            PageTransition::StripQualifier(callback.visits[0].transition));
804}
805
806// Make sure that the load routine deletes prepopulated engines that no longer
807// exist in the prepopulate data.
808TEST_F(TemplateURLModelTest, LoadDeletesUnusedProvider) {
809  // Create a preloaded template url. Add it to a loaded model and wait for the
810  // saves to finish.
811  TemplateURL* t_url = CreatePreloadedTemplateURL();
812  ChangeModelToLoadState();
813  model()->Add(t_url);
814  ASSERT_TRUE(model()->GetTemplateURLForKeyword(L"unittest") != NULL);
815  BlockTillServiceProcessesRequests();
816
817  // Ensure that merging clears this engine.
818  ResetModel(true);
819  ASSERT_TRUE(model()->GetTemplateURLForKeyword(L"unittest") == NULL);
820
821  // Wait for any saves to finish.
822  BlockTillServiceProcessesRequests();
823
824  // Reload the model to verify that the database was updated as a result of the
825  // merge.
826  ResetModel(true);
827  ASSERT_TRUE(model()->GetTemplateURLForKeyword(L"unittest") == NULL);
828}
829
830// Make sure that load routine doesn't delete prepopulated engines that no
831// longer exist in the prepopulate data if it has been modified by the user.
832TEST_F(TemplateURLModelTest, LoadRetainsModifiedProvider) {
833  // Create a preloaded template url and add it to a loaded model.
834  TemplateURL* t_url = CreatePreloadedTemplateURL();
835  t_url->set_safe_for_autoreplace(false);
836  ChangeModelToLoadState();
837  model()->Add(t_url);
838
839  // Do the copy after t_url is added so that the id is set.
840  TemplateURL copy_t_url = *t_url;
841  ASSERT_EQ(t_url, model()->GetTemplateURLForKeyword(L"unittest"));
842
843  // Wait for any saves to finish.
844  BlockTillServiceProcessesRequests();
845
846  // Ensure that merging won't clear it if the user has edited it.
847  ResetModel(true);
848  const TemplateURL* url_for_unittest =
849      model()->GetTemplateURLForKeyword(L"unittest");
850  ASSERT_TRUE(url_for_unittest != NULL);
851  AssertEquals(copy_t_url, *url_for_unittest);
852
853  // Wait for any saves to finish.
854  BlockTillServiceProcessesRequests();
855
856  // Reload the model to verify that save/reload retains the item.
857  ResetModel(true);
858  ASSERT_TRUE(model()->GetTemplateURLForKeyword(L"unittest") != NULL);
859}
860
861// Make sure that load routine doesn't delete
862// prepopulated engines that no longer exist in the prepopulate data if
863// it has been modified by the user.
864TEST_F(TemplateURLModelTest, LoadSavesPrepopulatedDefaultSearchProvider) {
865  VerifyLoad();
866  // Verify that the default search provider is set to something.
867  ASSERT_TRUE(model()->GetDefaultSearchProvider() != NULL);
868  TemplateURL default_url = *model()->GetDefaultSearchProvider();
869
870  // Wait for any saves to finish.
871  BlockTillServiceProcessesRequests();
872
873  // Reload the model and check that the default search provider
874  // was properly saved.
875  ResetModel(true);
876  ASSERT_TRUE(model()->GetDefaultSearchProvider() != NULL);
877  AssertEquals(default_url, *model()->GetDefaultSearchProvider());
878}
879
880// Make sure that the load routine doesn't delete
881// prepopulated engines that no longer exist in the prepopulate data if
882// it is the default search provider.
883TEST_F(TemplateURLModelTest, LoadRetainsDefaultProvider) {
884  // Set the default search provider to a preloaded template url which
885  // is not in the current set of preloaded template urls and save
886  // the result.
887  TemplateURL* t_url = CreatePreloadedTemplateURL();
888  ChangeModelToLoadState();
889  model()->Add(t_url);
890  model()->SetDefaultSearchProvider(t_url);
891  // Do the copy after t_url is added and set as default so that its
892  // internal state is correct.
893  TemplateURL copy_t_url = *t_url;
894
895  ASSERT_EQ(t_url, model()->GetTemplateURLForKeyword(L"unittest"));
896  ASSERT_EQ(t_url, model()->GetDefaultSearchProvider());
897  BlockTillServiceProcessesRequests();
898
899  // Ensure that merging won't clear the prepopulated template url
900  // which is no longer present if it's the default engine.
901  ResetModel(true);
902  {
903    const TemplateURL* keyword_url =
904        model()->GetTemplateURLForKeyword(L"unittest");
905    ASSERT_TRUE(keyword_url != NULL);
906    AssertEquals(copy_t_url, *keyword_url);
907    ASSERT_EQ(keyword_url, model()->GetDefaultSearchProvider());
908  }
909
910  // Wait for any saves to finish.
911  BlockTillServiceProcessesRequests();
912
913  // Reload the model to verify that the update was saved.
914  ResetModel(true);
915  {
916    const TemplateURL* keyword_url =
917        model()->GetTemplateURLForKeyword(L"unittest");
918    ASSERT_TRUE(keyword_url != NULL);
919    AssertEquals(copy_t_url, *keyword_url);
920    ASSERT_EQ(keyword_url, model()->GetDefaultSearchProvider());
921  }
922}
923
924// Make sure that the load routine updates the url of a preexisting
925// default search engine provider and that the result is saved correctly.
926TEST_F(TemplateURLModelTest, LoadUpdatesDefaultSearchURL) {
927  TestLoadUpdatingPreloadedURL(0);
928}
929
930// Make sure that the load routine updates the url of a preexisting
931// non-default search engine provider and that the result is saved correctly.
932TEST_F(TemplateURLModelTest, LoadUpdatesSearchURL) {
933  TestLoadUpdatingPreloadedURL(1);
934}
935
936// Make sure that the load does update of auto-keywords correctly.
937// This test basically verifies that no asserts or crashes occur
938// during this operation.
939TEST_F(TemplateURLModelTest, LoadDoesAutoKeywordUpdate) {
940  std::wstring prepopulated_url;
941  TemplateURL* t_url = CreateReplaceablePreloadedTemplateURL(
942      0, &prepopulated_url);
943  t_url->set_safe_for_autoreplace(false);
944  t_url->SetURL("{google:baseURL}?q={searchTerms}", 0, 0);
945  t_url->set_autogenerate_keyword(true);
946
947  // Then add it to the model and save it all.
948  ChangeModelToLoadState();
949  model()->Add(t_url);
950  BlockTillServiceProcessesRequests();
951
952  // Now reload the model and verify that the merge updates the url.
953  ResetModel(true);
954
955  // Wait for any saves to finish.
956  BlockTillServiceProcessesRequests();
957}
958
959// Simulates failing to load the webdb and makes sure the default search
960// provider is valid.
961TEST_F(TemplateURLModelTest, FailedInit) {
962  VerifyLoad();
963
964  test_util_.ClearModel();
965  test_util_.GetWebDataService()->UnloadDatabase();
966  test_util_.GetWebDataService()->set_failed_init(true);
967
968  ResetModel(false);
969  model()->Load();
970  BlockTillServiceProcessesRequests();
971
972  ASSERT_TRUE(model()->GetDefaultSearchProvider());
973}
974
975// Verifies that if the default search URL preference is managed, we report
976// the default search as managed.
977TEST_F(TemplateURLModelTest, ReportDefaultSearchIsManaged) {
978  TestingPrefService* service = profile()->GetTestingPrefService();
979  service->RegisterStringPref(prefs::kDefaultSearchProviderSearchURL,
980                              std::string());
981  service->SetManagedPref(prefs::kDefaultSearchProviderSearchURL,
982      Value::CreateStringValue("http://test.com/{searchTerms}"));
983  ASSERT_TRUE(model()->IsDefaultSearchManaged());
984}
985
986