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