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