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