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