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 <string>
8
9#include "base/command_line.h"
10#include "base/metrics/field_trial.h"
11#include "base/prefs/pref_service.h"
12#include "base/run_loop.h"
13#include "base/strings/string16.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/time/time.h"
18#include "build/build_config.h"
19#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20#include "chrome/browser/autocomplete/autocomplete_controller.h"
21#include "chrome/browser/autocomplete/autocomplete_input.h"
22#include "chrome/browser/autocomplete/autocomplete_match.h"
23#include "chrome/browser/autocomplete/autocomplete_provider.h"
24#include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
25#include "chrome/browser/autocomplete/history_url_provider.h"
26#include "chrome/browser/history/history_service.h"
27#include "chrome/browser/history/history_service_factory.h"
28#include "chrome/browser/omnibox/omnibox_field_trial.h"
29#include "chrome/browser/search_engines/search_engine_type.h"
30#include "chrome/browser/search_engines/template_url.h"
31#include "chrome/browser/search_engines/template_url_service.h"
32#include "chrome/browser/search_engines/template_url_service_factory.h"
33#include "chrome/browser/signin/signin_manager.h"
34#include "chrome/browser/signin/signin_manager_factory.h"
35#include "chrome/browser/sync/profile_sync_service.h"
36#include "chrome/browser/sync/profile_sync_service_factory.h"
37#include "chrome/common/chrome_switches.h"
38#include "chrome/common/metrics/variations/variations_util.h"
39#include "chrome/common/pref_names.h"
40#include "chrome/test/base/testing_browser_process.h"
41#include "chrome/test/base/testing_profile.h"
42#include "components/variations/entropy_provider.h"
43#include "content/public/test/test_browser_thread_bundle.h"
44#include "net/url_request/test_url_fetcher_factory.h"
45#include "net/url_request/url_request_status.h"
46#include "testing/gtest/include/gtest/gtest.h"
47
48namespace {
49
50// Returns the first match in |matches| with |allowed_to_be_default_match|
51// set to true.
52ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
53  ACMatches::const_iterator it = matches.begin();
54  while ((it != matches.end()) && !it->allowed_to_be_default_match)
55    ++it;
56  return it;
57}
58
59class SuggestionDeletionHandler;
60class SearchProviderForTest : public SearchProvider {
61 public:
62  SearchProviderForTest(
63      AutocompleteProviderListener* listener,
64      Profile* profile);
65  bool is_success() { return is_success_; };
66
67 protected:
68  virtual ~SearchProviderForTest();
69
70 private:
71  virtual void RecordDeletionResult(bool success) OVERRIDE;
72  bool is_success_;
73  DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
74};
75
76SearchProviderForTest::SearchProviderForTest(
77    AutocompleteProviderListener* listener,
78    Profile* profile)
79    : SearchProvider(listener, profile), is_success_(false) {
80}
81
82SearchProviderForTest::~SearchProviderForTest() {
83}
84
85void SearchProviderForTest::RecordDeletionResult(bool success) {
86  is_success_ = success;
87}
88
89} // namespace
90
91// SearchProviderTest ---------------------------------------------------------
92
93// The following environment is configured for these tests:
94// . The TemplateURL default_t_url_ is set as the default provider.
95// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
96//   TemplateURL has a valid suggest and search URL.
97// . The URL created by using the search term term1_ with default_t_url_ is
98//   added to history.
99// . The URL created by using the search term keyword_term_ with keyword_t_url_
100//   is added to history.
101// . test_factory_ is set as the URLFetcherFactory.
102class SearchProviderTest : public testing::Test,
103                           public AutocompleteProviderListener {
104 public:
105  struct ResultInfo {
106    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
107                   allowed_to_be_default_match(false) {
108    }
109    ResultInfo(GURL gurl,
110               AutocompleteMatch::Type result_type,
111               bool allowed_to_be_default_match,
112               base::string16 fill_into_edit)
113      : gurl(gurl),
114        result_type(result_type),
115        allowed_to_be_default_match(allowed_to_be_default_match),
116        fill_into_edit(fill_into_edit) {
117    }
118
119    const GURL gurl;
120    const AutocompleteMatch::Type result_type;
121    const bool allowed_to_be_default_match;
122    const base::string16 fill_into_edit;
123  };
124
125  struct TestData {
126    const base::string16 input;
127    const size_t num_results;
128    const ResultInfo output[3];
129  };
130
131  SearchProviderTest()
132      : default_t_url_(NULL),
133        term1_(ASCIIToUTF16("term1")),
134        keyword_t_url_(NULL),
135        keyword_term_(ASCIIToUTF16("keyword")),
136        run_loop_(NULL) {
137    ResetFieldTrialList();
138  }
139
140  // See description above class for what this registers.
141  virtual void SetUp() OVERRIDE;
142  virtual void TearDown() OVERRIDE;
143
144  void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
145
146 protected:
147  // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
148  scoped_ptr<base::FieldTrialList> field_trial_list_;
149
150  // Default value used for testing.
151  static const std::string kNotApplicable;
152
153  // Adds a search for |term|, using the engine |t_url| to the history, and
154  // returns the URL for that search.
155  GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
156
157  // Looks for a match in |provider_| with |contents| equal to |contents|.
158  // Sets |match| to it if found.  Returns whether |match| was set.
159  bool FindMatchWithContents(const base::string16& contents,
160                             AutocompleteMatch* match);
161
162  // Looks for a match in |provider_| with destination |url|.  Sets |match| to
163  // it if found.  Returns whether |match| was set.
164  bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
165
166  // AutocompleteProviderListener:
167  // If we're waiting for the provider to finish, this exits the message loop.
168  virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
169
170  // Runs a nested message loop until provider_ is done. The message loop is
171  // exited by way of OnProviderUpdate.
172  void RunTillProviderDone();
173
174  // Invokes Start on provider_, then runs all pending tasks.
175  void QueryForInput(const base::string16& text,
176                     bool prevent_inline_autocomplete,
177                     bool prefer_keyword);
178
179  // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
180  // non-NULL, sets it to the "what you typed" entry for |text|.
181  void QueryForInputAndSetWYTMatch(const base::string16& text,
182                                   AutocompleteMatch* wyt_match);
183
184  // Notifies the URLFetcher for the suggest query corresponding to the default
185  // search provider that it's done.
186  // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
187  void FinishDefaultSuggestQuery();
188
189  // Runs SearchProvider on |input|, for which the suggest server replies
190  // with |json|, and expects that the resulting matches' contents equals
191  // that in |matches|.  An empty entry in |matches| means no match should
192  // be returned in that position.  Reports any errors with a message that
193  // includes |error_description|.
194  void ForcedQueryTestHelper(const std::string& input,
195                             const std::string& json,
196                             const std::string matches[3],
197                             const std::string& error_description);
198
199  void ResetFieldTrialList();
200
201  void ClearAllResults();
202
203  // See description above class for details of these fields.
204  TemplateURL* default_t_url_;
205  const base::string16 term1_;
206  GURL term1_url_;
207  TemplateURL* keyword_t_url_;
208  const base::string16 keyword_term_;
209  GURL keyword_url_;
210
211  content::TestBrowserThreadBundle thread_bundle_;
212
213  // URLFetcherFactory implementation registered.
214  net::TestURLFetcherFactory test_factory_;
215
216  // Profile we use.
217  TestingProfile profile_;
218
219  // The provider.
220  scoped_refptr<SearchProviderForTest> provider_;
221
222  // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
223  base::RunLoop* run_loop_;
224
225  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
226};
227
228// static
229const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
230
231void SearchProviderTest::SetUp() {
232  // Make sure that fetchers are automatically ungregistered upon destruction.
233  test_factory_.set_remove_fetcher_on_delete(true);
234
235  // We need both the history service and template url model loaded.
236  ASSERT_TRUE(profile_.CreateHistoryService(true, false));
237  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
238      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
239
240  TemplateURLService* turl_model =
241      TemplateURLServiceFactory::GetForProfile(&profile_);
242
243  turl_model->Load();
244
245  // Reset the default TemplateURL.
246  TemplateURLData data;
247  data.short_name = ASCIIToUTF16("t");
248  data.SetURL("http://defaultturl/{searchTerms}");
249  data.suggestions_url = "http://defaultturl2/{searchTerms}";
250  data.instant_url = "http://does/not/exist?strk=1";
251  data.search_terms_replacement_key = "strk";
252  default_t_url_ = new TemplateURL(&profile_, data);
253  turl_model->Add(default_t_url_);
254  turl_model->SetDefaultSearchProvider(default_t_url_);
255  TemplateURLID default_provider_id = default_t_url_->id();
256  ASSERT_NE(0, default_provider_id);
257
258  // Add url1, with search term term1_.
259  term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
260
261  // Create another TemplateURL.
262  data.short_name = ASCIIToUTF16("k");
263  data.SetKeyword(ASCIIToUTF16("k"));
264  data.SetURL("http://keyword/{searchTerms}");
265  data.suggestions_url = "http://suggest_keyword/{searchTerms}";
266  keyword_t_url_ = new TemplateURL(&profile_, data);
267  turl_model->Add(keyword_t_url_);
268  ASSERT_NE(0, keyword_t_url_->id());
269
270  // Add a page and search term for keyword_t_url_.
271  keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
272
273  // Keywords are updated by the InMemoryHistoryBackend only after the message
274  // has been processed on the history thread. Block until history processes all
275  // requests to ensure the InMemoryDatabase is the state we expect it.
276  profile_.BlockUntilHistoryProcessesPendingRequests();
277
278  provider_ = new SearchProviderForTest(this, &profile_);
279  provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
280}
281
282void SearchProviderTest::TearDown() {
283  base::RunLoop().RunUntilIdle();
284
285  // Shutdown the provider before the profile.
286  provider_ = NULL;
287}
288
289void SearchProviderTest::RunTest(TestData* cases,
290                                 int num_cases,
291                                 bool prefer_keyword) {
292  ACMatches matches;
293  for (int i = 0; i < num_cases; ++i) {
294    AutocompleteInput input(cases[i].input, base::string16::npos, base::string16(), GURL(),
295                            AutocompleteInput::INVALID_SPEC, false,
296                            prefer_keyword, true,
297                            AutocompleteInput::ALL_MATCHES);
298    provider_->Start(input, false);
299    matches = provider_->matches();
300    base::string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input +
301        ASCIIToUTF16("; prefer_keyword was: ") +
302        (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
303    EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
304    if (matches.size() == cases[i].num_results) {
305      for (size_t j = 0; j < cases[i].num_results; ++j) {
306        EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
307            diagnostic_details;
308        EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
309            diagnostic_details;
310        EXPECT_EQ(cases[i].output[j].fill_into_edit,
311                  matches[j].fill_into_edit) << diagnostic_details;
312        EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
313                  matches[j].allowed_to_be_default_match) << diagnostic_details;
314      }
315    }
316  }
317}
318
319void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
320  if (run_loop_ && provider_->done()) {
321    run_loop_->Quit();
322    run_loop_ = NULL;
323  }
324}
325
326void SearchProviderTest::RunTillProviderDone() {
327  if (provider_->done())
328    return;
329
330  base::RunLoop run_loop;
331  run_loop_ = &run_loop;
332  run_loop.Run();
333}
334
335void SearchProviderTest::QueryForInput(const base::string16& text,
336                                       bool prevent_inline_autocomplete,
337                                       bool prefer_keyword) {
338  // Start a query.
339  AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(),
340                          AutocompleteInput::INVALID_SPEC,
341                          prevent_inline_autocomplete, prefer_keyword, true,
342                          AutocompleteInput::ALL_MATCHES);
343  provider_->Start(input, false);
344
345  // RunUntilIdle so that the task scheduled by SearchProvider to create the
346  // URLFetchers runs.
347  base::RunLoop().RunUntilIdle();
348}
349
350void SearchProviderTest::QueryForInputAndSetWYTMatch(
351    const base::string16& text,
352    AutocompleteMatch* wyt_match) {
353  QueryForInput(text, false, false);
354  profile_.BlockUntilHistoryProcessesPendingRequests();
355  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
356  if (!wyt_match)
357    return;
358  ASSERT_GE(provider_->matches().size(), 1u);
359  EXPECT_TRUE(FindMatchWithDestination(GURL(
360      default_t_url_->url_ref().ReplaceSearchTerms(
361          TemplateURLRef::SearchTermsArgs(text))),
362      wyt_match));
363}
364
365GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
366                                            base::string16 term,
367                                            int visit_count) {
368  HistoryService* history =
369      HistoryServiceFactory::GetForProfile(&profile_,
370                                           Profile::EXPLICIT_ACCESS);
371  GURL search(t_url->url_ref().ReplaceSearchTerms(
372      TemplateURLRef::SearchTermsArgs(term)));
373  static base::Time last_added_time;
374  last_added_time = std::max(base::Time::Now(),
375      last_added_time + base::TimeDelta::FromMicroseconds(1));
376  history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
377      last_added_time, false, history::SOURCE_BROWSED);
378  history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
379  return search;
380}
381
382bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
383                                               AutocompleteMatch* match) {
384  for (ACMatches::const_iterator i = provider_->matches().begin();
385       i != provider_->matches().end(); ++i) {
386    if (i->contents == contents) {
387      *match = *i;
388      return true;
389    }
390  }
391  return false;
392}
393
394bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
395                                                  AutocompleteMatch* match) {
396  for (ACMatches::const_iterator i = provider_->matches().begin();
397       i != provider_->matches().end(); ++i) {
398    if (i->destination_url == url) {
399      *match = *i;
400      return true;
401    }
402  }
403  return false;
404}
405
406void SearchProviderTest::FinishDefaultSuggestQuery() {
407  net::TestURLFetcher* default_fetcher =
408      test_factory_.GetFetcherByID(
409          SearchProvider::kDefaultProviderURLFetcherID);
410  ASSERT_TRUE(default_fetcher);
411
412  // Tell the SearchProvider the default suggest query is done.
413  default_fetcher->set_response_code(200);
414  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
415}
416
417void SearchProviderTest::ForcedQueryTestHelper(
418    const std::string& input,
419    const std::string& json,
420    const std::string expected_matches[3],
421    const std::string& error_description) {
422  QueryForInput(ASCIIToUTF16(input), false, false);
423  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
424      SearchProvider::kDefaultProviderURLFetcherID);
425  ASSERT_TRUE(fetcher);
426  fetcher->set_response_code(200);
427  fetcher->SetResponseString(json);
428  fetcher->delegate()->OnURLFetchComplete(fetcher);
429  RunTillProviderDone();
430
431  const ACMatches& matches = provider_->matches();
432  ASSERT_LE(matches.size(), 3u);
433  size_t i = 0;
434  // Ensure that the returned matches equal the expectations.
435  for (; i < matches.size(); ++i) {
436    EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
437        error_description;
438  }
439  // Ensure that no expected matches are missing.
440  for (; i < 3u; ++i) {
441    EXPECT_EQ(std::string(), expected_matches[i]) <<
442        "Case #" << i << ": " << error_description;
443  }
444}
445
446void SearchProviderTest::ResetFieldTrialList() {
447  // Destroy the existing FieldTrialList before creating a new one to avoid
448  // a DCHECK.
449  field_trial_list_.reset();
450  field_trial_list_.reset(new base::FieldTrialList(
451      new metrics::SHA1EntropyProvider("foo")));
452  chrome_variations::testing::ClearAllVariationParams();
453  base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
454      "AutocompleteDynamicTrial_0", "DefaultGroup");
455  trial->group();
456}
457
458void SearchProviderTest::ClearAllResults() {
459  provider_->ClearAllResults();
460}
461
462// Actual Tests ---------------------------------------------------------------
463
464// Make sure we query history for the default provider and a URLFetcher is
465// created for the default provider suggest results.
466TEST_F(SearchProviderTest, QueryDefaultProvider) {
467  base::string16 term = term1_.substr(0, term1_.length() - 1);
468  QueryForInput(term, false, false);
469
470  // Make sure the default providers suggest service was queried.
471  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
472      SearchProvider::kDefaultProviderURLFetcherID);
473  ASSERT_TRUE(fetcher);
474
475  // And the URL matches what we expected.
476  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
477      TemplateURLRef::SearchTermsArgs(term)));
478  ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
479
480  // Tell the SearchProvider the suggest query is done.
481  fetcher->set_response_code(200);
482  fetcher->delegate()->OnURLFetchComplete(fetcher);
483  fetcher = NULL;
484
485  // Run till the history results complete.
486  RunTillProviderDone();
487
488  // The SearchProvider is done. Make sure it has a result for the history
489  // term term1.
490  AutocompleteMatch term1_match;
491  EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
492  // Term1 should not have a description, it's set later.
493  EXPECT_TRUE(term1_match.description.empty());
494
495  AutocompleteMatch wyt_match;
496  EXPECT_TRUE(FindMatchWithDestination(
497      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
498          TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
499  EXPECT_TRUE(wyt_match.description.empty());
500
501  // The match for term1 should be more relevant than the what you typed match.
502  EXPECT_GT(term1_match.relevance, wyt_match.relevance);
503  // This longer match should be inlineable.
504  EXPECT_TRUE(term1_match.allowed_to_be_default_match);
505  // The what you typed match should be too, of course.
506  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
507}
508
509TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
510  base::string16 term = term1_.substr(0, term1_.length() - 1);
511  QueryForInput(term, true, false);
512
513  ASSERT_FALSE(provider_->matches().empty());
514  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
515            provider_->matches()[0].type);
516  EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
517}
518
519// Issues a query that matches the registered keyword and makes sure history
520// is queried as well as URLFetchers getting created.
521TEST_F(SearchProviderTest, QueryKeywordProvider) {
522  base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
523  QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
524                false,
525                false);
526
527  // Make sure the default providers suggest service was queried.
528  net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
529      SearchProvider::kDefaultProviderURLFetcherID);
530  ASSERT_TRUE(default_fetcher);
531
532  // Tell the SearchProvider the default suggest query is done.
533  default_fetcher->set_response_code(200);
534  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
535  default_fetcher = NULL;
536
537  // Make sure the keyword providers suggest service was queried.
538  net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
539      SearchProvider::kKeywordProviderURLFetcherID);
540  ASSERT_TRUE(keyword_fetcher);
541
542  // And the URL matches what we expected.
543  GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
544      TemplateURLRef::SearchTermsArgs(term)));
545  ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
546
547  // Tell the SearchProvider the keyword suggest query is done.
548  keyword_fetcher->set_response_code(200);
549  keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
550  keyword_fetcher = NULL;
551
552  // Run till the history results complete.
553  RunTillProviderDone();
554
555  // The SearchProvider is done. Make sure it has a result for the history
556  // term keyword.
557  AutocompleteMatch match;
558  EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
559
560  // The match should have an associated keyword.
561  EXPECT_FALSE(match.keyword.empty());
562
563  // The fill into edit should contain the keyword.
564  EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
565            match.fill_into_edit);
566}
567
568TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
569  // None of the following input strings should be sent to the suggest server,
570  // because they may contain private data.
571  const char* inputs[] = {
572    "username:password",
573    "http://username:password",
574    "https://username:password",
575    "username:password@hostname",
576    "http://username:password@hostname/",
577    "file://filename",
578    "data://data",
579    "unknownscheme:anything",
580    "http://hostname/?query=q",
581    "http://hostname/path#ref",
582    "http://hostname/path #ref",
583    "https://hostname/path",
584  };
585
586  for (size_t i = 0; i < arraysize(inputs); ++i) {
587    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
588    // Make sure the default provider's suggest service was not queried.
589    ASSERT_TRUE(test_factory_.GetFetcherByID(
590        SearchProvider::kDefaultProviderURLFetcherID) == NULL);
591    // Run till the history results complete.
592    RunTillProviderDone();
593  }
594}
595
596TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
597  // All of the following input strings should be sent to the suggest server,
598  // because they should not get caught by the private data checks.
599  const char* inputs[] = {
600    "query",
601    "query with spaces",
602    "http://hostname",
603    "http://hostname/path",
604    "http://hostname #ref",
605    "www.hostname.com #ref",
606    "https://hostname",
607    "#hashtag",
608    "foo https://hostname/path"
609  };
610
611  profile_.BlockUntilHistoryProcessesPendingRequests();
612  for (size_t i = 0; i < arraysize(inputs); ++i) {
613    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
614    // Make sure the default provider's suggest service was queried.
615    ASSERT_TRUE(test_factory_.GetFetcherByID(
616        SearchProvider::kDefaultProviderURLFetcherID) != NULL);
617  }
618}
619
620TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
621  AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
622      &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
623  GURL url = AddSearchToHistory(default_t_url_,
624                                ASCIIToUTF16("docs.google.com"), 1);
625
626  // Add the term as a url.
627  HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
628      AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1,
629                         base::Time::Now(), false, history::SOURCE_BROWSED);
630  profile_.BlockUntilHistoryProcessesPendingRequests();
631
632  AutocompleteMatch wyt_match;
633  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
634                                                      &wyt_match));
635
636  // There should be two matches, one for what you typed, the other for
637  // 'docs.google.com'. The search term should have a lower priority than the
638  // what you typed match.
639  ASSERT_EQ(2u, provider_->matches().size());
640  AutocompleteMatch term_match;
641  EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
642  EXPECT_GT(wyt_match.relevance, term_match.relevance);
643  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
644  EXPECT_TRUE(term_match.allowed_to_be_default_match);
645}
646
647TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
648  const std::string kEmptyMatch;
649  struct {
650    const std::string json;
651    const std::string matches_in_default_mode[3];
652    const std::string matches_in_forced_query_mode[3];
653  } cases[] = {
654    // Without suggested relevance scores.
655    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
656       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
657      { "a", "a1.com", "a2" },
658      { "a", "a2", kEmptyMatch } },
659
660    // With suggested relevance scores in a situation where navsuggest would
661    // go second.
662    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
663       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
664        "\"google:suggestrelevance\":[1250, 1200]}]",
665      { "a", "a1.com", "a2" },
666      { "a", "a2", kEmptyMatch } },
667
668    // With suggested relevance scores in a situation where navsuggest
669    // would go first.
670    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
671       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
672        "\"google:suggestrelevance\":[1350, 1250]}]",
673      { "a1.com", "a", "a2" },
674      { "a", "a2", kEmptyMatch } },
675
676    // With suggested relevance scores in a situation where navsuggest
677    // would go first only because verbatim has been demoted.
678    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
679       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
680        "\"google:suggestrelevance\":[1450, 1400],"
681        "\"google:verbatimrelevance\":1350}]",
682      { "a1.com", "a2", "a" },
683      { "a2", "a", kEmptyMatch } },
684  };
685
686  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
687    ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
688                           "regular input with json=" + cases[i].json);
689    ForcedQueryTestHelper("?a", cases[i].json,
690                          cases[i].matches_in_forced_query_mode,
691                          "forced query input with json=" + cases[i].json);
692  }
693}
694
695// A multiword search with one visit should not autocomplete until multiple
696// words are typed.
697TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
698  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
699                                   1));
700  profile_.BlockUntilHistoryProcessesPendingRequests();
701
702  AutocompleteMatch wyt_match;
703  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
704                                                      &wyt_match));
705  ASSERT_EQ(2u, provider_->matches().size());
706  AutocompleteMatch term_match;
707  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
708  EXPECT_GT(wyt_match.relevance, term_match.relevance);
709  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
710  EXPECT_TRUE(term_match.allowed_to_be_default_match);
711
712  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
713                                                      &wyt_match));
714  ASSERT_EQ(2u, provider_->matches().size());
715  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
716  EXPECT_GT(term_match.relevance, wyt_match.relevance);
717  EXPECT_TRUE(term_match.allowed_to_be_default_match);
718  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
719}
720
721// A multiword search with more than one visit should autocomplete immediately.
722TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
723  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
724                                   2));
725  profile_.BlockUntilHistoryProcessesPendingRequests();
726
727  AutocompleteMatch wyt_match;
728  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
729                                                      &wyt_match));
730  ASSERT_EQ(2u, provider_->matches().size());
731  AutocompleteMatch term_match;
732  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
733  EXPECT_GT(term_match.relevance, wyt_match.relevance);
734  EXPECT_TRUE(term_match.allowed_to_be_default_match);
735  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
736}
737
738// Autocompletion should work at a word boundary after a space.
739TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
740  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
741                                   2));
742  profile_.BlockUntilHistoryProcessesPendingRequests();
743
744  AutocompleteMatch wyt_match;
745  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
746                                                      &wyt_match));
747  ASSERT_EQ(2u, provider_->matches().size());
748  AutocompleteMatch term_match;
749  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
750  EXPECT_GT(term_match.relevance, wyt_match.relevance);
751  EXPECT_TRUE(term_match.allowed_to_be_default_match);
752  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
753}
754
755// Newer multiword searches should score more highly than older ones.
756TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
757  GURL term_url_a(AddSearchToHistory(default_t_url_,
758                                     ASCIIToUTF16("three searches aaa"), 1));
759  GURL term_url_b(AddSearchToHistory(default_t_url_,
760                                     ASCIIToUTF16("three searches bbb"), 1));
761  profile_.BlockUntilHistoryProcessesPendingRequests();
762
763  AutocompleteMatch wyt_match;
764  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
765                                                      &wyt_match));
766  ASSERT_EQ(3u, provider_->matches().size());
767  AutocompleteMatch term_match_a;
768  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
769  AutocompleteMatch term_match_b;
770  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
771  EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
772  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
773  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
774  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
775  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
776}
777
778// An autocompleted multiword search should not be replaced by a different
779// autocompletion while the user is still typing a valid prefix.
780TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
781  GURL term_url_a(AddSearchToHistory(default_t_url_,
782                                     ASCIIToUTF16("four searches aaa"), 2));
783  GURL term_url_b(AddSearchToHistory(default_t_url_,
784                                     ASCIIToUTF16("four searches bbb"), 1));
785  profile_.BlockUntilHistoryProcessesPendingRequests();
786
787  AutocompleteMatch wyt_match;
788  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
789                                                      &wyt_match));
790  ASSERT_EQ(3u, provider_->matches().size());
791  AutocompleteMatch term_match_a;
792  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
793  AutocompleteMatch term_match_b;
794  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
795  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
796  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
797  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
798  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
799  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
800
801  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
802                                                      &wyt_match));
803  ASSERT_EQ(3u, provider_->matches().size());
804  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
805  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
806  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
807  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
808  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
809  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
810  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
811}
812
813// Non-completable multiword searches should not crowd out single-word searches.
814TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
815  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
816  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
817  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
818  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
819  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
820  profile_.BlockUntilHistoryProcessesPendingRequests();
821
822  AutocompleteMatch wyt_match;
823  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
824                                                      &wyt_match));
825  ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
826  AutocompleteMatch term_match;
827  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
828  EXPECT_GT(term_match.relevance, wyt_match.relevance);
829  EXPECT_TRUE(term_match.allowed_to_be_default_match);
830  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
831}
832
833// Inline autocomplete matches regardless of case differences from the input.
834TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
835  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
836  profile_.BlockUntilHistoryProcessesPendingRequests();
837
838  AutocompleteMatch wyt_match;
839  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
840                                                      &wyt_match));
841  ASSERT_EQ(2u, provider_->matches().size());
842  AutocompleteMatch term_match;
843  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
844  EXPECT_GT(term_match.relevance, wyt_match.relevance);
845  EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
846  EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
847  EXPECT_TRUE(term_match.allowed_to_be_default_match);
848}
849
850// Verifies AutocompleteControllers return results (including keyword
851// results) in the right order and set descriptions for them correctly.
852TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
853  // Add an entry that corresponds to a keyword search with 'term2'.
854  AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
855  profile_.BlockUntilHistoryProcessesPendingRequests();
856
857  AutocompleteController controller(&profile_, NULL,
858      AutocompleteProvider::TYPE_SEARCH);
859  controller.Start(AutocompleteInput(
860      ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(),
861      AutocompleteInput::INVALID_SPEC, false, false, true,
862      AutocompleteInput::ALL_MATCHES));
863  const AutocompleteResult& result = controller.result();
864
865  // There should be three matches, one for the keyword history, one for
866  // keyword provider's what-you-typed, and one for the default provider's
867  // what you typed, in that order.
868  ASSERT_EQ(3u, result.size());
869  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
870  EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
871            result.match_at(1).type);
872  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
873            result.match_at(2).type);
874  EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
875  EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
876  EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
877  EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
878  EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
879
880  // The two keyword results should come with the keyword we expect.
881  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
882  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
883  // The default provider has a different keyword.  (We don't explicitly
884  // set it during this test, so all we do is assert that it's different.)
885  EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
886
887  // The top result will always have a description.  The third result,
888  // coming from a different provider than the first two, should also.
889  // Whether the second result has one doesn't matter much.  (If it was
890  // missing, people would infer that it's the same search provider as
891  // the one above it.)
892  EXPECT_FALSE(result.match_at(0).description.empty());
893  EXPECT_FALSE(result.match_at(2).description.empty());
894  EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
895}
896
897TEST_F(SearchProviderTest, KeywordVerbatim) {
898  TestData cases[] = {
899    // Test a simple keyword input.
900    { ASCIIToUTF16("k foo"), 2,
901      { ResultInfo(GURL("http://keyword/foo"),
902                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
903                   true,
904                   ASCIIToUTF16("k foo")),
905        ResultInfo(GURL("http://defaultturl/k%20foo"),
906                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
907                   false,
908                   ASCIIToUTF16("k foo") ) } },
909
910    // Make sure extra whitespace after the keyword doesn't change the
911    // keyword verbatim query.
912    { ASCIIToUTF16("k   foo"), 2,
913      { ResultInfo(GURL("http://keyword/foo"),
914                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
915                   true,
916                   ASCIIToUTF16("k foo")),
917        ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
918                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
919                   false,
920                   ASCIIToUTF16("k   foo")) } },
921    // Leading whitespace should be stripped before SearchProvider gets the
922    // input; hence there are no tests here about how it handles those inputs.
923
924    // But whitespace elsewhere in the query string should matter to both
925    // matches.
926    { ASCIIToUTF16("k  foo  bar"), 2,
927      { ResultInfo(GURL("http://keyword/foo%20%20bar"),
928                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
929                   true,
930                   ASCIIToUTF16("k foo  bar")),
931        ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
932                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
933                   false,
934                   ASCIIToUTF16("k  foo  bar")) } },
935    // Note in the above test case we don't test trailing whitespace because
936    // SearchProvider still doesn't handle this well.  See related bugs:
937    // 102690, 99239, 164635.
938
939    // Keywords can be prefixed by certain things that should get ignored
940    // when constructing the keyword match.
941    { ASCIIToUTF16("www.k foo"), 2,
942      { ResultInfo(GURL("http://keyword/foo"),
943                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
944                   true,
945                   ASCIIToUTF16("k foo")),
946        ResultInfo(GURL("http://defaultturl/www.k%20foo"),
947                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
948                   false,
949                   ASCIIToUTF16("www.k foo")) } },
950    { ASCIIToUTF16("http://k foo"), 2,
951      { ResultInfo(GURL("http://keyword/foo"),
952                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
953                   true,
954                   ASCIIToUTF16("k foo")),
955        ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
956                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
957                   false,
958                   ASCIIToUTF16("http://k foo")) } },
959    { ASCIIToUTF16("http://www.k foo"), 2,
960      { ResultInfo(GURL("http://keyword/foo"),
961                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
962                   true,
963                   ASCIIToUTF16("k foo")),
964        ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
965                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
966                   false,
967                   ASCIIToUTF16("http://www.k foo")) } },
968
969    // A keyword with no remaining input shouldn't get a keyword
970    // verbatim match.
971    { ASCIIToUTF16("k"), 1,
972      { ResultInfo(GURL("http://defaultturl/k"),
973                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
974                   true,
975                   ASCIIToUTF16("k")) } },
976    { ASCIIToUTF16("k "), 1,
977      { ResultInfo(GURL("http://defaultturl/k%20"),
978                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
979                   true,
980                   ASCIIToUTF16("k ")) } }
981
982    // The fact that verbatim queries to keyword are handled by KeywordProvider
983    // not SearchProvider is tested in
984    // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
985  };
986
987  // Test not in keyword mode.
988  RunTest(cases, arraysize(cases), false);
989
990  // Test in keyword mode.  (Both modes should give the same result.)
991  RunTest(cases, arraysize(cases), true);
992}
993
994// Ensures command-line flags are reflected in the URLs the search provider
995// generates.
996TEST_F(SearchProviderTest, CommandLineOverrides) {
997  TemplateURLService* turl_model =
998      TemplateURLServiceFactory::GetForProfile(&profile_);
999
1000  TemplateURLData data;
1001  data.short_name = ASCIIToUTF16("default");
1002  data.SetKeyword(data.short_name);
1003  data.SetURL("{google:baseURL}{searchTerms}");
1004  default_t_url_ = new TemplateURL(&profile_, data);
1005  turl_model->Add(default_t_url_);
1006  turl_model->SetDefaultSearchProvider(default_t_url_);
1007
1008  CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
1009                                                      "http://www.bar.com/");
1010  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1011      switches::kExtraSearchQueryParams, "a=b");
1012
1013  TestData cases[] = {
1014    { ASCIIToUTF16("k a"), 2,
1015      { ResultInfo(GURL("http://keyword/a"),
1016                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1017                   true,
1018                   ASCIIToUTF16("k a")),
1019        ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1020                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1021                   false,
1022                   ASCIIToUTF16("k a")) } },
1023  };
1024
1025  RunTest(cases, arraysize(cases), false);
1026}
1027
1028// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1029// Also verifies that just the *first* navigational result is listed as a match
1030// if suggested relevance scores were not sent.
1031TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1032  QueryForInput(ASCIIToUTF16("a.c"), false, false);
1033
1034  // Make sure the default providers suggest service was queried.
1035  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1036      SearchProvider::kDefaultProviderURLFetcherID);
1037  ASSERT_TRUE(fetcher);
1038
1039  // Tell the SearchProvider the suggest query is done.
1040  fetcher->set_response_code(200);
1041  fetcher->SetResponseString(
1042      "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1043      "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
1044  fetcher->delegate()->OnURLFetchComplete(fetcher);
1045  fetcher = NULL;
1046
1047  // Run till the history results complete.
1048  RunTillProviderDone();
1049
1050  // Make sure the only match is 'a.com' and it doesn't have a template_url.
1051  AutocompleteMatch nav_match;
1052  EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1053  EXPECT_TRUE(nav_match.keyword.empty());
1054  EXPECT_TRUE(nav_match.allowed_to_be_default_match);
1055  EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1056}
1057
1058// Verifies that the most relevant suggest results are added properly.
1059TEST_F(SearchProviderTest, SuggestRelevance) {
1060  QueryForInput(ASCIIToUTF16("a"), false, false);
1061
1062  // Make sure the default provider's suggest service was queried.
1063  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1064      SearchProvider::kDefaultProviderURLFetcherID);
1065  ASSERT_TRUE(fetcher);
1066
1067  // Tell the SearchProvider the suggest query is done.
1068  fetcher->set_response_code(200);
1069  fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
1070  fetcher->delegate()->OnURLFetchComplete(fetcher);
1071  fetcher = NULL;
1072
1073  // Run till the history results complete.
1074  RunTillProviderDone();
1075
1076  // Check the expected verbatim and (first 3) suggestions' relative relevances.
1077  AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1078  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1079  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1080  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1081  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1082  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1083  EXPECT_GT(verbatim.relevance, match_a1.relevance);
1084  EXPECT_GT(match_a1.relevance, match_a2.relevance);
1085  EXPECT_GT(match_a2.relevance, match_a3.relevance);
1086  EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1087  EXPECT_TRUE(match_a1.allowed_to_be_default_match);
1088  EXPECT_TRUE(match_a2.allowed_to_be_default_match);
1089  EXPECT_TRUE(match_a3.allowed_to_be_default_match);
1090}
1091
1092// Verifies that the default provider abandons suggested relevance scores
1093// when in keyword mode.  This should happen regardless of whether the
1094// keyword provider returns suggested relevance scores.
1095TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1096  struct {
1097    const std::string default_provider_json;
1098    const std::string keyword_provider_json;
1099    const std::string matches[5];
1100  } cases[] = {
1101    // First, try an input where the keyword provider does not deliver
1102    // suggested relevance scores.
1103    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1104      "{\"google:verbatimrelevance\":9700,"
1105      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1106      "\"google:suggestrelevance\":[9900, 9800]}]",
1107      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1108      { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1109
1110    // Now try with keyword provider suggested relevance scores.
1111    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1112      "{\"google:verbatimrelevance\":9700,"
1113      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1114      "\"google:suggestrelevance\":[9900, 9800]}]",
1115      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1116      "\"google:verbatimrelevance\":9500,"
1117      "\"google:suggestrelevance\":[9600]}]",
1118      { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1119  };
1120
1121  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1122    QueryForInput(ASCIIToUTF16("k a"), false, true);
1123    net::TestURLFetcher* default_fetcher =
1124        test_factory_.GetFetcherByID(
1125            SearchProvider::kDefaultProviderURLFetcherID);
1126    ASSERT_TRUE(default_fetcher);
1127    default_fetcher->set_response_code(200);
1128    default_fetcher->SetResponseString(cases[i].default_provider_json);
1129    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1130    net::TestURLFetcher* keyword_fetcher =
1131        test_factory_.GetFetcherByID(
1132            SearchProvider::kKeywordProviderURLFetcherID);
1133    ASSERT_TRUE(keyword_fetcher);
1134    keyword_fetcher->set_response_code(200);
1135    keyword_fetcher->SetResponseString(cases[i].keyword_provider_json);
1136    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1137    RunTillProviderDone();
1138
1139    const std::string description = "for input with default_provider_json=" +
1140        cases[i].default_provider_json + " and keyword_provider_json=" +
1141        cases[i].keyword_provider_json;
1142    const ACMatches& matches = provider_->matches();
1143    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1144    size_t j = 0;
1145    // Ensure that the returned matches equal the expectations.
1146    for (; j < matches.size(); ++j) {
1147      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) <<
1148          description;
1149    }
1150    // Ensure that no expected matches are missing.
1151    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1152      EXPECT_EQ(std::string(), cases[i].matches[j]) << description;
1153  }
1154}
1155
1156// Verifies that suggest results with relevance scores are added
1157// properly when using the default fetcher.  When adding a new test
1158// case to this test, please consider adding it to the tests in
1159// KeywordFetcherSuggestRelevance below.
1160TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1161  struct DefaultFetcherMatch {
1162    std::string contents;
1163    bool allowed_to_be_default_match;
1164  };
1165  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1166  struct {
1167    const std::string json;
1168    const DefaultFetcherMatch matches[6];
1169    const std::string inline_autocompletion;
1170  } cases[] = {
1171    // Ensure that suggestrelevance scores reorder matches.
1172    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1173      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch,
1174        kEmptyMatch, kEmptyMatch },
1175      std::string() },
1176    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1177       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1178        "\"google:suggestrelevance\":[1, 2]}]",
1179      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1180        kEmptyMatch, kEmptyMatch },
1181      std::string() },
1182
1183    // Without suggested relevance scores, we should only allow one
1184    // navsuggest result to be be displayed.
1185    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1186       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1187      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1188        kEmptyMatch, kEmptyMatch },
1189      std::string() },
1190
1191    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1192    // Negative values will have no effect; the calculated value will be used.
1193    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1194                             "\"google:suggestrelevance\":[9998]}]",
1195      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1196        kEmptyMatch },
1197      std::string() },
1198    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1199                             "\"google:suggestrelevance\":[9999]}]",
1200      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1201        kEmptyMatch },
1202      "1" },
1203    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1204                             "\"google:suggestrelevance\":[9999]}]",
1205      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1206        kEmptyMatch },
1207      "1" },
1208    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1209                             "\"google:suggestrelevance\":[9999]}]",
1210      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1211        kEmptyMatch },
1212      "1" },
1213    { "[\"a\",[\"http://a.com\"],[],[],"
1214       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1215        "\"google:verbatimrelevance\":9999,"
1216        "\"google:suggestrelevance\":[9998]}]",
1217      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1218        kEmptyMatch },
1219      std::string() },
1220    { "[\"a\",[\"http://a.com\"],[],[],"
1221       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1222        "\"google:verbatimrelevance\":9998,"
1223        "\"google:suggestrelevance\":[9999]}]",
1224      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1225        kEmptyMatch },
1226      ".com" },
1227    { "[\"a\",[\"http://a.com\"],[],[],"
1228       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1229        "\"google:verbatimrelevance\":0,"
1230        "\"google:suggestrelevance\":[9999]}]",
1231      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1232        kEmptyMatch },
1233      ".com" },
1234    { "[\"a\",[\"http://a.com\"],[],[],"
1235       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1236        "\"google:verbatimrelevance\":-1,"
1237        "\"google:suggestrelevance\":[9999]}]",
1238      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1239        kEmptyMatch },
1240      ".com" },
1241
1242    // Ensure that both types of relevance scores reorder matches together.
1243    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1244                                     "\"google:verbatimrelevance\":9998}]",
1245      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1246        kEmptyMatch },
1247      "1" },
1248
1249    // Ensure that only inlinable matches may be ranked as the highest result.
1250    // Ignore all suggested relevance scores if this constraint is violated.
1251    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1252      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1253        kEmptyMatch },
1254      std::string() },
1255    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1256                            "\"google:verbatimrelevance\":0}]",
1257      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1258        kEmptyMatch },
1259      std::string() },
1260    { "[\"a\",[\"http://b.com\"],[],[],"
1261       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1262        "\"google:suggestrelevance\":[9999]}]",
1263      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1264        kEmptyMatch, kEmptyMatch },
1265      std::string() },
1266    { "[\"a\",[\"http://b.com\"],[],[],"
1267       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1268        "\"google:suggestrelevance\":[9999],"
1269        "\"google:verbatimrelevance\":0}]",
1270      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1271        kEmptyMatch, kEmptyMatch },
1272      std::string() },
1273    { "[\"a\",[\"https://a/\"],[],[],"
1274       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1275        "\"google:suggestrelevance\":[9999]}]",
1276      { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch,
1277        kEmptyMatch, kEmptyMatch },
1278      std::string() },
1279
1280    // Ensure that the top result is ranked as highly as calculated verbatim.
1281    // Ignore the suggested verbatim relevance if this constraint is violated.
1282    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1283      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1284        kEmptyMatch },
1285      std::string() },
1286    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1287      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1288        kEmptyMatch },
1289      std::string() },
1290    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1291                             "\"google:verbatimrelevance\":0}]",
1292      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1293        kEmptyMatch },
1294      std::string() },
1295    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1296                                     "\"google:verbatimrelevance\":0}]",
1297      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1298        kEmptyMatch },
1299      std::string() },
1300    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1301      "\"google:verbatimrelevance\":2}]",
1302      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1303        kEmptyMatch },
1304      std::string() },
1305    { "[\"a\",[\"http://a.com\"],[],[],"
1306       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1307        "\"google:suggestrelevance\":[1],"
1308        "\"google:verbatimrelevance\":0}]",
1309      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1310        kEmptyMatch },
1311      std::string() },
1312    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1313       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1314        "\"google:suggestrelevance\":[1, 2],"
1315        "\"google:verbatimrelevance\":0}]",
1316      { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch,
1317        kEmptyMatch, kEmptyMatch },
1318      std::string() },
1319
1320    // Ensure that all suggestions are considered, regardless of order.
1321    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1322       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1323      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1324        {"e", false }, {"d", false } },
1325      std::string() },
1326    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1327              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1328              "\"http://h.com\"],[],[],"
1329       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1330                                "\"NAVIGATION\", \"NAVIGATION\","
1331                                "\"NAVIGATION\", \"NAVIGATION\","
1332                                "\"NAVIGATION\"],"
1333        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1334      { { "a", true }, { "h.com", false }, { "g.com", false },
1335        { "f.com", false }, {"e.com", false }, {"d.com", false } },
1336      std::string() },
1337
1338    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1339    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1340      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1341        kEmptyMatch },
1342      std::string() },
1343    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1344      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1345        kEmptyMatch },
1346      std::string() },
1347    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1348       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1349        "\"google:suggestrelevance\":[1]}]",
1350      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1351        kEmptyMatch, kEmptyMatch },
1352      std::string() },
1353    { "[\"a\",[\"http://a1.com\"],[],[],"
1354       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1355       "\"google:suggestrelevance\":[9999, 1]}]",
1356      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1357        kEmptyMatch, kEmptyMatch },
1358      std::string() },
1359
1360    // Ensure that all 'verbatim' results are merged with their maximum score.
1361    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1362       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1363      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1364        kEmptyMatch },
1365      "2" },
1366    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1367       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1368        "\"google:verbatimrelevance\":0}]",
1369      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1370        kEmptyMatch },
1371      "2" },
1372
1373    // Ensure that verbatim is always generated without other suggestions.
1374    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1375    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1376      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1377        kEmptyMatch },
1378      std::string() },
1379    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1380      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1381        kEmptyMatch },
1382      std::string() },
1383  };
1384
1385  std::map<std::string, std::string> params;
1386  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1387      ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
1388  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1389      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1390  base::FieldTrialList::CreateFieldTrial(
1391      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1392
1393  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1394    QueryForInput(ASCIIToUTF16("a"), false, false);
1395    net::TestURLFetcher* fetcher =
1396        test_factory_.GetFetcherByID(
1397            SearchProvider::kDefaultProviderURLFetcherID);
1398    ASSERT_TRUE(fetcher);
1399    fetcher->set_response_code(200);
1400    fetcher->SetResponseString(cases[i].json);
1401    fetcher->delegate()->OnURLFetchComplete(fetcher);
1402    RunTillProviderDone();
1403
1404    const std::string description = "for input with json=" + cases[i].json;
1405    const ACMatches& matches = provider_->matches();
1406    // The top match must inline and score as highly as calculated verbatim.
1407    ASSERT_FALSE(matches.empty());
1408    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1409              matches[0].inline_autocompletion) << description;
1410    EXPECT_GE(matches[0].relevance, 1300) << description;
1411
1412    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1413    size_t j = 0;
1414    // Ensure that the returned matches equal the expectations.
1415    for (; j < matches.size(); ++j) {
1416      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1417                matches[j].contents) << description;
1418      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1419                matches[j].allowed_to_be_default_match) << description;
1420    }
1421    // Ensure that no expected matches are missing.
1422    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1423      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1424          "Case # " << i << " " << description;
1425  }
1426}
1427
1428// This test is like DefaultFetcherSuggestRelevance above except it enables
1429// the field trial that causes the omnibox to be willing to reorder matches
1430// to guarantee the top result is a legal default match.  This field trial
1431// causes SearchProvider to allow some constraints to be violated that it
1432// wouldn't normally because the omnibox will fix the problems later.
1433TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1434  struct DefaultFetcherMatch {
1435    std::string contents;
1436    bool allowed_to_be_default_match;
1437  };
1438  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1439  struct {
1440    const std::string json;
1441    const DefaultFetcherMatch matches[6];
1442    const std::string inline_autocompletion;
1443  } cases[] = {
1444    // Ensure that suggestrelevance scores reorder matches.
1445    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1446      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch,
1447        kEmptyMatch },
1448      std::string() },
1449    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1450       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1451        "\"google:suggestrelevance\":[1, 2]}]",
1452      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1453        kEmptyMatch, kEmptyMatch },
1454      std::string() },
1455
1456    // Without suggested relevance scores, we should only allow one
1457    // navsuggest result to be be displayed.
1458    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1459       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1460      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1461        kEmptyMatch, kEmptyMatch },
1462      std::string() },
1463
1464    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1465    // Negative values will have no effect; the calculated value will be used.
1466    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1467                             "\"google:suggestrelevance\":[9998]}]",
1468      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1469        kEmptyMatch },
1470      std::string() },
1471    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1472                             "\"google:suggestrelevance\":[9999]}]",
1473      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1474        kEmptyMatch },
1475      "1" },
1476    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1477                             "\"google:suggestrelevance\":[9999]}]",
1478      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1479        kEmptyMatch },
1480      "1" },
1481    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1482                             "\"google:suggestrelevance\":[9999]}]",
1483      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1484        kEmptyMatch },
1485      "1" },
1486    { "[\"a\",[\"http://a.com\"],[],[],"
1487       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1488        "\"google:verbatimrelevance\":9999,"
1489        "\"google:suggestrelevance\":[9998]}]",
1490      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1491        kEmptyMatch },
1492      std::string() },
1493    { "[\"a\",[\"http://a.com\"],[],[],"
1494       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1495        "\"google:verbatimrelevance\":9998,"
1496        "\"google:suggestrelevance\":[9999]}]",
1497      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1498        kEmptyMatch },
1499      ".com" },
1500    { "[\"a\",[\"http://a.com\"],[],[],"
1501       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1502        "\"google:verbatimrelevance\":0,"
1503        "\"google:suggestrelevance\":[9999]}]",
1504      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1505        kEmptyMatch },
1506      ".com" },
1507    { "[\"a\",[\"http://a.com\"],[],[],"
1508       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1509        "\"google:verbatimrelevance\":-1,"
1510        "\"google:suggestrelevance\":[9999]}]",
1511      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1512        kEmptyMatch },
1513      ".com" },
1514
1515    // Ensure that both types of relevance scores reorder matches together.
1516    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1517                                     "\"google:verbatimrelevance\":9998}]",
1518      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1519        kEmptyMatch },
1520      "1" },
1521
1522    // Allow non-inlineable matches to be the highest-scoring match but,
1523    // if the result set lacks a single inlineable result, abandon suggested
1524    // relevance scores entirely.
1525    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1526      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1527        kEmptyMatch },
1528      std::string() },
1529    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1530                            "\"google:verbatimrelevance\":0}]",
1531      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1532        kEmptyMatch },
1533      std::string() },
1534    { "[\"a\",[\"http://b.com\"],[],[],"
1535       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1536        "\"google:suggestrelevance\":[9999]}]",
1537      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch,
1538        kEmptyMatch, kEmptyMatch },
1539      std::string() },
1540    { "[\"a\",[\"http://b.com\"],[],[],"
1541       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1542        "\"google:suggestrelevance\":[9999],"
1543        "\"google:verbatimrelevance\":0}]",
1544      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1545        kEmptyMatch, kEmptyMatch },
1546      std::string() },
1547
1548    // Allow low-scoring matches.
1549    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1550      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1551        kEmptyMatch },
1552      "1" },
1553    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1554      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1555        kEmptyMatch },
1556      "1" },
1557    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1558                             "\"google:verbatimrelevance\":0}]",
1559      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1560        kEmptyMatch },
1561      "1" },
1562    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1563                                     "\"google:verbatimrelevance\":0}]",
1564      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1565        kEmptyMatch },
1566      "2" },
1567    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1568      "\"google:verbatimrelevance\":2}]",
1569      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1570        kEmptyMatch },
1571      "2" },
1572    { "[\"a\",[\"http://a.com\"],[],[],"
1573       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1574        "\"google:suggestrelevance\":[1],"
1575        "\"google:verbatimrelevance\":0}]",
1576      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1577        kEmptyMatch },
1578      ".com" },
1579    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1580       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1581        "\"google:suggestrelevance\":[1, 2],"
1582        "\"google:verbatimrelevance\":0}]",
1583      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1584        kEmptyMatch, kEmptyMatch },
1585      "2.com" },
1586
1587    // Ensure that all suggestions are considered, regardless of order.
1588    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1589       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1590      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1591        { "e", false }, { "d", false } },
1592      std::string() },
1593    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1594              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1595              "\"http://h.com\"],[],[],"
1596       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1597                                "\"NAVIGATION\", \"NAVIGATION\","
1598                                "\"NAVIGATION\", \"NAVIGATION\","
1599                                "\"NAVIGATION\"],"
1600        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1601      { { "a", true }, { "h.com", false }, { "g.com", false },
1602        { "f.com", false }, { "e.com", false }, { "d.com", false } },
1603      std::string() },
1604
1605    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1606    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1607      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1608        kEmptyMatch },
1609      std::string() },
1610    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1611      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1612        kEmptyMatch },
1613      std::string() },
1614    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1615       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1616        "\"google:suggestrelevance\":[1]}]",
1617      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1618        kEmptyMatch, kEmptyMatch },
1619      std::string() },
1620    { "[\"a\",[\"http://a1.com\"],[],[],"
1621       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1622       "\"google:suggestrelevance\":[9999, 1]}]",
1623      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1624        kEmptyMatch, kEmptyMatch },
1625      std::string() },
1626
1627    // Ensure that all 'verbatim' results are merged with their maximum score.
1628    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1629       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1630      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1631        kEmptyMatch },
1632      "2" },
1633    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1634       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1635        "\"google:verbatimrelevance\":0}]",
1636      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1637        kEmptyMatch },
1638      "2" },
1639
1640    // Ensure that verbatim is always generated without other suggestions.
1641    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1642    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1643      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1644        kEmptyMatch },
1645      std::string() },
1646    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1647      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1648        kEmptyMatch },
1649      std::string() },
1650  };
1651
1652  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1653    QueryForInput(ASCIIToUTF16("a"), false, false);
1654    net::TestURLFetcher* fetcher =
1655        test_factory_.GetFetcherByID(
1656            SearchProvider::kDefaultProviderURLFetcherID);
1657    ASSERT_TRUE(fetcher);
1658    fetcher->set_response_code(200);
1659    fetcher->SetResponseString(cases[i].json);
1660    fetcher->delegate()->OnURLFetchComplete(fetcher);
1661    RunTillProviderDone();
1662
1663    const std::string description = "for input with json=" + cases[i].json;
1664    const ACMatches& matches = provider_->matches();
1665    ASSERT_FALSE(matches.empty());
1666    // Find the first match that's allowed to be the default match and check
1667    // its inline_autocompletion.
1668    ACMatches::const_iterator it = FindDefaultMatch(matches);
1669    ASSERT_NE(matches.end(), it);
1670    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1671              it->inline_autocompletion) << description;
1672
1673    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1674    size_t j = 0;
1675    // Ensure that the returned matches equal the expectations.
1676    for (; j < matches.size(); ++j) {
1677      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1678                matches[j].contents) << description;
1679      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1680                matches[j].allowed_to_be_default_match) << description;
1681    }
1682    // Ensure that no expected matches are missing.
1683    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1684      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1685          "Case # " << i << " " << description;
1686  }
1687}
1688
1689// Verifies that suggest results with relevance scores are added
1690// properly when using the keyword fetcher.  This is similar to the
1691// test DefaultFetcherSuggestRelevance above but this uses inputs that
1692// trigger keyword suggestions (i.e., "k a" rather than "a") and has
1693// different expectations (because now the results are a mix of
1694// keyword suggestions and default provider suggestions).  When a new
1695// test is added to this TEST_F, please consider if it would be
1696// appropriate to add to DefaultFetcherSuggestRelevance as well.
1697TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1698  struct KeywordFetcherMatch {
1699    std::string contents;
1700    bool from_keyword;
1701    bool allowed_to_be_default_match;
1702  };
1703  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1704  struct {
1705    const std::string json;
1706    const KeywordFetcherMatch matches[6];
1707    const std::string inline_autocompletion;
1708  } cases[] = {
1709    // Ensure that suggest relevance scores reorder matches and that
1710    // the keyword verbatim (lacking a suggested verbatim score) beats
1711    // the default provider verbatim.
1712    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1713      { { "a",   true,  true },
1714        { "k a", false, false },
1715        { "c",   true,  false },
1716        { "b",   true,  false },
1717        kEmptyMatch, kEmptyMatch },
1718      std::string() },
1719    // Again, check that relevance scores reorder matches, just this
1720    // time with navigation matches.  This also checks that with
1721    // suggested relevance scores we allow multiple navsuggest results.
1722    // Note that navsuggest results that come from a keyword provider
1723    // are marked as not a keyword result.  (They don't go to a
1724    // keyword search engine.)
1725    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1726       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1727       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1728      { { "a",     true,  true },
1729        { "d",     true,  false },
1730        { "c.com", false, false },
1731        { "b.com", false, false },
1732        { "k a",   false, false },
1733        kEmptyMatch },
1734      std::string() },
1735
1736    // Without suggested relevance scores, we should only allow one
1737    // navsuggest result to be be displayed.
1738    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1739       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1740      { { "a",     true,  true },
1741        { "b.com", false, false },
1742        { "k a",   false, false },
1743        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1744      std::string() },
1745
1746    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1747    // Negative values will have no effect; the calculated value will be used.
1748    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1749                             "\"google:suggestrelevance\":[9998]}]",
1750      { { "a",   true,  true },
1751        { "a1",  true,  true },
1752        { "k a", false, false },
1753        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1754      std::string() },
1755    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1756                             "\"google:suggestrelevance\":[9999]}]",
1757      { { "a1",  true,  true },
1758        { "a",   true,  true },
1759        { "k a", false, false },
1760        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1761      "1" },
1762    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1763                             "\"google:suggestrelevance\":[9999]}]",
1764      { { "a1",  true,  true },
1765        { "k a", false, false },
1766        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1767      "1" },
1768    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1769                             "\"google:suggestrelevance\":[9999]}]",
1770      { { "a1",  true,  true },
1771        { "a",   true,  true },
1772        { "k a", false, false },
1773        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1774      "1" },
1775    { "[\"a\",[\"http://a.com\"],[],[],"
1776       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1777        "\"google:verbatimrelevance\":9999,"
1778        "\"google:suggestrelevance\":[9998]}]",
1779      { { "a",     true,  true },
1780        { "a.com", false, false },
1781        { "k a",   false, false },
1782        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1783      std::string() },
1784
1785    // Ensure that both types of relevance scores reorder matches together.
1786    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1787                                     "\"google:verbatimrelevance\":9998}]",
1788      { { "a1",  true,  true },
1789        { "a",   true,  true },
1790        { "a2",  true,  true },
1791        { "k a", false, false },
1792        kEmptyMatch, kEmptyMatch },
1793      "1" },
1794
1795    // Ensure that only inlinable matches may be ranked as the highest result.
1796    // Ignore all suggested relevance scores if this constraint is violated.
1797    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1798      { { "a",   true,  true },
1799        { "b",   true,  false },
1800        { "k a", false, false },
1801        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1802      std::string() },
1803    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1804                            "\"google:verbatimrelevance\":0}]",
1805      { { "a",   true,  true },
1806        { "b",   true,  false },
1807        { "k a", false, false },
1808        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1809      std::string() },
1810    { "[\"a\",[\"http://b.com\"],[],[],"
1811       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1812        "\"google:suggestrelevance\":[9999]}]",
1813      { { "a",     true,  true },
1814        { "b.com", false, false },
1815        { "k a",   false, false },
1816        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1817      std::string() },
1818    { "[\"a\",[\"http://b.com\"],[],[],"
1819       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1820        "\"google:suggestrelevance\":[9999],"
1821        "\"google:verbatimrelevance\":0}]",
1822      { { "a",     true,  true },
1823        { "b.com", false, false },
1824        { "k a",   false, false },
1825        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1826      std::string() },
1827
1828    // Ensure that the top result is ranked as highly as calculated verbatim.
1829    // Ignore the suggested verbatim relevance if this constraint is violated.
1830    // Note that keyword suggestions by default (not in suggested relevance
1831    // mode) score more highly than the default verbatim.
1832    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1833      { { "a",   true,  true },
1834        { "a1",  true,  true },
1835        { "k a", false, false },
1836        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1837      std::string() },
1838    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1839      { { "a",   true,  true },
1840        { "a1",  true,  true },
1841        { "k a", false, false },
1842        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1843      std::string() },
1844    // Continuing the same category of tests, but make sure we keep the
1845    // suggested relevance scores even as we discard the verbatim relevance
1846    // scores.
1847    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1848                             "\"google:verbatimrelevance\":0}]",
1849      { { "a",   true,  true },
1850        { "k a", false, false },
1851        { "a1",   true, true },
1852        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1853      std::string() },
1854    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1855                                     "\"google:verbatimrelevance\":0}]",
1856      { { "a",   true,  true },
1857        { "k a", false, false },
1858        { "a2",  true,  true },
1859        { "a1",  true,  true },
1860        kEmptyMatch, kEmptyMatch },
1861      std::string() },
1862    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1863      "\"google:verbatimrelevance\":2}]",
1864      { { "a",   true,  true },
1865        { "k a", false, false },
1866        { "a2",  true,  true },
1867        { "a1",  true,  true },
1868        kEmptyMatch, kEmptyMatch },
1869      std::string() },
1870
1871    // Ensure that all suggestions are considered, regardless of order.
1872    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1873       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1874      { { "a",   true,  true },
1875        { "k a", false, false },
1876        { "h",   true,  false },
1877        { "g",   true,  false },
1878        { "f",   true,  false },
1879        { "e",   true,  false } },
1880      std::string() },
1881    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1882              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1883              "\"http://h.com\"],[],[],"
1884       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1885                                "\"NAVIGATION\", \"NAVIGATION\","
1886                                "\"NAVIGATION\", \"NAVIGATION\","
1887                                "\"NAVIGATION\"],"
1888        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1889      { { "a",     true,  true },
1890        { "k a",   false, false },
1891        { "h.com", false, false },
1892        { "g.com", false, false },
1893        { "f.com", false, false },
1894        { "e.com", false, false } },
1895      std::string() },
1896
1897    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1898    // Note that keyword suggestions by default (not in suggested relevance
1899    // mode) score more highly than the default verbatim.
1900    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1901      { { "a",   true,  true },
1902        { "a1",  true,  true },
1903        { "a2",  true,  true },
1904        { "k a", false, false },
1905        kEmptyMatch, kEmptyMatch },
1906      std::string() },
1907    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1908      { { "a",   true,  true },
1909        { "a1",  true,  true },
1910        { "k a", false, false },
1911        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1912      std::string() },
1913    // In this case, ignoring the suggested relevance scores means we keep
1914    // only one navsuggest result.
1915    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1916       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1917        "\"google:suggestrelevance\":[1]}]",
1918      { { "a",      true,  true },
1919        { "a1.com", false, false },
1920        { "k a",    false, false },
1921        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1922      std::string() },
1923    { "[\"a\",[\"http://a1.com\"],[],[],"
1924       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1925       "\"google:suggestrelevance\":[9999, 1]}]",
1926      { { "a",      true,  true },
1927        { "a1.com", false, false },
1928        { "k a",    false, false },
1929        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1930      std::string() },
1931
1932    // Ensure that all 'verbatim' results are merged with their maximum score.
1933    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1934       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1935      { { "a2",  true,  true },
1936        { "a",   true,  true },
1937        { "a1",  true,  true },
1938        { "k a", false, false },
1939        kEmptyMatch, kEmptyMatch },
1940      "2" },
1941    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1942       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1943        "\"google:verbatimrelevance\":0}]",
1944      { { "a2",  true,  true },
1945        { "a",   true,  true },
1946        { "a1",  true,  true },
1947        { "k a", false, false },
1948        kEmptyMatch, kEmptyMatch },
1949      "2" },
1950
1951    // Ensure that verbatim is always generated without other suggestions.
1952    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1953    // (except when suggested relevances are ignored).
1954    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1955      { { "a",   true,  true },
1956        { "k a", false, false },
1957        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1958      std::string() },
1959    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1960      { { "a",   true,  true },
1961        { "k a", false, false },
1962        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1963      std::string() },
1964
1965    // Check that navsuggestions will be demoted below queries.
1966    // (Navsuggestions are not allowed to appear first.)  In the process,
1967    // make sure the navsuggestions still remain in the same order.
1968    // First, check the situation where navsuggest scores more than verbatim
1969    // and there are no query suggestions.
1970    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1971       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1972        "\"google:verbatimrelevance\":9990,"
1973        "\"google:suggestrelevance\":[9998, 9999]}]",
1974      { { "a",      true,  true },
1975        { "a2.com", false, false },
1976        { "a1.com", false, false },
1977        { "k a",    false, false },
1978        kEmptyMatch, kEmptyMatch },
1979      std::string() },
1980    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1981       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1982        "\"google:verbatimrelevance\":9990,"
1983        "\"google:suggestrelevance\":[9999, 9998]}]",
1984      { { "a",      true,  true },
1985        { "a1.com", false, false },
1986        { "a2.com", false, false },
1987        { "k a",    false, false },
1988        kEmptyMatch, kEmptyMatch },
1989      std::string() },
1990    { "[\"a\",[\"https://a/\"],[],[],"
1991       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1992        "\"google:suggestrelevance\":[9999]}]",
1993      { { "a",         true,  true },
1994        { "https://a", false, false },
1995        { "k a",       false, false },
1996        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1997      std::string() },
1998    // Check when navsuggest scores more than verbatim and there is query
1999    // suggestion but it scores lower.
2000    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2001       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2002        "\"google:verbatimrelevance\":9990,"
2003        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
2004      { { "a",      true,  true },
2005        { "a2.com", false, false },
2006        { "a1.com", false, false },
2007        { "a3",     true,  true },
2008        { "k a",    false, false },
2009        kEmptyMatch },
2010      std::string() },
2011    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2012       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2013        "\"google:verbatimrelevance\":9990,"
2014        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
2015      { { "a",      true,  true },
2016        { "a1.com", false, false },
2017        { "a2.com", false, false },
2018        { "a3",     true,  true },
2019        { "k a",    false, false },
2020        kEmptyMatch },
2021      std::string() },
2022    // Check when navsuggest scores more than a query suggestion.  There is
2023    // a verbatim but it scores lower.
2024    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2025       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2026        "\"google:verbatimrelevance\":9990,"
2027        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2028      { { "a3",     true,  true },
2029        { "a2.com", false, false },
2030        { "a1.com", false, false },
2031        { "a",      true,  true },
2032        { "k a",    false, false },
2033        kEmptyMatch },
2034      "3" },
2035    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2036       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2037        "\"google:verbatimrelevance\":9990,"
2038        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2039      { { "a3",     true,  true },
2040        { "a1.com", false, false },
2041        { "a2.com", false, false },
2042        { "a",      true,  true },
2043        { "k a",    false, false },
2044        kEmptyMatch },
2045      "3" },
2046    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2047       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2048        "\"google:verbatimrelevance\":0,"
2049        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2050      { { "a3",     true,  true },
2051        { "a2.com", false, false },
2052        { "a1.com", false, false },
2053        { "k a",    false, false },
2054        kEmptyMatch, kEmptyMatch },
2055      "3" },
2056    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2057       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2058        "\"google:verbatimrelevance\":0,"
2059        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2060      { { "a3",     true,  true },
2061        { "a1.com", false, false },
2062        { "a2.com", false, false },
2063        { "k a",    false, false },
2064        kEmptyMatch, kEmptyMatch },
2065      "3" },
2066    // Check when there is neither verbatim nor a query suggestion that,
2067    // because we can't demote navsuggestions below a query suggestion,
2068    // we abandon suggested relevance scores entirely.  One consequence is
2069    // that this means we restore the keyword verbatim match.  Note
2070    // that in this case of abandoning suggested relevance scores, we still
2071    // keep the navsuggestions in the same order, but we revert to only allowing
2072    // one navigation to appear because the scores are completely local.
2073    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2074       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2075        "\"google:verbatimrelevance\":0,"
2076        "\"google:suggestrelevance\":[9998, 9999]}]",
2077      { { "a",      true,  true },
2078        { "a2.com", false, false },
2079        { "k a",    false, false },
2080        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2081      std::string() },
2082    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2083       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2084        "\"google:verbatimrelevance\":0,"
2085        "\"google:suggestrelevance\":[9999, 9998]}]",
2086      { { "a",      true,  true },
2087        { "a1.com", false, false },
2088        { "k a",    false, false },
2089        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2090      std::string() },
2091    // More checks that everything works when it's not necessary to demote.
2092    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2093       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2094        "\"google:verbatimrelevance\":9990,"
2095        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
2096      { { "a3",     true,  true },
2097        { "a2.com", false, false },
2098        { "a1.com", false, false },
2099        { "a",      true,  true },
2100        { "k a",    false, false },
2101        kEmptyMatch },
2102      "3" },
2103    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2104       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2105        "\"google:verbatimrelevance\":9990,"
2106        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2107      { { "a3",     true,  true },
2108        { "a1.com", false, false },
2109        { "a2.com", false, false },
2110        { "a",      true,  true },
2111        { "k a",    false, false },
2112        kEmptyMatch },
2113      "3" },
2114  };
2115
2116  std::map<std::string, std::string> params;
2117  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2118      ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
2119  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2120      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2121  base::FieldTrialList::CreateFieldTrial(
2122      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2123
2124  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2125    QueryForInput(ASCIIToUTF16("k a"), false, true);
2126
2127    // Set up a default fetcher with no results.
2128    net::TestURLFetcher* default_fetcher =
2129        test_factory_.GetFetcherByID(
2130            SearchProvider::kDefaultProviderURLFetcherID);
2131    ASSERT_TRUE(default_fetcher);
2132    default_fetcher->set_response_code(200);
2133    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2134    default_fetcher = NULL;
2135
2136    // Set up a keyword fetcher with provided results.
2137    net::TestURLFetcher* keyword_fetcher =
2138        test_factory_.GetFetcherByID(
2139            SearchProvider::kKeywordProviderURLFetcherID);
2140    ASSERT_TRUE(keyword_fetcher);
2141    keyword_fetcher->set_response_code(200);
2142    keyword_fetcher->SetResponseString(cases[i].json);
2143    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2144    keyword_fetcher = NULL;
2145    RunTillProviderDone();
2146
2147    const std::string description = "for input with json=" + cases[i].json;
2148    const ACMatches& matches = provider_->matches();
2149    // The top match must inline and score as highly as calculated verbatim.
2150    ASSERT_FALSE(matches.empty());
2151    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2152              matches[0].inline_autocompletion) << description;
2153    EXPECT_GE(matches[0].relevance, 1300) << description;
2154
2155    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2156    size_t j = 0;
2157    // Ensure that the returned matches equal the expectations.
2158    for (; j < matches.size(); ++j) {
2159      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2160                matches[j].contents) << description;
2161      EXPECT_EQ(cases[i].matches[j].from_keyword,
2162                matches[j].keyword == ASCIIToUTF16("k")) << description;
2163      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2164                matches[j].allowed_to_be_default_match) << description;
2165    }
2166    // Ensure that no expected matches are missing.
2167    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2168      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
2169          "Case # " << i << " " << description;
2170  }
2171}
2172
2173// This test is like KeywordFetcherSuggestRelevance above except it
2174// enables the field trial that causes the omnibox to be willing to
2175// reorder matches to guarantee the top result is a legal default
2176// match.  This field trial causes SearchProvider to allow some
2177// constraints to be violated that it wouldn't normally because the
2178// omnibox will fix the problems later.
2179TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevanceWithReorder) {
2180  struct KeywordFetcherMatch {
2181    std::string contents;
2182    bool from_keyword;
2183    bool allowed_to_be_default_match;
2184  };
2185  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
2186  struct {
2187    const std::string json;
2188    const KeywordFetcherMatch matches[6];
2189    const std::string inline_autocompletion;
2190  } cases[] = {
2191    // Ensure that suggest relevance scores reorder matches and that
2192    // the keyword verbatim (lacking a suggested verbatim score) beats
2193    // the default provider verbatim.
2194    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2195      { { "a",   true,  true },
2196        { "k a", false, false },
2197        { "c",   true,  false },
2198        { "b",   true,  false },
2199        kEmptyMatch, kEmptyMatch },
2200      std::string() },
2201    // Again, check that relevance scores reorder matches, just this
2202    // time with navigation matches.  This also checks that with
2203    // suggested relevance scores we allow multiple navsuggest results.
2204    // Note that navsuggest results that come from a keyword provider
2205    // are marked as not a keyword result.  (They don't go to a
2206    // keyword search engine.)
2207    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
2208       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2209       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
2210      { { "a",     true,  true },
2211        { "d",     true,  false },
2212        { "c.com", false, false },
2213        { "b.com", false, false },
2214        { "k a",   false, false },
2215        kEmptyMatch },
2216      std::string() },
2217
2218    // Without suggested relevance scores, we should only allow one
2219    // navsuggest result to be be displayed.
2220    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
2221       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
2222      { { "a",     true,  true },
2223        { "b.com", false, false },
2224        { "k a",   false, false },
2225        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2226      std::string() },
2227
2228    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
2229    // Negative values will have no effect; the calculated value will be used.
2230    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
2231                             "\"google:suggestrelevance\":[9998]}]",
2232      { { "a",   true,  true },
2233        { "a1",  true,  true },
2234        { "k a", false, false },
2235        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2236      std::string() },
2237    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
2238                             "\"google:suggestrelevance\":[9999]}]",
2239      { { "a1",  true,  true },
2240        { "a",   true,  true },
2241        { "k a", false, false },
2242        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2243      "1" },
2244    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
2245                             "\"google:suggestrelevance\":[9999]}]",
2246      { { "a1",  true,  true },
2247        { "k a", false, false },
2248        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2249      "1" },
2250    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
2251                             "\"google:suggestrelevance\":[9999]}]",
2252      { { "a1",  true,  true },
2253        { "a",   true,  true },
2254        { "k a", false, false },
2255        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2256      "1" },
2257    { "[\"a\",[\"http://a.com\"],[],[],"
2258       "{\"google:suggesttype\":[\"NAVIGATION\"],"
2259        "\"google:verbatimrelevance\":9999,"
2260        "\"google:suggestrelevance\":[9998]}]",
2261      { { "a",     true,  true },
2262        { "a.com", false, false },
2263        { "k a",   false, false },
2264        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2265      std::string() },
2266
2267    // Ensure that both types of relevance scores reorder matches together.
2268    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
2269                                     "\"google:verbatimrelevance\":9998}]",
2270      { { "a1",  true,  true },
2271        { "a",   true,  true },
2272        { "a2",  true,  true },
2273        { "k a", false, false },
2274        kEmptyMatch, kEmptyMatch },
2275      "1" },
2276
2277    // Check that non-inlinable matches may be ranked as the highest result
2278    // if there is at least one inlineable match.
2279    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
2280      { { "b",   true,  false },
2281        { "a",   true,  true },
2282        { "k a", false, false },
2283        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2284      std::string() },
2285    { "[\"a\",[\"http://b.com\"],[],[],"
2286       "{\"google:suggesttype\":[\"NAVIGATION\"],"
2287        "\"google:suggestrelevance\":[9999]}]",
2288      { { "b.com", false, false },
2289        { "a",     true,  true },
2290        { "k a",   false, false },
2291        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2292      std::string() },
2293    // On the other hand, if there is no inlineable match, restore
2294    // the keyword verbatim score.
2295    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
2296                            "\"google:verbatimrelevance\":0}]",
2297      { { "b",   true,  false },
2298        { "a",   true,  true },
2299        { "k a", false, false },
2300        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2301      std::string() },
2302    { "[\"a\",[\"http://b.com\"],[],[],"
2303       "{\"google:suggesttype\":[\"NAVIGATION\"],"
2304        "\"google:suggestrelevance\":[9999],"
2305        "\"google:verbatimrelevance\":0}]",
2306      { { "b.com", false, false },
2307        { "a",     true,  true },
2308        { "k a",   false, false },
2309        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2310      std::string() },
2311
2312    // The top result does not have to score as highly as calculated
2313    // verbatim.  i.e., there are no minimum score restrictions in
2314    // this provider.
2315    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
2316      { { "a1",  true,  true },
2317        { "k a", false, false },
2318        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2319      "1" },
2320    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
2321      { { "a1",  true,  true },
2322        { "k a", false, false },
2323        { "a",   true,  true },
2324        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2325      "1" },
2326    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
2327                             "\"google:verbatimrelevance\":0}]",
2328      { { "k a", false, false },
2329        { "a1",   true, true },
2330        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2331      "1" },
2332    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
2333                                     "\"google:verbatimrelevance\":0}]",
2334      {
2335        { "k a", false, false },
2336        { "a2",  true,  true },
2337        { "a1",  true,  true },
2338        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2339      "2" },
2340    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
2341      "\"google:verbatimrelevance\":2}]",
2342      { { "k a", false, false },
2343        { "a2",  true,  true },
2344        { "a",   true,  true },
2345        { "a1",  true,  true },
2346        kEmptyMatch, kEmptyMatch },
2347      "2" },
2348
2349    // Ensure that all suggestions are considered, regardless of order.
2350    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
2351       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2352      { { "a",   true,  true },
2353        { "k a", false, false },
2354        { "h",   true,  false },
2355        { "g",   true,  false },
2356        { "f",   true,  false },
2357        { "e",   true,  false } },
2358      std::string() },
2359    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
2360              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
2361              "\"http://h.com\"],[],[],"
2362       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
2363                                "\"NAVIGATION\", \"NAVIGATION\","
2364                                "\"NAVIGATION\", \"NAVIGATION\","
2365                                "\"NAVIGATION\"],"
2366        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2367      { { "a",     true,  true },
2368        { "k a",   false, false },
2369        { "h.com", false, false },
2370        { "g.com", false, false },
2371        { "f.com", false, false },
2372        { "e.com", false, false } },
2373      std::string() },
2374
2375    // Ensure that incorrectly sized suggestion relevance lists are ignored.
2376    // Note that keyword suggestions by default (not in suggested relevance
2377    // mode) score more highly than the default verbatim.
2378    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
2379      { { "a",   true,  true },
2380        { "a1",  true,  true },
2381        { "a2",  true,  true },
2382        { "k a", false, false },
2383        kEmptyMatch, kEmptyMatch },
2384      std::string() },
2385    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
2386      { { "a",   true,  true },
2387        { "a1",  true,  true },
2388        { "k a", false, false },
2389        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2390      std::string() },
2391    // In this case, ignoring the suggested relevance scores means we keep
2392    // only one navsuggest result.
2393    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2394       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2395        "\"google:suggestrelevance\":[1]}]",
2396      { { "a",      true,  true },
2397        { "a1.com", false, false },
2398        { "k a",    false, false },
2399        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2400      std::string() },
2401    { "[\"a\",[\"http://a1.com\"],[],[],"
2402       "{\"google:suggesttype\":[\"NAVIGATION\"],"
2403       "\"google:suggestrelevance\":[9999, 1]}]",
2404      { { "a",      true,  true },
2405        { "a1.com", false, false },
2406        { "k a",    false, false },
2407        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2408      std::string() },
2409
2410    // Ensure that all 'verbatim' results are merged with their maximum score.
2411    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2412       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2413      { { "a2",  true,  true },
2414        { "a",   true,  true },
2415        { "a1",  true,  true },
2416        { "k a", false, false },
2417        kEmptyMatch, kEmptyMatch },
2418      "2" },
2419    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2420       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
2421        "\"google:verbatimrelevance\":0}]",
2422      { { "a2",  true,  true },
2423        { "a",   true,  true },
2424        { "a1",  true,  true },
2425        { "k a", false, false },
2426        kEmptyMatch, kEmptyMatch },
2427      "2" },
2428
2429    // Ensure that verbatim is always generated without other suggestions.
2430    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
2431    // (except when suggested relevances are ignored).
2432    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
2433      { { "k a", false, false },
2434        { "a",   true,  true },
2435        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2436      std::string() },
2437    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
2438      { { "a",   true,  true },
2439        { "k a", false, false },
2440        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2441      std::string() },
2442
2443    // In reorder mode, navsuggestions will not need to be demoted (because
2444    // they are marked as not allowed to be default match and will be
2445    // reordered as necessary).
2446    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2447       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2448        "\"google:verbatimrelevance\":9990,"
2449        "\"google:suggestrelevance\":[9998, 9999]}]",
2450      { { "a2.com", false, false },
2451        { "a1.com", false, false },
2452        { "a",      true,  true },
2453        { "k a",    false, false },
2454        kEmptyMatch, kEmptyMatch },
2455      std::string() },
2456    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2457       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2458        "\"google:verbatimrelevance\":9990,"
2459        "\"google:suggestrelevance\":[9999, 9998]}]",
2460      { { "a1.com", false, false },
2461        { "a2.com", false, false },
2462        { "a",      true,  true },
2463        { "k a",    false, false },
2464        kEmptyMatch, kEmptyMatch },
2465      std::string() },
2466    { "[\"a\",[\"https://a/\"],[],[],"
2467       "{\"google:suggesttype\":[\"NAVIGATION\"],"
2468        "\"google:suggestrelevance\":[9999]}]",
2469      { { "https://a", false, false },
2470        { "a",         true,  true },
2471        { "k a",       false, false },
2472        kEmptyMatch, kEmptyMatch, kEmptyMatch },
2473      std::string() },
2474    // Check when navsuggest scores more than verbatim and there is query
2475    // suggestion but it scores lower.
2476    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2477       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2478        "\"google:verbatimrelevance\":9990,"
2479        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
2480      { { "a2.com", false, false },
2481        { "a1.com", false, false },
2482        { "a",      true,  true },
2483        { "a3",     true,  true },
2484        { "k a",    false, false },
2485        kEmptyMatch },
2486      std::string() },
2487    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2488       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2489        "\"google:verbatimrelevance\":9990,"
2490        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
2491      { { "a1.com", false, false },
2492        { "a2.com", false, false },
2493        { "a",      true,  true },
2494        { "a3",     true,  true },
2495        { "k a",    false, false },
2496        kEmptyMatch },
2497      std::string() },
2498    // Check when navsuggest scores more than a query suggestion.  There is
2499    // a verbatim but it scores lower.
2500    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2501       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2502        "\"google:verbatimrelevance\":9990,"
2503        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2504      { { "a2.com", false, false },
2505        { "a1.com", false, false },
2506        { "a3",     true,  true },
2507        { "a",      true,  true },
2508        { "k a",    false, false },
2509        kEmptyMatch },
2510      "3" },
2511    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2512       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2513        "\"google:verbatimrelevance\":9990,"
2514        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2515      { { "a1.com", false, false },
2516        { "a2.com", false, false },
2517        { "a3",     true,  true },
2518        { "a",      true,  true },
2519        { "k a",    false, false },
2520        kEmptyMatch },
2521      "3" },
2522    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2523       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2524        "\"google:verbatimrelevance\":0,"
2525        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2526      { { "a2.com", false, false },
2527        { "a1.com", false, false },
2528        { "a3",     true,  true },
2529        { "k a",    false, false },
2530        kEmptyMatch, kEmptyMatch },
2531      "3" },
2532    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2533       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2534        "\"google:verbatimrelevance\":0,"
2535        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2536      { { "a1.com", false, false },
2537        { "a2.com", false, false },
2538        { "a3",     true,  true },
2539        { "k a",    false, false },
2540        kEmptyMatch, kEmptyMatch },
2541      "3" },
2542    // Check when there is neither verbatim nor a query suggestion that,
2543    // because we can't demote navsuggestions below a query suggestion,
2544    // we restore the keyword verbatim score.
2545    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2546       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2547        "\"google:verbatimrelevance\":0,"
2548        "\"google:suggestrelevance\":[9998, 9999]}]",
2549      { { "a2.com", false, false },
2550        { "a1.com", false, false },
2551        { "a",      true,  true },
2552        { "k a",    false, false },
2553        kEmptyMatch, kEmptyMatch },
2554      std::string() },
2555    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2556       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2557        "\"google:verbatimrelevance\":0,"
2558        "\"google:suggestrelevance\":[9999, 9998]}]",
2559      { { "a1.com", false, false },
2560        { "a2.com", false, false },
2561        { "a",      true,  true },
2562        { "k a",    false, false },
2563        kEmptyMatch, kEmptyMatch },
2564      std::string() },
2565    // More checks that everything works when it's not necessary to demote.
2566    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2567       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2568        "\"google:verbatimrelevance\":9990,"
2569        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
2570      { { "a3",     true,  true },
2571        { "a2.com", false, false },
2572        { "a1.com", false, false },
2573        { "a",      true,  true },
2574        { "k a",    false, false },
2575        kEmptyMatch },
2576      "3" },
2577    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2578       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2579        "\"google:verbatimrelevance\":9990,"
2580        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2581      { { "a3",     true,  true },
2582        { "a1.com", false, false },
2583        { "a2.com", false, false },
2584        { "a",      true,  true },
2585        { "k a",    false, false },
2586        kEmptyMatch },
2587      "3" },
2588  };
2589
2590  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2591    QueryForInput(ASCIIToUTF16("k a"), false, true);
2592
2593    // Set up a default fetcher with no results.
2594    net::TestURLFetcher* default_fetcher =
2595        test_factory_.GetFetcherByID(
2596            SearchProvider::kDefaultProviderURLFetcherID);
2597    ASSERT_TRUE(default_fetcher);
2598    default_fetcher->set_response_code(200);
2599    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2600    default_fetcher = NULL;
2601
2602    // Set up a keyword fetcher with provided results.
2603    net::TestURLFetcher* keyword_fetcher =
2604        test_factory_.GetFetcherByID(
2605            SearchProvider::kKeywordProviderURLFetcherID);
2606    ASSERT_TRUE(keyword_fetcher);
2607    keyword_fetcher->set_response_code(200);
2608    keyword_fetcher->SetResponseString(cases[i].json);
2609    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2610    keyword_fetcher = NULL;
2611    RunTillProviderDone();
2612
2613    const std::string description = "for input with json=" + cases[i].json;
2614    const ACMatches& matches = provider_->matches();
2615    ASSERT_FALSE(matches.empty());
2616    // Find the first match that's allowed to be the default match and check
2617    // its inline_autocompletion.
2618    ACMatches::const_iterator it = FindDefaultMatch(matches);
2619    ASSERT_NE(matches.end(), it);
2620    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2621              it->inline_autocompletion) << description;
2622
2623    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2624    size_t j = 0;
2625    // Ensure that the returned matches equal the expectations.
2626    for (; j < matches.size(); ++j) {
2627      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2628                matches[j].contents) << description;
2629      EXPECT_EQ(cases[i].matches[j].from_keyword,
2630                matches[j].keyword == ASCIIToUTF16("k")) << description;
2631      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2632                matches[j].allowed_to_be_default_match) << description;
2633    }
2634    // Ensure that no expected matches are missing.
2635    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2636      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
2637          "Case # " << i << " " << description;
2638  }
2639}
2640
2641TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
2642  // We hardcode the string "term1" below, so ensure that the search term that
2643  // got added to history already is that string.
2644  ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
2645  base::string16 term = term1_.substr(0, term1_.length() - 1);
2646
2647  AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2648  profile_.BlockUntilHistoryProcessesPendingRequests();
2649
2650  struct {
2651    const base::string16 input;
2652    const std::string json;
2653    const std::string matches[6];
2654  } cases[] = {
2655    // The history results outscore the default verbatim score.  term2 has more
2656    // visits so it outscores term1.  The suggestions are still returned since
2657    // they're server-scored.
2658    { term,
2659      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2660       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2661        "\"google:suggestrelevance\":[1, 2, 3]}]",
2662      { "term2", "term1", "term", "a3", "a2", "a1" } },
2663    // Because we already have three suggestions by the time we see the history
2664    // results, they don't get returned.
2665    { term,
2666      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2667       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2668        "\"google:verbatimrelevance\":1450,"
2669        "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
2670      { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
2671    // If we only have two suggestions, we have room for a history result.
2672    { term,
2673      "[\"term\",[\"a1\", \"a2\"],[],[],"
2674       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2675        "\"google:verbatimrelevance\":1450,"
2676        "\"google:suggestrelevance\":[1430, 1410]}]",
2677      { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2678    // If we have more than three suggestions, they should all be returned as
2679    // long as we have enough total space for them.
2680    { term,
2681      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2682       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2683        "\"google:verbatimrelevance\":1450,"
2684        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2685      { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2686    { term,
2687      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2688       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2689                                "\"QUERY\", \"QUERY\"],"
2690        "\"google:verbatimrelevance\":1450,"
2691        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2692      { "term", "a1", "a2", "a3", "a4", "a5" } },
2693    { term,
2694      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2695       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2696        "\"google:verbatimrelevance\":1450,"
2697        "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2698      { "term", "a1", "a2", "term2", "a3", "a4" } }
2699  };
2700
2701  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2702    QueryForInput(cases[i].input, false, false);
2703    net::TestURLFetcher* fetcher =
2704        test_factory_.GetFetcherByID(
2705            SearchProvider::kDefaultProviderURLFetcherID);
2706    ASSERT_TRUE(fetcher);
2707    fetcher->set_response_code(200);
2708    fetcher->SetResponseString(cases[i].json);
2709    fetcher->delegate()->OnURLFetchComplete(fetcher);
2710    RunTillProviderDone();
2711
2712    const std::string description = "for input with json=" + cases[i].json;
2713    const ACMatches& matches = provider_->matches();
2714
2715    // Ensure no extra matches are present.
2716    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2717
2718    size_t j = 0;
2719    // Ensure that the returned matches equal the expectations.
2720    for (; j < matches.size(); ++j)
2721      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2722                matches[j].contents) << description;
2723    // Ensure that no expected matches are missing.
2724    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2725      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2726          "Case # " << i << " " << description;
2727  }
2728}
2729
2730// Verifies suggest relevance behavior for URL input.
2731TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2732  struct DefaultFetcherUrlInputMatch {
2733    const std::string match_contents;
2734    AutocompleteMatch::Type match_type;
2735    bool allowed_to_be_default_match;
2736  };
2737  const DefaultFetcherUrlInputMatch kEmptyMatch =
2738      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2739  struct {
2740    const std::string input;
2741    const std::string json;
2742    const DefaultFetcherUrlInputMatch output[4];
2743  } cases[] = {
2744    // Ensure topmost NAVIGATION matches are allowed for URL input.
2745    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2746                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2747                 "\"google:suggestrelevance\":[9999]}]",
2748      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
2749        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2750        kEmptyMatch, kEmptyMatch } },
2751    { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2752                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2753                 "\"google:suggestrelevance\":[9999]}]",
2754      { { "https://a.com", AutocompleteMatchType::NAVSUGGEST,            true },
2755        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2756        kEmptyMatch, kEmptyMatch } },
2757
2758    // Ensure topmost SUGGEST matches are not allowed for URL input.
2759    // SearchProvider disregards search and verbatim suggested relevances.
2760    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2761                "{\"google:suggestrelevance\":[9999]}]",
2762      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2763        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2764        kEmptyMatch, kEmptyMatch } },
2765    { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
2766                "{\"google:suggestrelevance\":[9999]}]",
2767      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2768        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2769        kEmptyMatch, kEmptyMatch } },
2770
2771    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2772    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2773                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2774                 "\"google:suggestrelevance\":[9999, 9998]}]",
2775      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
2776        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2777        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2778        kEmptyMatch } },
2779    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2780                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2781                 "\"google:suggestrelevance\":[9998, 9997],"
2782                 "\"google:verbatimrelevance\":9999}]",
2783      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
2784        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2785        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2786        kEmptyMatch } },
2787
2788    // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
2789    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2790                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2791      "\"google:suggestrelevance\":[9999, 9998]}]",
2792      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2793        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
2794        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2795        kEmptyMatch } },
2796    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2797                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2798                 "\"google:suggestrelevance\":[9998, 9997],"
2799                 "\"google:verbatimrelevance\":9999}]",
2800      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2801        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
2802        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2803        kEmptyMatch } },
2804  };
2805
2806  std::map<std::string, std::string> params;
2807  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2808      ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
2809  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2810      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2811  base::FieldTrialList::CreateFieldTrial(
2812      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2813
2814  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2815    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2816    net::TestURLFetcher* fetcher =
2817        test_factory_.GetFetcherByID(
2818            SearchProvider::kDefaultProviderURLFetcherID);
2819    ASSERT_TRUE(fetcher);
2820    fetcher->set_response_code(200);
2821    fetcher->SetResponseString(cases[i].json);
2822    fetcher->delegate()->OnURLFetchComplete(fetcher);
2823    RunTillProviderDone();
2824
2825    size_t j = 0;
2826    const ACMatches& matches = provider_->matches();
2827    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2828    // Ensure that the returned matches equal the expectations.
2829    for (; j < matches.size(); ++j) {
2830      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2831                matches[j].contents);
2832      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2833      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2834                matches[j].allowed_to_be_default_match);
2835    }
2836    // Ensure that no expected matches are missing.
2837    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2838      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2839      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2840                cases[i].output[j].match_type);
2841      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2842    }
2843  }
2844}
2845
2846// This test is like DefaultProviderSuggestRelevanceScoringUrlInput
2847// above except it enables the field trial that causes the omnibox to
2848// be willing to reorder matches to guarantee the top result is a
2849// legal default match.  This field trial causes SearchProvider to
2850// allow some constraints to be violated that it wouldn't normally
2851// because the omnibox will fix the problems later.
2852TEST_F(SearchProviderTest,
2853    DefaultProviderSuggestRelevanceScoringUrlInputWithReorder) {
2854  struct DefaultFetcherUrlInputMatch {
2855    const std::string match_contents;
2856    AutocompleteMatch::Type match_type;
2857    bool allowed_to_be_default_match;
2858  };
2859  const DefaultFetcherUrlInputMatch kEmptyMatch =
2860      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2861  struct {
2862    const std::string input;
2863    const std::string json;
2864    const DefaultFetcherUrlInputMatch output[4];
2865  } cases[] = {
2866    // Ensure NAVIGATION matches are allowed to be listed first for URL
2867    // input regardless of whether the match is inlineable.  Note that
2868    // non-inlineable matches should not be allowed to be the default match.
2869    { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2870                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2871                 "\"google:suggestrelevance\":[9999]}]",
2872      { { "b.com",   AutocompleteMatchType::NAVSUGGEST,            false },
2873        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2874        kEmptyMatch, kEmptyMatch } },
2875    { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2876                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2877                 "\"google:suggestrelevance\":[9999]}]",
2878      { { "https://b.com", AutocompleteMatchType::NAVSUGGEST,           false },
2879        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2880        kEmptyMatch, kEmptyMatch } },
2881    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2882                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2883                 "\"google:suggestrelevance\":[9999]}]",
2884      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
2885        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2886        kEmptyMatch, kEmptyMatch } },
2887    { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2888                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2889                 "\"google:suggestrelevance\":[9999]}]",
2890      { { "https://a.com", AutocompleteMatchType::NAVSUGGEST,            true },
2891        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2892        kEmptyMatch, kEmptyMatch } },
2893
2894    // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2895    // input.  SearchProvider disregards search and verbatim suggested
2896    // relevances.
2897    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2898                "{\"google:suggestrelevance\":[9999]}]",
2899      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2900        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2901        kEmptyMatch, kEmptyMatch } },
2902    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2903                "{\"google:suggestrelevance\":[9999]}]",
2904      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,   true },
2905        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2906        kEmptyMatch, kEmptyMatch } },
2907
2908    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2909    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2910                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2911                 "\"google:suggestrelevance\":[9999, 9998]}]",
2912      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2913        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2914        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2915        kEmptyMatch } },
2916    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2917                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2918                 "\"google:suggestrelevance\":[9998, 9997],"
2919                 "\"google:verbatimrelevance\":9999}]",
2920      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2921        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2922        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2923        kEmptyMatch } },
2924
2925    // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
2926    // input assuming the top inlineable match is not a query (i.e., is a
2927    // NAVSUGGEST).
2928    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2929                "{\"google:suggestrelevance\":[9999]}]",
2930      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2931        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2932        kEmptyMatch, kEmptyMatch } },
2933    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2934                "{\"google:suggestrelevance\":[9999]}]",
2935      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2936        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2937        kEmptyMatch, kEmptyMatch } },
2938  };
2939
2940  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2941    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2942    net::TestURLFetcher* fetcher =
2943        test_factory_.GetFetcherByID(
2944            SearchProvider::kDefaultProviderURLFetcherID);
2945    ASSERT_TRUE(fetcher);
2946    fetcher->set_response_code(200);
2947    fetcher->SetResponseString(cases[i].json);
2948    fetcher->delegate()->OnURLFetchComplete(fetcher);
2949    RunTillProviderDone();
2950
2951    size_t j = 0;
2952    const ACMatches& matches = provider_->matches();
2953    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2954    // Ensure that the returned matches equal the expectations.
2955    for (; j < matches.size(); ++j) {
2956      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2957                matches[j].contents);
2958      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2959      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2960                matches[j].allowed_to_be_default_match);
2961    }
2962    // Ensure that no expected matches are missing.
2963    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2964      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2965      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2966                cases[i].output[j].match_type);
2967      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2968    }
2969  }
2970}
2971
2972// A basic test that verifies the field trial triggered parsing logic.
2973TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2974  QueryForInput(ASCIIToUTF16("foo"), false, false);
2975
2976  // Make sure the default providers suggest service was queried.
2977  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2978      SearchProvider::kDefaultProviderURLFetcherID);
2979  ASSERT_TRUE(fetcher);
2980
2981  // Tell the SearchProvider the suggest query is done.
2982  fetcher->set_response_code(200);
2983  fetcher->SetResponseString(
2984      "[\"foo\",[\"foo bar\"],[\"\"],[],"
2985      "{\"google:suggesttype\":[\"QUERY\"],"
2986      "\"google:fieldtrialtriggered\":true}]");
2987  fetcher->delegate()->OnURLFetchComplete(fetcher);
2988  fetcher = NULL;
2989
2990  // Run till the history results complete.
2991  RunTillProviderDone();
2992
2993  {
2994    // Check for the match and field trial triggered bits.
2995    AutocompleteMatch match;
2996    EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2997    ProvidersInfo providers_info;
2998    provider_->AddProviderInfo(&providers_info);
2999    ASSERT_EQ(1U, providers_info.size());
3000    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
3001    EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
3002  }
3003  {
3004    // Reset the session and check that bits are reset.
3005    provider_->ResetSession();
3006    ProvidersInfo providers_info;
3007    provider_->AddProviderInfo(&providers_info);
3008    ASSERT_EQ(1U, providers_info.size());
3009    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
3010    EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
3011  }
3012}
3013
3014// Verifies inline autocompletion of navigational results.
3015TEST_F(SearchProviderTest, NavigationInline) {
3016  struct {
3017    const std::string input;
3018    const std::string url;
3019    // Test the expected fill_into_edit, which may drop "http://".
3020    // Some cases do not trim "http://" to match from the start of the scheme.
3021    const std::string fill_into_edit;
3022    const std::string inline_autocompletion;
3023    const bool allowed_to_be_default_match_in_regular_mode;
3024    const bool allowed_to_be_default_match_in_prevent_inline_mode;
3025  } cases[] = {
3026    // Do not inline matches that do not contain the input; trim http as needed.
3027    { "x",                 "http://www.abc.com",
3028                                  "www.abc.com",  std::string(), false, false },
3029    { "https:",            "http://www.abc.com",
3030                                  "www.abc.com",  std::string(), false, false },
3031    { "http://www.abc.com/a", "http://www.abc.com",
3032                              "http://www.abc.com",  std::string(), false,
3033                                                                    false },
3034    { "http://www.abc.com",   "https://www.abc.com",
3035                              "https://www.abc.com", std::string(), false,
3036                                                                    false },
3037    { "http://abc.com",       "ftp://abc.com",
3038                              "ftp://abc.com",       std::string(), false,
3039                                                                    false },
3040    { "https://www.abc.com",  "http://www.abc.com",
3041                                     "www.abc.com",  std::string(), false,
3042                                                                    false },
3043    { "ftp://abc.com",        "http://abc.com",
3044                                     "abc.com",      std::string(), false,
3045                                                                    false },
3046
3047    // Do not inline matches with invalid input prefixes; trim http as needed.
3048    { "ttp",              "http://www.abc.com",
3049                                 "www.abc.com", std::string(), false, false },
3050    { "://w",             "http://www.abc.com",
3051                                 "www.abc.com", std::string(), false, false },
3052    { "ww.",              "http://www.abc.com",
3053                                 "www.abc.com", std::string(), false, false },
3054    { ".ab",              "http://www.abc.com",
3055                                 "www.abc.com", std::string(), false, false },
3056    { "bc",               "http://www.abc.com",
3057                                 "www.abc.com", std::string(), false, false },
3058    { ".com",             "http://www.abc.com",
3059                                 "www.abc.com", std::string(), false, false },
3060
3061    // Do not inline matches that omit input domain labels; trim http as needed.
3062    { "www.a",            "http://a.com",
3063                                 "a.com",       std::string(), false, false },
3064    { "http://www.a",     "http://a.com",
3065                          "http://a.com",       std::string(), false, false },
3066    { "www.a",            "ftp://a.com",
3067                          "ftp://a.com",        std::string(), false, false },
3068    { "ftp://www.a",      "ftp://a.com",
3069                          "ftp://a.com",        std::string(), false, false },
3070
3071    // Input matching but with nothing to inline will not yield an offset, but
3072    // will be allowed to be default.
3073    { "abc.com",             "http://www.abc.com",
3074                                    "www.abc.com", std::string(), true, true },
3075    { "abc.com/",            "http://www.abc.com",
3076                                    "www.abc.com", std::string(), true, true },
3077    { "http://www.abc.com",  "http://www.abc.com",
3078                             "http://www.abc.com", std::string(), true, true },
3079    { "http://www.abc.com/", "http://www.abc.com",
3080                             "http://www.abc.com", std::string(), true, true },
3081
3082    // Inline matches when the input is a leading substring of the scheme.
3083    { "h",             "http://www.abc.com",
3084                       "http://www.abc.com", "ttp://www.abc.com", true, false },
3085    { "http",          "http://www.abc.com",
3086                       "http://www.abc.com", "://www.abc.com",    true, false },
3087
3088    // Inline matches when the input is a leading substring of the full URL.
3089    { "http:",             "http://www.abc.com",
3090                           "http://www.abc.com", "//www.abc.com", true, false },
3091    { "http://w",          "http://www.abc.com",
3092                           "http://www.abc.com", "ww.abc.com",    true, false },
3093    { "http://www.",       "http://www.abc.com",
3094                           "http://www.abc.com", "abc.com",       true, false },
3095    { "http://www.ab",     "http://www.abc.com",
3096                           "http://www.abc.com", "c.com",         true, false },
3097    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
3098                              "http://www.abc.com/path/file.htm?q=x#foo",
3099                                                  "ath/file.htm?q=x#foo",
3100                                                                  true, false },
3101    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
3102                              "http://abc.com/path/file.htm?q=x#foo",
3103                                              "ath/file.htm?q=x#foo",
3104                                                                  true, false},
3105
3106    // Inline matches with valid URLPrefixes; only trim "http://".
3107    { "w",               "http://www.abc.com",
3108                                "www.abc.com", "ww.abc.com", true, false },
3109    { "www.a",           "http://www.abc.com",
3110                                "www.abc.com", "bc.com",     true, false },
3111    { "abc",             "http://www.abc.com",
3112                                "www.abc.com", ".com",       true, false },
3113    { "abc.c",           "http://www.abc.com",
3114                                "www.abc.com", "om",         true, false },
3115    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
3116                                "www.abc.com/path/file.htm?q=x#foo",
3117                                             "ath/file.htm?q=x#foo",
3118                                                             true, false },
3119    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
3120                                "abc.com/path/file.htm?q=x#foo",
3121                                         "ath/file.htm?q=x#foo",
3122                                                             true, false },
3123
3124    // Inline matches using the maximal URLPrefix components.
3125    { "h",               "http://help.com",
3126                                "help.com", "elp.com",     true, false },
3127    { "http",            "http://http.com",
3128                                "http.com", ".com",        true, false },
3129    { "h",               "http://www.help.com",
3130                                "www.help.com", "elp.com", true, false },
3131    { "http",            "http://www.http.com",
3132                                "www.http.com", ".com",    true, false },
3133    { "w",               "http://www.www.com",
3134                                "www.www.com",  "ww.com",  true, false },
3135
3136    // Test similar behavior for the ftp and https schemes.
3137    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
3138                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3139                                  "c.com/path/file.htm?q=x#foo",  true, false },
3140    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
3141                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3142                                   "c.com/path/file.htm?q=x#foo", true, false },
3143    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
3144                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3145                                   "c.com/path/file.htm?q=x#foo", true, false },
3146    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
3147                       "ftp://abc.com/path/file.htm?q=x#foo",
3148                               "c.com/path/file.htm?q=x#foo",     true, false },
3149    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
3150                         "https://www.abc.com/path/file.htm?q=x#foo",
3151                                       "c.com/path/file.htm?q=x#foo",
3152                                                                  true, false },
3153    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
3154                     "https://www.abc.com/path/file.htm?q=x#foo",
3155                                   "c.com/path/file.htm?q=x#foo", true, false },
3156    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
3157                     "https://www.abc.com/path/file.htm?q=x#foo",
3158                                   "c.com/path/file.htm?q=x#foo", true, false },
3159    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
3160                     "https://abc.com/path/file.htm?q=x#foo",
3161                               "c.com/path/file.htm?q=x#foo",     true, false },
3162
3163    // Forced query input should inline and retain the "?" prefix.
3164    { "?http://www.ab",  "http://www.abc.com",
3165                        "?http://www.abc.com", "c.com", true, false },
3166    { "?www.ab",         "http://www.abc.com",
3167                               "?www.abc.com", "c.com", true, false },
3168    { "?ab",             "http://www.abc.com",
3169                               "?www.abc.com", "c.com", true, false },
3170    { "?abc.com",        "http://www.abc.com",
3171                               "?www.abc.com", "",      true, true },
3172  };
3173
3174  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3175    // First test regular mode.
3176    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
3177    AutocompleteMatch match(
3178        provider_->NavigationToMatch(SearchProvider::NavigationResult(
3179            *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3180            false)));
3181    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3182              match.inline_autocompletion);
3183    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
3184    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
3185              match.allowed_to_be_default_match);
3186
3187    // Then test prevent-inline-autocomplete mode.
3188    QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
3189    AutocompleteMatch match_prevent_inline(
3190        provider_->NavigationToMatch(SearchProvider::NavigationResult(
3191            *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3192            false)));
3193    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3194              match_prevent_inline.inline_autocompletion);
3195    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
3196              match_prevent_inline.fill_into_edit);
3197    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
3198              match_prevent_inline.allowed_to_be_default_match);
3199  }
3200}
3201
3202// Verifies that "http://" is not trimmed for input that is a leading substring.
3203TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
3204  const base::string16 input(ASCIIToUTF16("ht"));
3205  const base::string16 url(ASCIIToUTF16("http://a.com"));
3206  const SearchProvider::NavigationResult result(
3207      *provider_.get(), GURL(url), base::string16(), false, 0, false);
3208
3209  // Check the offset and strings when inline autocompletion is allowed.
3210  QueryForInput(input, false, false);
3211  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
3212  EXPECT_EQ(url, match_inline.fill_into_edit);
3213  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
3214  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
3215  EXPECT_EQ(url, match_inline.contents);
3216
3217  // Check the same strings when inline autocompletion is prevented.
3218  QueryForInput(input, true, false);
3219  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
3220  EXPECT_EQ(url, match_prevent.fill_into_edit);
3221  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
3222  EXPECT_EQ(url, match_prevent.contents);
3223}
3224
3225// Verifies that input "w" marks a more significant domain label than "www.".
3226TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
3227  QueryForInput(ASCIIToUTF16("w"), false, false);
3228  AutocompleteMatch match(
3229      provider_->NavigationToMatch(SearchProvider::NavigationResult(
3230          *provider_.get(), GURL("http://www.wow.com"), base::string16(), false, 0,
3231          false)));
3232  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
3233  EXPECT_TRUE(match.allowed_to_be_default_match);
3234  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
3235  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
3236
3237  // Ensure that the match for input "w" is marked on "wow" and not "www".
3238  ASSERT_EQ(3U, match.contents_class.size());
3239  EXPECT_EQ(0U, match.contents_class[0].offset);
3240  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3241            match.contents_class[0].style);
3242  EXPECT_EQ(4U, match.contents_class[1].offset);
3243  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
3244            AutocompleteMatch::ACMatchClassification::MATCH,
3245            match.contents_class[1].style);
3246  EXPECT_EQ(5U, match.contents_class[2].offset);
3247  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3248            match.contents_class[2].style);
3249}
3250
3251TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
3252  // TODO(mpearson): Consider expanding this test to explicitly cover
3253  // testing staleness for keyword results.
3254  struct {
3255    const std::string omnibox_input;
3256    const int verbatim_relevance;
3257    // These cached suggestions should already be sorted.
3258    // The particular number 5 as the length of the array is
3259    // unimportant; it's merely enough cached results to fully test
3260    // the functioning of RemoveAllStaleResults().
3261    struct {
3262      const std::string suggestion;
3263      const bool is_navigation_result;
3264      const int relevance;
3265      // |expect_match| is true if this result should survive
3266      // RemoveAllStaleResults() filtering against |omnibox_input| below.
3267      const bool expect_match;
3268    } results[5];
3269  } cases[] = {
3270    // Simple case: multiple query suggestions and no navsuggestions.
3271    // All query suggestions score less than search-what-you-typed and
3272    // thus none should be filtered because none will appear first.
3273    { "x", 1300,
3274      { { "food",         false, 1299, true  },
3275        { "foobar",       false, 1298, true  },
3276        { "crazy",        false, 1297, true  },
3277        { "friend",       false, 1296, true  },
3278        { kNotApplicable, false, 0,    false } } },
3279
3280    // Similarly simple cases, but the query suggestion appears first.
3281    { "f", 1200,
3282      { { "food",         false, 1299, true  },
3283        { "foobar",       false, 1298, true  },
3284        { "crazy",        false, 1297, true  },
3285        { "friend",       false, 1296, true  },
3286        { kNotApplicable, false, 0,    false } } },
3287    { "c", 1200,
3288      { { "food",         false, 1299, false },
3289        { "foobar",       false, 1298, false },
3290        { "crazy",        false, 1297, true  },
3291        { "friend",       false, 1296, true  },
3292        { kNotApplicable, false, 0,    false } } },
3293    { "x", 1200,
3294      { { "food",         false, 1299, false },
3295        { "foobar",       false, 1298, false },
3296        { "crazy",        false, 1297, false },
3297        { "friend",       false, 1296, false },
3298        { kNotApplicable, false, 0,    false } } },
3299
3300    // The same sort of cases, just using a mix of queries and navsuggestions.
3301    { "x", 1300,
3302      { { "http://food.com/",   true,  1299, true },
3303        { "foobar",             false, 1298, true },
3304        { "http://crazy.com/",  true,  1297, true },
3305        { "friend",             false, 1296, true },
3306        { "http://friend.com/", true,  1295, true } } },
3307    { "f", 1200,
3308      { { "http://food.com/",   true,  1299, true },
3309        { "foobar",             false, 1298, true },
3310        { "http://crazy.com/",  true,  1297, true },
3311        { "friend",             false, 1296, true },
3312        { "http://friend.com/", true,  1295, true } } },
3313    { "c", 1200,
3314      { { "http://food.com/",   true,  1299, false },
3315        { "foobar",             false, 1298, false },
3316        { "http://crazy.com/",  true,  1297, true  },
3317        { "friend",             false, 1296, true  },
3318        { "http://friend.com/", true,  1295, true  } } },
3319    { "x", 1200,
3320      { { "http://food.com/",   true,  1299, false },
3321        { "foobar",             false, 1298, false },
3322        { "http://crazy.com/",  true,  1297, false },
3323        { "friend",             false, 1296, false },
3324        { "http://friend.com/", true,  1295, false } } },
3325
3326    // Run the three tests immediately above again, just with verbatim
3327    // suppressed.  Note that in the last case, all results are filtered.
3328    // Because verbatim is also suppressed, SearchProvider will realize
3329    // in UpdateMatches() that it needs to restore verbatim to fulfill
3330    // its constraints.  This restoration does not happen in
3331    // RemoveAllStaleResults() and hence is not tested here.  This restoration
3332    // is tested in the DefaultFetcherSuggestRelevance test.
3333    { "f", 0,
3334      { { "http://food.com/",   true,  1299, true },
3335        { "foobar",             false, 1298, true },
3336        { "http://crazy.com/",  true,  1297, true },
3337        { "friend",             false, 1296, true },
3338        { "http://friend.com/", true,  1295, true } } },
3339    { "c", 0,
3340      { { "http://food.com/",   true,  1299, false },
3341        { "foobar",             false, 1298, false },
3342        { "http://crazy.com/",  true,  1297, true  },
3343        { "friend",             false, 1296, true  },
3344        { "http://friend.com/", true,  1295, true  } } },
3345    { "x", 0,
3346      { { "http://food.com/",   true,  1299, false },
3347        { "foobar",             false, 1298, false },
3348        { "http://crazy.com/",  true,  1297, false },
3349        { "friend",             false, 1296, false },
3350        { "http://friend.com/", true,  1295, false } } },
3351
3352    // The same sort of tests again, just with verbatim with a score
3353    // that would place it in between other suggestions.
3354    { "f", 1290,
3355      { { "http://food.com/",   true,  1299, true },
3356        { "foobar",             false, 1288, true },
3357        { "http://crazy.com/",  true,  1277, true },
3358        { "friend",             false, 1266, true },
3359        { "http://friend.com/", true,  1255, true } } },
3360    { "c", 1290,
3361      { { "http://food.com/",   true,  1299, false },
3362        { "foobar",             false, 1288, true  },
3363        { "http://crazy.com/",  true,  1277, true  },
3364        { "friend",             false, 1266, true  },
3365        { "http://friend.com/", true,  1255, true  } } },
3366    { "c", 1270,
3367      { { "http://food.com/",   true,  1299, false },
3368        { "foobar",             false, 1288, false },
3369        { "http://crazy.com/",  true,  1277, true  },
3370        { "friend",             false, 1266, true  },
3371        { "http://friend.com/", true,  1255, true  } } },
3372    { "x", 1280,
3373      { { "http://food.com/",   true,  1299, false },
3374        { "foobar",             false, 1288, false },
3375        { "http://crazy.com/",  true,  1277, true  },
3376        { "friend",             false, 1266, true  },
3377        { "http://friend.com/", true,  1255, true  } } },
3378  };
3379
3380  std::map<std::string, std::string> params;
3381  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
3382      ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
3383  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
3384      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
3385  base::FieldTrialList::CreateFieldTrial(
3386      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
3387
3388  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3389    // Initialize cached results for this test case.
3390    provider_->default_results_.verbatim_relevance =
3391        cases[i].verbatim_relevance;
3392    provider_->default_results_.navigation_results.clear();
3393    provider_->default_results_.suggest_results.clear();
3394    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3395      const std::string& suggestion = cases[i].results[j].suggestion;
3396      if (suggestion == kNotApplicable)
3397        break;
3398      if (cases[i].results[j].is_navigation_result) {
3399        provider_->default_results_.navigation_results.push_back(
3400            SearchProvider::NavigationResult(
3401                *provider_.get(), GURL(suggestion), base::string16(), false,
3402                cases[i].results[j].relevance, false));
3403      } else {
3404        provider_->default_results_.suggest_results.push_back(
3405            SearchProvider::SuggestResult(
3406                ASCIIToUTF16(suggestion), AutocompleteMatchType::SEARCH_SUGGEST,
3407                base::string16(), base::string16(), std::string(),
3408                std::string(), false, cases[i].results[j].relevance, false,
3409                false));
3410      }
3411    }
3412
3413    provider_->input_ = AutocompleteInput(
3414        ASCIIToUTF16(cases[i].omnibox_input), base::string16::npos, base::string16(),
3415        GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
3416        AutocompleteInput::ALL_MATCHES);
3417    provider_->RemoveAllStaleResults();
3418
3419    // Check cached results.
3420    SearchProvider::SuggestResults::const_iterator sug_it =
3421        provider_->default_results_.suggest_results.begin();
3422    const SearchProvider::SuggestResults::const_iterator sug_end =
3423        provider_->default_results_.suggest_results.end();
3424    SearchProvider::NavigationResults::const_iterator nav_it =
3425        provider_->default_results_.navigation_results.begin();
3426    const SearchProvider::NavigationResults::const_iterator nav_end =
3427        provider_->default_results_.navigation_results.end();
3428    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3429      const std::string& suggestion = cases[i].results[j].suggestion;
3430      if (suggestion == kNotApplicable)
3431        continue;
3432      if (!cases[i].results[j].expect_match)
3433        continue;
3434      if (cases[i].results[j].is_navigation_result) {
3435        ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
3436        EXPECT_EQ(suggestion, nav_it->url().spec());
3437        ++nav_it;
3438      } else {
3439        ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
3440        EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
3441        ++sug_it;
3442      }
3443    }
3444    EXPECT_EQ(sug_end, sug_it);
3445    EXPECT_EQ(nav_end, nav_it);
3446  }
3447}
3448
3449#if !defined(OS_WIN)
3450// Verify entity suggestion parsing.
3451TEST_F(SearchProviderTest, ParseEntitySuggestion) {
3452  struct Match {
3453    std::string contents;
3454    std::string description;
3455    std::string query_params;
3456    std::string fill_into_edit;
3457    AutocompleteMatchType::Type type;
3458  };
3459  const Match kEmptyMatch = {
3460    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
3461    AutocompleteMatchType::NUM_TYPES};
3462
3463  struct {
3464    const std::string input_text;
3465    const std::string response_json;
3466    const Match matches[5];
3467  } cases[] = {
3468    // A query and an entity suggestion with different search terms.
3469    { "x",
3470      "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
3471      " {\"google:suggestdetail\":[{},"
3472      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3473      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3474      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3475        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3476        { "xy", "A", "p=v", "yy",
3477          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3478        kEmptyMatch,
3479        kEmptyMatch
3480      },
3481    },
3482    // A query and an entity suggestion with same search terms.
3483    { "x",
3484      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3485      " {\"google:suggestdetail\":[{},"
3486      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3487      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3488      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3489        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3490        { "xy", "A", "p=v", "xy",
3491          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3492        kEmptyMatch,
3493        kEmptyMatch
3494      },
3495    },
3496  };
3497  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3498    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3499
3500    // Set up a default fetcher with provided results.
3501    net::TestURLFetcher* fetcher =
3502        test_factory_.GetFetcherByID(
3503            SearchProvider::kDefaultProviderURLFetcherID);
3504    ASSERT_TRUE(fetcher);
3505    fetcher->set_response_code(200);
3506    fetcher->SetResponseString(cases[i].response_json);
3507    fetcher->delegate()->OnURLFetchComplete(fetcher);
3508
3509    RunTillProviderDone();
3510
3511    const ACMatches& matches = provider_->matches();
3512    ASSERT_FALSE(matches.empty());
3513
3514    SCOPED_TRACE("for input with json = " + cases[i].response_json);
3515
3516    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3517    size_t j = 0;
3518    // Ensure that the returned matches equal the expectations.
3519    for (; j < matches.size(); ++j) {
3520      const Match& match = cases[i].matches[j];
3521      SCOPED_TRACE(" and match index: " + base::IntToString(j));
3522      EXPECT_EQ(match.contents,
3523                UTF16ToUTF8(matches[j].contents));
3524      EXPECT_EQ(match.description,
3525                UTF16ToUTF8(matches[j].description));
3526      EXPECT_EQ(match.query_params,
3527                matches[j].search_terms_args->suggest_query_params);
3528      EXPECT_EQ(match.fill_into_edit,
3529                UTF16ToUTF8(matches[j].fill_into_edit));
3530      EXPECT_EQ(match.type, matches[j].type);
3531    }
3532    // Ensure that no expected matches are missing.
3533    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3534      SCOPED_TRACE(" and match index: " + base::IntToString(j));
3535      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3536      EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
3537      EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
3538      EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
3539      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3540    }
3541  }
3542}
3543#endif  // !defined(OS_WIN)
3544
3545
3546// A basic test that verifies the prefetch metadata parsing logic.
3547TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
3548  struct Match {
3549    std::string contents;
3550    bool allowed_to_be_prefetched;
3551    AutocompleteMatchType::Type type;
3552    bool from_keyword;
3553  };
3554  const Match kEmptyMatch = { kNotApplicable,
3555                              false,
3556                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3557                              false };
3558
3559  struct {
3560    const std::string input_text;
3561    bool prefer_keyword_provider_results;
3562    const std::string default_provider_response_json;
3563    const std::string keyword_provider_response_json;
3564    const Match matches[5];
3565  } cases[] = {
3566    // Default provider response does not have prefetch details. Ensure that the
3567    // suggestions are not marked as prefetch query.
3568    { "a",
3569      false,
3570      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3571      std::string(),
3572      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3573        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3574        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3575        kEmptyMatch,
3576        kEmptyMatch
3577      },
3578    },
3579    // Ensure that default provider suggest response prefetch details are
3580    // parsed and recorded in AutocompleteMatch.
3581    { "ab",
3582      false,
3583      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
3584          "{\"google:clientdata\":{\"phi\": 0},"
3585          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
3586          "\"google:suggestrelevance\":[999, 12, 1]}]",
3587      std::string(),
3588      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3589        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
3590        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3591        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3592        kEmptyMatch
3593      },
3594    },
3595    // Default provider suggest response has prefetch details.
3596    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
3597    // the same query string. Ensure that the prefetch details from
3598    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
3599    { "ab",
3600      false,
3601      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
3602          "{\"google:clientdata\":{\"phi\": 0},"
3603          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
3604          "\"google:suggestrelevance\":[99, 98]}]",
3605      std::string(),
3606      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3607        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3608        kEmptyMatch,
3609        kEmptyMatch,
3610        kEmptyMatch
3611      },
3612    },
3613    // Default provider response has prefetch details. We prefer keyword
3614    // provider results. Ensure that prefetch bit for a suggestion from the
3615    // default search provider does not get copied onto a higher-scoring match
3616    // for the same query string from the keyword provider.
3617    { "k a",
3618      true,
3619      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
3620          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
3621          "\"google:suggestrelevance\":[9, 12]}]",
3622      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3623      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
3624        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3625        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3626        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
3627        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
3628      },
3629    }
3630  };
3631
3632  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3633    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
3634                  cases[i].prefer_keyword_provider_results);
3635
3636    // Set up a default fetcher with provided results.
3637    net::TestURLFetcher* fetcher =
3638        test_factory_.GetFetcherByID(
3639            SearchProvider::kDefaultProviderURLFetcherID);
3640    ASSERT_TRUE(fetcher);
3641    fetcher->set_response_code(200);
3642    fetcher->SetResponseString(cases[i].default_provider_response_json);
3643    fetcher->delegate()->OnURLFetchComplete(fetcher);
3644
3645    if (cases[i].prefer_keyword_provider_results) {
3646      // Set up a keyword fetcher with provided results.
3647      net::TestURLFetcher* keyword_fetcher =
3648          test_factory_.GetFetcherByID(
3649              SearchProvider::kKeywordProviderURLFetcherID);
3650      ASSERT_TRUE(keyword_fetcher);
3651      keyword_fetcher->set_response_code(200);
3652      keyword_fetcher->SetResponseString(
3653          cases[i].keyword_provider_response_json);
3654      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
3655      keyword_fetcher = NULL;
3656    }
3657
3658    RunTillProviderDone();
3659
3660    const std::string description =
3661        "for input with json =" + cases[i].default_provider_response_json;
3662    const ACMatches& matches = provider_->matches();
3663    // The top match must inline and score as highly as calculated verbatim.
3664    ASSERT_FALSE(matches.empty());
3665    EXPECT_GE(matches[0].relevance, 1300);
3666
3667    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3668    // Ensure that the returned matches equal the expectations.
3669    for (size_t j = 0; j < matches.size(); ++j) {
3670      SCOPED_TRACE(description);
3671      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
3672      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
3673                SearchProvider::ShouldPrefetch(matches[j]));
3674      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3675      EXPECT_EQ(cases[i].matches[j].from_keyword,
3676                matches[j].keyword == ASCIIToUTF16("k"));
3677    }
3678  }
3679}
3680
3681TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
3682  ClearAllResults();
3683
3684  std::string input_str("abc");
3685  QueryForInput(ASCIIToUTF16(input_str), false, false);
3686
3687  // Set up a default fetcher with provided results.
3688  net::TestURLFetcher* fetcher =
3689      test_factory_.GetFetcherByID(
3690          SearchProvider::kDefaultProviderURLFetcherID);
3691  ASSERT_TRUE(fetcher);
3692  fetcher->set_response_code(200);
3693  fetcher->SetResponseString("this is a bad non-json response");
3694  fetcher->delegate()->OnURLFetchComplete(fetcher);
3695
3696  RunTillProviderDone();
3697
3698  const ACMatches& matches = provider_->matches();
3699
3700  // Should have exactly one "search what you typed" match
3701  ASSERT_TRUE(matches.size() == 1);
3702  EXPECT_EQ(input_str, UTF16ToUTF8(matches[0].contents));
3703  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3704            matches[0].type);
3705}
3706
3707// A basic test that verifies that the XSSI guarded JSON response is parsed
3708// correctly.
3709TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
3710  struct Match {
3711    std::string contents;
3712    AutocompleteMatchType::Type type;
3713  };
3714  const Match kEmptyMatch = {
3715      kNotApplicable, AutocompleteMatchType::NUM_TYPES
3716  };
3717
3718  struct {
3719    const std::string input_text;
3720    const std::string default_provider_response_json;
3721    const Match matches[4];
3722  } cases[] = {
3723    // No XSSI guard.
3724    { "a",
3725      "[\"a\",[\"b\", \"c\"],[],[],"
3726      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3727      "\"google:suggestrelevance\":[1, 2]}]",
3728      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3729        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3730        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3731        kEmptyMatch,
3732      },
3733    },
3734    // Standard XSSI guard - )]}'\n.
3735    { "a",
3736      ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3737      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3738      "\"google:suggestrelevance\":[1, 2]}]",
3739      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3740        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3741        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3742        kEmptyMatch,
3743      },
3744    },
3745    // Modified XSSI guard - contains "[".
3746    { "a",
3747      ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3748      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3749      "\"google:suggestrelevance\":[1, 2]}]",
3750      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3751        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3752        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3753        kEmptyMatch,
3754      },
3755    },
3756  };
3757
3758  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3759    ClearAllResults();
3760    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3761
3762    // Set up a default fetcher with provided results.
3763    net::TestURLFetcher* fetcher =
3764        test_factory_.GetFetcherByID(
3765            SearchProvider::kDefaultProviderURLFetcherID);
3766    ASSERT_TRUE(fetcher);
3767    fetcher->set_response_code(200);
3768    fetcher->SetResponseString(cases[i].default_provider_response_json);
3769    fetcher->delegate()->OnURLFetchComplete(fetcher);
3770
3771    RunTillProviderDone();
3772
3773    const ACMatches& matches = provider_->matches();
3774    // The top match must inline and score as highly as calculated verbatim.
3775    ASSERT_FALSE(matches.empty());
3776    EXPECT_GE(matches[0].relevance, 1300);
3777
3778    SCOPED_TRACE("for case: " + base::IntToString(i));
3779    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3780    size_t j = 0;
3781    // Ensure that the returned matches equal the expectations.
3782    for (; j < matches.size(); ++j) {
3783      SCOPED_TRACE("and match: " + base::IntToString(j));
3784      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
3785      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3786    }
3787    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3788      SCOPED_TRACE("and match: " + base::IntToString(j));
3789      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3790      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3791    }
3792  }
3793}
3794
3795// Test that deletion url gets set on an AutocompleteMatch when available for a
3796// personalized query.
3797TEST_F(SearchProviderTest, ParseDeletionUrl) {
3798   struct Match {
3799     std::string contents;
3800     std::string deletion_url;
3801     AutocompleteMatchType::Type type;
3802   };
3803
3804   const Match kEmptyMatch = {
3805       kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
3806   };
3807
3808   const char url[] = "https://www.google.com/complete/deleteitems"
3809       "?delq=ab&client=chrome&deltok=xsrf123";
3810
3811   struct {
3812       const std::string input_text;
3813       const std::string response_json;
3814       const Match matches[4];
3815     } cases[] = {
3816       // A deletion URL on a personalized query should be reflected in the
3817       // resulting AutocompleteMatch.
3818       { "a",
3819         "[\"a\",[\"ab\", \"ac\"],[],[],"
3820         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3821         "\"google:suggestrelevance\":[1, 2],"
3822         "\"google:suggestdetail\":[{\"du\":"
3823         "\"https://www.google.com/complete/deleteitems?delq=ab&client=chrome"
3824         "&deltok=xsrf123\"}, {}]}]",
3825         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3826           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3827           { "ab", url, AutocompleteMatchType::SEARCH_SUGGEST },
3828           kEmptyMatch,
3829         },
3830       },
3831       // Personalized queries without deletion URLs shouldn't cause errors.
3832       { "a",
3833         "[\"a\",[\"ab\", \"ac\"],[],[],"
3834         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3835         "\"google:suggestrelevance\":[1, 2],"
3836         "\"google:suggestdetail\":[{}, {}]}]",
3837         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3838           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3839           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3840           kEmptyMatch,
3841         },
3842       },
3843     };
3844
3845     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3846       QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3847
3848       net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3849           SearchProvider::kDefaultProviderURLFetcherID);
3850       ASSERT_TRUE(fetcher);
3851       fetcher->set_response_code(200);
3852       fetcher->SetResponseString(cases[i].response_json);
3853       fetcher->delegate()->OnURLFetchComplete(fetcher);
3854
3855       RunTillProviderDone();
3856
3857       const ACMatches& matches = provider_->matches();
3858       ASSERT_FALSE(matches.empty());
3859
3860       SCOPED_TRACE("for input with json = " + cases[i].response_json);
3861
3862       for (size_t j = 0; j < matches.size(); ++j) {
3863         const Match& match = cases[i].matches[j];
3864         SCOPED_TRACE(" and match index: " + base::IntToString(j));
3865         EXPECT_EQ(match.contents, UTF16ToUTF8(matches[j].contents));
3866         EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
3867             "deletion_url"));
3868       }
3869     }
3870}
3871
3872TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3873  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
3874  base::string16 term = term1_.substr(0, term1_.length() - 1);
3875  QueryForInput(term, true, false);
3876  ASSERT_FALSE(provider_->matches().empty());
3877  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3878            provider_->matches()[0].type);
3879  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3880  EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3881
3882  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
3883  term = term1_.substr(0, term1_.length() - 1);
3884  QueryForInput(term, true, false);
3885  ASSERT_FALSE(provider_->matches().empty());
3886  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3887            provider_->matches()[0].type);
3888  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3889  EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3890}
3891
3892TEST_F(SearchProviderTest, CanSendURL) {
3893  TemplateURLData template_url_data;
3894  template_url_data.short_name = ASCIIToUTF16("t");
3895  template_url_data.SetURL("http://www.google.com/{searchTerms}");
3896  template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3897  template_url_data.instant_url = "http://does/not/exist?strk=1";
3898  template_url_data.search_terms_replacement_key = "strk";
3899  template_url_data.id = SEARCH_ENGINE_GOOGLE;
3900  TemplateURL google_template_url(&profile_, template_url_data);
3901
3902  // Create field trial.
3903  base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
3904      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3905  field_trial->group();
3906
3907  // Not signed in.
3908  EXPECT_FALSE(SearchProvider::CanSendURL(
3909      GURL("http://www.google.com/search"),
3910      GURL("https://www.google.com/complete/search"), &google_template_url,
3911      AutocompleteInput::OTHER, &profile_));
3912  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3913  signin->SetAuthenticatedUsername("test");
3914
3915  // All conditions should be met.
3916  EXPECT_TRUE(SearchProvider::CanSendURL(
3917      GURL("http://www.google.com/search"),
3918      GURL("https://www.google.com/complete/search"), &google_template_url,
3919      AutocompleteInput::OTHER, &profile_));
3920
3921  // Not in field trial.
3922  ResetFieldTrialList();
3923  EXPECT_FALSE(SearchProvider::CanSendURL(
3924      GURL("http://www.google.com/search"),
3925      GURL("https://www.google.com/complete/search"), &google_template_url,
3926      AutocompleteInput::OTHER, &profile_));
3927  field_trial = base::FieldTrialList::CreateFieldTrial(
3928      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3929  field_trial->group();
3930
3931  // Invalid page URL.
3932  EXPECT_FALSE(SearchProvider::CanSendURL(
3933      GURL("badpageurl"),
3934      GURL("https://www.google.com/complete/search"), &google_template_url,
3935      AutocompleteInput::OTHER, &profile_));
3936
3937  // Invalid page classification.
3938  EXPECT_FALSE(SearchProvider::CanSendURL(
3939      GURL("http://www.google.com/search"),
3940      GURL("https://www.google.com/complete/search"), &google_template_url,
3941      AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3942      &profile_));
3943
3944  // Invalid page classification.
3945  EXPECT_FALSE(SearchProvider::CanSendURL(
3946      GURL("http://www.google.com/search"),
3947      GURL("https://www.google.com/complete/search"), &google_template_url,
3948      AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3949      &profile_));
3950
3951  // HTTPS page URL on same domain as provider.
3952  EXPECT_TRUE(SearchProvider::CanSendURL(
3953      GURL("https://www.google.com/search"),
3954      GURL("https://www.google.com/complete/search"),
3955      &google_template_url, AutocompleteInput::OTHER, &profile_));
3956
3957  // Non-HTTP[S] page URL on same domain as provider.
3958  EXPECT_FALSE(SearchProvider::CanSendURL(
3959      GURL("ftp://www.google.com/search"),
3960      GURL("https://www.google.com/complete/search"), &google_template_url,
3961      AutocompleteInput::OTHER, &profile_));
3962
3963  // Non-HTTP page URL on different domain.
3964  EXPECT_FALSE(SearchProvider::CanSendURL(
3965      GURL("https://www.notgoogle.com/search"),
3966      GURL("https://www.google.com/complete/search"), &google_template_url,
3967      AutocompleteInput::OTHER, &profile_));
3968
3969  // Non-HTTPS provider.
3970  EXPECT_FALSE(SearchProvider::CanSendURL(
3971      GURL("http://www.google.com/search"),
3972      GURL("http://www.google.com/complete/search"), &google_template_url,
3973      AutocompleteInput::OTHER, &profile_));
3974
3975  // Suggest disabled.
3976  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3977  EXPECT_FALSE(SearchProvider::CanSendURL(
3978      GURL("http://www.google.com/search"),
3979      GURL("https://www.google.com/complete/search"), &google_template_url,
3980      AutocompleteInput::OTHER, &profile_));
3981  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3982
3983  // Incognito.
3984  EXPECT_FALSE(SearchProvider::CanSendURL(
3985      GURL("http://www.google.com/search"),
3986      GURL("https://www.google.com/complete/search"), &google_template_url,
3987      AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile()));
3988
3989  // Tab sync not enabled.
3990  profile_.GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
3991  profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, false);
3992  EXPECT_FALSE(SearchProvider::CanSendURL(
3993      GURL("http://www.google.com/search"),
3994      GURL("https://www.google.com/complete/search"), &google_template_url,
3995      AutocompleteInput::OTHER, &profile_));
3996  profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, true);
3997
3998  // Tab sync is encrypted.
3999  ProfileSyncService* service =
4000      ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
4001  syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
4002  encrypted_types.Put(syncer::SESSIONS);
4003  service->OnEncryptedTypesChanged(encrypted_types, false);
4004  EXPECT_FALSE(SearchProvider::CanSendURL(
4005      GURL("http://www.google.com/search"),
4006      GURL("https://www.google.com/complete/search"), &google_template_url,
4007      AutocompleteInput::OTHER, &profile_));
4008  encrypted_types.Remove(syncer::SESSIONS);
4009  service->OnEncryptedTypesChanged(encrypted_types, false);
4010
4011  // Check that there were no side effects from previous tests.
4012  EXPECT_TRUE(SearchProvider::CanSendURL(
4013      GURL("http://www.google.com/search"),
4014      GURL("https://www.google.com/complete/search"), &google_template_url,
4015      AutocompleteInput::OTHER, &profile_));
4016}
4017
4018TEST_F(SearchProviderTest, TestDeleteMatch) {
4019  AutocompleteMatch match(provider_, 0, true,
4020                          AutocompleteMatchType::SEARCH_SUGGEST);
4021  match.RecordAdditionalInfo(
4022      SearchProvider::kDeletionUrlKey,
4023      "https://www.google.com/complete/deleteitem?q=foo");
4024
4025  // Test a successful deletion request.
4026  provider_->matches_.push_back(match);
4027  provider_->DeleteMatch(match);
4028  EXPECT_FALSE(provider_->deletion_handlers_.empty());
4029  EXPECT_TRUE(provider_->matches_.empty());
4030  // Set up a default fetcher with provided results.
4031  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
4032      SearchProvider::kDeletionURLFetcherID);
4033  ASSERT_TRUE(fetcher);
4034  fetcher->set_response_code(200);
4035  fetcher->delegate()->OnURLFetchComplete(fetcher);
4036  EXPECT_TRUE(provider_->deletion_handlers_.empty());
4037  EXPECT_TRUE(provider_->is_success());
4038
4039  // Test a failing deletion request.
4040  provider_->matches_.push_back(match);
4041  provider_->DeleteMatch(match);
4042  EXPECT_FALSE(provider_->deletion_handlers_.empty());
4043  // Set up a default fetcher with provided results.
4044  fetcher = test_factory_.GetFetcherByID(
4045      SearchProvider::kDeletionURLFetcherID);
4046  ASSERT_TRUE(fetcher);
4047  fetcher->set_response_code(500);
4048  fetcher->delegate()->OnURLFetchComplete(fetcher);
4049  EXPECT_TRUE(provider_->deletion_handlers_.empty());
4050  EXPECT_FALSE(provider_->is_success());
4051}
4052