search_provider_unittest.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file. 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/search_provider.h" 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/command_line.h" 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/metrics/field_trial.h" 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/prefs/pref_service.h" 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/run_loop.h" 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/strings/string_util.h" 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/strings/utf_string_conversions.h" 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/time/time.h" 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "build/build_config.h" 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_controller.h" 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_input.h" 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_match.h" 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_provider.h" 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/autocomplete/history_url_provider.h" 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/history/history_service.h" 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/history/history_service_factory.h" 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/omnibox/omnibox_field_trial.h" 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/search/search.h" 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/search_engines/template_url.h" 2753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "chrome/browser/search_engines/template_url_service.h" 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/search_engines/template_url_service_factory.h" 2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/metrics/entropy_provider.h" 31c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)#include "chrome/common/pref_names.h" 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/test/base/testing_browser_process.h" 33f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles)#include "chrome/test/base/testing_profile.h" 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/test/test_browser_thread.h" 35f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles)#include "net/url_request/test_url_fetcher_factory.h" 36f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles)#include "net/url_request/url_request_status.h" 37f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h" 38f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) 39f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) 40c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)// SearchProviderTest --------------------------------------------------------- 41f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) 42f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles)// The following environment is configured for these tests: 43c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)// . The TemplateURL default_t_url_ is set as the default provider. 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// TemplateURL has a valid suggest and search URL. 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// . The URL created by using the search term term1_ with default_t_url_ is 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// added to history. 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// . The URL created by using the search term keyword_term_ with keyword_t_url_ 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// is added to history. 5002772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch// . test_factory_ is set as the URLFetcherFactory. 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class SearchProviderTest : public testing::Test, 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) public AutocompleteProviderListener { 53f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) public: 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) struct ResultInfo { 5509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) { 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ResultInfo(GURL gurl, 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) AutocompleteMatch::Type result_type, 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) string16 fill_into_edit) 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : gurl(gurl), 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result_type(result_type), 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) fill_into_edit(fill_into_edit) { 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const GURL gurl; 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const AutocompleteMatch::Type result_type; 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const string16 fill_into_edit; 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) }; 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) struct TestData { 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const string16 input; 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const size_t num_results; 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const ResultInfo output[3]; 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) }; 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) SearchProviderTest() 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : default_t_url_(NULL), 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) term1_(ASCIIToUTF16("term1")), 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) keyword_t_url_(NULL), 80f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) keyword_term_(ASCIIToUTF16("keyword")), 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ui_thread_(content::BrowserThread::UI, &message_loop_), 82f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) io_thread_(content::BrowserThread::IO), 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) quit_when_done_(false) { 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) io_thread_.Start(); 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) static void SetUpTestCase(); 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) static void TearDownTestCase(); 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 90f523d2789ac2f83c4eca0ee4d5161bfdb5f2d052Torne (Richard Coles) // See description above class for what this registers. 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) virtual void SetUp() OVERRIDE; 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) virtual void TearDown() OVERRIDE; 9309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 9409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 9509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 9609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) protected: 9709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 9809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) static base::FieldTrialList* field_trial_list_; 9909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 10009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) // Default value used for testing. 101c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) static const std::string kNotApplicable; 102 103 // Adds a search for |term|, using the engine |t_url| to the history, and 104 // returns the URL for that search. 105 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count); 106 107 // Looks for a match in |provider_| with |contents| equal to |contents|. 108 // Sets |match| to it if found. Returns whether |match| was set. 109 bool FindMatchWithContents(const string16& contents, 110 AutocompleteMatch* match); 111 112 // Looks for a match in |provider_| with destination |url|. Sets |match| to 113 // it if found. Returns whether |match| was set. 114 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 115 116 // AutocompleteProviderListener: 117 // If we're waiting for the provider to finish, this exits the message loop. 118 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 119 120 // Waits until the provider instantiates a URLFetcher and returns it. 121 net::TestURLFetcher* WaitUntilURLFetcherIsReady(int fetcher_id); 122 123 // Runs a nested message loop until provider_ is done. The message loop is 124 // exited by way of OnProviderUpdate. 125 void RunTillProviderDone(); 126 127 // Invokes Start on provider_, then runs all pending tasks. 128 void QueryForInput(const string16& text, 129 bool prevent_inline_autocomplete, 130 bool prefer_keyword); 131 132 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 133 // non-NULL, sets it to the "what you typed" entry for |text|. 134 void QueryForInputAndSetWYTMatch(const string16& text, 135 AutocompleteMatch* wyt_match); 136 137 // Notifies the URLFetcher for the suggest query corresponding to the default 138 // search provider that it's done. 139 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 140 void FinishDefaultSuggestQuery(); 141 142 // See description above class for details of these fields. 143 TemplateURL* default_t_url_; 144 const string16 term1_; 145 GURL term1_url_; 146 TemplateURL* keyword_t_url_; 147 const string16 keyword_term_; 148 GURL keyword_url_; 149 150 base::MessageLoopForUI message_loop_; 151 content::TestBrowserThread ui_thread_; 152 content::TestBrowserThread io_thread_; 153 154 // URLFetcherFactory implementation registered. 155 net::TestURLFetcherFactory test_factory_; 156 157 // Profile we use. 158 TestingProfile profile_; 159 160 // The provider. 161 scoped_refptr<SearchProvider> provider_; 162 163 // If true, OnProviderUpdate exits out of the current message loop. 164 bool quit_when_done_; 165 166 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 167}; 168 169// static 170base::FieldTrialList* SearchProviderTest::field_trial_list_ = NULL; 171const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 172 173// static 174void SearchProviderTest::SetUpTestCase() { 175 // Set up Suggest experiments. 176 field_trial_list_ = new base::FieldTrialList( 177 new metrics::SHA1EntropyProvider("foo")); 178 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 179 "AutocompleteDynamicTrial_0", "DefaultGroup"); 180 trial->group(); 181} 182 183// static 184void SearchProviderTest::TearDownTestCase() { 185 // Make sure the global instance of FieldTrialList is gone. 186 delete field_trial_list_; 187} 188 189void SearchProviderTest::SetUp() { 190 // Make sure that fetchers are automatically ungregistered upon destruction. 191 test_factory_.set_remove_fetcher_on_delete(true); 192 193 // We need both the history service and template url model loaded. 194 profile_.CreateHistoryService(true, false); 195 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 196 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 197 198 TemplateURLService* turl_model = 199 TemplateURLServiceFactory::GetForProfile(&profile_); 200 201 turl_model->Load(); 202 203 // Reset the default TemplateURL. 204 TemplateURLData data; 205 data.short_name = ASCIIToUTF16("t"); 206 data.SetURL("http://defaultturl/{searchTerms}"); 207 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 208 data.instant_url = "http://does/not/exist?strk=1"; 209 data.search_terms_replacement_key = "strk"; 210 default_t_url_ = new TemplateURL(&profile_, data); 211 turl_model->Add(default_t_url_); 212 turl_model->SetDefaultSearchProvider(default_t_url_); 213 TemplateURLID default_provider_id = default_t_url_->id(); 214 ASSERT_NE(0, default_provider_id); 215 216 // Add url1, with search term term1_. 217 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 218 219 // Create another TemplateURL. 220 data.short_name = ASCIIToUTF16("k"); 221 data.SetKeyword(ASCIIToUTF16("k")); 222 data.SetURL("http://keyword/{searchTerms}"); 223 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 224 keyword_t_url_ = new TemplateURL(&profile_, data); 225 turl_model->Add(keyword_t_url_); 226 ASSERT_NE(0, keyword_t_url_->id()); 227 228 // Add a page and search term for keyword_t_url_. 229 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 230 231 // Keywords are updated by the InMemoryHistoryBackend only after the message 232 // has been processed on the history thread. Block until history processes all 233 // requests to ensure the InMemoryDatabase is the state we expect it. 234 profile_.BlockUntilHistoryProcessesPendingRequests(); 235 236 provider_ = new SearchProvider(this, &profile_); 237 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 238} 239 240void SearchProviderTest::TearDown() { 241 message_loop_.RunUntilIdle(); 242 243 // Shutdown the provider before the profile. 244 provider_ = NULL; 245} 246 247void SearchProviderTest::RunTest(TestData* cases, 248 int num_cases, 249 bool prefer_keyword) { 250 ACMatches matches; 251 for (int i = 0; i < num_cases; ++i) { 252 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(), 253 false, prefer_keyword, true, 254 AutocompleteInput::ALL_MATCHES); 255 provider_->Start(input, false); 256 matches = provider_->matches(); 257 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input + 258 ASCIIToUTF16("; prefer_keyword was: ") + 259 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 260 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 261 if (matches.size() == cases[i].num_results) { 262 for (size_t j = 0; j < cases[i].num_results; ++j) { 263 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 264 diagnostic_details; 265 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 266 diagnostic_details; 267 EXPECT_EQ(cases[i].output[j].fill_into_edit, 268 matches[j].fill_into_edit) << 269 diagnostic_details; 270 } 271 } 272 } 273} 274 275void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 276 if (quit_when_done_ && provider_->done()) { 277 quit_when_done_ = false; 278 message_loop_.Quit(); 279 } 280} 281 282net::TestURLFetcher* SearchProviderTest::WaitUntilURLFetcherIsReady( 283 int fetcher_id) { 284 net::TestURLFetcher* url_fetcher = test_factory_.GetFetcherByID(fetcher_id); 285 for (; !url_fetcher; url_fetcher = test_factory_.GetFetcherByID(fetcher_id)) 286 message_loop_.RunUntilIdle(); 287 return url_fetcher; 288} 289 290void SearchProviderTest::RunTillProviderDone() { 291 if (provider_->done()) 292 return; 293 294 quit_when_done_ = true; 295#if defined(OS_ANDROID) 296 // Android doesn't have Run(), only Start(). 297 message_loop_.Start(); 298#else 299 base::RunLoop run_loop; 300 run_loop.Run(); 301#endif 302} 303 304void SearchProviderTest::QueryForInput(const string16& text, 305 bool prevent_inline_autocomplete, 306 bool prefer_keyword) { 307 // Start a query. 308 AutocompleteInput input(text, string16::npos, string16(), GURL(), 309 prevent_inline_autocomplete, 310 prefer_keyword, true, AutocompleteInput::ALL_MATCHES); 311 provider_->Start(input, false); 312 313 // RunUntilIdle so that the task scheduled by SearchProvider to create the 314 // URLFetchers runs. 315 message_loop_.RunUntilIdle(); 316} 317 318void SearchProviderTest::QueryForInputAndSetWYTMatch( 319 const string16& text, 320 AutocompleteMatch* wyt_match) { 321 QueryForInput(text, false, false); 322 profile_.BlockUntilHistoryProcessesPendingRequests(); 323 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 324 if (!wyt_match) 325 return; 326 ASSERT_GE(provider_->matches().size(), 1u); 327 EXPECT_TRUE(FindMatchWithDestination(GURL( 328 default_t_url_->url_ref().ReplaceSearchTerms( 329 TemplateURLRef::SearchTermsArgs(text))), 330 wyt_match)); 331} 332 333GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 334 string16 term, 335 int visit_count) { 336 HistoryService* history = 337 HistoryServiceFactory::GetForProfile(&profile_, 338 Profile::EXPLICIT_ACCESS); 339 GURL search(t_url->url_ref().ReplaceSearchTerms( 340 TemplateURLRef::SearchTermsArgs(term))); 341 static base::Time last_added_time; 342 last_added_time = std::max(base::Time::Now(), 343 last_added_time + base::TimeDelta::FromMicroseconds(1)); 344 history->AddPageWithDetails(search, string16(), visit_count, visit_count, 345 last_added_time, false, history::SOURCE_BROWSED); 346 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 347 return search; 348} 349 350bool SearchProviderTest::FindMatchWithContents(const string16& contents, 351 AutocompleteMatch* match) { 352 for (ACMatches::const_iterator i = provider_->matches().begin(); 353 i != provider_->matches().end(); ++i) { 354 if (i->contents == contents) { 355 *match = *i; 356 return true; 357 } 358 } 359 return false; 360} 361 362bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 363 AutocompleteMatch* match) { 364 for (ACMatches::const_iterator i = provider_->matches().begin(); 365 i != provider_->matches().end(); ++i) { 366 if (i->destination_url == url) { 367 *match = *i; 368 return true; 369 } 370 } 371 return false; 372} 373 374void SearchProviderTest::FinishDefaultSuggestQuery() { 375 net::TestURLFetcher* default_fetcher = WaitUntilURLFetcherIsReady( 376 SearchProvider::kDefaultProviderURLFetcherID); 377 ASSERT_TRUE(default_fetcher); 378 379 // Tell the SearchProvider the default suggest query is done. 380 default_fetcher->set_response_code(200); 381 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 382} 383 384 385// Actual Tests --------------------------------------------------------------- 386 387// Make sure we query history for the default provider and a URLFetcher is 388// created for the default provider suggest results. 389TEST_F(SearchProviderTest, QueryDefaultProvider) { 390 string16 term = term1_.substr(0, term1_.length() - 1); 391 QueryForInput(term, false, false); 392 393 // Make sure the default providers suggest service was queried. 394 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 395 SearchProvider::kDefaultProviderURLFetcherID); 396 ASSERT_TRUE(fetcher); 397 398 // And the URL matches what we expected. 399 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 400 TemplateURLRef::SearchTermsArgs(term))); 401 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 402 403 // Tell the SearchProvider the suggest query is done. 404 fetcher->set_response_code(200); 405 fetcher->delegate()->OnURLFetchComplete(fetcher); 406 fetcher = NULL; 407 408 // Run till the history results complete. 409 RunTillProviderDone(); 410 411 // The SearchProvider is done. Make sure it has a result for the history 412 // term term1. 413 AutocompleteMatch term1_match; 414 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 415 // Term1 should not have a description, it's set later. 416 EXPECT_TRUE(term1_match.description.empty()); 417 418 AutocompleteMatch wyt_match; 419 EXPECT_TRUE(FindMatchWithDestination( 420 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 421 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 422 EXPECT_TRUE(wyt_match.description.empty()); 423 424 // The match for term1 should be more relevant than the what you typed result. 425 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 426} 427 428TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 429 string16 term = term1_.substr(0, term1_.length() - 1); 430 QueryForInput(term, true, false); 431 432 ASSERT_FALSE(provider_->matches().empty()); 433 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 434 provider_->matches()[0].type); 435} 436 437// Issues a query that matches the registered keyword and makes sure history 438// is queried as well as URLFetchers getting created. 439TEST_F(SearchProviderTest, QueryKeywordProvider) { 440 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 441 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 442 false, 443 false); 444 445 // Make sure the default providers suggest service was queried. 446 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 447 SearchProvider::kDefaultProviderURLFetcherID); 448 ASSERT_TRUE(default_fetcher); 449 450 // Tell the SearchProvider the default suggest query is done. 451 default_fetcher->set_response_code(200); 452 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 453 default_fetcher = NULL; 454 455 // Make sure the keyword providers suggest service was queried. 456 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 457 SearchProvider::kKeywordProviderURLFetcherID); 458 ASSERT_TRUE(keyword_fetcher); 459 460 // And the URL matches what we expected. 461 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 462 TemplateURLRef::SearchTermsArgs(term))); 463 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 464 465 // Tell the SearchProvider the keyword suggest query is done. 466 keyword_fetcher->set_response_code(200); 467 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 468 keyword_fetcher = NULL; 469 470 // Run till the history results complete. 471 RunTillProviderDone(); 472 473 // The SearchProvider is done. Make sure it has a result for the history 474 // term keyword. 475 AutocompleteMatch match; 476 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 477 478 // The match should have an associated keyword. 479 EXPECT_FALSE(match.keyword.empty()); 480 481 // The fill into edit should contain the keyword. 482 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_, 483 match.fill_into_edit); 484} 485 486TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 487 // None of the following input strings should be sent to the suggest server, 488 // because they may contain private data. 489 const char* inputs[] = { 490 "username:password", 491 "http://username:password", 492 "https://username:password", 493 "username:password@hostname", 494 "http://username:password@hostname/", 495 "file://filename", 496 "data://data", 497 "unknownscheme:anything", 498 "http://hostname/?query=q", 499 "http://hostname/path#ref", 500 "https://hostname/path", 501 }; 502 503 for (size_t i = 0; i < arraysize(inputs); ++i) { 504 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 505 // Make sure the default providers suggest service was not queried. 506 ASSERT_TRUE(test_factory_.GetFetcherByID( 507 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 508 // Run till the history results complete. 509 RunTillProviderDone(); 510 } 511} 512 513TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 514 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 515 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 516 GURL url = AddSearchToHistory(default_t_url_, 517 ASCIIToUTF16("docs.google.com"), 1); 518 519 // Add the term as a url. 520 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 521 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1, 522 base::Time::Now(), false, history::SOURCE_BROWSED); 523 profile_.BlockUntilHistoryProcessesPendingRequests(); 524 525 AutocompleteMatch wyt_match; 526 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 527 &wyt_match)); 528 529 // There should be two matches, one for what you typed, the other for 530 // 'docs.google.com'. The search term should have a lower priority than the 531 // what you typed match. 532 ASSERT_EQ(2u, provider_->matches().size()); 533 AutocompleteMatch term_match; 534 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 535 EXPECT_GT(wyt_match.relevance, term_match.relevance); 536} 537 538// A multiword search with one visit should not autocomplete until multiple 539// words are typed. 540TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 541 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 542 1)); 543 profile_.BlockUntilHistoryProcessesPendingRequests(); 544 545 AutocompleteMatch wyt_match; 546 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 547 &wyt_match)); 548 ASSERT_EQ(2u, provider_->matches().size()); 549 AutocompleteMatch term_match; 550 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 551 EXPECT_GT(wyt_match.relevance, term_match.relevance); 552 553 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 554 &wyt_match)); 555 ASSERT_EQ(2u, provider_->matches().size()); 556 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 557 EXPECT_GT(term_match.relevance, wyt_match.relevance); 558} 559 560// A multiword search with more than one visit should autocomplete immediately. 561TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 562 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 563 2)); 564 profile_.BlockUntilHistoryProcessesPendingRequests(); 565 566 AutocompleteMatch wyt_match; 567 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 568 &wyt_match)); 569 ASSERT_EQ(2u, provider_->matches().size()); 570 AutocompleteMatch term_match; 571 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 572 EXPECT_GT(term_match.relevance, wyt_match.relevance); 573} 574 575// Autocompletion should work at a word boundary after a space. 576TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 577 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 578 2)); 579 profile_.BlockUntilHistoryProcessesPendingRequests(); 580 581 AutocompleteMatch wyt_match; 582 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 583 &wyt_match)); 584 ASSERT_EQ(2u, provider_->matches().size()); 585 AutocompleteMatch term_match; 586 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 587 EXPECT_GT(term_match.relevance, wyt_match.relevance); 588} 589 590// Newer multiword searches should score more highly than older ones. 591TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 592 GURL term_url_a(AddSearchToHistory(default_t_url_, 593 ASCIIToUTF16("three searches aaa"), 1)); 594 GURL term_url_b(AddSearchToHistory(default_t_url_, 595 ASCIIToUTF16("three searches bbb"), 1)); 596 profile_.BlockUntilHistoryProcessesPendingRequests(); 597 598 AutocompleteMatch wyt_match; 599 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 600 &wyt_match)); 601 ASSERT_EQ(3u, provider_->matches().size()); 602 AutocompleteMatch term_match_a; 603 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 604 AutocompleteMatch term_match_b; 605 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 606 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 607 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 608} 609 610// An autocompleted multiword search should not be replaced by a different 611// autocompletion while the user is still typing a valid prefix. 612TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 613 GURL term_url_a(AddSearchToHistory(default_t_url_, 614 ASCIIToUTF16("four searches aaa"), 2)); 615 GURL term_url_b(AddSearchToHistory(default_t_url_, 616 ASCIIToUTF16("four searches bbb"), 1)); 617 profile_.BlockUntilHistoryProcessesPendingRequests(); 618 619 AutocompleteMatch wyt_match; 620 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 621 &wyt_match)); 622 ASSERT_EQ(3u, provider_->matches().size()); 623 AutocompleteMatch term_match_a; 624 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 625 AutocompleteMatch term_match_b; 626 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 627 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 628 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 629 630 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 631 &wyt_match)); 632 ASSERT_EQ(3u, provider_->matches().size()); 633 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 634 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 635 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 636 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 637} 638 639// Non-completable multiword searches should not crowd out single-word searches. 640TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 641 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 642 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 643 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 644 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 645 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 646 profile_.BlockUntilHistoryProcessesPendingRequests(); 647 648 AutocompleteMatch wyt_match; 649 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 650 &wyt_match)); 651 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 652 AutocompleteMatch term_match; 653 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 654 EXPECT_GT(term_match.relevance, wyt_match.relevance); 655} 656 657// Inline autocomplete matches regardless of case differences from the input. 658TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 659 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 660 profile_.BlockUntilHistoryProcessesPendingRequests(); 661 662 AutocompleteMatch wyt_match; 663 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 664 &wyt_match)); 665 ASSERT_EQ(2u, provider_->matches().size()); 666 AutocompleteMatch term_match; 667 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 668 EXPECT_GT(term_match.relevance, wyt_match.relevance); 669 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 670 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 671} 672 673// Verifies AutocompleteControllers return results (including keyword 674// results) in the right order and set descriptions for them correctly. 675TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 676 // Add an entry that corresponds to a keyword search with 'term2'. 677 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 678 profile_.BlockUntilHistoryProcessesPendingRequests(); 679 680 AutocompleteController controller(&profile_, NULL, 681 AutocompleteProvider::TYPE_SEARCH); 682 controller.Start(AutocompleteInput( 683 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(), false, false, 684 true, AutocompleteInput::ALL_MATCHES)); 685 const AutocompleteResult& result = controller.result(); 686 687 // There should be three matches, one for the keyword history, one for 688 // keyword provider's what-you-typed, and one for the default provider's 689 // what you typed, in that order. 690 ASSERT_EQ(3u, result.size()); 691 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 692 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 693 result.match_at(1).type); 694 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 695 result.match_at(2).type); 696 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 697 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 698 699 // The two keyword results should come with the keyword we expect. 700 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 701 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 702 // The default provider has a different keyword. (We don't explicitly 703 // set it during this test, so all we do is assert that it's different.) 704 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 705 706 // The top result will always have a description. The third result, 707 // coming from a different provider than the first two, should also. 708 // Whether the second result has one doesn't matter much. (If it was 709 // missing, people would infer that it's the same search provider as 710 // the one above it.) 711 EXPECT_FALSE(result.match_at(0).description.empty()); 712 EXPECT_FALSE(result.match_at(2).description.empty()); 713 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 714} 715 716TEST_F(SearchProviderTest, KeywordVerbatim) { 717 TestData cases[] = { 718 // Test a simple keyword input. 719 { ASCIIToUTF16("k foo"), 2, 720 { ResultInfo(GURL("http://keyword/foo"), 721 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 722 ASCIIToUTF16("k foo")), 723 ResultInfo(GURL("http://defaultturl/k%20foo"), 724 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 725 ASCIIToUTF16("k foo") ) } }, 726 727 // Make sure extra whitespace after the keyword doesn't change the 728 // keyword verbatim query. 729 { ASCIIToUTF16("k foo"), 2, 730 { ResultInfo(GURL("http://keyword/foo"), 731 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 732 ASCIIToUTF16("k foo")), 733 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 734 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 735 ASCIIToUTF16("k foo")) } }, 736 // Leading whitespace should be stripped before SearchProvider gets the 737 // input; hence there are no tests here about how it handles those inputs. 738 739 // But whitespace elsewhere in the query string should matter to both 740 // matches. 741 { ASCIIToUTF16("k foo bar"), 2, 742 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 743 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 744 ASCIIToUTF16("k foo bar")), 745 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 746 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 747 ASCIIToUTF16("k foo bar")) } }, 748 // Note in the above test case we don't test trailing whitespace because 749 // SearchProvider still doesn't handle this well. See related bugs: 750 // 102690, 99239, 164635. 751 752 // Keywords can be prefixed by certain things that should get ignored 753 // when constructing the keyword match. 754 { ASCIIToUTF16("www.k foo"), 2, 755 { ResultInfo(GURL("http://keyword/foo"), 756 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 757 ASCIIToUTF16("k foo")), 758 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 759 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 760 ASCIIToUTF16("www.k foo")) } }, 761 { ASCIIToUTF16("http://k foo"), 2, 762 { ResultInfo(GURL("http://keyword/foo"), 763 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 764 ASCIIToUTF16("k foo")), 765 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 766 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 767 ASCIIToUTF16("http://k foo")) } }, 768 { ASCIIToUTF16("http://www.k foo"), 2, 769 { ResultInfo(GURL("http://keyword/foo"), 770 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 771 ASCIIToUTF16("k foo")), 772 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 773 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 774 ASCIIToUTF16("http://www.k foo")) } }, 775 776 // A keyword with no remaining input shouldn't get a keyword 777 // verbatim match. 778 { ASCIIToUTF16("k"), 1, 779 { ResultInfo(GURL("http://defaultturl/k"), 780 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 781 ASCIIToUTF16("k")) } }, 782 { ASCIIToUTF16("k "), 1, 783 { ResultInfo(GURL("http://defaultturl/k%20"), 784 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 785 ASCIIToUTF16("k ")) } } 786 787 // The fact that verbatim queries to keyword are handled by KeywordProvider 788 // not SearchProvider is tested in 789 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 790 }; 791 792 // Test not in keyword mode. 793 RunTest(cases, arraysize(cases), false); 794 795 // Test in keyword mode. (Both modes should give the same result.) 796 RunTest(cases, arraysize(cases), true); 797} 798 799// Ensures command-line flags are reflected in the URLs the search provider 800// generates. 801TEST_F(SearchProviderTest, CommandLineOverrides) { 802 TemplateURLService* turl_model = 803 TemplateURLServiceFactory::GetForProfile(&profile_); 804 805 TemplateURLData data; 806 data.short_name = ASCIIToUTF16("default"); 807 data.SetKeyword(data.short_name); 808 data.SetURL("{google:baseURL}{searchTerms}"); 809 default_t_url_ = new TemplateURL(&profile_, data); 810 turl_model->Add(default_t_url_); 811 turl_model->SetDefaultSearchProvider(default_t_url_); 812 813 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 814 "http://www.bar.com/"); 815 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 816 switches::kExtraSearchQueryParams, "a=b"); 817 818 TestData cases[] = { 819 { ASCIIToUTF16("k a"), 2, 820 { ResultInfo(GURL("http://keyword/a"), 821 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 822 ASCIIToUTF16("k a")), 823 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 824 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 825 ASCIIToUTF16("k a")) } }, 826 }; 827 828 RunTest(cases, arraysize(cases), false); 829} 830 831// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 832// Also verifies that just the *first* navigational result is listed as a match 833// if suggested relevance scores were not sent. 834TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 835 QueryForInput(ASCIIToUTF16("a.c"), false, false); 836 837 // Make sure the default providers suggest service was queried. 838 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 839 SearchProvider::kDefaultProviderURLFetcherID); 840 ASSERT_TRUE(fetcher); 841 842 // Tell the SearchProvider the suggest query is done. 843 fetcher->set_response_code(200); 844 fetcher->SetResponseString( 845 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 846 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 847 fetcher->delegate()->OnURLFetchComplete(fetcher); 848 fetcher = NULL; 849 850 // Run till the history results complete. 851 RunTillProviderDone(); 852 853 // Make sure the only match is 'a.com' and it doesn't have a template_url. 854 AutocompleteMatch nav_match; 855 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 856 EXPECT_TRUE(nav_match.keyword.empty()); 857 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 858} 859 860// Verifies that the most relevant suggest results are added properly. 861TEST_F(SearchProviderTest, SuggestRelevance) { 862 QueryForInput(ASCIIToUTF16("a"), false, false); 863 864 // Make sure the default provider's suggest service was queried. 865 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 866 SearchProvider::kDefaultProviderURLFetcherID); 867 ASSERT_TRUE(fetcher); 868 869 // Tell the SearchProvider the suggest query is done. 870 fetcher->set_response_code(200); 871 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 872 fetcher->delegate()->OnURLFetchComplete(fetcher); 873 fetcher = NULL; 874 875 // Run till the history results complete. 876 RunTillProviderDone(); 877 878 // Check the expected verbatim and (first 3) suggestions' relative relevances. 879 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 880 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 881 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 882 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 883 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 884 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 885 EXPECT_GT(verbatim.relevance, match_a1.relevance); 886 EXPECT_GT(match_a1.relevance, match_a2.relevance); 887 EXPECT_GT(match_a2.relevance, match_a3.relevance); 888} 889 890// Verifies that suggest results with relevance scores are added 891// properly when using the default fetcher. When adding a new test 892// case to this test, please consider adding it to the tests in 893// KeywordFetcherSuggestRelevance below. 894TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 895 struct { 896 const std::string json; 897 const std::string matches[4]; 898 const std::string inline_autocompletion; 899 } cases[] = { 900 // Ensure that suggestrelevance scores reorder matches. 901 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 902 { "a", "c", "b", kNotApplicable }, std::string() }, 903 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 904 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 905 "\"google:suggestrelevance\":[1, 2]}]", 906 { "a", "c.com", "b.com", kNotApplicable }, std::string() }, 907 908 // Without suggested relevance scores, we should only allow one 909 // navsuggest result to be be displayed. 910 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 911 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 912 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 913 914 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 915 // Negative values will have no effect; the calculated value will be used. 916 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 917 "\"google:suggestrelevance\":[9998]}]", 918 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 919 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 920 "\"google:suggestrelevance\":[9999]}]", 921 { "a1", "a", kNotApplicable, kNotApplicable }, "1" }, 922 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 923 "\"google:suggestrelevance\":[9999]}]", 924 { "a1", kNotApplicable, kNotApplicable, kNotApplicable }, "1" }, 925 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 926 "\"google:suggestrelevance\":[9999]}]", 927 { "a1", "a", kNotApplicable, kNotApplicable }, "1" }, 928 { "[\"a\",[\"http://a.com\"],[],[]," 929 "{\"google:suggesttype\":[\"NAVIGATION\"]," 930 "\"google:verbatimrelevance\":9999," 931 "\"google:suggestrelevance\":[9998]}]", 932 { "a", "a.com", kNotApplicable, kNotApplicable }, std::string() }, 933 { "[\"a\",[\"http://a.com\"],[],[]," 934 "{\"google:suggesttype\":[\"NAVIGATION\"]," 935 "\"google:verbatimrelevance\":9998," 936 "\"google:suggestrelevance\":[9999]}]", 937 { "a.com", "a", kNotApplicable, kNotApplicable }, ".com" }, 938 { "[\"a\",[\"http://a.com\"],[],[]," 939 "{\"google:suggesttype\":[\"NAVIGATION\"]," 940 "\"google:verbatimrelevance\":0," 941 "\"google:suggestrelevance\":[9999]}]", 942 { "a.com", kNotApplicable, kNotApplicable, kNotApplicable }, ".com" }, 943 { "[\"a\",[\"http://a.com\"],[],[]," 944 "{\"google:suggesttype\":[\"NAVIGATION\"]," 945 "\"google:verbatimrelevance\":-1," 946 "\"google:suggestrelevance\":[9999]}]", 947 { "a.com", "a", kNotApplicable, kNotApplicable }, ".com" }, 948 949 // Ensure that both types of relevance scores reorder matches together. 950 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 951 "\"google:verbatimrelevance\":9998}]", 952 { "a1", "a", "a2", kNotApplicable }, "1" }, 953 954 // Ensure that only inlinable matches may be ranked as the highest result. 955 // Ignore all suggested relevance scores if this constraint is violated. 956 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 957 { "a", "b", kNotApplicable, kNotApplicable }, std::string() }, 958 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 959 "\"google:verbatimrelevance\":0}]", 960 { "a", "b", kNotApplicable, kNotApplicable }, std::string() }, 961 { "[\"a\",[\"http://b.com\"],[],[]," 962 "{\"google:suggesttype\":[\"NAVIGATION\"]," 963 "\"google:suggestrelevance\":[9999]}]", 964 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 965 { "[\"a\",[\"http://b.com\"],[],[]," 966 "{\"google:suggesttype\":[\"NAVIGATION\"]," 967 "\"google:suggestrelevance\":[9999]," 968 "\"google:verbatimrelevance\":0}]", 969 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 970 971 // Ensure that the top result is ranked as highly as calculated verbatim. 972 // Ignore the suggested verbatim relevance if this constraint is violated. 973 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 974 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 975 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 976 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 977 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 978 "\"google:verbatimrelevance\":0}]", 979 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 980 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 981 "\"google:verbatimrelevance\":0}]", 982 { "a", "a2", "a1", kNotApplicable }, std::string() }, 983 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 984 "\"google:verbatimrelevance\":2}]", 985 { "a", "a2", "a1", kNotApplicable }, std::string() }, 986 { "[\"a\",[\"http://a.com\"],[],[]," 987 "{\"google:suggesttype\":[\"NAVIGATION\"]," 988 "\"google:suggestrelevance\":[1]," 989 "\"google:verbatimrelevance\":0}]", 990 { "a", "a.com", kNotApplicable, kNotApplicable }, std::string() }, 991 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 992 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 993 "\"google:suggestrelevance\":[1, 2]," 994 "\"google:verbatimrelevance\":0}]", 995 { "a", "a2.com", "a1.com", kNotApplicable }, std::string() }, 996 997 // Ensure that all suggestions are considered, regardless of order. 998 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 999 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1000 { "a", "h", "g", "f" }, std::string() }, 1001 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1002 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1003 "\"http://h.com\"],[],[]," 1004 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1005 "\"NAVIGATION\", \"NAVIGATION\"," 1006 "\"NAVIGATION\", \"NAVIGATION\"," 1007 "\"NAVIGATION\"]," 1008 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1009 { "a", "h.com", "g.com", "f.com" }, std::string() }, 1010 1011 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1012 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1013 { "a", "a1", "a2", kNotApplicable }, std::string() }, 1014 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1015 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 1016 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1017 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1018 "\"google:suggestrelevance\":[1]}]", 1019 { "a", "a1.com", kNotApplicable, kNotApplicable }, std::string() }, 1020 { "[\"a\",[\"http://a1.com\"],[],[]," 1021 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1022 "\"google:suggestrelevance\":[9999, 1]}]", 1023 { "a", "a1.com", kNotApplicable, kNotApplicable }, std::string() }, 1024 1025 // Ensure that all 'verbatim' results are merged with their maximum score. 1026 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1027 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1028 { "a2", "a", "a1", kNotApplicable }, "2" }, 1029 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1030 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1031 "\"google:verbatimrelevance\":0}]", 1032 { "a2", "a", "a1", kNotApplicable }, "2" }, 1033 1034 // Ensure that verbatim is always generated without other suggestions. 1035 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1036 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1037 { "a", kNotApplicable, kNotApplicable, kNotApplicable }, std::string() }, 1038 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1039 { "a", kNotApplicable, kNotApplicable, kNotApplicable }, std::string() }, 1040 }; 1041 1042 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1043 QueryForInput(ASCIIToUTF16("a"), false, false); 1044 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1045 SearchProvider::kDefaultProviderURLFetcherID); 1046 ASSERT_TRUE(fetcher); 1047 fetcher->set_response_code(200); 1048 fetcher->SetResponseString(cases[i].json); 1049 fetcher->delegate()->OnURLFetchComplete(fetcher); 1050 RunTillProviderDone(); 1051 1052 const std::string description = "for input with json=" + cases[i].json; 1053 const ACMatches& matches = provider_->matches(); 1054 // The top match must inline and score as highly as calculated verbatim. 1055 ASSERT_FALSE(matches.empty()); 1056 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1057 matches[0].inline_autocompletion) << description; 1058 EXPECT_GE(matches[0].relevance, 1300) << description; 1059 1060 size_t j = 0; 1061 // Ensure that the returned matches equal the expectations. 1062 for (; j < matches.size(); ++j) 1063 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1064 matches[j].contents) << description; 1065 // Ensure that no expected matches are missing. 1066 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1067 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1068 "Case # " << i << " " << description; 1069 } 1070} 1071 1072// Verifies that suggest results with relevance scores are added 1073// properly when using the keyword fetcher. This is similar to the 1074// test DefaultFetcherSuggestRelevance above but this uses inputs that 1075// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1076// different expectations (because now the results are a mix of 1077// keyword suggestions and default provider suggestions). When a new 1078// test is added to this TEST_F, please consider if it would be 1079// appropriate to add to DefaultFetcherSuggestRelevance as well. 1080TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1081 struct { 1082 const std::string json; 1083 const struct { 1084 const std::string contents; 1085 const bool from_keyword; 1086 } matches[5]; 1087 const std::string inline_autocompletion; 1088 } cases[] = { 1089 // Ensure that suggest relevance scores reorder matches and that 1090 // the keyword verbatim (lacking a suggested verbatim score) beats 1091 // the default provider verbatim. 1092 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1093 { { "a", true }, 1094 { "k a", false }, 1095 { "c", true }, 1096 { "b", true }, 1097 { kNotApplicable, false } }, 1098 std::string() }, 1099 // Again, check that relevance scores reorder matches, just this 1100 // time with navigation matches. This also checks that with 1101 // suggested relevance scores we allow multiple navsuggest results. 1102 // It's odd that navsuggest results that come from a keyword 1103 // provider are marked as not a keyword result. I think this 1104 // comes from them not going to a keyword search engine). 1105 // TODO(mpearson): Investigate the implications (if any) of 1106 // tagging these results appropriately. If so, do it because it 1107 // makes more sense. 1108 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1109 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1110 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1111 { { "a", true }, 1112 { "d", true }, 1113 { "c.com", false }, 1114 { "b.com", false }, 1115 { "k a", false }, }, 1116 std::string() }, 1117 1118 // Without suggested relevance scores, we should only allow one 1119 // navsuggest result to be be displayed. 1120 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1121 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1122 { { "a", true }, 1123 { "b.com", false }, 1124 { "k a", false }, 1125 { kNotApplicable, false }, 1126 { kNotApplicable, false } }, 1127 std::string() }, 1128 1129 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1130 // Negative values will have no effect; the calculated value will be used. 1131 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1132 "\"google:suggestrelevance\":[9998]}]", 1133 { { "a", true }, 1134 { "a1", true }, 1135 { "k a", false }, 1136 { kNotApplicable, false }, 1137 { kNotApplicable, false } }, 1138 std::string() }, 1139 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1140 "\"google:suggestrelevance\":[9999]}]", 1141 { { "a1", true }, 1142 { "a", true }, 1143 { "k a", false }, 1144 { kNotApplicable, false }, 1145 { kNotApplicable, false } }, 1146 "1" }, 1147 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1148 "\"google:suggestrelevance\":[9999]}]", 1149 { { "a1", true }, 1150 { "k a", false }, 1151 { kNotApplicable, false }, 1152 { kNotApplicable, false }, 1153 { kNotApplicable, false } }, 1154 "1" }, 1155 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1156 "\"google:suggestrelevance\":[9999]}]", 1157 { { "a1", true }, 1158 { "a", true }, 1159 { "k a", false }, 1160 { kNotApplicable, false }, 1161 { kNotApplicable, false } }, 1162 "1" }, 1163 { "[\"a\",[\"http://a.com\"],[],[]," 1164 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1165 "\"google:verbatimrelevance\":9999," 1166 "\"google:suggestrelevance\":[9998]}]", 1167 { { "a", true }, 1168 { "a.com", false }, 1169 { "k a", false }, 1170 { kNotApplicable, false }, 1171 { kNotApplicable, false } }, 1172 std::string() }, 1173 1174 // Ensure that both types of relevance scores reorder matches together. 1175 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1176 "\"google:verbatimrelevance\":9998}]", 1177 { { "a1", true }, 1178 { "a", true }, 1179 { "a2", true }, 1180 { "k a", false }, 1181 { kNotApplicable, false } }, 1182 "1" }, 1183 1184 // Ensure that only inlinable matches may be ranked as the highest result. 1185 // Ignore all suggested relevance scores if this constraint is violated. 1186 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1187 { { "a", true }, 1188 { "b", true }, 1189 { "k a", false }, 1190 { kNotApplicable, false }, 1191 { kNotApplicable, false } }, 1192 std::string() }, 1193 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1194 "\"google:verbatimrelevance\":0}]", 1195 { { "a", true }, 1196 { "b", true }, 1197 { "k a", false }, 1198 { kNotApplicable, false }, 1199 { kNotApplicable, false } }, 1200 std::string() }, 1201 { "[\"a\",[\"http://b.com\"],[],[]," 1202 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1203 "\"google:suggestrelevance\":[9999]}]", 1204 { { "a", true }, 1205 { "b.com", false }, 1206 { "k a", false }, 1207 { kNotApplicable, false }, 1208 { kNotApplicable, false } }, 1209 std::string() }, 1210 { "[\"a\",[\"http://b.com\"],[],[]," 1211 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1212 "\"google:suggestrelevance\":[9999]," 1213 "\"google:verbatimrelevance\":0}]", 1214 { { "a", true }, 1215 { "b.com", false }, 1216 { "k a", false }, 1217 { kNotApplicable, false }, 1218 { kNotApplicable, false } }, 1219 std::string() }, 1220 1221 // Ensure that the top result is ranked as highly as calculated verbatim. 1222 // Ignore the suggested verbatim relevance if this constraint is violated. 1223 // Note that keyword suggestions by default (not in suggested relevance 1224 // mode) score more highly than the default verbatim. 1225 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1226 { { "a", true }, 1227 { "a1", true }, 1228 { "k a", false }, 1229 { kNotApplicable, false }, 1230 { kNotApplicable, false } }, 1231 std::string() }, 1232 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1233 { { "a", true }, 1234 { "a1", true }, 1235 { "k a", false }, 1236 { kNotApplicable, false }, 1237 { kNotApplicable, false } }, 1238 std::string() }, 1239 // Continuing the same category of tests, but make sure we keep the 1240 // suggested relevance scores even as we discard the verbatim relevance 1241 // scores. 1242 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1243 "\"google:verbatimrelevance\":0}]", 1244 { { "a", true }, 1245 { "k a", false }, 1246 { "a1", true }, 1247 { kNotApplicable, false }, 1248 { kNotApplicable, false } }, 1249 std::string() }, 1250 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1251 "\"google:verbatimrelevance\":0}]", 1252 { { "a", true }, 1253 { "k a", false }, 1254 { "a2", true }, 1255 { "a1", true }, 1256 { kNotApplicable, false } }, 1257 std::string() }, 1258 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1259 "\"google:verbatimrelevance\":2}]", 1260 { { "a", true }, 1261 { "k a", false }, 1262 { "a2", true }, 1263 { "a1", true }, 1264 { kNotApplicable, false } }, 1265 std::string() }, 1266 1267 // Ensure that all suggestions are considered, regardless of order. 1268 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1269 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1270 { { "a", true }, 1271 { "k a", false }, 1272 { "h", true }, 1273 { "g", true }, 1274 { "f", true } }, 1275 std::string() }, 1276 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1277 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1278 "\"http://h.com\"],[],[]," 1279 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1280 "\"NAVIGATION\", \"NAVIGATION\"," 1281 "\"NAVIGATION\", \"NAVIGATION\"," 1282 "\"NAVIGATION\"]," 1283 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1284 { { "a", true }, 1285 { "k a", false }, 1286 { "h.com", false }, 1287 { "g.com", false }, 1288 { "f.com", false } }, 1289 std::string() }, 1290 1291 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1292 // Note that keyword suggestions by default (not in suggested relevance 1293 // mode) score more highly than the default verbatim. 1294 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1295 { { "a", true }, 1296 { "a1", true }, 1297 { "a2", true }, 1298 { "k a", false }, 1299 { kNotApplicable, false } }, 1300 std::string() }, 1301 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1302 { { "a", true }, 1303 { "a1", true }, 1304 { "k a", false }, 1305 { kNotApplicable, false }, 1306 { kNotApplicable, false } }, 1307 std::string() }, 1308 // In this case, ignored the suggested relevance scores means we keep 1309 // only one navsuggest result. 1310 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1311 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1312 "\"google:suggestrelevance\":[1]}]", 1313 { { "a", true }, 1314 { "a1.com", false }, 1315 { "k a", false }, 1316 { kNotApplicable, false }, 1317 { kNotApplicable, false } }, 1318 std::string() }, 1319 { "[\"a\",[\"http://a1.com\"],[],[]," 1320 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1321 "\"google:suggestrelevance\":[9999, 1]}]", 1322 { { "a", true }, 1323 { "a1.com", false }, 1324 { "k a", false }, 1325 { kNotApplicable, false }, 1326 { kNotApplicable, false } }, 1327 std::string() }, 1328 1329 // Ensure that all 'verbatim' results are merged with their maximum score. 1330 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1331 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1332 { { "a2", true }, 1333 { "a", true }, 1334 { "a1", true }, 1335 { "k a", false }, 1336 { kNotApplicable, false } }, 1337 "2" }, 1338 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1339 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1340 "\"google:verbatimrelevance\":0}]", 1341 { { "a2", true }, 1342 { "a", true }, 1343 { "a1", true }, 1344 { "k a", false }, 1345 { kNotApplicable, false } }, 1346 "2" }, 1347 1348 // Ensure that verbatim is always generated without other suggestions. 1349 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1350 // (except when suggested relevances are ignored). 1351 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1352 { { "a", true }, 1353 { "k a", false }, 1354 { kNotApplicable, false }, 1355 { kNotApplicable, false }, 1356 { kNotApplicable, false } }, 1357 std::string() }, 1358 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1359 { { "a", true }, 1360 { "k a", false }, 1361 { kNotApplicable, false }, 1362 { kNotApplicable, false }, 1363 { kNotApplicable, false } }, 1364 std::string() }, 1365 1366 // Check that navsuggestions will be demoted below queries. 1367 // (Navsuggestions are not allowed to appear first.) In the process, 1368 // make sure the navsuggestions still remain in the same order. 1369 // First, check the situation where navsuggest scores more than verbatim 1370 // and there are no query suggestions. 1371 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1372 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1373 "\"google:verbatimrelevance\":9990," 1374 "\"google:suggestrelevance\":[9998, 9999]}]", 1375 { { "a", true }, 1376 { "a2.com", false }, 1377 { "a1.com", false }, 1378 { "k a", false }, 1379 { kNotApplicable, false } }, 1380 std::string() }, 1381 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1382 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1383 "\"google:verbatimrelevance\":9990," 1384 "\"google:suggestrelevance\":[9999, 9998]}]", 1385 { { "a", true }, 1386 { "a1.com", false }, 1387 { "a2.com", false }, 1388 { "k a", false }, 1389 { kNotApplicable, false } }, 1390 std::string() }, 1391 // Check when navsuggest scores more than verbatim and there is query 1392 // suggestion but it scores lower. 1393 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1394 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1395 "\"google:verbatimrelevance\":9990," 1396 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1397 { { "a", true }, 1398 { "a2.com", false }, 1399 { "a1.com", false }, 1400 { "a3", true }, 1401 { "k a", false } }, 1402 std::string() }, 1403 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1404 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1405 "\"google:verbatimrelevance\":9990," 1406 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1407 { { "a", true }, 1408 { "a1.com", false }, 1409 { "a2.com", false }, 1410 { "a3", true }, 1411 { "k a", false } }, 1412 std::string() }, 1413 // Check when navsuggest scores more than a query suggestion. There is 1414 // a verbatim but it scores lower. 1415 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1416 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1417 "\"google:verbatimrelevance\":9990," 1418 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1419 { { "a3", true }, 1420 { "a2.com", false }, 1421 { "a1.com", false }, 1422 { "a", true }, 1423 { "k a", false } }, 1424 "3" }, 1425 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1426 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1427 "\"google:verbatimrelevance\":9990," 1428 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1429 { { "a3", true }, 1430 { "a1.com", false }, 1431 { "a2.com", false }, 1432 { "a", true }, 1433 { "k a", false } }, 1434 "3" }, 1435 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1436 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1437 "\"google:verbatimrelevance\":0," 1438 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1439 { { "a3", true }, 1440 { "a2.com", false }, 1441 { "a1.com", false }, 1442 { "k a", false }, 1443 { kNotApplicable, false } }, 1444 "3" }, 1445 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1446 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1447 "\"google:verbatimrelevance\":0," 1448 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1449 { { "a3", true }, 1450 { "a1.com", false }, 1451 { "a2.com", false }, 1452 { "k a", false }, 1453 { kNotApplicable, false } }, 1454 "3" }, 1455 // Check when there is neither verbatim nor a query suggestion that, 1456 // because we can't demote navsuggestions below a query suggestion, 1457 // we abandon suggested relevance scores entirely. One consequence is 1458 // that this means we restore the keyword verbatim match. Note 1459 // that in this case of abandoning suggested relevance scores, we still 1460 // keep the navsuggestions in the same order, but we revert to only allowing 1461 // one navigation to appear because the scores are completely local. 1462 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1463 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1464 "\"google:verbatimrelevance\":0," 1465 "\"google:suggestrelevance\":[9998, 9999]}]", 1466 { { "a", true }, 1467 { "a2.com", false }, 1468 { "k a", false }, 1469 { kNotApplicable, false }, 1470 { kNotApplicable, false } }, 1471 std::string() }, 1472 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1473 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1474 "\"google:verbatimrelevance\":0," 1475 "\"google:suggestrelevance\":[9999, 9998]}]", 1476 { { "a", true }, 1477 { "a1.com", false }, 1478 { "k a", false }, 1479 { kNotApplicable, false }, 1480 { kNotApplicable, false } }, 1481 std::string() }, 1482 // More checks that everything works when it's not necessary to demote. 1483 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1484 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1485 "\"google:verbatimrelevance\":9990," 1486 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1487 { { "a3", true }, 1488 { "a2.com", false }, 1489 { "a1.com", false }, 1490 { "a", true }, 1491 { "k a", false } }, 1492 "3" }, 1493 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1494 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1495 "\"google:verbatimrelevance\":9990," 1496 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1497 { { "a3", true }, 1498 { "a1.com", false }, 1499 { "a2.com", false }, 1500 { "a", true }, 1501 { "k a", false } }, 1502 "3" }, 1503 }; 1504 1505 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1506 QueryForInput(ASCIIToUTF16("k a"), false, true); 1507 1508 // Set up a default fetcher with no results. 1509 net::TestURLFetcher* default_fetcher = WaitUntilURLFetcherIsReady( 1510 SearchProvider::kDefaultProviderURLFetcherID); 1511 ASSERT_TRUE(default_fetcher); 1512 default_fetcher->set_response_code(200); 1513 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1514 default_fetcher = NULL; 1515 1516 // Set up a keyword fetcher with provided results. 1517 net::TestURLFetcher* keyword_fetcher = WaitUntilURLFetcherIsReady( 1518 SearchProvider::kKeywordProviderURLFetcherID); 1519 ASSERT_TRUE(keyword_fetcher); 1520 keyword_fetcher->set_response_code(200); 1521 keyword_fetcher->SetResponseString(cases[i].json); 1522 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1523 keyword_fetcher = NULL; 1524 RunTillProviderDone(); 1525 1526 const std::string description = "for input with json=" + cases[i].json; 1527 const ACMatches& matches = provider_->matches(); 1528 // The top match must inline and score as highly as calculated verbatim. 1529 ASSERT_FALSE(matches.empty()); 1530 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1531 matches[0].inline_autocompletion) << description; 1532 EXPECT_GE(matches[0].relevance, 1300) << description; 1533 1534 size_t j = 0; 1535 // Ensure that the returned matches equal the expectations. 1536 for (; j < matches.size(); ++j) { 1537 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1538 matches[j].contents) << description; 1539 EXPECT_EQ(cases[i].matches[j].from_keyword, 1540 matches[j].keyword == ASCIIToUTF16("k")) << description; 1541 } 1542 // Ensure that no expected matches are missing. 1543 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1544 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1545 "Case # " << i << " " << description; 1546 } 1547} 1548 1549TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1550 // Enable Instant Extended in order to allow an increased number of 1551 // suggestions. 1552 chrome::EnableInstantExtendedAPIForTesting(); 1553 1554 // We hardcode the string "term1" below, so ensure that the search term that 1555 // got added to history already is that string. 1556 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1557 string16 term = term1_.substr(0, term1_.length() - 1); 1558 1559 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1560 profile_.BlockUntilHistoryProcessesPendingRequests(); 1561 1562 struct { 1563 const string16 input; 1564 const std::string json; 1565 const std::string matches[6]; 1566 } cases[] = { 1567 // The history results outscore the default verbatim score. term2 has more 1568 // visits so it outscores term1. The suggestions are still returned since 1569 // they're server-scored. 1570 { term, 1571 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1572 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1573 "\"google:suggestrelevance\":[1, 2, 3]}]", 1574 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1575 // Because we already have three suggestions by the time we see the history 1576 // results, they don't get returned. 1577 { term, 1578 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1579 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1580 "\"google:verbatimrelevance\":1450," 1581 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1582 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1583 // If we only have two suggestions, we have room for a history result. 1584 { term, 1585 "[\"term\",[\"a1\", \"a2\"],[],[]," 1586 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1587 "\"google:verbatimrelevance\":1450," 1588 "\"google:suggestrelevance\":[1430, 1410]}]", 1589 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1590 // If we have more than three suggestions, they should all be returned as 1591 // long as we have enough total space for them. 1592 { term, 1593 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1594 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1595 "\"google:verbatimrelevance\":1450," 1596 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1597 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1598 { term, 1599 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1600 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1601 "\"QUERY\", \"QUERY\"]," 1602 "\"google:verbatimrelevance\":1450," 1603 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1604 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1605 { term, 1606 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1607 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1608 "\"google:verbatimrelevance\":1450," 1609 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1610 { "term", "a1", "a2", "term2", "a3", "a4" } }, 1611 // When the input looks like a URL, we disallow having a query as the 1612 // highest-ranking result. If the query was provided by a suggestion, we 1613 // reset the suggest scores to enforce this (see 1614 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores, 1615 // however, we should still allow navsuggestions to be treated as 1616 // server-provided. 1617 { ASCIIToUTF16("a.com"), 1618 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[]," 1619 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\"," 1620 "\"NAVIGATION\"]," 1621 // A verbatim query for URL-like input scores 850, so the navigation 1622 // scores here should bracket it. 1623 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]", 1624 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } }, 1625 }; 1626 1627 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1628 QueryForInput(cases[i].input, false, false); 1629 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1630 SearchProvider::kDefaultProviderURLFetcherID); 1631 ASSERT_TRUE(fetcher); 1632 fetcher->set_response_code(200); 1633 fetcher->SetResponseString(cases[i].json); 1634 fetcher->delegate()->OnURLFetchComplete(fetcher); 1635 RunTillProviderDone(); 1636 1637 const std::string description = "for input with json=" + cases[i].json; 1638 const ACMatches& matches = provider_->matches(); 1639 1640 // Ensure no extra matches are present. 1641 ASSERT_LE(matches.size(), 6U); 1642 1643 size_t j = 0; 1644 // Ensure that the returned matches equal the expectations. 1645 for (; j < matches.size(); ++j) 1646 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1647 matches[j].contents) << description; 1648 // Ensure that no expected matches are missing. 1649 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1650 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1651 "Case # " << i << " " << description; 1652 } 1653} 1654 1655// Verifies suggest relevance behavior for URL input. 1656TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1657 struct { 1658 const std::string input; 1659 const std::string json; 1660 const std::string match_contents[4]; 1661 const AutocompleteMatch::Type match_types[4]; 1662 } cases[] = { 1663 // Ensure topmost NAVIGATION matches are allowed for URL input. 1664 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1665 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1666 "\"google:suggestrelevance\":[9999]}]", 1667 { "a.com/a", "a.com", kNotApplicable, kNotApplicable }, 1668 { AutocompleteMatchType::NAVSUGGEST, 1669 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1670 AutocompleteMatchType::NUM_TYPES, 1671 AutocompleteMatchType::NUM_TYPES } }, 1672 1673 // Ensure topmost SUGGEST matches are not allowed for URL input. 1674 // SearchProvider disregards search and verbatim suggested relevances. 1675 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1676 "{\"google:suggestrelevance\":[9999]}]", 1677 { "a.com", "a.com info", kNotApplicable, kNotApplicable }, 1678 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1679 AutocompleteMatchType::SEARCH_SUGGEST, 1680 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1681 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1682 "{\"google:suggestrelevance\":[9999]}]", 1683 { "a.com", "a.com/a", kNotApplicable, kNotApplicable }, 1684 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1685 AutocompleteMatchType::SEARCH_SUGGEST, 1686 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1687 1688 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1689 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1690 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1691 "\"google:suggestrelevance\":[9999, 9998]}]", 1692 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1693 { AutocompleteMatchType::NAVSUGGEST, 1694 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1695 AutocompleteMatchType::SEARCH_SUGGEST, 1696 AutocompleteMatchType::NUM_TYPES } }, 1697 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1698 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1699 "\"google:suggestrelevance\":[9998, 9997]," 1700 "\"google:verbatimrelevance\":9999}]", 1701 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1702 { AutocompleteMatchType::NAVSUGGEST, 1703 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1704 AutocompleteMatchType::SEARCH_SUGGEST, 1705 AutocompleteMatchType::NUM_TYPES } }, 1706 1707 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 1708 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1709 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1710 "\"google:suggestrelevance\":[9999, 9998]}]", 1711 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1712 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1713 AutocompleteMatchType::NAVSUGGEST, 1714 AutocompleteMatchType::SEARCH_SUGGEST, 1715 AutocompleteMatchType::NUM_TYPES } }, 1716 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1717 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1718 "\"google:suggestrelevance\":[9998, 9997]," 1719 "\"google:verbatimrelevance\":9999}]", 1720 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1721 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1722 AutocompleteMatchType::NAVSUGGEST, 1723 AutocompleteMatchType::SEARCH_SUGGEST, 1724 AutocompleteMatchType::NUM_TYPES } }, 1725 }; 1726 1727 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1728 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1729 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1730 SearchProvider::kDefaultProviderURLFetcherID); 1731 ASSERT_TRUE(fetcher); 1732 fetcher->set_response_code(200); 1733 fetcher->SetResponseString(cases[i].json); 1734 fetcher->delegate()->OnURLFetchComplete(fetcher); 1735 RunTillProviderDone(); 1736 1737 size_t j = 0; 1738 const ACMatches& matches = provider_->matches(); 1739 // Ensure that the returned matches equal the expectations. 1740 for (; j < matches.size(); ++j) { 1741 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1742 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1743 } 1744 // Ensure that no expected matches are missing. 1745 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1746 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1747 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, cases[i].match_types[j]); 1748 } 1749 } 1750} 1751 1752// A basic test that verifies the field trial triggered parsing logic. 1753TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 1754 QueryForInput(ASCIIToUTF16("foo"), false, false); 1755 1756 // Make sure the default providers suggest service was queried. 1757 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1758 SearchProvider::kDefaultProviderURLFetcherID); 1759 ASSERT_TRUE(fetcher); 1760 1761 // Tell the SearchProvider the suggest query is done. 1762 fetcher->set_response_code(200); 1763 fetcher->SetResponseString( 1764 "[\"foo\",[\"foo bar\"],[\"\"],[]," 1765 "{\"google:suggesttype\":[\"QUERY\"]," 1766 "\"google:fieldtrialtriggered\":true}]"); 1767 fetcher->delegate()->OnURLFetchComplete(fetcher); 1768 fetcher = NULL; 1769 1770 // Run till the history results complete. 1771 RunTillProviderDone(); 1772 1773 { 1774 // Check for the match and field trial triggered bits. 1775 AutocompleteMatch match; 1776 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 1777 ProvidersInfo providers_info; 1778 provider_->AddProviderInfo(&providers_info); 1779 ASSERT_EQ(1U, providers_info.size()); 1780 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1781 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 1782 } 1783 { 1784 // Reset the session and check that bits are reset. 1785 provider_->ResetSession(); 1786 ProvidersInfo providers_info; 1787 provider_->AddProviderInfo(&providers_info); 1788 ASSERT_EQ(1U, providers_info.size()); 1789 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1790 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 1791 } 1792} 1793 1794// Verifies inline autocompletion of navigational results. 1795TEST_F(SearchProviderTest, NavigationInline) { 1796 struct { 1797 const std::string input; 1798 const std::string url; 1799 // Test the expected fill_into_edit, which may drop "http://". 1800 // Some cases do not trim "http://" to match from the start of the scheme. 1801 const std::string fill_into_edit; 1802 const std::string inline_autocompletion; 1803 } cases[] = { 1804 // Do not inline matches that do not contain the input; trim http as needed. 1805 { "x", "http://www.abc.com", 1806 "www.abc.com", std::string() }, 1807 { "https:", "http://www.abc.com", 1808 "www.abc.com", std::string() }, 1809 { "abc.com/", "http://www.abc.com", 1810 "www.abc.com", std::string() }, 1811 { "http://www.abc.com/a", "http://www.abc.com", 1812 "http://www.abc.com", std::string() }, 1813 { "http://www.abc.com", "https://www.abc.com", 1814 "https://www.abc.com", std::string() }, 1815 { "http://abc.com", "ftp://abc.com", 1816 "ftp://abc.com", std::string() }, 1817 { "https://www.abc.com", "http://www.abc.com", 1818 "www.abc.com", std::string() }, 1819 { "ftp://abc.com", "http://abc.com", 1820 "abc.com", std::string() }, 1821 1822 // Do not inline matches with invalid input prefixes; trim http as needed. 1823 { "ttp", "http://www.abc.com", 1824 "www.abc.com", std::string() }, 1825 { "://w", "http://www.abc.com", 1826 "www.abc.com", std::string() }, 1827 { "ww.", "http://www.abc.com", 1828 "www.abc.com", std::string() }, 1829 { ".ab", "http://www.abc.com", 1830 "www.abc.com", std::string() }, 1831 { "bc", "http://www.abc.com", 1832 "www.abc.com", std::string() }, 1833 { ".com", "http://www.abc.com", 1834 "www.abc.com", std::string() }, 1835 1836 // Do not inline matches that omit input domain labels; trim http as needed. 1837 { "www.a", "http://a.com", 1838 "a.com", std::string() }, 1839 { "http://www.a", "http://a.com", 1840 "http://a.com", std::string() }, 1841 { "www.a", "ftp://a.com", 1842 "ftp://a.com", std::string() }, 1843 { "ftp://www.a", "ftp://a.com", 1844 "ftp://a.com", std::string() }, 1845 1846 // Input matching but with nothing to inline will not yield an offset. 1847 { "abc.com", "http://www.abc.com", 1848 "www.abc.com", std::string() }, 1849 { "http://www.abc.com", "http://www.abc.com", 1850 "http://www.abc.com", std::string() }, 1851 1852 // Inline matches when the input is a leading substring of the scheme. 1853 { "h", "http://www.abc.com", 1854 "http://www.abc.com", "ttp://www.abc.com" }, 1855 { "http", "http://www.abc.com", 1856 "http://www.abc.com", "://www.abc.com" }, 1857 1858 // Inline matches when the input is a leading substring of the full URL. 1859 { "http:", "http://www.abc.com", 1860 "http://www.abc.com", "//www.abc.com" }, 1861 { "http://w", "http://www.abc.com", 1862 "http://www.abc.com", "ww.abc.com" }, 1863 { "http://www.", "http://www.abc.com", 1864 "http://www.abc.com", "abc.com" }, 1865 { "http://www.ab", "http://www.abc.com", 1866 "http://www.abc.com", "c.com" }, 1867 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1868 "http://www.abc.com/path/file.htm?q=x#foo", 1869 "ath/file.htm?q=x#foo" }, 1870 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1871 "http://abc.com/path/file.htm?q=x#foo", 1872 "ath/file.htm?q=x#foo"}, 1873 1874 // Inline matches with valid URLPrefixes; only trim "http://". 1875 { "w", "http://www.abc.com", 1876 "www.abc.com", "ww.abc.com" }, 1877 { "www.a", "http://www.abc.com", 1878 "www.abc.com", "bc.com" }, 1879 { "abc", "http://www.abc.com", 1880 "www.abc.com", ".com" }, 1881 { "abc.c", "http://www.abc.com", 1882 "www.abc.com", "om" }, 1883 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1884 "www.abc.com/path/file.htm?q=x#foo", 1885 "ath/file.htm?q=x#foo" }, 1886 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1887 "abc.com/path/file.htm?q=x#foo", 1888 "ath/file.htm?q=x#foo" }, 1889 1890 // Inline matches using the maximal URLPrefix components. 1891 { "h", "http://help.com", 1892 "help.com", "elp.com" }, 1893 { "http", "http://http.com", 1894 "http.com", ".com" }, 1895 { "h", "http://www.help.com", 1896 "www.help.com", "elp.com" }, 1897 { "http", "http://www.http.com", 1898 "www.http.com", ".com" }, 1899 { "w", "http://www.www.com", 1900 "www.www.com", "ww.com" }, 1901 1902 // Test similar behavior for the ftp and https schemes. 1903 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1904 "ftp://www.abc.com/path/file.htm?q=x#foo", 1905 "c.com/path/file.htm?q=x#foo" }, 1906 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1907 "ftp://www.abc.com/path/file.htm?q=x#foo", 1908 "c.com/path/file.htm?q=x#foo" }, 1909 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1910 "ftp://www.abc.com/path/file.htm?q=x#foo", 1911 "c.com/path/file.htm?q=x#foo" }, 1912 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 1913 "ftp://abc.com/path/file.htm?q=x#foo", 1914 "c.com/path/file.htm?q=x#foo" }, 1915 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1916 "https://www.abc.com/path/file.htm?q=x#foo", 1917 "c.com/path/file.htm?q=x#foo" }, 1918 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1919 "https://www.abc.com/path/file.htm?q=x#foo", 1920 "c.com/path/file.htm?q=x#foo" }, 1921 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 1922 "https://www.abc.com/path/file.htm?q=x#foo", 1923 "c.com/path/file.htm?q=x#foo" }, 1924 { "ab", "https://abc.com/path/file.htm?q=x#foo", 1925 "https://abc.com/path/file.htm?q=x#foo", 1926 "c.com/path/file.htm?q=x#foo"}, 1927 1928 // Forced query input should inline and retain the "?" prefix. 1929 { "?http://www.ab", "http://www.abc.com", 1930 "?http://www.abc.com", "c.com" }, 1931 { "?www.ab", "http://www.abc.com", 1932 "?www.abc.com", "c.com" }, 1933 { "?ab", "http://www.abc.com", 1934 "?www.abc.com", "c.com" }, 1935 }; 1936 1937 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1938 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1939 AutocompleteMatch match( 1940 provider_->NavigationToMatch(SearchProvider::NavigationResult( 1941 *provider_.get(), GURL(cases[i].url), string16(), false, 0, 1942 false))); 1943 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1944 match.inline_autocompletion); 1945 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 1946 } 1947} 1948 1949// Verifies that "http://" is not trimmed for input that is a leading substring. 1950TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 1951 const string16 input(ASCIIToUTF16("ht")); 1952 const string16 url(ASCIIToUTF16("http://a.com")); 1953 const SearchProvider::NavigationResult result( 1954 *provider_.get(), GURL(url), string16(), false, 0, false); 1955 1956 // Check the offset and strings when inline autocompletion is allowed. 1957 QueryForInput(input, false, false); 1958 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 1959 EXPECT_EQ(url, match_inline.fill_into_edit); 1960 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 1961 EXPECT_EQ(url, match_inline.contents); 1962 1963 // Check the same offset and strings when inline autocompletion is prevented. 1964 QueryForInput(input, true, false); 1965 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 1966 EXPECT_TRUE(match_prevent.inline_autocompletion.empty()); 1967 EXPECT_EQ(url, match_prevent.fill_into_edit); 1968 EXPECT_EQ(url, match_prevent.contents); 1969} 1970 1971// Verifies that input "w" marks a more significant domain label than "www.". 1972TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 1973 QueryForInput(ASCIIToUTF16("w"), false, false); 1974 AutocompleteMatch match( 1975 provider_->NavigationToMatch(SearchProvider::NavigationResult( 1976 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0, 1977 false))); 1978 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 1979 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 1980 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 1981 1982 // Ensure that the match for input "w" is marked on "wow" and not "www". 1983 ASSERT_EQ(3U, match.contents_class.size()); 1984 EXPECT_EQ(0U, match.contents_class[0].offset); 1985 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1986 match.contents_class[0].style); 1987 EXPECT_EQ(4U, match.contents_class[1].offset); 1988 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 1989 AutocompleteMatch::ACMatchClassification::MATCH, 1990 match.contents_class[1].style); 1991 EXPECT_EQ(5U, match.contents_class[2].offset); 1992 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1993 match.contents_class[2].style); 1994} 1995 1996TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 1997 // TODO(mpearson): Consider expanding this test to explicitly cover 1998 // testing staleness for keyword results. 1999 struct { 2000 const std::string omnibox_input; 2001 const int verbatim_relevance; 2002 // These cached suggestions should already be sorted. 2003 // The particular number 5 as the length of the array is 2004 // unimportant; it's merely enough cached results to fully test 2005 // the functioning of RemoveAllStaleResults(). 2006 struct { 2007 const std::string suggestion; 2008 const bool is_navigation_result; 2009 const int relevance; 2010 // |expect_match| is true if this result should survive 2011 // RemoveAllStaleResults() filtering against |omnibox_input| below. 2012 const bool expect_match; 2013 } results[5]; 2014 } cases[] = { 2015 // Simple case: multiple query suggestions and no navsuggestions. 2016 // All query suggestions score less than search-what-you-typed and 2017 // thus none should be filtered because none will appear first. 2018 { "x", 1300, 2019 { { "food", false, 1299, true }, 2020 { "foobar", false, 1298, true }, 2021 { "crazy", false, 1297, true }, 2022 { "friend", false, 1296, true }, 2023 { kNotApplicable, false, 0, false } } }, 2024 2025 // Similarly simple cases, but the query suggestion appears first. 2026 { "f", 1200, 2027 { { "food", false, 1299, true }, 2028 { "foobar", false, 1298, true }, 2029 { "crazy", false, 1297, true }, 2030 { "friend", false, 1296, true }, 2031 { kNotApplicable, false, 0, false } } }, 2032 { "c", 1200, 2033 { { "food", false, 1299, false }, 2034 { "foobar", false, 1298, false }, 2035 { "crazy", false, 1297, true }, 2036 { "friend", false, 1296, true }, 2037 { kNotApplicable, false, 0, false } } }, 2038 { "x", 1200, 2039 { { "food", false, 1299, false }, 2040 { "foobar", false, 1298, false }, 2041 { "crazy", false, 1297, false }, 2042 { "friend", false, 1296, false }, 2043 { kNotApplicable, false, 0, false } } }, 2044 2045 // The same sort of cases, just using a mix of queries and navsuggestions. 2046 { "x", 1300, 2047 { { "http://food.com/", true, 1299, true }, 2048 { "foobar", false, 1298, true }, 2049 { "http://crazy.com/", true, 1297, true }, 2050 { "friend", false, 1296, true }, 2051 { "http://friend.com/", true, 1295, true } } }, 2052 { "f", 1200, 2053 { { "http://food.com/", true, 1299, true }, 2054 { "foobar", false, 1298, true }, 2055 { "http://crazy.com/", true, 1297, true }, 2056 { "friend", false, 1296, true }, 2057 { "http://friend.com/", true, 1295, true } } }, 2058 { "c", 1200, 2059 { { "http://food.com/", true, 1299, false }, 2060 { "foobar", false, 1298, false }, 2061 { "http://crazy.com/", true, 1297, true }, 2062 { "friend", false, 1296, true }, 2063 { "http://friend.com/", true, 1295, true } } }, 2064 { "x", 1200, 2065 { { "http://food.com/", true, 1299, false }, 2066 { "foobar", false, 1298, false }, 2067 { "http://crazy.com/", true, 1297, false }, 2068 { "friend", false, 1296, false }, 2069 { "http://friend.com/", true, 1295, false } } }, 2070 2071 // Run the three tests immediately above again, just with verbatim 2072 // suppressed. Note that in the last case, all results are filtered. 2073 // Because verbatim is also suppressed, SearchProvider will realize 2074 // in UpdateMatches() that it needs to restore verbatim to fulfill 2075 // its constraints. This restoration does not happen in 2076 // RemoveAllStaleResults() and hence is not tested here. This restoration 2077 // is tested in the DefaultFetcherSuggestRelevance test. 2078 { "f", 0, 2079 { { "http://food.com/", true, 1299, true }, 2080 { "foobar", false, 1298, true }, 2081 { "http://crazy.com/", true, 1297, true }, 2082 { "friend", false, 1296, true }, 2083 { "http://friend.com/", true, 1295, true } } }, 2084 { "c", 0, 2085 { { "http://food.com/", true, 1299, false }, 2086 { "foobar", false, 1298, false }, 2087 { "http://crazy.com/", true, 1297, true }, 2088 { "friend", false, 1296, true }, 2089 { "http://friend.com/", true, 1295, true } } }, 2090 { "x", 0, 2091 { { "http://food.com/", true, 1299, false }, 2092 { "foobar", false, 1298, false }, 2093 { "http://crazy.com/", true, 1297, false }, 2094 { "friend", false, 1296, false }, 2095 { "http://friend.com/", true, 1295, false } } }, 2096 2097 // The same sort of tests again, just with verbatim with a score 2098 // that would place it in between other suggestions. 2099 { "f", 1290, 2100 { { "http://food.com/", true, 1299, true }, 2101 { "foobar", false, 1288, true }, 2102 { "http://crazy.com/", true, 1277, true }, 2103 { "friend", false, 1266, true }, 2104 { "http://friend.com/", true, 1255, true } } }, 2105 { "c", 1290, 2106 { { "http://food.com/", true, 1299, false }, 2107 { "foobar", false, 1288, true }, 2108 { "http://crazy.com/", true, 1277, true }, 2109 { "friend", false, 1266, true }, 2110 { "http://friend.com/", true, 1255, true } } }, 2111 { "c", 1270, 2112 { { "http://food.com/", true, 1299, false }, 2113 { "foobar", false, 1288, false }, 2114 { "http://crazy.com/", true, 1277, true }, 2115 { "friend", false, 1266, true }, 2116 { "http://friend.com/", true, 1255, true } } }, 2117 { "x", 1280, 2118 { { "http://food.com/", true, 1299, false }, 2119 { "foobar", false, 1288, false }, 2120 { "http://crazy.com/", true, 1277, true }, 2121 { "friend", false, 1266, true }, 2122 { "http://friend.com/", true, 1255, true } } }, 2123 }; 2124 2125 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2126 // Initialize cached results for this test case. 2127 provider_->default_results_.verbatim_relevance = 2128 cases[i].verbatim_relevance; 2129 provider_->default_results_.navigation_results.clear(); 2130 provider_->default_results_.suggest_results.clear(); 2131 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2132 const std::string& suggestion = cases[i].results[j].suggestion; 2133 if (suggestion == kNotApplicable) 2134 break; 2135 if (cases[i].results[j].is_navigation_result) { 2136 provider_->default_results_.navigation_results.push_back( 2137 SearchProvider::NavigationResult( 2138 *provider_.get(), GURL(suggestion), string16(), false, 2139 cases[i].results[j].relevance, false)); 2140 } else { 2141 provider_->default_results_.suggest_results.push_back( 2142 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, 2143 cases[i].results[j].relevance, 2144 false)); 2145 } 2146 } 2147 2148 provider_->input_ = AutocompleteInput( 2149 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(), 2150 GURL(), false, false, true, AutocompleteInput::ALL_MATCHES); 2151 provider_->RemoveAllStaleResults(); 2152 2153 // Check cached results. 2154 SearchProvider::SuggestResults::const_iterator sug_it = 2155 provider_->default_results_.suggest_results.begin(); 2156 const SearchProvider::SuggestResults::const_iterator sug_end = 2157 provider_->default_results_.suggest_results.end(); 2158 SearchProvider::NavigationResults::const_iterator nav_it = 2159 provider_->default_results_.navigation_results.begin(); 2160 const SearchProvider::NavigationResults::const_iterator nav_end = 2161 provider_->default_results_.navigation_results.end(); 2162 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2163 const std::string& suggestion = cases[i].results[j].suggestion; 2164 if (suggestion == kNotApplicable) 2165 continue; 2166 if (!cases[i].results[j].expect_match) 2167 continue; 2168 if (cases[i].results[j].is_navigation_result) { 2169 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 2170 EXPECT_EQ(suggestion, nav_it->url().spec()); 2171 ++nav_it; 2172 } else { 2173 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 2174 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 2175 ++sug_it; 2176 } 2177 } 2178 EXPECT_EQ(sug_end, sug_it); 2179 EXPECT_EQ(nav_end, nav_it); 2180 } 2181} 2182