search_provider_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "base/command_line.h"
8#include "base/metrics/field_trial.h"
9#include "base/prefs/pref_service.h"
10#include "base/run_loop.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "build/build_config.h"
15#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
16#include "chrome/browser/autocomplete/autocomplete_controller.h"
17#include "chrome/browser/autocomplete/autocomplete_input.h"
18#include "chrome/browser/autocomplete/autocomplete_match.h"
19#include "chrome/browser/autocomplete/autocomplete_provider.h"
20#include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
21#include "chrome/browser/autocomplete/history_url_provider.h"
22#include "chrome/browser/history/history_service.h"
23#include "chrome/browser/history/history_service_factory.h"
24#include "chrome/browser/omnibox/omnibox_field_trial.h"
25#include "chrome/browser/search/search.h"
26#include "chrome/browser/search_engines/template_url.h"
27#include "chrome/browser/search_engines/template_url_service.h"
28#include "chrome/browser/search_engines/template_url_service_factory.h"
29#include "chrome/common/chrome_switches.h"
30#include "chrome/common/metrics/variations/variations_util.h"
31#include "chrome/common/pref_names.h"
32#include "chrome/test/base/testing_browser_process.h"
33#include "chrome/test/base/testing_profile.h"
34#include "components/variations/entropy_provider.h"
35#include "content/public/test/test_browser_thread_bundle.h"
36#include "net/url_request/test_url_fetcher_factory.h"
37#include "net/url_request/url_request_status.h"
38#include "testing/gtest/include/gtest/gtest.h"
39
40// SearchProviderTest ---------------------------------------------------------
41
42// The following environment is configured for these tests:
43// . The TemplateURL default_t_url_ is set as the default provider.
44// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
45//   TemplateURL has a valid suggest and search URL.
46// . The URL created by using the search term term1_ with default_t_url_ is
47//   added to history.
48// . The URL created by using the search term keyword_term_ with keyword_t_url_
49//   is added to history.
50// . test_factory_ is set as the URLFetcherFactory.
51class SearchProviderTest : public testing::Test,
52                           public AutocompleteProviderListener {
53 public:
54  struct ResultInfo {
55    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) {
56    }
57    ResultInfo(GURL gurl,
58               AutocompleteMatch::Type result_type,
59               string16 fill_into_edit)
60      : gurl(gurl),
61        result_type(result_type),
62        fill_into_edit(fill_into_edit) {
63    }
64
65    const GURL gurl;
66    const AutocompleteMatch::Type result_type;
67    const string16 fill_into_edit;
68  };
69
70  struct TestData {
71    const string16 input;
72    const size_t num_results;
73    const ResultInfo output[3];
74  };
75
76  SearchProviderTest()
77      : default_t_url_(NULL),
78        term1_(ASCIIToUTF16("term1")),
79        keyword_t_url_(NULL),
80        keyword_term_(ASCIIToUTF16("keyword")),
81        run_loop_(NULL) {
82    ResetFieldTrialList();
83  }
84
85  // See description above class for what this registers.
86  virtual void SetUp() OVERRIDE;
87  virtual void TearDown() OVERRIDE;
88
89  void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
90
91 protected:
92  // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
93  scoped_ptr<base::FieldTrialList> field_trial_list_;
94
95  // Default value used for testing.
96  static const std::string kNotApplicable;
97
98  // Adds a search for |term|, using the engine |t_url| to the history, and
99  // returns the URL for that search.
100  GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count);
101
102  // Looks for a match in |provider_| with |contents| equal to |contents|.
103  // Sets |match| to it if found.  Returns whether |match| was set.
104  bool FindMatchWithContents(const string16& contents,
105                             AutocompleteMatch* match);
106
107  // Looks for a match in |provider_| with destination |url|.  Sets |match| to
108  // it if found.  Returns whether |match| was set.
109  bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
110
111  // AutocompleteProviderListener:
112  // If we're waiting for the provider to finish, this exits the message loop.
113  virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
114
115  // Runs a nested message loop until provider_ is done. The message loop is
116  // exited by way of OnProviderUpdate.
117  void RunTillProviderDone();
118
119  // Invokes Start on provider_, then runs all pending tasks.
120  void QueryForInput(const string16& text,
121                     bool prevent_inline_autocomplete,
122                     bool prefer_keyword);
123
124  // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
125  // non-NULL, sets it to the "what you typed" entry for |text|.
126  void QueryForInputAndSetWYTMatch(const string16& text,
127                                   AutocompleteMatch* wyt_match);
128
129  // Notifies the URLFetcher for the suggest query corresponding to the default
130  // search provider that it's done.
131  // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
132  void FinishDefaultSuggestQuery();
133
134  void ResetFieldTrialList();
135
136  // See description above class for details of these fields.
137  TemplateURL* default_t_url_;
138  const string16 term1_;
139  GURL term1_url_;
140  TemplateURL* keyword_t_url_;
141  const string16 keyword_term_;
142  GURL keyword_url_;
143
144  content::TestBrowserThreadBundle thread_bundle_;
145
146  // URLFetcherFactory implementation registered.
147  net::TestURLFetcherFactory test_factory_;
148
149  // Profile we use.
150  TestingProfile profile_;
151
152  // The provider.
153  scoped_refptr<SearchProvider> provider_;
154
155  // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
156  base::RunLoop* run_loop_;
157
158  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
159};
160
161// static
162const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
163
164void SearchProviderTest::SetUp() {
165  // Make sure that fetchers are automatically ungregistered upon destruction.
166  test_factory_.set_remove_fetcher_on_delete(true);
167
168  // We need both the history service and template url model loaded.
169  ASSERT_TRUE(profile_.CreateHistoryService(true, false));
170  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
171      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
172
173  TemplateURLService* turl_model =
174      TemplateURLServiceFactory::GetForProfile(&profile_);
175
176  turl_model->Load();
177
178  // Reset the default TemplateURL.
179  TemplateURLData data;
180  data.short_name = ASCIIToUTF16("t");
181  data.SetURL("http://defaultturl/{searchTerms}");
182  data.suggestions_url = "http://defaultturl2/{searchTerms}";
183  data.instant_url = "http://does/not/exist?strk=1";
184  data.search_terms_replacement_key = "strk";
185  default_t_url_ = new TemplateURL(&profile_, data);
186  turl_model->Add(default_t_url_);
187  turl_model->SetDefaultSearchProvider(default_t_url_);
188  TemplateURLID default_provider_id = default_t_url_->id();
189  ASSERT_NE(0, default_provider_id);
190
191  // Add url1, with search term term1_.
192  term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
193
194  // Create another TemplateURL.
195  data.short_name = ASCIIToUTF16("k");
196  data.SetKeyword(ASCIIToUTF16("k"));
197  data.SetURL("http://keyword/{searchTerms}");
198  data.suggestions_url = "http://suggest_keyword/{searchTerms}";
199  keyword_t_url_ = new TemplateURL(&profile_, data);
200  turl_model->Add(keyword_t_url_);
201  ASSERT_NE(0, keyword_t_url_->id());
202
203  // Add a page and search term for keyword_t_url_.
204  keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
205
206  // Keywords are updated by the InMemoryHistoryBackend only after the message
207  // has been processed on the history thread. Block until history processes all
208  // requests to ensure the InMemoryDatabase is the state we expect it.
209  profile_.BlockUntilHistoryProcessesPendingRequests();
210
211  provider_ = new SearchProvider(this, &profile_);
212  provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
213}
214
215void SearchProviderTest::TearDown() {
216  base::RunLoop().RunUntilIdle();
217
218  // Shutdown the provider before the profile.
219  provider_ = NULL;
220}
221
222void SearchProviderTest::RunTest(TestData* cases,
223                                 int num_cases,
224                                 bool prefer_keyword) {
225  ACMatches matches;
226  for (int i = 0; i < num_cases; ++i) {
227    AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(),
228                            AutocompleteInput::INVALID_SPEC, false,
229                            prefer_keyword, true,
230                            AutocompleteInput::ALL_MATCHES);
231    provider_->Start(input, false);
232    matches = provider_->matches();
233    string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input +
234        ASCIIToUTF16("; prefer_keyword was: ") +
235        (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
236    EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
237    if (matches.size() == cases[i].num_results) {
238      for (size_t j = 0; j < cases[i].num_results; ++j) {
239        EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
240            diagnostic_details;
241        EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
242            diagnostic_details;
243        EXPECT_EQ(cases[i].output[j].fill_into_edit,
244                  matches[j].fill_into_edit) <<
245            diagnostic_details;
246        // All callers that use this helper function at the moment produce
247        // matches that are always allowed to be the default match.
248        EXPECT_TRUE(matches[j].allowed_to_be_default_match);
249      }
250    }
251  }
252}
253
254void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
255  if (run_loop_ && provider_->done()) {
256    run_loop_->Quit();
257    run_loop_ = NULL;
258  }
259}
260
261void SearchProviderTest::RunTillProviderDone() {
262  if (provider_->done())
263    return;
264
265  base::RunLoop run_loop;
266  run_loop_ = &run_loop;
267  run_loop.Run();
268}
269
270void SearchProviderTest::QueryForInput(const string16& text,
271                                       bool prevent_inline_autocomplete,
272                                       bool prefer_keyword) {
273  // Start a query.
274  AutocompleteInput input(text, string16::npos, string16(), GURL(),
275                          AutocompleteInput::INVALID_SPEC,
276                          prevent_inline_autocomplete, prefer_keyword, true,
277                          AutocompleteInput::ALL_MATCHES);
278  provider_->Start(input, false);
279
280  // RunUntilIdle so that the task scheduled by SearchProvider to create the
281  // URLFetchers runs.
282  base::RunLoop().RunUntilIdle();
283}
284
285void SearchProviderTest::QueryForInputAndSetWYTMatch(
286    const string16& text,
287    AutocompleteMatch* wyt_match) {
288  QueryForInput(text, false, false);
289  profile_.BlockUntilHistoryProcessesPendingRequests();
290  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
291  if (!wyt_match)
292    return;
293  ASSERT_GE(provider_->matches().size(), 1u);
294  EXPECT_TRUE(FindMatchWithDestination(GURL(
295      default_t_url_->url_ref().ReplaceSearchTerms(
296          TemplateURLRef::SearchTermsArgs(text))),
297      wyt_match));
298}
299
300GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
301                                            string16 term,
302                                            int visit_count) {
303  HistoryService* history =
304      HistoryServiceFactory::GetForProfile(&profile_,
305                                           Profile::EXPLICIT_ACCESS);
306  GURL search(t_url->url_ref().ReplaceSearchTerms(
307      TemplateURLRef::SearchTermsArgs(term)));
308  static base::Time last_added_time;
309  last_added_time = std::max(base::Time::Now(),
310      last_added_time + base::TimeDelta::FromMicroseconds(1));
311  history->AddPageWithDetails(search, string16(), visit_count, visit_count,
312      last_added_time, false, history::SOURCE_BROWSED);
313  history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
314  return search;
315}
316
317bool SearchProviderTest::FindMatchWithContents(const string16& contents,
318                                               AutocompleteMatch* match) {
319  for (ACMatches::const_iterator i = provider_->matches().begin();
320       i != provider_->matches().end(); ++i) {
321    if (i->contents == contents) {
322      *match = *i;
323      return true;
324    }
325  }
326  return false;
327}
328
329bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
330                                                  AutocompleteMatch* match) {
331  for (ACMatches::const_iterator i = provider_->matches().begin();
332       i != provider_->matches().end(); ++i) {
333    if (i->destination_url == url) {
334      *match = *i;
335      return true;
336    }
337  }
338  return false;
339}
340
341void SearchProviderTest::FinishDefaultSuggestQuery() {
342  net::TestURLFetcher* default_fetcher =
343      test_factory_.GetFetcherByID(
344          SearchProvider::kDefaultProviderURLFetcherID);
345  ASSERT_TRUE(default_fetcher);
346
347  // Tell the SearchProvider the default suggest query is done.
348  default_fetcher->set_response_code(200);
349  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
350}
351
352void SearchProviderTest::ResetFieldTrialList() {
353  // Destroy the existing FieldTrialList before creating a new one to avoid
354  // a DCHECK.
355  field_trial_list_.reset();
356  field_trial_list_.reset(new base::FieldTrialList(
357      new metrics::SHA1EntropyProvider("foo")));
358  chrome_variations::testing::ClearAllVariationParams();
359  base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
360      "AutocompleteDynamicTrial_0", "DefaultGroup");
361  trial->group();
362}
363
364// Actual Tests ---------------------------------------------------------------
365
366// Make sure we query history for the default provider and a URLFetcher is
367// created for the default provider suggest results.
368TEST_F(SearchProviderTest, QueryDefaultProvider) {
369  string16 term = term1_.substr(0, term1_.length() - 1);
370  QueryForInput(term, false, false);
371
372  // Make sure the default providers suggest service was queried.
373  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
374      SearchProvider::kDefaultProviderURLFetcherID);
375  ASSERT_TRUE(fetcher);
376
377  // And the URL matches what we expected.
378  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
379      TemplateURLRef::SearchTermsArgs(term)));
380  ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
381
382  // Tell the SearchProvider the suggest query is done.
383  fetcher->set_response_code(200);
384  fetcher->delegate()->OnURLFetchComplete(fetcher);
385  fetcher = NULL;
386
387  // Run till the history results complete.
388  RunTillProviderDone();
389
390  // The SearchProvider is done. Make sure it has a result for the history
391  // term term1.
392  AutocompleteMatch term1_match;
393  EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
394  // Term1 should not have a description, it's set later.
395  EXPECT_TRUE(term1_match.description.empty());
396
397  AutocompleteMatch wyt_match;
398  EXPECT_TRUE(FindMatchWithDestination(
399      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
400          TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
401  EXPECT_TRUE(wyt_match.description.empty());
402
403  // The match for term1 should be more relevant than the what you typed match.
404  EXPECT_GT(term1_match.relevance, wyt_match.relevance);
405  // This longer match should be inlineable.
406  EXPECT_TRUE(term1_match.allowed_to_be_default_match);
407  // The what you typed match should be too, of course.
408  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
409}
410
411TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
412  string16 term = term1_.substr(0, term1_.length() - 1);
413  QueryForInput(term, true, false);
414
415  ASSERT_FALSE(provider_->matches().empty());
416  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
417            provider_->matches()[0].type);
418  EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
419}
420
421// Issues a query that matches the registered keyword and makes sure history
422// is queried as well as URLFetchers getting created.
423TEST_F(SearchProviderTest, QueryKeywordProvider) {
424  string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
425  QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
426                false,
427                false);
428
429  // Make sure the default providers suggest service was queried.
430  net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
431      SearchProvider::kDefaultProviderURLFetcherID);
432  ASSERT_TRUE(default_fetcher);
433
434  // Tell the SearchProvider the default suggest query is done.
435  default_fetcher->set_response_code(200);
436  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
437  default_fetcher = NULL;
438
439  // Make sure the keyword providers suggest service was queried.
440  net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
441      SearchProvider::kKeywordProviderURLFetcherID);
442  ASSERT_TRUE(keyword_fetcher);
443
444  // And the URL matches what we expected.
445  GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
446      TemplateURLRef::SearchTermsArgs(term)));
447  ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
448
449  // Tell the SearchProvider the keyword suggest query is done.
450  keyword_fetcher->set_response_code(200);
451  keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
452  keyword_fetcher = NULL;
453
454  // Run till the history results complete.
455  RunTillProviderDone();
456
457  // The SearchProvider is done. Make sure it has a result for the history
458  // term keyword.
459  AutocompleteMatch match;
460  EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
461
462  // The match should have an associated keyword.
463  EXPECT_FALSE(match.keyword.empty());
464
465  // The fill into edit should contain the keyword.
466  EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
467            match.fill_into_edit);
468}
469
470TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
471  // None of the following input strings should be sent to the suggest server,
472  // because they may contain private data.
473  const char* inputs[] = {
474    "username:password",
475    "http://username:password",
476    "https://username:password",
477    "username:password@hostname",
478    "http://username:password@hostname/",
479    "file://filename",
480    "data://data",
481    "unknownscheme:anything",
482    "http://hostname/?query=q",
483    "http://hostname/path#ref",
484    "https://hostname/path",
485  };
486
487  for (size_t i = 0; i < arraysize(inputs); ++i) {
488    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
489    // Make sure the default providers suggest service was not queried.
490    ASSERT_TRUE(test_factory_.GetFetcherByID(
491        SearchProvider::kDefaultProviderURLFetcherID) == NULL);
492    // Run till the history results complete.
493    RunTillProviderDone();
494  }
495}
496
497TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
498  AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
499      &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
500  GURL url = AddSearchToHistory(default_t_url_,
501                                ASCIIToUTF16("docs.google.com"), 1);
502
503  // Add the term as a url.
504  HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
505      AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1,
506                         base::Time::Now(), false, history::SOURCE_BROWSED);
507  profile_.BlockUntilHistoryProcessesPendingRequests();
508
509  AutocompleteMatch wyt_match;
510  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
511                                                      &wyt_match));
512
513  // There should be two matches, one for what you typed, the other for
514  // 'docs.google.com'. The search term should have a lower priority than the
515  // what you typed match.
516  ASSERT_EQ(2u, provider_->matches().size());
517  AutocompleteMatch term_match;
518  EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
519  EXPECT_GT(wyt_match.relevance, term_match.relevance);
520  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
521  EXPECT_TRUE(term_match.allowed_to_be_default_match);
522}
523
524// A multiword search with one visit should not autocomplete until multiple
525// words are typed.
526TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
527  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
528                                   1));
529  profile_.BlockUntilHistoryProcessesPendingRequests();
530
531  AutocompleteMatch wyt_match;
532  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
533                                                      &wyt_match));
534  ASSERT_EQ(2u, provider_->matches().size());
535  AutocompleteMatch term_match;
536  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
537  EXPECT_GT(wyt_match.relevance, term_match.relevance);
538  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
539  EXPECT_TRUE(term_match.allowed_to_be_default_match);
540
541  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
542                                                      &wyt_match));
543  ASSERT_EQ(2u, provider_->matches().size());
544  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
545  EXPECT_GT(term_match.relevance, wyt_match.relevance);
546  EXPECT_TRUE(term_match.allowed_to_be_default_match);
547  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
548}
549
550// A multiword search with more than one visit should autocomplete immediately.
551TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
552  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
553                                   2));
554  profile_.BlockUntilHistoryProcessesPendingRequests();
555
556  AutocompleteMatch wyt_match;
557  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
558                                                      &wyt_match));
559  ASSERT_EQ(2u, provider_->matches().size());
560  AutocompleteMatch term_match;
561  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
562  EXPECT_GT(term_match.relevance, wyt_match.relevance);
563  EXPECT_TRUE(term_match.allowed_to_be_default_match);
564  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
565}
566
567// Autocompletion should work at a word boundary after a space.
568TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
569  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
570                                   2));
571  profile_.BlockUntilHistoryProcessesPendingRequests();
572
573  AutocompleteMatch wyt_match;
574  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
575                                                      &wyt_match));
576  ASSERT_EQ(2u, provider_->matches().size());
577  AutocompleteMatch term_match;
578  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
579  EXPECT_GT(term_match.relevance, wyt_match.relevance);
580  EXPECT_TRUE(term_match.allowed_to_be_default_match);
581  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
582}
583
584// Newer multiword searches should score more highly than older ones.
585TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
586  GURL term_url_a(AddSearchToHistory(default_t_url_,
587                                     ASCIIToUTF16("three searches aaa"), 1));
588  GURL term_url_b(AddSearchToHistory(default_t_url_,
589                                     ASCIIToUTF16("three searches bbb"), 1));
590  profile_.BlockUntilHistoryProcessesPendingRequests();
591
592  AutocompleteMatch wyt_match;
593  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
594                                                      &wyt_match));
595  ASSERT_EQ(3u, provider_->matches().size());
596  AutocompleteMatch term_match_a;
597  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
598  AutocompleteMatch term_match_b;
599  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
600  EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
601  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
602  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
603  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
604  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
605}
606
607// An autocompleted multiword search should not be replaced by a different
608// autocompletion while the user is still typing a valid prefix.
609TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
610  GURL term_url_a(AddSearchToHistory(default_t_url_,
611                                     ASCIIToUTF16("four searches aaa"), 2));
612  GURL term_url_b(AddSearchToHistory(default_t_url_,
613                                     ASCIIToUTF16("four searches bbb"), 1));
614  profile_.BlockUntilHistoryProcessesPendingRequests();
615
616  AutocompleteMatch wyt_match;
617  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
618                                                      &wyt_match));
619  ASSERT_EQ(3u, provider_->matches().size());
620  AutocompleteMatch term_match_a;
621  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
622  AutocompleteMatch term_match_b;
623  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
624  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
625  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
626  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
627  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
628  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
629
630  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
631                                                      &wyt_match));
632  ASSERT_EQ(3u, provider_->matches().size());
633  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
634  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
635  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
636  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
637  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
638  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
639  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
640}
641
642// Non-completable multiword searches should not crowd out single-word searches.
643TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
644  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
645  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
646  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
647  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
648  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
649  profile_.BlockUntilHistoryProcessesPendingRequests();
650
651  AutocompleteMatch wyt_match;
652  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
653                                                      &wyt_match));
654  ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
655  AutocompleteMatch term_match;
656  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
657  EXPECT_GT(term_match.relevance, wyt_match.relevance);
658  EXPECT_TRUE(term_match.allowed_to_be_default_match);
659  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
660}
661
662// Inline autocomplete matches regardless of case differences from the input.
663TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
664  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
665  profile_.BlockUntilHistoryProcessesPendingRequests();
666
667  AutocompleteMatch wyt_match;
668  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
669                                                      &wyt_match));
670  ASSERT_EQ(2u, provider_->matches().size());
671  AutocompleteMatch term_match;
672  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
673  EXPECT_GT(term_match.relevance, wyt_match.relevance);
674  EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
675  EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
676  EXPECT_TRUE(term_match.allowed_to_be_default_match);
677}
678
679// Verifies AutocompleteControllers return results (including keyword
680// results) in the right order and set descriptions for them correctly.
681TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
682  // Add an entry that corresponds to a keyword search with 'term2'.
683  AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
684  profile_.BlockUntilHistoryProcessesPendingRequests();
685
686  AutocompleteController controller(&profile_, NULL,
687      AutocompleteProvider::TYPE_SEARCH);
688  controller.Start(AutocompleteInput(
689      ASCIIToUTF16("k t"), string16::npos, string16(), GURL(),
690      AutocompleteInput::INVALID_SPEC, false, false, true,
691      AutocompleteInput::ALL_MATCHES));
692  const AutocompleteResult& result = controller.result();
693
694  // There should be three matches, one for the keyword history, one for
695  // keyword provider's what-you-typed, and one for the default provider's
696  // what you typed, in that order.
697  ASSERT_EQ(3u, result.size());
698  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
699  EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
700            result.match_at(1).type);
701  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
702            result.match_at(2).type);
703  EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
704  EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
705  EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
706  EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
707  EXPECT_TRUE(result.match_at(2).allowed_to_be_default_match);
708
709  // The two keyword results should come with the keyword we expect.
710  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
711  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
712  // The default provider has a different keyword.  (We don't explicitly
713  // set it during this test, so all we do is assert that it's different.)
714  EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
715
716  // The top result will always have a description.  The third result,
717  // coming from a different provider than the first two, should also.
718  // Whether the second result has one doesn't matter much.  (If it was
719  // missing, people would infer that it's the same search provider as
720  // the one above it.)
721  EXPECT_FALSE(result.match_at(0).description.empty());
722  EXPECT_FALSE(result.match_at(2).description.empty());
723  EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
724}
725
726TEST_F(SearchProviderTest, KeywordVerbatim) {
727  TestData cases[] = {
728    // Test a simple keyword input.
729    { ASCIIToUTF16("k foo"), 2,
730      { ResultInfo(GURL("http://keyword/foo"),
731                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
732                   ASCIIToUTF16("k foo")),
733        ResultInfo(GURL("http://defaultturl/k%20foo"),
734                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
735                   ASCIIToUTF16("k foo") ) } },
736
737    // Make sure extra whitespace after the keyword doesn't change the
738    // keyword verbatim query.
739    { ASCIIToUTF16("k   foo"), 2,
740      { ResultInfo(GURL("http://keyword/foo"),
741                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
742                   ASCIIToUTF16("k foo")),
743        ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
744                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
745                   ASCIIToUTF16("k   foo")) } },
746    // Leading whitespace should be stripped before SearchProvider gets the
747    // input; hence there are no tests here about how it handles those inputs.
748
749    // But whitespace elsewhere in the query string should matter to both
750    // matches.
751    { ASCIIToUTF16("k  foo  bar"), 2,
752      { ResultInfo(GURL("http://keyword/foo%20%20bar"),
753                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
754                   ASCIIToUTF16("k foo  bar")),
755        ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
756                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
757                   ASCIIToUTF16("k  foo  bar")) } },
758    // Note in the above test case we don't test trailing whitespace because
759    // SearchProvider still doesn't handle this well.  See related bugs:
760    // 102690, 99239, 164635.
761
762    // Keywords can be prefixed by certain things that should get ignored
763    // when constructing the keyword match.
764    { ASCIIToUTF16("www.k foo"), 2,
765      { ResultInfo(GURL("http://keyword/foo"),
766                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
767                   ASCIIToUTF16("k foo")),
768        ResultInfo(GURL("http://defaultturl/www.k%20foo"),
769                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
770                   ASCIIToUTF16("www.k foo")) } },
771    { ASCIIToUTF16("http://k foo"), 2,
772      { ResultInfo(GURL("http://keyword/foo"),
773                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
774                   ASCIIToUTF16("k foo")),
775        ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
776                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
777                   ASCIIToUTF16("http://k foo")) } },
778    { ASCIIToUTF16("http://www.k foo"), 2,
779      { ResultInfo(GURL("http://keyword/foo"),
780                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
781                   ASCIIToUTF16("k foo")),
782        ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
783                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
784                   ASCIIToUTF16("http://www.k foo")) } },
785
786    // A keyword with no remaining input shouldn't get a keyword
787    // verbatim match.
788    { ASCIIToUTF16("k"), 1,
789      { ResultInfo(GURL("http://defaultturl/k"),
790                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
791                   ASCIIToUTF16("k")) } },
792    { ASCIIToUTF16("k "), 1,
793      { ResultInfo(GURL("http://defaultturl/k%20"),
794                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
795                   ASCIIToUTF16("k ")) } }
796
797    // The fact that verbatim queries to keyword are handled by KeywordProvider
798    // not SearchProvider is tested in
799    // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
800  };
801
802  // Test not in keyword mode.
803  RunTest(cases, arraysize(cases), false);
804
805  // Test in keyword mode.  (Both modes should give the same result.)
806  RunTest(cases, arraysize(cases), true);
807}
808
809// Ensures command-line flags are reflected in the URLs the search provider
810// generates.
811TEST_F(SearchProviderTest, CommandLineOverrides) {
812  TemplateURLService* turl_model =
813      TemplateURLServiceFactory::GetForProfile(&profile_);
814
815  TemplateURLData data;
816  data.short_name = ASCIIToUTF16("default");
817  data.SetKeyword(data.short_name);
818  data.SetURL("{google:baseURL}{searchTerms}");
819  default_t_url_ = new TemplateURL(&profile_, data);
820  turl_model->Add(default_t_url_);
821  turl_model->SetDefaultSearchProvider(default_t_url_);
822
823  CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
824                                                      "http://www.bar.com/");
825  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
826      switches::kExtraSearchQueryParams, "a=b");
827
828  TestData cases[] = {
829    { ASCIIToUTF16("k a"), 2,
830      { ResultInfo(GURL("http://keyword/a"),
831                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
832                   ASCIIToUTF16("k a")),
833        ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
834                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
835                   ASCIIToUTF16("k a")) } },
836  };
837
838  RunTest(cases, arraysize(cases), false);
839}
840
841// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
842// Also verifies that just the *first* navigational result is listed as a match
843// if suggested relevance scores were not sent.
844TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
845  QueryForInput(ASCIIToUTF16("a.c"), false, false);
846
847  // Make sure the default providers suggest service was queried.
848  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
849      SearchProvider::kDefaultProviderURLFetcherID);
850  ASSERT_TRUE(fetcher);
851
852  // Tell the SearchProvider the suggest query is done.
853  fetcher->set_response_code(200);
854  fetcher->SetResponseString(
855      "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
856      "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
857  fetcher->delegate()->OnURLFetchComplete(fetcher);
858  fetcher = NULL;
859
860  // Run till the history results complete.
861  RunTillProviderDone();
862
863  // Make sure the only match is 'a.com' and it doesn't have a template_url.
864  AutocompleteMatch nav_match;
865  EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
866  EXPECT_TRUE(nav_match.keyword.empty());
867  EXPECT_TRUE(nav_match.allowed_to_be_default_match);
868  EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
869}
870
871// Verifies that the most relevant suggest results are added properly.
872TEST_F(SearchProviderTest, SuggestRelevance) {
873  QueryForInput(ASCIIToUTF16("a"), false, false);
874
875  // Make sure the default provider's suggest service was queried.
876  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
877      SearchProvider::kDefaultProviderURLFetcherID);
878  ASSERT_TRUE(fetcher);
879
880  // Tell the SearchProvider the suggest query is done.
881  fetcher->set_response_code(200);
882  fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
883  fetcher->delegate()->OnURLFetchComplete(fetcher);
884  fetcher = NULL;
885
886  // Run till the history results complete.
887  RunTillProviderDone();
888
889  // Check the expected verbatim and (first 3) suggestions' relative relevances.
890  AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
891  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
892  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
893  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
894  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
895  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
896  EXPECT_GT(verbatim.relevance, match_a1.relevance);
897  EXPECT_GT(match_a1.relevance, match_a2.relevance);
898  EXPECT_GT(match_a2.relevance, match_a3.relevance);
899  EXPECT_TRUE(verbatim.allowed_to_be_default_match);
900  EXPECT_TRUE(match_a1.allowed_to_be_default_match);
901  EXPECT_TRUE(match_a2.allowed_to_be_default_match);
902  EXPECT_TRUE(match_a3.allowed_to_be_default_match);
903}
904
905// Verifies that suggest results with relevance scores are added
906// properly when using the default fetcher.  When adding a new test
907// case to this test, please consider adding it to the tests in
908// KeywordFetcherSuggestRelevance below.
909TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
910  struct DefaultFetcherMatch {
911    std::string contents;
912    bool allowed_to_be_default_match;
913  };
914  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
915  struct {
916    const std::string json;
917    const DefaultFetcherMatch matches[4];
918    const std::string inline_autocompletion;
919  } cases[] = {
920    // Ensure that suggestrelevance scores reorder matches.
921    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
922      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
923      std::string() },
924    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
925       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
926        "\"google:suggestrelevance\":[1, 2]}]",
927      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
928      std::string() },
929
930    // Without suggested relevance scores, we should only allow one
931    // navsuggest result to be be displayed.
932    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
933       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
934      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
935      std::string() },
936
937    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
938    // Negative values will have no effect; the calculated value will be used.
939    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
940                             "\"google:suggestrelevance\":[9998]}]",
941      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
942      std::string() },
943    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
944                             "\"google:suggestrelevance\":[9999]}]",
945      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
946      "1" },
947    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
948                             "\"google:suggestrelevance\":[9999]}]",
949      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
950      "1" },
951    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
952                             "\"google:suggestrelevance\":[9999]}]",
953      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
954      "1" },
955    { "[\"a\",[\"http://a.com\"],[],[],"
956       "{\"google:suggesttype\":[\"NAVIGATION\"],"
957        "\"google:verbatimrelevance\":9999,"
958        "\"google:suggestrelevance\":[9998]}]",
959      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
960      std::string() },
961    { "[\"a\",[\"http://a.com\"],[],[],"
962       "{\"google:suggesttype\":[\"NAVIGATION\"],"
963        "\"google:verbatimrelevance\":9998,"
964        "\"google:suggestrelevance\":[9999]}]",
965      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
966      ".com" },
967    { "[\"a\",[\"http://a.com\"],[],[],"
968       "{\"google:suggesttype\":[\"NAVIGATION\"],"
969        "\"google:verbatimrelevance\":0,"
970        "\"google:suggestrelevance\":[9999]}]",
971      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
972      ".com" },
973    { "[\"a\",[\"http://a.com\"],[],[],"
974       "{\"google:suggesttype\":[\"NAVIGATION\"],"
975        "\"google:verbatimrelevance\":-1,"
976        "\"google:suggestrelevance\":[9999]}]",
977      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
978      ".com" },
979
980    // Ensure that both types of relevance scores reorder matches together.
981    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
982                                     "\"google:verbatimrelevance\":9998}]",
983      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
984      "1" },
985
986    // Ensure that only inlinable matches may be ranked as the highest result.
987    // Ignore all suggested relevance scores if this constraint is violated.
988    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
989      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
990      std::string() },
991    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
992                            "\"google:verbatimrelevance\":0}]",
993      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
994      std::string() },
995    { "[\"a\",[\"http://b.com\"],[],[],"
996       "{\"google:suggesttype\":[\"NAVIGATION\"],"
997        "\"google:suggestrelevance\":[9999]}]",
998      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
999      std::string() },
1000    { "[\"a\",[\"http://b.com\"],[],[],"
1001       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1002        "\"google:suggestrelevance\":[9999],"
1003        "\"google:verbatimrelevance\":0}]",
1004      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1005      std::string() },
1006    { "[\"a\",[\"https://a/\"],[],[],"
1007       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1008        "\"google:suggestrelevance\":[9999]}]",
1009      { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1010      std::string() },
1011
1012    // Ensure that the top result is ranked as highly as calculated verbatim.
1013    // Ignore the suggested verbatim relevance if this constraint is violated.
1014    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1015      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1016      std::string() },
1017    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1018      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1019      std::string() },
1020    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1021                             "\"google:verbatimrelevance\":0}]",
1022      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1023      std::string() },
1024    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1025                                     "\"google:verbatimrelevance\":0}]",
1026      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1027      std::string() },
1028    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1029      "\"google:verbatimrelevance\":2}]",
1030      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1031      std::string() },
1032    { "[\"a\",[\"http://a.com\"],[],[],"
1033       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1034        "\"google:suggestrelevance\":[1],"
1035        "\"google:verbatimrelevance\":0}]",
1036      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1037      std::string() },
1038    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1039       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1040        "\"google:suggestrelevance\":[1, 2],"
1041        "\"google:verbatimrelevance\":0}]",
1042      { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch },
1043      std::string() },
1044
1045    // Ensure that all suggestions are considered, regardless of order.
1046    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1047       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1048      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1049      std::string() },
1050    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1051              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1052              "\"http://h.com\"],[],[],"
1053       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1054                                "\"NAVIGATION\", \"NAVIGATION\","
1055                                "\"NAVIGATION\", \"NAVIGATION\","
1056                                "\"NAVIGATION\"],"
1057        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1058      { { "a", true }, { "h.com", false }, { "g.com", false },
1059        { "f.com", false } },
1060      std::string() },
1061
1062    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1063    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1064      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1065      std::string() },
1066    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1067      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1068      std::string() },
1069    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1070       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1071        "\"google:suggestrelevance\":[1]}]",
1072      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1073      std::string() },
1074    { "[\"a\",[\"http://a1.com\"],[],[],"
1075       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1076       "\"google:suggestrelevance\":[9999, 1]}]",
1077      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1078      std::string() },
1079
1080    // Ensure that all 'verbatim' results are merged with their maximum score.
1081    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1082       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1083      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1084      "2" },
1085    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1086       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1087        "\"google:verbatimrelevance\":0}]",
1088      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1089      "2" },
1090
1091    // Ensure that verbatim is always generated without other suggestions.
1092    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1093    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1094      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1095      std::string() },
1096    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1097      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1098      std::string() },
1099  };
1100
1101  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1102    QueryForInput(ASCIIToUTF16("a"), false, false);
1103    net::TestURLFetcher* fetcher =
1104        test_factory_.GetFetcherByID(
1105            SearchProvider::kDefaultProviderURLFetcherID);
1106    ASSERT_TRUE(fetcher);
1107    fetcher->set_response_code(200);
1108    fetcher->SetResponseString(cases[i].json);
1109    fetcher->delegate()->OnURLFetchComplete(fetcher);
1110    RunTillProviderDone();
1111
1112    const std::string description = "for input with json=" + cases[i].json;
1113    const ACMatches& matches = provider_->matches();
1114    // The top match must inline and score as highly as calculated verbatim.
1115    ASSERT_FALSE(matches.empty());
1116    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1117              matches[0].inline_autocompletion) << description;
1118    EXPECT_GE(matches[0].relevance, 1300) << description;
1119
1120    size_t j = 0;
1121    // Ensure that the returned matches equal the expectations.
1122    for (; j < matches.size(); ++j) {
1123      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1124                matches[j].contents) << description;
1125      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1126                matches[j].allowed_to_be_default_match) << description;
1127    }
1128    // Ensure that no expected matches are missing.
1129    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1130      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1131          "Case # " << i << " " << description;
1132  }
1133}
1134
1135// This test is like DefaultFetcherSuggestRelevance above except it enables
1136// the field trial that causes the omnibox to be willing to reorder matches
1137// to guarantee the top result is a legal default match.  This field trial
1138// causes SearchProvider to allow some constraints to be violated that it
1139// wouldn't normally because the omnibox will fix the problems later.
1140TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1141  struct DefaultFetcherMatch {
1142    std::string contents;
1143    bool allowed_to_be_default_match;
1144  };
1145  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1146  struct {
1147    const std::string json;
1148    const DefaultFetcherMatch matches[4];
1149    const std::string inline_autocompletion;
1150  } cases[] = {
1151    // Ensure that suggestrelevance scores reorder matches.
1152    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1153      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
1154      std::string() },
1155    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1156       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1157        "\"google:suggestrelevance\":[1, 2]}]",
1158      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
1159      std::string() },
1160
1161    // Without suggested relevance scores, we should only allow one
1162    // navsuggest result to be be displayed.
1163    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1164       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1165      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1166      std::string() },
1167
1168    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1169    // Negative values will have no effect; the calculated value will be used.
1170    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1171                             "\"google:suggestrelevance\":[9998]}]",
1172      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
1173      std::string() },
1174    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1175                             "\"google:suggestrelevance\":[9999]}]",
1176      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1177      "1" },
1178    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1179                             "\"google:suggestrelevance\":[9999]}]",
1180      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1181      "1" },
1182    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1183                             "\"google:suggestrelevance\":[9999]}]",
1184      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1185      "1" },
1186    { "[\"a\",[\"http://a.com\"],[],[],"
1187       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1188        "\"google:verbatimrelevance\":9999,"
1189        "\"google:suggestrelevance\":[9998]}]",
1190      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1191      std::string() },
1192    { "[\"a\",[\"http://a.com\"],[],[],"
1193       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1194        "\"google:verbatimrelevance\":9998,"
1195        "\"google:suggestrelevance\":[9999]}]",
1196      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1197      ".com" },
1198    { "[\"a\",[\"http://a.com\"],[],[],"
1199       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1200        "\"google:verbatimrelevance\":0,"
1201        "\"google:suggestrelevance\":[9999]}]",
1202      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1203      ".com" },
1204    { "[\"a\",[\"http://a.com\"],[],[],"
1205       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1206        "\"google:verbatimrelevance\":-1,"
1207        "\"google:suggestrelevance\":[9999]}]",
1208      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1209      ".com" },
1210
1211    // Ensure that both types of relevance scores reorder matches together.
1212    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1213                                     "\"google:verbatimrelevance\":9998}]",
1214      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1215      "1" },
1216
1217    // Allow non-inlineable matches to be the highest-scoring match but,
1218    // if the result set lacks a single inlineable result, abandon suggested
1219    // relevance scores entirely.
1220    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1221      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1222      std::string() },
1223    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1224                            "\"google:verbatimrelevance\":0}]",
1225      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1226      std::string() },
1227    { "[\"a\",[\"http://b.com\"],[],[],"
1228       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1229        "\"google:suggestrelevance\":[9999]}]",
1230      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1231      std::string() },
1232    { "[\"a\",[\"http://b.com\"],[],[],"
1233       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1234        "\"google:suggestrelevance\":[9999],"
1235        "\"google:verbatimrelevance\":0}]",
1236      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1237      std::string() },
1238
1239    // Allow low-scoring matches.
1240    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1241      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1242      "1" },
1243    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1244      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1245      "1" },
1246    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1247                             "\"google:verbatimrelevance\":0}]",
1248      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1249      "1" },
1250    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1251                                     "\"google:verbatimrelevance\":0}]",
1252      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1253      "2" },
1254    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1255      "\"google:verbatimrelevance\":2}]",
1256      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1257      "2" },
1258    { "[\"a\",[\"http://a.com\"],[],[],"
1259       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1260        "\"google:suggestrelevance\":[1],"
1261        "\"google:verbatimrelevance\":0}]",
1262      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1263      ".com" },
1264    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1265       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1266        "\"google:suggestrelevance\":[1, 2],"
1267        "\"google:verbatimrelevance\":0}]",
1268      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1269      "2.com" },
1270
1271    // Ensure that all suggestions are considered, regardless of order.
1272    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1273       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1274      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1275      std::string() },
1276    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1277              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1278              "\"http://h.com\"],[],[],"
1279       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1280                                "\"NAVIGATION\", \"NAVIGATION\","
1281                                "\"NAVIGATION\", \"NAVIGATION\","
1282                                "\"NAVIGATION\"],"
1283        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1284      { { "a", true }, { "h.com", false }, { "g.com", false },
1285        { "f.com", false } },
1286      std::string() },
1287
1288    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1289    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1290      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1291      std::string() },
1292    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1293      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1294      std::string() },
1295    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1296       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1297        "\"google:suggestrelevance\":[1]}]",
1298      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1299      std::string() },
1300    { "[\"a\",[\"http://a1.com\"],[],[],"
1301       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1302       "\"google:suggestrelevance\":[9999, 1]}]",
1303      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1304      std::string() },
1305
1306    // Ensure that all 'verbatim' results are merged with their maximum score.
1307    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1308       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1309      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1310      "2" },
1311    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1312       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1313        "\"google:verbatimrelevance\":0}]",
1314      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1315      "2" },
1316
1317    // Ensure that verbatim is always generated without other suggestions.
1318    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1319    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1320      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1321      std::string() },
1322    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1323      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1324      std::string() },
1325  };
1326
1327  std::map<std::string, std::string> params;
1328  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1329         ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
1330  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1331      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1332  base::FieldTrialList::CreateFieldTrial(
1333      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1334
1335  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1336    QueryForInput(ASCIIToUTF16("a"), false, false);
1337    net::TestURLFetcher* fetcher =
1338        test_factory_.GetFetcherByID(
1339            SearchProvider::kDefaultProviderURLFetcherID);
1340    ASSERT_TRUE(fetcher);
1341    fetcher->set_response_code(200);
1342    fetcher->SetResponseString(cases[i].json);
1343    fetcher->delegate()->OnURLFetchComplete(fetcher);
1344    RunTillProviderDone();
1345
1346    const std::string description = "for input with json=" + cases[i].json;
1347    const ACMatches& matches = provider_->matches();
1348    // The top match must inline and score as highly as calculated verbatim.
1349    ASSERT_FALSE(matches.empty());
1350    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1351              matches[0].inline_autocompletion) << description;
1352
1353    size_t j = 0;
1354    // Ensure that the returned matches equal the expectations.
1355    for (; j < matches.size(); ++j) {
1356      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1357                matches[j].contents) << description;
1358      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1359                matches[j].allowed_to_be_default_match) << description;
1360    }
1361    // Ensure that no expected matches are missing.
1362    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1363      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1364          "Case # " << i << " " << description;
1365  }
1366}
1367
1368// Verifies that suggest results with relevance scores are added
1369// properly when using the keyword fetcher.  This is similar to the
1370// test DefaultFetcherSuggestRelevance above but this uses inputs that
1371// trigger keyword suggestions (i.e., "k a" rather than "a") and has
1372// different expectations (because now the results are a mix of
1373// keyword suggestions and default provider suggestions).  When a new
1374// test is added to this TEST_F, please consider if it would be
1375// appropriate to add to DefaultFetcherSuggestRelevance as well.
1376TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1377  struct KeywordFetcherMatch {
1378    std::string contents;
1379    bool from_keyword;
1380    bool allowed_to_be_default_match;
1381  };
1382  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1383  struct {
1384    const std::string json;
1385    const KeywordFetcherMatch matches[5];
1386    const std::string inline_autocompletion;
1387  } cases[] = {
1388    // Ensure that suggest relevance scores reorder matches and that
1389    // the keyword verbatim (lacking a suggested verbatim score) beats
1390    // the default provider verbatim.
1391    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1392      { { "a",   true,  true },
1393        { "k a", false, true },
1394        { "c",   true,  false },
1395        { "b",   true,  false },
1396        kEmptyMatch },
1397      std::string() },
1398    // Again, check that relevance scores reorder matches, just this
1399    // time with navigation matches.  This also checks that with
1400    // suggested relevance scores we allow multiple navsuggest results.
1401    // It's odd that navsuggest results that come from a keyword
1402    // provider are marked as not a keyword result.  I think this
1403    // comes from them not going to a keyword search engine).
1404    // TODO(mpearson): Investigate the implications (if any) of
1405    // tagging these results appropriately.  If so, do it because it
1406    // makes more sense.
1407    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1408       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1409       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1410      { { "a",     true,  true },
1411        { "d",     true,  false },
1412        { "c.com", false, false },
1413        { "b.com", false, false },
1414        { "k a",   false, true }, },
1415      std::string() },
1416
1417    // Without suggested relevance scores, we should only allow one
1418    // navsuggest result to be be displayed.
1419    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1420       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1421      { { "a",     true,  true },
1422        { "b.com", false, false },
1423        { "k a",   false, true },
1424        kEmptyMatch, kEmptyMatch },
1425      std::string() },
1426
1427    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1428    // Negative values will have no effect; the calculated value will be used.
1429    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1430                             "\"google:suggestrelevance\":[9998]}]",
1431      { { "a",   true,  true },
1432        { "a1",  true,  true },
1433        { "k a", false, true },
1434        kEmptyMatch, kEmptyMatch },
1435      std::string() },
1436    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1437                             "\"google:suggestrelevance\":[9999]}]",
1438      { { "a1",  true,  true },
1439        { "a",   true,  true },
1440        { "k a", false, true },
1441        kEmptyMatch, kEmptyMatch },
1442      "1" },
1443    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1444                             "\"google:suggestrelevance\":[9999]}]",
1445      { { "a1",  true,  true },
1446        { "k a", false, true },
1447        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1448      "1" },
1449    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1450                             "\"google:suggestrelevance\":[9999]}]",
1451      { { "a1",  true,  true },
1452        { "a",   true,  true },
1453        { "k a", false, true },
1454        kEmptyMatch, kEmptyMatch },
1455      "1" },
1456    { "[\"a\",[\"http://a.com\"],[],[],"
1457       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1458        "\"google:verbatimrelevance\":9999,"
1459        "\"google:suggestrelevance\":[9998]}]",
1460      { { "a",     true,  true },
1461        { "a.com", false, true },
1462        { "k a",   false, true },
1463        kEmptyMatch, kEmptyMatch },
1464      std::string() },
1465
1466    // Ensure that both types of relevance scores reorder matches together.
1467    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1468                                     "\"google:verbatimrelevance\":9998}]",
1469      { { "a1",  true,  true },
1470        { "a",   true,  true },
1471        { "a2",  true,  true },
1472        { "k a", false, true },
1473        kEmptyMatch },
1474      "1" },
1475
1476    // Ensure that only inlinable matches may be ranked as the highest result.
1477    // Ignore all suggested relevance scores if this constraint is violated.
1478    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1479      { { "a",   true,  true },
1480        { "b",   true,  false },
1481        { "k a", false, true },
1482        kEmptyMatch, kEmptyMatch },
1483      std::string() },
1484    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1485                            "\"google:verbatimrelevance\":0}]",
1486      { { "a",   true,  true },
1487        { "b",   true,  false },
1488        { "k a", false, true },
1489        kEmptyMatch, kEmptyMatch },
1490      std::string() },
1491    { "[\"a\",[\"http://b.com\"],[],[],"
1492       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1493        "\"google:suggestrelevance\":[9999]}]",
1494      { { "a",     true,  true },
1495        { "b.com", false, false },
1496        { "k a",   false, true },
1497        kEmptyMatch, kEmptyMatch },
1498      std::string() },
1499    { "[\"a\",[\"http://b.com\"],[],[],"
1500       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1501        "\"google:suggestrelevance\":[9999],"
1502        "\"google:verbatimrelevance\":0}]",
1503      { { "a",     true,  true },
1504        { "b.com", false, false },
1505        { "k a",   false, true },
1506        kEmptyMatch, kEmptyMatch },
1507      std::string() },
1508
1509    // Ensure that the top result is ranked as highly as calculated verbatim.
1510    // Ignore the suggested verbatim relevance if this constraint is violated.
1511    // Note that keyword suggestions by default (not in suggested relevance
1512    // mode) score more highly than the default verbatim.
1513    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1514      { { "a",   true,  true },
1515        { "a1",  true,  true },
1516        { "k a", false, true },
1517        kEmptyMatch, kEmptyMatch },
1518      std::string() },
1519    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1520      { { "a",   true,  true },
1521        { "a1",  true,  true },
1522        { "k a", false, true },
1523        kEmptyMatch, kEmptyMatch},
1524      std::string() },
1525    // Continuing the same category of tests, but make sure we keep the
1526    // suggested relevance scores even as we discard the verbatim relevance
1527    // scores.
1528    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1529                             "\"google:verbatimrelevance\":0}]",
1530      { { "a",   true,  true },
1531        { "k a", false, true },
1532        { "a1",   true, true },
1533        kEmptyMatch, kEmptyMatch},
1534      std::string() },
1535    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1536                                     "\"google:verbatimrelevance\":0}]",
1537      { { "a",   true,  true },
1538        { "k a", false, true },
1539        { "a2",  true,  true },
1540        { "a1",  true,  true },
1541        kEmptyMatch },
1542      std::string() },
1543    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1544      "\"google:verbatimrelevance\":2}]",
1545      { { "a",   true,  true },
1546        { "k a", false, true },
1547        { "a2",  true,  true },
1548        { "a1",  true,  true },
1549        kEmptyMatch },
1550      std::string() },
1551
1552    // Ensure that all suggestions are considered, regardless of order.
1553    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1554       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1555      { { "a",   true,  true },
1556        { "k a", false, true },
1557        { "h",   true,  false },
1558        { "g",   true,  false },
1559        { "f",   true,  false } },
1560      std::string() },
1561    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1562              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1563              "\"http://h.com\"],[],[],"
1564       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1565                                "\"NAVIGATION\", \"NAVIGATION\","
1566                                "\"NAVIGATION\", \"NAVIGATION\","
1567                                "\"NAVIGATION\"],"
1568        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1569      { { "a",     true,  true },
1570        { "k a",   false, true },
1571        { "h.com", false, false },
1572        { "g.com", false, false },
1573        { "f.com", false, false } },
1574      std::string() },
1575
1576    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1577    // Note that keyword suggestions by default (not in suggested relevance
1578    // mode) score more highly than the default verbatim.
1579    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1580      { { "a",   true,  true },
1581        { "a1",  true,  true },
1582        { "a2",  true,  true },
1583        { "k a", false, true },
1584        kEmptyMatch },
1585      std::string() },
1586    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1587      { { "a",   true,  true },
1588        { "a1",  true,  true },
1589        { "k a", false, true },
1590        kEmptyMatch, kEmptyMatch},
1591      std::string() },
1592    // In this case, ignored the suggested relevance scores means we keep
1593    // only one navsuggest result.
1594    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1595       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1596        "\"google:suggestrelevance\":[1]}]",
1597      { { "a",      true,  true },
1598        { "a1.com", false, true },
1599        { "k a",    false, true },
1600        kEmptyMatch, kEmptyMatch},
1601      std::string() },
1602    { "[\"a\",[\"http://a1.com\"],[],[],"
1603       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1604       "\"google:suggestrelevance\":[9999, 1]}]",
1605      { { "a",      true,  true },
1606        { "a1.com", false, true },
1607        { "k a",    false, true },
1608        kEmptyMatch, kEmptyMatch},
1609      std::string() },
1610
1611    // Ensure that all 'verbatim' results are merged with their maximum score.
1612    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1613       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1614      { { "a2",  true,  true },
1615        { "a",   true,  true },
1616        { "a1",  true,  true },
1617        { "k a", false, true },
1618        kEmptyMatch },
1619      "2" },
1620    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1621       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1622        "\"google:verbatimrelevance\":0}]",
1623      { { "a2",  true,  true },
1624        { "a",   true,  true },
1625        { "a1",  true,  true },
1626        { "k a", false, true },
1627        kEmptyMatch },
1628      "2" },
1629
1630    // Ensure that verbatim is always generated without other suggestions.
1631    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1632    // (except when suggested relevances are ignored).
1633    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1634      { { "a",   true,  true },
1635        { "k a", false, true },
1636        kEmptyMatch, kEmptyMatch, kEmptyMatch},
1637      std::string() },
1638    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1639      { { "a",   true,  true },
1640        { "k a", false, true },
1641        kEmptyMatch, kEmptyMatch, kEmptyMatch},
1642      std::string() },
1643
1644    // Check that navsuggestions will be demoted below queries.
1645    // (Navsuggestions are not allowed to appear first.)  In the process,
1646    // make sure the navsuggestions still remain in the same order.
1647    // First, check the situation where navsuggest scores more than verbatim
1648    // and there are no query suggestions.
1649    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1650       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1651        "\"google:verbatimrelevance\":9990,"
1652        "\"google:suggestrelevance\":[9998, 9999]}]",
1653      { { "a",      true,  true },
1654        { "a2.com", false, true },
1655        { "a1.com", false, true },
1656        { "k a",    false, true },
1657        kEmptyMatch },
1658      std::string() },
1659    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1660       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1661        "\"google:verbatimrelevance\":9990,"
1662        "\"google:suggestrelevance\":[9999, 9998]}]",
1663      { { "a",      true,  true },
1664        { "a1.com", false, true },
1665        { "a2.com", false, true },
1666        { "k a",    false, true },
1667        kEmptyMatch },
1668      std::string() },
1669    { "[\"a\",[\"https://a/\"],[],[],"
1670       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1671        "\"google:suggestrelevance\":[9999]}]",
1672      { { "a",         true, true },
1673        { "https://a", false, true },
1674        { "k a",       false, true },
1675        kEmptyMatch,
1676        kEmptyMatch },
1677      std::string() },
1678    // Check when navsuggest scores more than verbatim and there is query
1679    // suggestion but it scores lower.
1680    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1681       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1682        "\"google:verbatimrelevance\":9990,"
1683        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1684      { { "a",      true,  true },
1685        { "a2.com", false, true },
1686        { "a1.com", false, true },
1687        { "a3",     true,  true },
1688        { "k a",    false, true } },
1689      std::string() },
1690    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1691       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1692        "\"google:verbatimrelevance\":9990,"
1693        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1694      { { "a",      true,  true },
1695        { "a1.com", false, true },
1696        { "a2.com", false, true },
1697        { "a3",     true,  true },
1698        { "k a",    false, true } },
1699      std::string() },
1700    // Check when navsuggest scores more than a query suggestion.  There is
1701    // a verbatim but it scores lower.
1702    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1703       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1704        "\"google:verbatimrelevance\":9990,"
1705        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1706      { { "a3",     true,  true },
1707        { "a2.com", false, true },
1708        { "a1.com", false, true },
1709        { "a",      true,  true },
1710        { "k a",    false, true } },
1711      "3" },
1712    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1713       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1714        "\"google:verbatimrelevance\":9990,"
1715        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1716      { { "a3",     true,  true },
1717        { "a1.com", false, true },
1718        { "a2.com", false, true },
1719        { "a",      true,  true },
1720        { "k a",    false, true } },
1721      "3" },
1722    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1723       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1724        "\"google:verbatimrelevance\":0,"
1725        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1726      { { "a3",     true,  true },
1727        { "a2.com", false, true },
1728        { "a1.com", false, true },
1729        { "k a",    false, true },
1730        kEmptyMatch },
1731      "3" },
1732    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1733       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1734        "\"google:verbatimrelevance\":0,"
1735        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1736      { { "a3",     true,  true },
1737        { "a1.com", false, true },
1738        { "a2.com", false, true },
1739        { "k a",    false, true },
1740        kEmptyMatch },
1741      "3" },
1742    // Check when there is neither verbatim nor a query suggestion that,
1743    // because we can't demote navsuggestions below a query suggestion,
1744    // we abandon suggested relevance scores entirely.  One consequence is
1745    // that this means we restore the keyword verbatim match.  Note
1746    // that in this case of abandoning suggested relevance scores, we still
1747    // keep the navsuggestions in the same order, but we revert to only allowing
1748    // one navigation to appear because the scores are completely local.
1749    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1750       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1751        "\"google:verbatimrelevance\":0,"
1752        "\"google:suggestrelevance\":[9998, 9999]}]",
1753      { { "a",      true,  true },
1754        { "a2.com", false, true },
1755        { "k a",    false, true },
1756        kEmptyMatch, kEmptyMatch},
1757      std::string() },
1758    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1759       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1760        "\"google:verbatimrelevance\":0,"
1761        "\"google:suggestrelevance\":[9999, 9998]}]",
1762      { { "a",      true,  true },
1763        { "a1.com", false, true },
1764        { "k a",    false, true },
1765        kEmptyMatch, kEmptyMatch},
1766      std::string() },
1767    // More checks that everything works when it's not necessary to demote.
1768    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1769       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1770        "\"google:verbatimrelevance\":9990,"
1771        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1772      { { "a3",     true,  true },
1773        { "a2.com", false, true },
1774        { "a1.com", false, true },
1775        { "a",      true,  true },
1776        { "k a",    false, true } },
1777      "3" },
1778    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1779       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1780        "\"google:verbatimrelevance\":9990,"
1781        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1782      { { "a3",     true,  true },
1783        { "a1.com", false, true },
1784        { "a2.com", false, true },
1785        { "a",      true,  true },
1786        { "k a",    false, true } },
1787      "3" },
1788  };
1789
1790  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1791    QueryForInput(ASCIIToUTF16("k a"), false, true);
1792
1793    // Set up a default fetcher with no results.
1794    net::TestURLFetcher* default_fetcher =
1795        test_factory_.GetFetcherByID(
1796            SearchProvider::kDefaultProviderURLFetcherID);
1797    ASSERT_TRUE(default_fetcher);
1798    default_fetcher->set_response_code(200);
1799    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1800    default_fetcher = NULL;
1801
1802    // Set up a keyword fetcher with provided results.
1803    net::TestURLFetcher* keyword_fetcher =
1804        test_factory_.GetFetcherByID(
1805            SearchProvider::kKeywordProviderURLFetcherID);
1806    ASSERT_TRUE(keyword_fetcher);
1807    keyword_fetcher->set_response_code(200);
1808    keyword_fetcher->SetResponseString(cases[i].json);
1809    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1810    keyword_fetcher = NULL;
1811    RunTillProviderDone();
1812
1813    const std::string description = "for input with json=" + cases[i].json;
1814    const ACMatches& matches = provider_->matches();
1815    // The top match must inline and score as highly as calculated verbatim.
1816    ASSERT_FALSE(matches.empty());
1817    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1818              matches[0].inline_autocompletion) << description;
1819    EXPECT_GE(matches[0].relevance, 1300) << description;
1820
1821    size_t j = 0;
1822    // Ensure that the returned matches equal the expectations.
1823    for (; j < matches.size(); ++j) {
1824      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1825                matches[j].contents) << description;
1826      EXPECT_EQ(cases[i].matches[j].from_keyword,
1827                matches[j].keyword == ASCIIToUTF16("k")) << description;
1828      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1829                matches[j].allowed_to_be_default_match) << description;
1830    }
1831    // Ensure that no expected matches are missing.
1832    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1833      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1834          "Case # " << i << " " << description;
1835  }
1836}
1837
1838TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
1839  // Enable Instant Extended in order to allow an increased number of
1840  // suggestions.
1841  chrome::EnableInstantExtendedAPIForTesting();
1842
1843  // We hardcode the string "term1" below, so ensure that the search term that
1844  // got added to history already is that string.
1845  ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
1846  string16 term = term1_.substr(0, term1_.length() - 1);
1847
1848  AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
1849  profile_.BlockUntilHistoryProcessesPendingRequests();
1850
1851  struct {
1852    const string16 input;
1853    const std::string json;
1854    const std::string matches[6];
1855  } cases[] = {
1856    // The history results outscore the default verbatim score.  term2 has more
1857    // visits so it outscores term1.  The suggestions are still returned since
1858    // they're server-scored.
1859    { term,
1860      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1861       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1862        "\"google:suggestrelevance\":[1, 2, 3]}]",
1863      { "term2", "term1", "term", "a3", "a2", "a1" } },
1864    // Because we already have three suggestions by the time we see the history
1865    // results, they don't get returned.
1866    { term,
1867      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1868       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1869        "\"google:verbatimrelevance\":1450,"
1870        "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
1871      { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
1872    // If we only have two suggestions, we have room for a history result.
1873    { term,
1874      "[\"term\",[\"a1\", \"a2\"],[],[],"
1875       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
1876        "\"google:verbatimrelevance\":1450,"
1877        "\"google:suggestrelevance\":[1430, 1410]}]",
1878      { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
1879    // If we have more than three suggestions, they should all be returned as
1880    // long as we have enough total space for them.
1881    { term,
1882      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1883       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1884        "\"google:verbatimrelevance\":1450,"
1885        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
1886      { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
1887    { term,
1888      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
1889       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
1890                                "\"QUERY\", \"QUERY\"],"
1891        "\"google:verbatimrelevance\":1450,"
1892        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
1893      { "term", "a1", "a2", "a3", "a4", "a5" } },
1894    { term,
1895      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1896       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1897        "\"google:verbatimrelevance\":1450,"
1898        "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
1899      { "term", "a1", "a2", "term2", "a3", "a4" } },
1900    // When the input looks like a URL, we disallow having a query as the
1901    // highest-ranking result.  If the query was provided by a suggestion, we
1902    // reset the suggest scores to enforce this (see
1903    // SearchProvider::UpdateMatches()).  Even if we reset the suggest scores,
1904    // however, we should still allow navsuggestions to be treated as
1905    // server-provided.
1906    { ASCIIToUTF16("a.com"),
1907      "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[],"
1908       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\","
1909                                "\"NAVIGATION\"],"
1910        // A verbatim query for URL-like input scores 850, so the navigation
1911        // scores here should bracket it.
1912        "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]",
1913      { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } },
1914  };
1915
1916  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1917    QueryForInput(cases[i].input, false, false);
1918    net::TestURLFetcher* fetcher =
1919        test_factory_.GetFetcherByID(
1920            SearchProvider::kDefaultProviderURLFetcherID);
1921    ASSERT_TRUE(fetcher);
1922    fetcher->set_response_code(200);
1923    fetcher->SetResponseString(cases[i].json);
1924    fetcher->delegate()->OnURLFetchComplete(fetcher);
1925    RunTillProviderDone();
1926
1927    const std::string description = "for input with json=" + cases[i].json;
1928    const ACMatches& matches = provider_->matches();
1929
1930    // Ensure no extra matches are present.
1931    ASSERT_LE(matches.size(), 6U);
1932
1933    size_t j = 0;
1934    // Ensure that the returned matches equal the expectations.
1935    for (; j < matches.size(); ++j)
1936      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
1937                matches[j].contents) << description;
1938    // Ensure that no expected matches are missing.
1939    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1940      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
1941          "Case # " << i << " " << description;
1942  }
1943}
1944
1945// Verifies suggest relevance behavior for URL input.
1946TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
1947  struct DefaultFetcherUrlInputMatch {
1948    const std::string match_contents;
1949    AutocompleteMatch::Type match_type;
1950    bool allowed_to_be_default_match;
1951  };
1952  const DefaultFetcherUrlInputMatch kEmptyMatch =
1953      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
1954  struct {
1955    const std::string input;
1956    const std::string json;
1957    const DefaultFetcherUrlInputMatch output[4];
1958  } cases[] = {
1959    // Ensure topmost NAVIGATION matches are allowed for URL input.
1960    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
1961                "{\"google:suggesttype\":[\"NAVIGATION\"],"
1962                 "\"google:suggestrelevance\":[9999]}]",
1963      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
1964        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1965        kEmptyMatch, kEmptyMatch } },
1966    { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
1967                "{\"google:suggesttype\":[\"NAVIGATION\"],"
1968                 "\"google:suggestrelevance\":[9999]}]",
1969      { { "https://a.com", AutocompleteMatchType::NAVSUGGEST,            true },
1970        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1971        kEmptyMatch, kEmptyMatch } },
1972
1973    // Ensure topmost SUGGEST matches are not allowed for URL input.
1974    // SearchProvider disregards search and verbatim suggested relevances.
1975    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
1976                "{\"google:suggestrelevance\":[9999]}]",
1977      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1978        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1979        kEmptyMatch, kEmptyMatch } },
1980    { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
1981                "{\"google:suggestrelevance\":[9999]}]",
1982      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1983        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1984        kEmptyMatch, kEmptyMatch } },
1985
1986    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
1987    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
1988                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1989                 "\"google:suggestrelevance\":[9999, 9998]}]",
1990      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
1991        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1992        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1993        kEmptyMatch } },
1994    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
1995                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1996                 "\"google:suggestrelevance\":[9998, 9997],"
1997                 "\"google:verbatimrelevance\":9999}]",
1998      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
1999        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2000        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2001        kEmptyMatch } },
2002
2003    // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
2004    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2005                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2006      "\"google:suggestrelevance\":[9999, 9998]}]",
2007      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2008        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
2009        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2010        kEmptyMatch } },
2011    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2012                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2013                 "\"google:suggestrelevance\":[9998, 9997],"
2014                 "\"google:verbatimrelevance\":9999}]",
2015      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2016        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
2017        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2018        kEmptyMatch } },
2019  };
2020
2021  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2022    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2023    net::TestURLFetcher* fetcher =
2024        test_factory_.GetFetcherByID(
2025            SearchProvider::kDefaultProviderURLFetcherID);
2026    ASSERT_TRUE(fetcher);
2027    fetcher->set_response_code(200);
2028    fetcher->SetResponseString(cases[i].json);
2029    fetcher->delegate()->OnURLFetchComplete(fetcher);
2030    RunTillProviderDone();
2031
2032    size_t j = 0;
2033    const ACMatches& matches = provider_->matches();
2034    // Ensure that the returned matches equal the expectations.
2035    for (; j < matches.size(); ++j) {
2036      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2037                matches[j].contents);
2038      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2039      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2040                matches[j].allowed_to_be_default_match);
2041    }
2042    // Ensure that no expected matches are missing.
2043    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2044      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2045      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2046                cases[i].output[j].match_type);
2047      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2048    }
2049  }
2050}
2051
2052// A basic test that verifies the field trial triggered parsing logic.
2053TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2054  QueryForInput(ASCIIToUTF16("foo"), false, false);
2055
2056  // Make sure the default providers suggest service was queried.
2057  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2058      SearchProvider::kDefaultProviderURLFetcherID);
2059  ASSERT_TRUE(fetcher);
2060
2061  // Tell the SearchProvider the suggest query is done.
2062  fetcher->set_response_code(200);
2063  fetcher->SetResponseString(
2064      "[\"foo\",[\"foo bar\"],[\"\"],[],"
2065      "{\"google:suggesttype\":[\"QUERY\"],"
2066      "\"google:fieldtrialtriggered\":true}]");
2067  fetcher->delegate()->OnURLFetchComplete(fetcher);
2068  fetcher = NULL;
2069
2070  // Run till the history results complete.
2071  RunTillProviderDone();
2072
2073  {
2074    // Check for the match and field trial triggered bits.
2075    AutocompleteMatch match;
2076    EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2077    ProvidersInfo providers_info;
2078    provider_->AddProviderInfo(&providers_info);
2079    ASSERT_EQ(1U, providers_info.size());
2080    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2081    EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2082  }
2083  {
2084    // Reset the session and check that bits are reset.
2085    provider_->ResetSession();
2086    ProvidersInfo providers_info;
2087    provider_->AddProviderInfo(&providers_info);
2088    ASSERT_EQ(1U, providers_info.size());
2089    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2090    EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2091  }
2092}
2093
2094// Verifies inline autocompletion of navigational results.
2095TEST_F(SearchProviderTest, NavigationInline) {
2096  struct {
2097    const std::string input;
2098    const std::string url;
2099    // Test the expected fill_into_edit, which may drop "http://".
2100    // Some cases do not trim "http://" to match from the start of the scheme.
2101    const std::string fill_into_edit;
2102    const std::string inline_autocompletion;
2103    const bool allowed_to_be_default_match;
2104  } cases[] = {
2105    // Do not inline matches that do not contain the input; trim http as needed.
2106    { "x",                    "http://www.abc.com",
2107                                     "www.abc.com",  std::string(), false },
2108    { "https:",               "http://www.abc.com",
2109                                     "www.abc.com",  std::string(), false },
2110    { "abc.com/",             "http://www.abc.com",
2111                                     "www.abc.com",  std::string(), false },
2112    { "http://www.abc.com/a", "http://www.abc.com",
2113                              "http://www.abc.com",  std::string(), false },
2114    { "http://www.abc.com",   "https://www.abc.com",
2115                              "https://www.abc.com", std::string(), false },
2116    { "http://abc.com",       "ftp://abc.com",
2117                              "ftp://abc.com",       std::string(), false },
2118    { "https://www.abc.com",  "http://www.abc.com",
2119                                     "www.abc.com",  std::string(), false },
2120    { "ftp://abc.com",        "http://abc.com",
2121                                     "abc.com",      std::string(), false },
2122
2123    // Do not inline matches with invalid input prefixes; trim http as needed.
2124    { "ttp",                  "http://www.abc.com",
2125                                     "www.abc.com", std::string(), false },
2126    { "://w",                 "http://www.abc.com",
2127                                     "www.abc.com", std::string(), false },
2128    { "ww.",                  "http://www.abc.com",
2129                                     "www.abc.com", std::string(), false },
2130    { ".ab",                  "http://www.abc.com",
2131                                     "www.abc.com", std::string(), false },
2132    { "bc",                   "http://www.abc.com",
2133                                     "www.abc.com", std::string(), false },
2134    { ".com",                 "http://www.abc.com",
2135                                     "www.abc.com", std::string(), false },
2136
2137    // Do not inline matches that omit input domain labels; trim http as needed.
2138    { "www.a",                "http://a.com",
2139                                     "a.com",       std::string(), false },
2140    { "http://www.a",         "http://a.com",
2141                              "http://a.com",       std::string(), false },
2142    { "www.a",                "ftp://a.com",
2143                              "ftp://a.com",        std::string(), false },
2144    { "ftp://www.a",          "ftp://a.com",
2145                              "ftp://a.com",        std::string(), false },
2146
2147    // Input matching but with nothing to inline will not yield an offset, but
2148    // will be allowed to be default.
2149    { "abc.com",              "http://www.abc.com",
2150                                     "www.abc.com", std::string(), true },
2151    { "http://www.abc.com",   "http://www.abc.com",
2152                              "http://www.abc.com", std::string(), true },
2153
2154    // Inline matches when the input is a leading substring of the scheme.
2155    { "h",                    "http://www.abc.com",
2156                              "http://www.abc.com", "ttp://www.abc.com", true },
2157    { "http",                 "http://www.abc.com",
2158                              "http://www.abc.com", "://www.abc.com",    true },
2159
2160    // Inline matches when the input is a leading substring of the full URL.
2161    { "http:",                "http://www.abc.com",
2162                              "http://www.abc.com", "//www.abc.com", true },
2163    { "http://w",             "http://www.abc.com",
2164                              "http://www.abc.com", "ww.abc.com",    true },
2165    { "http://www.",          "http://www.abc.com",
2166                              "http://www.abc.com", "abc.com",       true },
2167    { "http://www.ab",        "http://www.abc.com",
2168                              "http://www.abc.com", "c.com",         true },
2169    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2170                              "http://www.abc.com/path/file.htm?q=x#foo",
2171                                                  "ath/file.htm?q=x#foo",
2172                              true },
2173    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
2174                              "http://abc.com/path/file.htm?q=x#foo",
2175                                              "ath/file.htm?q=x#foo", true},
2176
2177    // Inline matches with valid URLPrefixes; only trim "http://".
2178    { "w",               "http://www.abc.com",
2179                                "www.abc.com", "ww.abc.com", true },
2180    { "www.a",           "http://www.abc.com",
2181                                "www.abc.com", "bc.com",     true },
2182    { "abc",             "http://www.abc.com",
2183                                "www.abc.com", ".com",       true },
2184    { "abc.c",           "http://www.abc.com",
2185                                "www.abc.com", "om",         true },
2186    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
2187                                "www.abc.com/path/file.htm?q=x#foo",
2188                                             "ath/file.htm?q=x#foo", true },
2189    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
2190                                "abc.com/path/file.htm?q=x#foo",
2191                                         "ath/file.htm?q=x#foo", true },
2192
2193    // Inline matches using the maximal URLPrefix components.
2194    { "h",               "http://help.com",
2195                                "help.com", "elp.com",     true },
2196    { "http",            "http://http.com",
2197                                "http.com", ".com",        true },
2198    { "h",               "http://www.help.com",
2199                                "www.help.com", "elp.com", true },
2200    { "http",            "http://www.http.com",
2201                                "www.http.com", ".com",    true },
2202    { "w",               "http://www.www.com",
2203                                "www.www.com",  "ww.com",  true },
2204
2205    // Test similar behavior for the ftp and https schemes.
2206    { "ftp://www.ab",    "ftp://www.abc.com/path/file.htm?q=x#foo",
2207                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2208                                     "c.com/path/file.htm?q=x#foo", true },
2209    { "www.ab",          "ftp://www.abc.com/path/file.htm?q=x#foo",
2210                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2211                                     "c.com/path/file.htm?q=x#foo", true },
2212    { "ab",              "ftp://www.abc.com/path/file.htm?q=x#foo",
2213                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2214                                     "c.com/path/file.htm?q=x#foo", true },
2215    { "ab",              "ftp://abc.com/path/file.htm?q=x#foo",
2216                         "ftp://abc.com/path/file.htm?q=x#foo",
2217                                 "c.com/path/file.htm?q=x#foo",     true },
2218    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
2219                         "https://www.abc.com/path/file.htm?q=x#foo",
2220                                       "c.com/path/file.htm?q=x#foo", true },
2221    { "www.ab",          "https://www.abc.com/path/file.htm?q=x#foo",
2222                         "https://www.abc.com/path/file.htm?q=x#foo",
2223                                       "c.com/path/file.htm?q=x#foo", true },
2224    { "ab",              "https://www.abc.com/path/file.htm?q=x#foo",
2225                         "https://www.abc.com/path/file.htm?q=x#foo",
2226                                       "c.com/path/file.htm?q=x#foo", true },
2227    { "ab",              "https://abc.com/path/file.htm?q=x#foo",
2228                         "https://abc.com/path/file.htm?q=x#foo",
2229                                   "c.com/path/file.htm?q=x#foo", true },
2230
2231    // Forced query input should inline and retain the "?" prefix.
2232    { "?http://www.ab",  "http://www.abc.com",
2233                        "?http://www.abc.com", "c.com", true },
2234    { "?www.ab",         "http://www.abc.com",
2235                               "?www.abc.com", "c.com", true },
2236    { "?ab",             "http://www.abc.com",
2237                               "?www.abc.com", "c.com", true },
2238  };
2239
2240  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2241    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2242    AutocompleteMatch match(
2243        provider_->NavigationToMatch(SearchProvider::NavigationResult(
2244            *provider_.get(), GURL(cases[i].url), string16(), false, 0,
2245            false)));
2246    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2247              match.inline_autocompletion);
2248    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2249    EXPECT_EQ(cases[i].allowed_to_be_default_match,
2250              match.allowed_to_be_default_match);
2251  }
2252}
2253
2254// Verifies that "http://" is not trimmed for input that is a leading substring.
2255TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2256  const string16 input(ASCIIToUTF16("ht"));
2257  const string16 url(ASCIIToUTF16("http://a.com"));
2258  const SearchProvider::NavigationResult result(
2259      *provider_.get(), GURL(url), string16(), false, 0, false);
2260
2261  // Check the offset and strings when inline autocompletion is allowed.
2262  QueryForInput(input, false, false);
2263  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2264  EXPECT_EQ(url, match_inline.fill_into_edit);
2265  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2266  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2267  EXPECT_EQ(url, match_inline.contents);
2268
2269  // Check the same offset and strings when inline autocompletion is prevented.
2270  QueryForInput(input, true, false);
2271  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2272  EXPECT_EQ(url, match_prevent.fill_into_edit);
2273  EXPECT_TRUE(match_prevent.inline_autocompletion.empty());
2274  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2275  EXPECT_EQ(url, match_prevent.contents);
2276}
2277
2278// Verifies that input "w" marks a more significant domain label than "www.".
2279TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2280  QueryForInput(ASCIIToUTF16("w"), false, false);
2281  AutocompleteMatch match(
2282      provider_->NavigationToMatch(SearchProvider::NavigationResult(
2283          *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0,
2284          false)));
2285  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2286  EXPECT_TRUE(match.allowed_to_be_default_match);
2287  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2288  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2289
2290  // Ensure that the match for input "w" is marked on "wow" and not "www".
2291  ASSERT_EQ(3U, match.contents_class.size());
2292  EXPECT_EQ(0U, match.contents_class[0].offset);
2293  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2294            match.contents_class[0].style);
2295  EXPECT_EQ(4U, match.contents_class[1].offset);
2296  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2297            AutocompleteMatch::ACMatchClassification::MATCH,
2298            match.contents_class[1].style);
2299  EXPECT_EQ(5U, match.contents_class[2].offset);
2300  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2301            match.contents_class[2].style);
2302}
2303
2304TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
2305  // TODO(mpearson): Consider expanding this test to explicitly cover
2306  // testing staleness for keyword results.
2307  struct {
2308    const std::string omnibox_input;
2309    const int verbatim_relevance;
2310    // These cached suggestions should already be sorted.
2311    // The particular number 5 as the length of the array is
2312    // unimportant; it's merely enough cached results to fully test
2313    // the functioning of RemoveAllStaleResults().
2314    struct {
2315      const std::string suggestion;
2316      const bool is_navigation_result;
2317      const int relevance;
2318      // |expect_match| is true if this result should survive
2319      // RemoveAllStaleResults() filtering against |omnibox_input| below.
2320      const bool expect_match;
2321    } results[5];
2322  } cases[] = {
2323    // Simple case: multiple query suggestions and no navsuggestions.
2324    // All query suggestions score less than search-what-you-typed and
2325    // thus none should be filtered because none will appear first.
2326    { "x", 1300,
2327      { { "food",         false, 1299, true  },
2328        { "foobar",       false, 1298, true  },
2329        { "crazy",        false, 1297, true  },
2330        { "friend",       false, 1296, true  },
2331        { kNotApplicable, false, 0,    false } } },
2332
2333    // Similarly simple cases, but the query suggestion appears first.
2334    { "f", 1200,
2335      { { "food",         false, 1299, true  },
2336        { "foobar",       false, 1298, true  },
2337        { "crazy",        false, 1297, true  },
2338        { "friend",       false, 1296, true  },
2339        { kNotApplicable, false, 0,    false } } },
2340    { "c", 1200,
2341      { { "food",         false, 1299, false },
2342        { "foobar",       false, 1298, false },
2343        { "crazy",        false, 1297, true  },
2344        { "friend",       false, 1296, true  },
2345        { kNotApplicable, false, 0,    false } } },
2346    { "x", 1200,
2347      { { "food",         false, 1299, false },
2348        { "foobar",       false, 1298, false },
2349        { "crazy",        false, 1297, false },
2350        { "friend",       false, 1296, false },
2351        { kNotApplicable, false, 0,    false } } },
2352
2353    // The same sort of cases, just using a mix of queries and navsuggestions.
2354    { "x", 1300,
2355      { { "http://food.com/",   true,  1299, true },
2356        { "foobar",             false, 1298, true },
2357        { "http://crazy.com/",  true,  1297, true },
2358        { "friend",             false, 1296, true },
2359        { "http://friend.com/", true,  1295, true } } },
2360    { "f", 1200,
2361      { { "http://food.com/",   true,  1299, true },
2362        { "foobar",             false, 1298, true },
2363        { "http://crazy.com/",  true,  1297, true },
2364        { "friend",             false, 1296, true },
2365        { "http://friend.com/", true,  1295, true } } },
2366    { "c", 1200,
2367      { { "http://food.com/",   true,  1299, false },
2368        { "foobar",             false, 1298, false },
2369        { "http://crazy.com/",  true,  1297, true  },
2370        { "friend",             false, 1296, true  },
2371        { "http://friend.com/", true,  1295, true  } } },
2372    { "x", 1200,
2373      { { "http://food.com/",   true,  1299, false },
2374        { "foobar",             false, 1298, false },
2375        { "http://crazy.com/",  true,  1297, false },
2376        { "friend",             false, 1296, false },
2377        { "http://friend.com/", true,  1295, false } } },
2378
2379    // Run the three tests immediately above again, just with verbatim
2380    // suppressed.  Note that in the last case, all results are filtered.
2381    // Because verbatim is also suppressed, SearchProvider will realize
2382    // in UpdateMatches() that it needs to restore verbatim to fulfill
2383    // its constraints.  This restoration does not happen in
2384    // RemoveAllStaleResults() and hence is not tested here.  This restoration
2385    // is tested in the DefaultFetcherSuggestRelevance test.
2386    { "f", 0,
2387      { { "http://food.com/",   true,  1299, true },
2388        { "foobar",             false, 1298, true },
2389        { "http://crazy.com/",  true,  1297, true },
2390        { "friend",             false, 1296, true },
2391        { "http://friend.com/", true,  1295, true } } },
2392    { "c", 0,
2393      { { "http://food.com/",   true,  1299, false },
2394        { "foobar",             false, 1298, false },
2395        { "http://crazy.com/",  true,  1297, true  },
2396        { "friend",             false, 1296, true  },
2397        { "http://friend.com/", true,  1295, true  } } },
2398    { "x", 0,
2399      { { "http://food.com/",   true,  1299, false },
2400        { "foobar",             false, 1298, false },
2401        { "http://crazy.com/",  true,  1297, false },
2402        { "friend",             false, 1296, false },
2403        { "http://friend.com/", true,  1295, false } } },
2404
2405    // The same sort of tests again, just with verbatim with a score
2406    // that would place it in between other suggestions.
2407    { "f", 1290,
2408      { { "http://food.com/",   true,  1299, true },
2409        { "foobar",             false, 1288, true },
2410        { "http://crazy.com/",  true,  1277, true },
2411        { "friend",             false, 1266, true },
2412        { "http://friend.com/", true,  1255, true } } },
2413    { "c", 1290,
2414      { { "http://food.com/",   true,  1299, false },
2415        { "foobar",             false, 1288, true  },
2416        { "http://crazy.com/",  true,  1277, true  },
2417        { "friend",             false, 1266, true  },
2418        { "http://friend.com/", true,  1255, true  } } },
2419    { "c", 1270,
2420      { { "http://food.com/",   true,  1299, false },
2421        { "foobar",             false, 1288, false },
2422        { "http://crazy.com/",  true,  1277, true  },
2423        { "friend",             false, 1266, true  },
2424        { "http://friend.com/", true,  1255, true  } } },
2425    { "x", 1280,
2426      { { "http://food.com/",   true,  1299, false },
2427        { "foobar",             false, 1288, false },
2428        { "http://crazy.com/",  true,  1277, true  },
2429        { "friend",             false, 1266, true  },
2430        { "http://friend.com/", true,  1255, true  } } },
2431  };
2432
2433  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2434    // Initialize cached results for this test case.
2435    provider_->default_results_.verbatim_relevance =
2436        cases[i].verbatim_relevance;
2437    provider_->default_results_.navigation_results.clear();
2438    provider_->default_results_.suggest_results.clear();
2439    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2440      const std::string& suggestion = cases[i].results[j].suggestion;
2441      if (suggestion == kNotApplicable)
2442        break;
2443      if (cases[i].results[j].is_navigation_result) {
2444        provider_->default_results_.navigation_results.push_back(
2445            SearchProvider::NavigationResult(
2446                *provider_.get(), GURL(suggestion), string16(), false,
2447                cases[i].results[j].relevance, false));
2448      } else {
2449        provider_->default_results_.suggest_results.push_back(
2450            SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false,
2451                                          cases[i].results[j].relevance,
2452                                          false, false));
2453      }
2454    }
2455
2456    provider_->input_ = AutocompleteInput(
2457        ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(),
2458        GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
2459        AutocompleteInput::ALL_MATCHES);
2460    provider_->RemoveAllStaleResults();
2461
2462    // Check cached results.
2463    SearchProvider::SuggestResults::const_iterator sug_it =
2464        provider_->default_results_.suggest_results.begin();
2465    const SearchProvider::SuggestResults::const_iterator sug_end =
2466        provider_->default_results_.suggest_results.end();
2467    SearchProvider::NavigationResults::const_iterator nav_it =
2468        provider_->default_results_.navigation_results.begin();
2469    const SearchProvider::NavigationResults::const_iterator nav_end =
2470        provider_->default_results_.navigation_results.end();
2471    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2472      const std::string& suggestion = cases[i].results[j].suggestion;
2473      if (suggestion == kNotApplicable)
2474        continue;
2475      if (!cases[i].results[j].expect_match)
2476        continue;
2477      if (cases[i].results[j].is_navigation_result) {
2478        ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
2479        EXPECT_EQ(suggestion, nav_it->url().spec());
2480        ++nav_it;
2481      } else {
2482        ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
2483        EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
2484        ++sug_it;
2485      }
2486    }
2487    EXPECT_EQ(sug_end, sug_it);
2488    EXPECT_EQ(nav_end, nav_it);
2489  }
2490}
2491
2492// A basic test that verifies the prefetch metadata parsing logic.
2493TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2494  struct Match {
2495    std::string contents;
2496    bool allowed_to_be_prefetched;
2497    AutocompleteMatchType::Type type;
2498    bool from_keyword;
2499  };
2500  const Match kEmptyMatch = { kNotApplicable,
2501                              false,
2502                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2503                              false };
2504
2505  struct {
2506    const std::string input_text;
2507    bool prefer_keyword_provider_results;
2508    const std::string default_provider_response_json;
2509    const std::string keyword_provider_response_json;
2510    const Match matches[5];
2511  } cases[] = {
2512    // Default provider response does not have prefetch details. Ensure that the
2513    // suggestions are not marked as prefetch query.
2514    { "a",
2515      false,
2516      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2517      std::string(),
2518      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2519        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2520        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2521        kEmptyMatch,
2522        kEmptyMatch
2523      },
2524    },
2525    // Ensure that default provider suggest response prefetch details are
2526    // parsed and recorded in AutocompleteMatch.
2527    { "ab",
2528      false,
2529      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2530          "{\"google:clientdata\":{\"phi\": 0},"
2531          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2532          "\"google:suggestrelevance\":[999, 12, 1]}]",
2533      std::string(),
2534      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2535        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
2536        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2537        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2538        kEmptyMatch
2539      },
2540    },
2541    // Default provider suggest response has prefetch details.
2542    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2543    // the same query string. Ensure that the prefetch details from
2544    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2545    { "ab",
2546      false,
2547      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2548          "{\"google:clientdata\":{\"phi\": 0},"
2549          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2550          "\"google:suggestrelevance\":[99, 98]}]",
2551      std::string(),
2552      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2553        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2554        kEmptyMatch,
2555        kEmptyMatch,
2556        kEmptyMatch
2557      },
2558    },
2559    // Default provider response has prefetch details. We prefer keyword
2560    // provider results. Ensure that prefetch bit for a suggestion from the
2561    // default search provider does not get copied onto a higher-scoring match
2562    // for the same query string from the keyword provider.
2563    { "k a",
2564      true,
2565      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2566          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2567          "\"google:suggestrelevance\":[9, 12]}]",
2568      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2569      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2570        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2571        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2572        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2573        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2574      },
2575    }
2576  };
2577
2578  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2579    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
2580                  cases[i].prefer_keyword_provider_results);
2581
2582    // Set up a default fetcher with provided results.
2583    net::TestURLFetcher* fetcher =
2584        test_factory_.GetFetcherByID(
2585            SearchProvider::kDefaultProviderURLFetcherID);
2586    ASSERT_TRUE(fetcher);
2587    fetcher->set_response_code(200);
2588    fetcher->SetResponseString(cases[i].default_provider_response_json);
2589    fetcher->delegate()->OnURLFetchComplete(fetcher);
2590
2591    if (cases[i].prefer_keyword_provider_results) {
2592      // Set up a keyword fetcher with provided results.
2593      net::TestURLFetcher* keyword_fetcher =
2594          test_factory_.GetFetcherByID(
2595              SearchProvider::kKeywordProviderURLFetcherID);
2596      ASSERT_TRUE(keyword_fetcher);
2597      keyword_fetcher->set_response_code(200);
2598      keyword_fetcher->SetResponseString(
2599          cases[i].keyword_provider_response_json);
2600      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2601      keyword_fetcher = NULL;
2602    }
2603
2604    RunTillProviderDone();
2605
2606    const std::string description =
2607        "for input with json =" + cases[i].default_provider_response_json;
2608    const ACMatches& matches = provider_->matches();
2609    // The top match must inline and score as highly as calculated verbatim.
2610    ASSERT_FALSE(matches.empty());
2611    EXPECT_GE(matches[0].relevance, 1300);
2612
2613    // Ensure that the returned matches equal the expectations.
2614    for (size_t j = 0; j < matches.size(); ++j) {
2615      SCOPED_TRACE(description);
2616      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
2617      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2618                SearchProvider::ShouldPrefetch(matches[j]));
2619      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2620      EXPECT_EQ(cases[i].matches[j].from_keyword,
2621                matches[j].keyword == ASCIIToUTF16("k"));
2622    }
2623  }
2624}
2625