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