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