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