search_provider_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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_in_regular_mode;
3029    const bool allowed_to_be_default_match_in_prevent_inline_mode;
3030  } cases[] = {
3031    // Do not inline matches that do not contain the input; trim http as needed.
3032    { "x",                 "http://www.abc.com",
3033                                  "www.abc.com",  std::string(), false, false },
3034    { "https:",            "http://www.abc.com",
3035                                  "www.abc.com",  std::string(), false, false },
3036    { "http://www.abc.com/a", "http://www.abc.com",
3037                              "http://www.abc.com",  std::string(), false,
3038                                                                    false },
3039    { "http://www.abc.com",   "https://www.abc.com",
3040                              "https://www.abc.com", std::string(), false,
3041                                                                    false },
3042    { "http://abc.com",       "ftp://abc.com",
3043                              "ftp://abc.com",       std::string(), false,
3044                                                                    false },
3045    { "https://www.abc.com",  "http://www.abc.com",
3046                                     "www.abc.com",  std::string(), false,
3047                                                                    false },
3048    { "ftp://abc.com",        "http://abc.com",
3049                                     "abc.com",      std::string(), false,
3050                                                                    false },
3051
3052    // Do not inline matches with invalid input prefixes; trim http as needed.
3053    { "ttp",              "http://www.abc.com",
3054                                 "www.abc.com", std::string(), false, false },
3055    { "://w",             "http://www.abc.com",
3056                                 "www.abc.com", std::string(), false, false },
3057    { "ww.",              "http://www.abc.com",
3058                                 "www.abc.com", std::string(), false, false },
3059    { ".ab",              "http://www.abc.com",
3060                                 "www.abc.com", std::string(), false, false },
3061    { "bc",               "http://www.abc.com",
3062                                 "www.abc.com", std::string(), false, false },
3063    { ".com",             "http://www.abc.com",
3064                                 "www.abc.com", std::string(), false, false },
3065
3066    // Do not inline matches that omit input domain labels; trim http as needed.
3067    { "www.a",            "http://a.com",
3068                                 "a.com",       std::string(), false, false },
3069    { "http://www.a",     "http://a.com",
3070                          "http://a.com",       std::string(), false, false },
3071    { "www.a",            "ftp://a.com",
3072                          "ftp://a.com",        std::string(), false, false },
3073    { "ftp://www.a",      "ftp://a.com",
3074                          "ftp://a.com",        std::string(), false, false },
3075
3076    // Input matching but with nothing to inline will not yield an offset, but
3077    // will be allowed to be default.
3078    { "abc.com",             "http://www.abc.com",
3079                                    "www.abc.com", std::string(), true, true },
3080    { "abc.com/",            "http://www.abc.com",
3081                                    "www.abc.com", std::string(), true, true },
3082    { "http://www.abc.com",  "http://www.abc.com",
3083                             "http://www.abc.com", std::string(), true, true },
3084    { "http://www.abc.com/", "http://www.abc.com",
3085                             "http://www.abc.com", std::string(), true, true },
3086
3087    // Inline matches when the input is a leading substring of the scheme.
3088    { "h",             "http://www.abc.com",
3089                       "http://www.abc.com", "ttp://www.abc.com", true, false },
3090    { "http",          "http://www.abc.com",
3091                       "http://www.abc.com", "://www.abc.com",    true, false },
3092
3093    // Inline matches when the input is a leading substring of the full URL.
3094    { "http:",             "http://www.abc.com",
3095                           "http://www.abc.com", "//www.abc.com", true, false },
3096    { "http://w",          "http://www.abc.com",
3097                           "http://www.abc.com", "ww.abc.com",    true, false },
3098    { "http://www.",       "http://www.abc.com",
3099                           "http://www.abc.com", "abc.com",       true, false },
3100    { "http://www.ab",     "http://www.abc.com",
3101                           "http://www.abc.com", "c.com",         true, false },
3102    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
3103                              "http://www.abc.com/path/file.htm?q=x#foo",
3104                                                  "ath/file.htm?q=x#foo",
3105                                                                  true, false },
3106    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
3107                              "http://abc.com/path/file.htm?q=x#foo",
3108                                              "ath/file.htm?q=x#foo",
3109                                                                  true, false},
3110
3111    // Inline matches with valid URLPrefixes; only trim "http://".
3112    { "w",               "http://www.abc.com",
3113                                "www.abc.com", "ww.abc.com", true, false },
3114    { "www.a",           "http://www.abc.com",
3115                                "www.abc.com", "bc.com",     true, false },
3116    { "abc",             "http://www.abc.com",
3117                                "www.abc.com", ".com",       true, false },
3118    { "abc.c",           "http://www.abc.com",
3119                                "www.abc.com", "om",         true, false },
3120    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
3121                                "www.abc.com/path/file.htm?q=x#foo",
3122                                             "ath/file.htm?q=x#foo",
3123                                                             true, false },
3124    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
3125                                "abc.com/path/file.htm?q=x#foo",
3126                                         "ath/file.htm?q=x#foo",
3127                                                             true, false },
3128
3129    // Inline matches using the maximal URLPrefix components.
3130    { "h",               "http://help.com",
3131                                "help.com", "elp.com",     true, false },
3132    { "http",            "http://http.com",
3133                                "http.com", ".com",        true, false },
3134    { "h",               "http://www.help.com",
3135                                "www.help.com", "elp.com", true, false },
3136    { "http",            "http://www.http.com",
3137                                "www.http.com", ".com",    true, false },
3138    { "w",               "http://www.www.com",
3139                                "www.www.com",  "ww.com",  true, false },
3140
3141    // Test similar behavior for the ftp and https schemes.
3142    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
3143                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3144                                  "c.com/path/file.htm?q=x#foo",  true, false },
3145    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
3146                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3147                                   "c.com/path/file.htm?q=x#foo", true, false },
3148    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
3149                       "ftp://www.abc.com/path/file.htm?q=x#foo",
3150                                   "c.com/path/file.htm?q=x#foo", true, false },
3151    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
3152                       "ftp://abc.com/path/file.htm?q=x#foo",
3153                               "c.com/path/file.htm?q=x#foo",     true, false },
3154    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
3155                         "https://www.abc.com/path/file.htm?q=x#foo",
3156                                       "c.com/path/file.htm?q=x#foo",
3157                                                                  true, false },
3158    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
3159                     "https://www.abc.com/path/file.htm?q=x#foo",
3160                                   "c.com/path/file.htm?q=x#foo", true, false },
3161    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
3162                     "https://www.abc.com/path/file.htm?q=x#foo",
3163                                   "c.com/path/file.htm?q=x#foo", true, false },
3164    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
3165                     "https://abc.com/path/file.htm?q=x#foo",
3166                               "c.com/path/file.htm?q=x#foo",     true, false },
3167
3168    // Forced query input should inline and retain the "?" prefix.
3169    { "?http://www.ab",  "http://www.abc.com",
3170                        "?http://www.abc.com", "c.com", true, false },
3171    { "?www.ab",         "http://www.abc.com",
3172                               "?www.abc.com", "c.com", true, false },
3173    { "?ab",             "http://www.abc.com",
3174                               "?www.abc.com", "c.com", true, false },
3175    { "?abc.com",        "http://www.abc.com",
3176                               "?www.abc.com", "",      true, true },
3177  };
3178
3179  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3180    // First test regular mode.
3181    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
3182    AutocompleteMatch match(
3183        provider_->NavigationToMatch(SearchProvider::NavigationResult(
3184            *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3185            false, ASCIIToUTF16(cases[i].input), std::string())));
3186    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3187              match.inline_autocompletion);
3188    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
3189    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
3190              match.allowed_to_be_default_match);
3191
3192    // Then test prevent-inline-autocomplete mode.
3193    QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
3194    AutocompleteMatch match_prevent_inline(
3195        provider_->NavigationToMatch(SearchProvider::NavigationResult(
3196            *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3197            false, ASCIIToUTF16(cases[i].input), std::string())));
3198    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3199              match_prevent_inline.inline_autocompletion);
3200    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
3201              match_prevent_inline.fill_into_edit);
3202    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
3203              match_prevent_inline.allowed_to_be_default_match);
3204  }
3205}
3206
3207// Verifies that "http://" is not trimmed for input that is a leading substring.
3208TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
3209  const base::string16 input(ASCIIToUTF16("ht"));
3210  const base::string16 url(ASCIIToUTF16("http://a.com"));
3211  const SearchProvider::NavigationResult result(
3212      *provider_.get(), GURL(url), base::string16(), false, 0, false,
3213      input, std::string());
3214
3215  // Check the offset and strings when inline autocompletion is allowed.
3216  QueryForInput(input, false, false);
3217  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
3218  EXPECT_EQ(url, match_inline.fill_into_edit);
3219  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
3220  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
3221  EXPECT_EQ(url, match_inline.contents);
3222
3223  // Check the same strings when inline autocompletion is prevented.
3224  QueryForInput(input, true, false);
3225  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
3226  EXPECT_EQ(url, match_prevent.fill_into_edit);
3227  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
3228  EXPECT_EQ(url, match_prevent.contents);
3229}
3230
3231// Verifies that input "w" marks a more significant domain label than "www.".
3232TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
3233  QueryForInput(ASCIIToUTF16("w"), false, false);
3234  AutocompleteMatch match(
3235      provider_->NavigationToMatch(SearchProvider::NavigationResult(
3236          *provider_.get(), GURL("http://www.wow.com"), base::string16(), false,
3237          0, false, ASCIIToUTF16("w"), std::string())));
3238  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
3239  EXPECT_TRUE(match.allowed_to_be_default_match);
3240  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
3241  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
3242
3243  // Ensure that the match for input "w" is marked on "wow" and not "www".
3244  ASSERT_EQ(3U, match.contents_class.size());
3245  EXPECT_EQ(0U, match.contents_class[0].offset);
3246  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3247            match.contents_class[0].style);
3248  EXPECT_EQ(4U, match.contents_class[1].offset);
3249  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
3250            AutocompleteMatch::ACMatchClassification::MATCH,
3251            match.contents_class[1].style);
3252  EXPECT_EQ(5U, match.contents_class[2].offset);
3253  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3254            match.contents_class[2].style);
3255}
3256
3257TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
3258  // TODO(mpearson): Consider expanding this test to explicitly cover
3259  // testing staleness for keyword results.
3260  struct {
3261    const std::string omnibox_input;
3262    const int verbatim_relevance;
3263    // These cached suggestions should already be sorted.
3264    // The particular number 5 as the length of the array is
3265    // unimportant; it's merely enough cached results to fully test
3266    // the functioning of RemoveAllStaleResults().
3267    struct {
3268      const std::string suggestion;
3269      const bool is_navigation_result;
3270      const int relevance;
3271      // |expect_match| is true if this result should survive
3272      // RemoveAllStaleResults() filtering against |omnibox_input| below.
3273      const bool expect_match;
3274    } results[5];
3275  } cases[] = {
3276    // Simple case: multiple query suggestions and no navsuggestions.
3277    // All query suggestions score less than search-what-you-typed and
3278    // thus none should be filtered because none will appear first.
3279    { "x", 1300,
3280      { { "food",         false, 1299, true  },
3281        { "foobar",       false, 1298, true  },
3282        { "crazy",        false, 1297, true  },
3283        { "friend",       false, 1296, true  },
3284        { kNotApplicable, false, 0,    false } } },
3285
3286    // Similarly simple cases, but the query suggestion appears first.
3287    { "f", 1200,
3288      { { "food",         false, 1299, true  },
3289        { "foobar",       false, 1298, true  },
3290        { "crazy",        false, 1297, true  },
3291        { "friend",       false, 1296, true  },
3292        { kNotApplicable, false, 0,    false } } },
3293    { "c", 1200,
3294      { { "food",         false, 1299, false },
3295        { "foobar",       false, 1298, false },
3296        { "crazy",        false, 1297, true  },
3297        { "friend",       false, 1296, true  },
3298        { kNotApplicable, false, 0,    false } } },
3299    { "x", 1200,
3300      { { "food",         false, 1299, false },
3301        { "foobar",       false, 1298, false },
3302        { "crazy",        false, 1297, false },
3303        { "friend",       false, 1296, false },
3304        { kNotApplicable, false, 0,    false } } },
3305
3306    // The same sort of cases, just using a mix of queries and navsuggestions.
3307    { "x", 1300,
3308      { { "http://food.com/",   true,  1299, true },
3309        { "foobar",             false, 1298, true },
3310        { "http://crazy.com/",  true,  1297, true },
3311        { "friend",             false, 1296, true },
3312        { "http://friend.com/", true,  1295, true } } },
3313    { "f", 1200,
3314      { { "http://food.com/",   true,  1299, true },
3315        { "foobar",             false, 1298, true },
3316        { "http://crazy.com/",  true,  1297, true },
3317        { "friend",             false, 1296, true },
3318        { "http://friend.com/", true,  1295, true } } },
3319    { "c", 1200,
3320      { { "http://food.com/",   true,  1299, false },
3321        { "foobar",             false, 1298, false },
3322        { "http://crazy.com/",  true,  1297, true  },
3323        { "friend",             false, 1296, true  },
3324        { "http://friend.com/", true,  1295, true  } } },
3325    { "x", 1200,
3326      { { "http://food.com/",   true,  1299, false },
3327        { "foobar",             false, 1298, false },
3328        { "http://crazy.com/",  true,  1297, false },
3329        { "friend",             false, 1296, false },
3330        { "http://friend.com/", true,  1295, false } } },
3331
3332    // Run the three tests immediately above again, just with verbatim
3333    // suppressed.  Note that in the last case, all results are filtered.
3334    // Because verbatim is also suppressed, SearchProvider will realize
3335    // in UpdateMatches() that it needs to restore verbatim to fulfill
3336    // its constraints.  This restoration does not happen in
3337    // RemoveAllStaleResults() and hence is not tested here.  This restoration
3338    // is tested in the DefaultFetcherSuggestRelevance test.
3339    { "f", 0,
3340      { { "http://food.com/",   true,  1299, true },
3341        { "foobar",             false, 1298, true },
3342        { "http://crazy.com/",  true,  1297, true },
3343        { "friend",             false, 1296, true },
3344        { "http://friend.com/", true,  1295, true } } },
3345    { "c", 0,
3346      { { "http://food.com/",   true,  1299, false },
3347        { "foobar",             false, 1298, false },
3348        { "http://crazy.com/",  true,  1297, true  },
3349        { "friend",             false, 1296, true  },
3350        { "http://friend.com/", true,  1295, true  } } },
3351    { "x", 0,
3352      { { "http://food.com/",   true,  1299, false },
3353        { "foobar",             false, 1298, false },
3354        { "http://crazy.com/",  true,  1297, false },
3355        { "friend",             false, 1296, false },
3356        { "http://friend.com/", true,  1295, false } } },
3357
3358    // The same sort of tests again, just with verbatim with a score
3359    // that would place it in between other suggestions.
3360    { "f", 1290,
3361      { { "http://food.com/",   true,  1299, true },
3362        { "foobar",             false, 1288, true },
3363        { "http://crazy.com/",  true,  1277, true },
3364        { "friend",             false, 1266, true },
3365        { "http://friend.com/", true,  1255, true } } },
3366    { "c", 1290,
3367      { { "http://food.com/",   true,  1299, false },
3368        { "foobar",             false, 1288, true  },
3369        { "http://crazy.com/",  true,  1277, true  },
3370        { "friend",             false, 1266, true  },
3371        { "http://friend.com/", true,  1255, true  } } },
3372    { "c", 1270,
3373      { { "http://food.com/",   true,  1299, false },
3374        { "foobar",             false, 1288, false },
3375        { "http://crazy.com/",  true,  1277, true  },
3376        { "friend",             false, 1266, true  },
3377        { "http://friend.com/", true,  1255, true  } } },
3378    { "x", 1280,
3379      { { "http://food.com/",   true,  1299, false },
3380        { "foobar",             false, 1288, false },
3381        { "http://crazy.com/",  true,  1277, true  },
3382        { "friend",             false, 1266, true  },
3383        { "http://friend.com/", true,  1255, true  } } },
3384  };
3385
3386  std::map<std::string, std::string> params;
3387  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
3388      ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
3389  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
3390      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
3391  base::FieldTrialList::CreateFieldTrial(
3392      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
3393
3394  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3395    // Initialize cached results for this test case.
3396    provider_->default_results_.verbatim_relevance =
3397        cases[i].verbatim_relevance;
3398    provider_->default_results_.navigation_results.clear();
3399    provider_->default_results_.suggest_results.clear();
3400    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3401      const std::string& suggestion = cases[i].results[j].suggestion;
3402      if (suggestion == kNotApplicable)
3403        break;
3404      if (cases[i].results[j].is_navigation_result) {
3405        provider_->default_results_.navigation_results.push_back(
3406            SearchProvider::NavigationResult(
3407                *provider_.get(), GURL(suggestion), base::string16(), false,
3408                cases[i].results[j].relevance, false,
3409                ASCIIToUTF16(cases[i].omnibox_input), std::string()));
3410      } else {
3411        provider_->default_results_.suggest_results.push_back(
3412            SearchProvider::SuggestResult(
3413                ASCIIToUTF16(suggestion), AutocompleteMatchType::SEARCH_SUGGEST,
3414                ASCIIToUTF16(suggestion), base::string16(), base::string16(),
3415                std::string(), std::string(), false,
3416                cases[i].results[j].relevance, false, false,
3417                ASCIIToUTF16(cases[i].omnibox_input)));
3418      }
3419    }
3420
3421    provider_->input_ = AutocompleteInput(
3422        ASCIIToUTF16(cases[i].omnibox_input), base::string16::npos,
3423        base::string16(), GURL(), AutocompleteInput::INVALID_SPEC, false, false,
3424        true, AutocompleteInput::ALL_MATCHES);
3425    provider_->RemoveAllStaleResults();
3426
3427    // Check cached results.
3428    SearchProvider::SuggestResults::const_iterator sug_it =
3429        provider_->default_results_.suggest_results.begin();
3430    const SearchProvider::SuggestResults::const_iterator sug_end =
3431        provider_->default_results_.suggest_results.end();
3432    SearchProvider::NavigationResults::const_iterator nav_it =
3433        provider_->default_results_.navigation_results.begin();
3434    const SearchProvider::NavigationResults::const_iterator nav_end =
3435        provider_->default_results_.navigation_results.end();
3436    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3437      const std::string& suggestion = cases[i].results[j].suggestion;
3438      if (suggestion == kNotApplicable)
3439        continue;
3440      if (!cases[i].results[j].expect_match)
3441        continue;
3442      if (cases[i].results[j].is_navigation_result) {
3443        ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
3444        EXPECT_EQ(suggestion, nav_it->url().spec());
3445        ++nav_it;
3446      } else {
3447        ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
3448        EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
3449        ++sug_it;
3450      }
3451    }
3452    EXPECT_EQ(sug_end, sug_it);
3453    EXPECT_EQ(nav_end, nav_it);
3454  }
3455}
3456
3457#if !defined(OS_WIN)
3458// Verify entity suggestion parsing.
3459TEST_F(SearchProviderTest, ParseEntitySuggestion) {
3460  struct Match {
3461    std::string contents;
3462    std::string description;
3463    std::string query_params;
3464    std::string fill_into_edit;
3465    AutocompleteMatchType::Type type;
3466  };
3467  const Match kEmptyMatch = {
3468    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
3469    AutocompleteMatchType::NUM_TYPES};
3470
3471  struct {
3472    const std::string input_text;
3473    const std::string response_json;
3474    const Match matches[5];
3475  } cases[] = {
3476    // A query and an entity suggestion with different search terms.
3477    { "x",
3478      "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
3479      " {\"google:suggestdetail\":[{},"
3480      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3481      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3482      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3483        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3484        { "xy", "A", "p=v", "yy",
3485          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3486        kEmptyMatch,
3487        kEmptyMatch
3488      },
3489    },
3490    // A query and an entity suggestion with same search terms.
3491    { "x",
3492      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3493      " {\"google:suggestdetail\":[{},"
3494      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3495      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3496      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3497        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3498        { "xy", "A", "p=v", "xy",
3499          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3500        kEmptyMatch,
3501        kEmptyMatch
3502      },
3503    },
3504  };
3505  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3506    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3507
3508    // Set up a default fetcher with provided results.
3509    net::TestURLFetcher* fetcher =
3510        test_factory_.GetFetcherByID(
3511            SearchProvider::kDefaultProviderURLFetcherID);
3512    ASSERT_TRUE(fetcher);
3513    fetcher->set_response_code(200);
3514    fetcher->SetResponseString(cases[i].response_json);
3515    fetcher->delegate()->OnURLFetchComplete(fetcher);
3516
3517    RunTillProviderDone();
3518
3519    const ACMatches& matches = provider_->matches();
3520    ASSERT_FALSE(matches.empty());
3521
3522    SCOPED_TRACE("for input with json = " + cases[i].response_json);
3523
3524    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3525    size_t j = 0;
3526    // Ensure that the returned matches equal the expectations.
3527    for (; j < matches.size(); ++j) {
3528      const Match& match = cases[i].matches[j];
3529      SCOPED_TRACE(" and match index: " + base::IntToString(j));
3530      EXPECT_EQ(match.contents,
3531                base::UTF16ToUTF8(matches[j].contents));
3532      EXPECT_EQ(match.description,
3533                base::UTF16ToUTF8(matches[j].description));
3534      EXPECT_EQ(match.query_params,
3535                matches[j].search_terms_args->suggest_query_params);
3536      EXPECT_EQ(match.fill_into_edit,
3537                base::UTF16ToUTF8(matches[j].fill_into_edit));
3538      EXPECT_EQ(match.type, matches[j].type);
3539    }
3540    // Ensure that no expected matches are missing.
3541    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3542      SCOPED_TRACE(" and match index: " + base::IntToString(j));
3543      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3544      EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
3545      EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
3546      EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
3547      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3548    }
3549  }
3550}
3551#endif  // !defined(OS_WIN)
3552
3553
3554// A basic test that verifies the prefetch metadata parsing logic.
3555TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
3556  struct Match {
3557    std::string contents;
3558    bool allowed_to_be_prefetched;
3559    AutocompleteMatchType::Type type;
3560    bool from_keyword;
3561  };
3562  const Match kEmptyMatch = { kNotApplicable,
3563                              false,
3564                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3565                              false };
3566
3567  struct {
3568    const std::string input_text;
3569    bool prefer_keyword_provider_results;
3570    const std::string default_provider_response_json;
3571    const std::string keyword_provider_response_json;
3572    const Match matches[5];
3573  } cases[] = {
3574    // Default provider response does not have prefetch details. Ensure that the
3575    // suggestions are not marked as prefetch query.
3576    { "a",
3577      false,
3578      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3579      std::string(),
3580      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3581        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3582        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3583        kEmptyMatch,
3584        kEmptyMatch
3585      },
3586    },
3587    // Ensure that default provider suggest response prefetch details are
3588    // parsed and recorded in AutocompleteMatch.
3589    { "ab",
3590      false,
3591      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
3592          "{\"google:clientdata\":{\"phi\": 0},"
3593          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
3594          "\"google:suggestrelevance\":[999, 12, 1]}]",
3595      std::string(),
3596      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3597        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
3598        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3599        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3600        kEmptyMatch
3601      },
3602    },
3603    // Default provider suggest response has prefetch details.
3604    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
3605    // the same query string. Ensure that the prefetch details from
3606    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
3607    { "ab",
3608      false,
3609      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
3610          "{\"google:clientdata\":{\"phi\": 0},"
3611          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
3612          "\"google:suggestrelevance\":[99, 98]}]",
3613      std::string(),
3614      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3615        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3616        kEmptyMatch,
3617        kEmptyMatch,
3618        kEmptyMatch
3619      },
3620    },
3621    // Default provider response has prefetch details. We prefer keyword
3622    // provider results. Ensure that prefetch bit for a suggestion from the
3623    // default search provider does not get copied onto a higher-scoring match
3624    // for the same query string from the keyword provider.
3625    { "k a",
3626      true,
3627      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
3628          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
3629          "\"google:suggestrelevance\":[9, 12]}]",
3630      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3631      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
3632        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3633        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3634        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
3635        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
3636      },
3637    }
3638  };
3639
3640  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3641    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
3642                  cases[i].prefer_keyword_provider_results);
3643
3644    // Set up a default fetcher with provided results.
3645    net::TestURLFetcher* fetcher =
3646        test_factory_.GetFetcherByID(
3647            SearchProvider::kDefaultProviderURLFetcherID);
3648    ASSERT_TRUE(fetcher);
3649    fetcher->set_response_code(200);
3650    fetcher->SetResponseString(cases[i].default_provider_response_json);
3651    fetcher->delegate()->OnURLFetchComplete(fetcher);
3652
3653    if (cases[i].prefer_keyword_provider_results) {
3654      // Set up a keyword fetcher with provided results.
3655      net::TestURLFetcher* keyword_fetcher =
3656          test_factory_.GetFetcherByID(
3657              SearchProvider::kKeywordProviderURLFetcherID);
3658      ASSERT_TRUE(keyword_fetcher);
3659      keyword_fetcher->set_response_code(200);
3660      keyword_fetcher->SetResponseString(
3661          cases[i].keyword_provider_response_json);
3662      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
3663      keyword_fetcher = NULL;
3664    }
3665
3666    RunTillProviderDone();
3667
3668    const std::string description =
3669        "for input with json =" + cases[i].default_provider_response_json;
3670    const ACMatches& matches = provider_->matches();
3671    // The top match must inline and score as highly as calculated verbatim.
3672    ASSERT_FALSE(matches.empty());
3673    EXPECT_GE(matches[0].relevance, 1300);
3674
3675    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3676    // Ensure that the returned matches equal the expectations.
3677    for (size_t j = 0; j < matches.size(); ++j) {
3678      SCOPED_TRACE(description);
3679      EXPECT_EQ(cases[i].matches[j].contents,
3680                base::UTF16ToUTF8(matches[j].contents));
3681      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
3682                SearchProvider::ShouldPrefetch(matches[j]));
3683      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3684      EXPECT_EQ(cases[i].matches[j].from_keyword,
3685                matches[j].keyword == ASCIIToUTF16("k"));
3686    }
3687  }
3688}
3689
3690TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
3691  ClearAllResults();
3692
3693  std::string input_str("abc");
3694  QueryForInput(ASCIIToUTF16(input_str), false, false);
3695
3696  // Set up a default fetcher with provided results.
3697  net::TestURLFetcher* fetcher =
3698      test_factory_.GetFetcherByID(
3699          SearchProvider::kDefaultProviderURLFetcherID);
3700  ASSERT_TRUE(fetcher);
3701  fetcher->set_response_code(200);
3702  fetcher->SetResponseString("this is a bad non-json response");
3703  fetcher->delegate()->OnURLFetchComplete(fetcher);
3704
3705  RunTillProviderDone();
3706
3707  const ACMatches& matches = provider_->matches();
3708
3709  // Should have exactly one "search what you typed" match
3710  ASSERT_TRUE(matches.size() == 1);
3711  EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
3712  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3713            matches[0].type);
3714}
3715
3716// A basic test that verifies that the XSSI guarded JSON response is parsed
3717// correctly.
3718TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
3719  struct Match {
3720    std::string contents;
3721    AutocompleteMatchType::Type type;
3722  };
3723  const Match kEmptyMatch = {
3724      kNotApplicable, AutocompleteMatchType::NUM_TYPES
3725  };
3726
3727  struct {
3728    const std::string input_text;
3729    const std::string default_provider_response_json;
3730    const Match matches[4];
3731  } cases[] = {
3732    // No XSSI guard.
3733    { "a",
3734      "[\"a\",[\"b\", \"c\"],[],[],"
3735      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3736      "\"google:suggestrelevance\":[1, 2]}]",
3737      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3738        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3739        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3740        kEmptyMatch,
3741      },
3742    },
3743    // Standard XSSI guard - )]}'\n.
3744    { "a",
3745      ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3746      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3747      "\"google:suggestrelevance\":[1, 2]}]",
3748      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3749        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3750        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3751        kEmptyMatch,
3752      },
3753    },
3754    // Modified XSSI guard - contains "[".
3755    { "a",
3756      ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3757      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3758      "\"google:suggestrelevance\":[1, 2]}]",
3759      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3760        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3761        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3762        kEmptyMatch,
3763      },
3764    },
3765  };
3766
3767  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3768    ClearAllResults();
3769    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3770
3771    // Set up a default fetcher with provided results.
3772    net::TestURLFetcher* fetcher =
3773        test_factory_.GetFetcherByID(
3774            SearchProvider::kDefaultProviderURLFetcherID);
3775    ASSERT_TRUE(fetcher);
3776    fetcher->set_response_code(200);
3777    fetcher->SetResponseString(cases[i].default_provider_response_json);
3778    fetcher->delegate()->OnURLFetchComplete(fetcher);
3779
3780    RunTillProviderDone();
3781
3782    const ACMatches& matches = provider_->matches();
3783    // The top match must inline and score as highly as calculated verbatim.
3784    ASSERT_FALSE(matches.empty());
3785    EXPECT_GE(matches[0].relevance, 1300);
3786
3787    SCOPED_TRACE("for case: " + base::IntToString(i));
3788    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3789    size_t j = 0;
3790    // Ensure that the returned matches equal the expectations.
3791    for (; j < matches.size(); ++j) {
3792      SCOPED_TRACE("and match: " + base::IntToString(j));
3793      EXPECT_EQ(cases[i].matches[j].contents,
3794                base::UTF16ToUTF8(matches[j].contents));
3795      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3796    }
3797    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3798      SCOPED_TRACE("and match: " + base::IntToString(j));
3799      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3800      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3801    }
3802  }
3803}
3804
3805// Test that deletion url gets set on an AutocompleteMatch when available for a
3806// personalized query.
3807TEST_F(SearchProviderTest, ParseDeletionUrl) {
3808   struct Match {
3809     std::string contents;
3810     std::string deletion_url;
3811     AutocompleteMatchType::Type type;
3812   };
3813
3814   const Match kEmptyMatch = {
3815       kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
3816   };
3817
3818   const char url[] = "https://www.google.com/complete/deleteitems"
3819       "?delq=ab&client=chrome&deltok=xsrf123";
3820
3821   struct {
3822       const std::string input_text;
3823       const std::string response_json;
3824       const Match matches[4];
3825     } cases[] = {
3826       // A deletion URL on a personalized query should be reflected in the
3827       // resulting AutocompleteMatch.
3828       { "a",
3829         "[\"a\",[\"ab\", \"ac\"],[],[],"
3830         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3831         "\"google:suggestrelevance\":[1, 2],"
3832         "\"google:suggestdetail\":[{\"du\":"
3833         "\"https://www.google.com/complete/deleteitems?delq=ab&client=chrome"
3834         "&deltok=xsrf123\"}, {}]}]",
3835         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3836           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3837           { "ab", url, AutocompleteMatchType::SEARCH_SUGGEST },
3838           kEmptyMatch,
3839         },
3840       },
3841       // Personalized queries without deletion URLs shouldn't cause errors.
3842       { "a",
3843         "[\"a\",[\"ab\", \"ac\"],[],[],"
3844         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3845         "\"google:suggestrelevance\":[1, 2],"
3846         "\"google:suggestdetail\":[{}, {}]}]",
3847         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3848           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3849           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3850           kEmptyMatch,
3851         },
3852       },
3853     };
3854
3855     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3856       QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3857
3858       net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3859           SearchProvider::kDefaultProviderURLFetcherID);
3860       ASSERT_TRUE(fetcher);
3861       fetcher->set_response_code(200);
3862       fetcher->SetResponseString(cases[i].response_json);
3863       fetcher->delegate()->OnURLFetchComplete(fetcher);
3864
3865       RunTillProviderDone();
3866
3867       const ACMatches& matches = provider_->matches();
3868       ASSERT_FALSE(matches.empty());
3869
3870       SCOPED_TRACE("for input with json = " + cases[i].response_json);
3871
3872       for (size_t j = 0; j < matches.size(); ++j) {
3873         const Match& match = cases[i].matches[j];
3874         SCOPED_TRACE(" and match index: " + base::IntToString(j));
3875         EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
3876         EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
3877             "deletion_url"));
3878       }
3879     }
3880}
3881
3882TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3883  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
3884  base::string16 term = term1_.substr(0, term1_.length() - 1);
3885  QueryForInput(term, true, false);
3886  ASSERT_FALSE(provider_->matches().empty());
3887  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3888            provider_->matches()[0].type);
3889  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3890  EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3891
3892  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
3893  term = term1_.substr(0, term1_.length() - 1);
3894  QueryForInput(term, true, false);
3895  ASSERT_FALSE(provider_->matches().empty());
3896  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3897            provider_->matches()[0].type);
3898  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3899  EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3900}
3901
3902TEST_F(SearchProviderTest, CanSendURL) {
3903  TemplateURLData template_url_data;
3904  template_url_data.short_name = ASCIIToUTF16("t");
3905  template_url_data.SetURL("http://www.google.com/{searchTerms}");
3906  template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3907  template_url_data.instant_url = "http://does/not/exist?strk=1";
3908  template_url_data.search_terms_replacement_key = "strk";
3909  template_url_data.id = SEARCH_ENGINE_GOOGLE;
3910  TemplateURL google_template_url(&profile_, template_url_data);
3911
3912  // Create field trial.
3913  base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
3914      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3915  field_trial->group();
3916
3917  // Not signed in.
3918  EXPECT_FALSE(SearchProvider::CanSendURL(
3919      GURL("http://www.google.com/search"),
3920      GURL("https://www.google.com/complete/search"), &google_template_url,
3921      AutocompleteInput::OTHER, &profile_));
3922  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3923  signin->SetAuthenticatedUsername("test");
3924
3925  // All conditions should be met.
3926  EXPECT_TRUE(SearchProvider::CanSendURL(
3927      GURL("http://www.google.com/search"),
3928      GURL("https://www.google.com/complete/search"), &google_template_url,
3929      AutocompleteInput::OTHER, &profile_));
3930
3931  // Not in field trial.
3932  ResetFieldTrialList();
3933  EXPECT_FALSE(SearchProvider::CanSendURL(
3934      GURL("http://www.google.com/search"),
3935      GURL("https://www.google.com/complete/search"), &google_template_url,
3936      AutocompleteInput::OTHER, &profile_));
3937  field_trial = base::FieldTrialList::CreateFieldTrial(
3938      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3939  field_trial->group();
3940
3941  // Invalid page URL.
3942  EXPECT_FALSE(SearchProvider::CanSendURL(
3943      GURL("badpageurl"),
3944      GURL("https://www.google.com/complete/search"), &google_template_url,
3945      AutocompleteInput::OTHER, &profile_));
3946
3947  // Invalid page classification.
3948  EXPECT_FALSE(SearchProvider::CanSendURL(
3949      GURL("http://www.google.com/search"),
3950      GURL("https://www.google.com/complete/search"), &google_template_url,
3951      AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3952      &profile_));
3953
3954  // Invalid page classification.
3955  EXPECT_FALSE(SearchProvider::CanSendURL(
3956      GURL("http://www.google.com/search"),
3957      GURL("https://www.google.com/complete/search"), &google_template_url,
3958      AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3959      &profile_));
3960
3961  // HTTPS page URL on same domain as provider.
3962  EXPECT_TRUE(SearchProvider::CanSendURL(
3963      GURL("https://www.google.com/search"),
3964      GURL("https://www.google.com/complete/search"),
3965      &google_template_url, AutocompleteInput::OTHER, &profile_));
3966
3967  // Non-HTTP[S] page URL on same domain as provider.
3968  EXPECT_FALSE(SearchProvider::CanSendURL(
3969      GURL("ftp://www.google.com/search"),
3970      GURL("https://www.google.com/complete/search"), &google_template_url,
3971      AutocompleteInput::OTHER, &profile_));
3972
3973  // Non-HTTP page URL on different domain.
3974  EXPECT_FALSE(SearchProvider::CanSendURL(
3975      GURL("https://www.notgoogle.com/search"),
3976      GURL("https://www.google.com/complete/search"), &google_template_url,
3977      AutocompleteInput::OTHER, &profile_));
3978
3979  // Non-HTTPS provider.
3980  EXPECT_FALSE(SearchProvider::CanSendURL(
3981      GURL("http://www.google.com/search"),
3982      GURL("http://www.google.com/complete/search"), &google_template_url,
3983      AutocompleteInput::OTHER, &profile_));
3984
3985  // Suggest disabled.
3986  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3987  EXPECT_FALSE(SearchProvider::CanSendURL(
3988      GURL("http://www.google.com/search"),
3989      GURL("https://www.google.com/complete/search"), &google_template_url,
3990      AutocompleteInput::OTHER, &profile_));
3991  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3992
3993  // Incognito.
3994  EXPECT_FALSE(SearchProvider::CanSendURL(
3995      GURL("http://www.google.com/search"),
3996      GURL("https://www.google.com/complete/search"), &google_template_url,
3997      AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile()));
3998
3999  // Tab sync not enabled.
4000  profile_.GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
4001  profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, false);
4002  EXPECT_FALSE(SearchProvider::CanSendURL(
4003      GURL("http://www.google.com/search"),
4004      GURL("https://www.google.com/complete/search"), &google_template_url,
4005      AutocompleteInput::OTHER, &profile_));
4006  profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, true);
4007
4008  // Tab sync is encrypted.
4009  ProfileSyncService* service =
4010      ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
4011  syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
4012  encrypted_types.Put(syncer::SESSIONS);
4013  service->OnEncryptedTypesChanged(encrypted_types, false);
4014  EXPECT_FALSE(SearchProvider::CanSendURL(
4015      GURL("http://www.google.com/search"),
4016      GURL("https://www.google.com/complete/search"), &google_template_url,
4017      AutocompleteInput::OTHER, &profile_));
4018  encrypted_types.Remove(syncer::SESSIONS);
4019  service->OnEncryptedTypesChanged(encrypted_types, false);
4020
4021  // Check that there were no side effects from previous tests.
4022  EXPECT_TRUE(SearchProvider::CanSendURL(
4023      GURL("http://www.google.com/search"),
4024      GURL("https://www.google.com/complete/search"), &google_template_url,
4025      AutocompleteInput::OTHER, &profile_));
4026}
4027
4028TEST_F(SearchProviderTest, TestDeleteMatch) {
4029  AutocompleteMatch match(provider_, 0, true,
4030                          AutocompleteMatchType::SEARCH_SUGGEST);
4031  match.RecordAdditionalInfo(
4032      SearchProvider::kDeletionUrlKey,
4033      "https://www.google.com/complete/deleteitem?q=foo");
4034
4035  // Test a successful deletion request.
4036  provider_->matches_.push_back(match);
4037  provider_->DeleteMatch(match);
4038  EXPECT_FALSE(provider_->deletion_handlers_.empty());
4039  EXPECT_TRUE(provider_->matches_.empty());
4040  // Set up a default fetcher with provided results.
4041  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
4042      SearchProvider::kDeletionURLFetcherID);
4043  ASSERT_TRUE(fetcher);
4044  fetcher->set_response_code(200);
4045  fetcher->delegate()->OnURLFetchComplete(fetcher);
4046  EXPECT_TRUE(provider_->deletion_handlers_.empty());
4047  EXPECT_TRUE(provider_->is_success());
4048
4049  // Test a failing deletion request.
4050  provider_->matches_.push_back(match);
4051  provider_->DeleteMatch(match);
4052  EXPECT_FALSE(provider_->deletion_handlers_.empty());
4053  // Set up a default fetcher with provided results.
4054  fetcher = test_factory_.GetFetcherByID(
4055      SearchProvider::kDeletionURLFetcherID);
4056  ASSERT_TRUE(fetcher);
4057  fetcher->set_response_code(500);
4058  fetcher->delegate()->OnURLFetchComplete(fetcher);
4059  EXPECT_TRUE(provider_->deletion_handlers_.empty());
4060  EXPECT_FALSE(provider_->is_success());
4061}
4062
4063TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
4064  GURL term_url(
4065      AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
4066  profile_.BlockUntilHistoryProcessesPendingRequests();
4067
4068  AutocompleteMatch games;
4069  QueryForInput(ASCIIToUTF16("fla"), false, false);
4070  profile_.BlockUntilHistoryProcessesPendingRequests();
4071  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
4072  ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
4073
4074  size_t matches_before = provider_->matches().size();
4075  provider_->DeleteMatch(games);
4076  EXPECT_EQ(matches_before - 1, provider_->matches().size());
4077
4078  // Process history deletions.
4079  profile_.BlockUntilHistoryProcessesPendingRequests();
4080
4081  // Check that the match is gone.
4082  QueryForInput(ASCIIToUTF16("fla"), false, false);
4083  profile_.BlockUntilHistoryProcessesPendingRequests();
4084  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
4085  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
4086}
4087
4088// Verifies that duplicates are preserved in AddMatchToMap().
4089TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
4090  AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
4091  AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
4092  AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
4093
4094  profile_.BlockUntilHistoryProcessesPendingRequests();
4095  QueryForInput(ASCIIToUTF16("a"), false, false);
4096
4097  // Make sure the default provider's suggest service was queried.
4098  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
4099      SearchProvider::kDefaultProviderURLFetcherID);
4100  ASSERT_TRUE(fetcher);
4101
4102  // Tell the SearchProvider the suggest query is done.
4103  fetcher->set_response_code(200);
4104  fetcher->SetResponseString(
4105      "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
4106      "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
4107      "\"google:verbatimrelevance\":1350}]");
4108  fetcher->delegate()->OnURLFetchComplete(fetcher);
4109  fetcher = NULL;
4110
4111  // Run till the history results complete.
4112  RunTillProviderDone();
4113
4114  AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
4115  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
4116  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
4117  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
4118  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
4119
4120  // Verbatim match duplicates are added such that each one has a higher
4121  // relevance than the previous one.
4122  EXPECT_EQ(2U, verbatim.duplicate_matches.size());
4123
4124  // Other match duplicates are added in descending relevance order.
4125  EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
4126  EXPECT_EQ(1U, match_avid.duplicate_matches.size());
4127
4128  EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
4129}
4130