search_provider_unittest.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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
1007    // Ensure that the top result is ranked as highly as calculated verbatim.
1008    // Ignore the suggested verbatim relevance if this constraint is violated.
1009    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1010      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1011      std::string() },
1012    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1013      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1014      std::string() },
1015    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1016                             "\"google:verbatimrelevance\":0}]",
1017      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1018      std::string() },
1019    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1020                                     "\"google:verbatimrelevance\":0}]",
1021      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1022      std::string() },
1023    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1024      "\"google:verbatimrelevance\":2}]",
1025      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1026      std::string() },
1027    { "[\"a\",[\"http://a.com\"],[],[],"
1028       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1029        "\"google:suggestrelevance\":[1],"
1030        "\"google:verbatimrelevance\":0}]",
1031      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1032      std::string() },
1033    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1034       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1035        "\"google:suggestrelevance\":[1, 2],"
1036        "\"google:verbatimrelevance\":0}]",
1037      { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch },
1038      std::string() },
1039
1040    // Ensure that all suggestions are considered, regardless of order.
1041    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1042       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1043      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1044      std::string() },
1045    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1046              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1047              "\"http://h.com\"],[],[],"
1048       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1049                                "\"NAVIGATION\", \"NAVIGATION\","
1050                                "\"NAVIGATION\", \"NAVIGATION\","
1051                                "\"NAVIGATION\"],"
1052        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1053      { { "a", true }, { "h.com", false }, { "g.com", false },
1054        { "f.com", false } },
1055      std::string() },
1056
1057    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1058    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1059      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1060      std::string() },
1061    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1062      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1063      std::string() },
1064    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1065       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1066        "\"google:suggestrelevance\":[1]}]",
1067      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1068      std::string() },
1069    { "[\"a\",[\"http://a1.com\"],[],[],"
1070       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1071       "\"google:suggestrelevance\":[9999, 1]}]",
1072      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1073      std::string() },
1074
1075    // Ensure that all 'verbatim' results are merged with their maximum score.
1076    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1077       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1078      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1079      "2" },
1080    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1081       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1082        "\"google:verbatimrelevance\":0}]",
1083      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1084      "2" },
1085
1086    // Ensure that verbatim is always generated without other suggestions.
1087    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1088    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1089      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1090      std::string() },
1091    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1092      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1093      std::string() },
1094  };
1095
1096  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1097    QueryForInput(ASCIIToUTF16("a"), false, false);
1098    net::TestURLFetcher* fetcher =
1099        test_factory_.GetFetcherByID(
1100            SearchProvider::kDefaultProviderURLFetcherID);
1101    ASSERT_TRUE(fetcher);
1102    fetcher->set_response_code(200);
1103    fetcher->SetResponseString(cases[i].json);
1104    fetcher->delegate()->OnURLFetchComplete(fetcher);
1105    RunTillProviderDone();
1106
1107    const std::string description = "for input with json=" + cases[i].json;
1108    const ACMatches& matches = provider_->matches();
1109    // The top match must inline and score as highly as calculated verbatim.
1110    ASSERT_FALSE(matches.empty());
1111    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1112              matches[0].inline_autocompletion) << description;
1113    EXPECT_GE(matches[0].relevance, 1300) << description;
1114
1115    size_t j = 0;
1116    // Ensure that the returned matches equal the expectations.
1117    for (; j < matches.size(); ++j) {
1118      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1119                matches[j].contents) << description;
1120      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1121                matches[j].allowed_to_be_default_match) << description;
1122    }
1123    // Ensure that no expected matches are missing.
1124    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1125      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1126          "Case # " << i << " " << description;
1127  }
1128}
1129
1130// This test is like DefaultFetcherSuggestRelevance above except it enables
1131// the field trial that causes the omnibox to be willing to reorder matches
1132// to guarantee the top result is a legal default match.  This field trial
1133// causes SearchProvider to allow some constraints to be violated that it
1134// wouldn't normally because the omnibox will fix the problems later.
1135TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1136  struct DefaultFetcherMatch {
1137    std::string contents;
1138    bool allowed_to_be_default_match;
1139  };
1140  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1141  struct {
1142    const std::string json;
1143    const DefaultFetcherMatch matches[4];
1144    const std::string inline_autocompletion;
1145  } cases[] = {
1146    // Ensure that suggestrelevance scores reorder matches.
1147    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1148      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
1149      std::string() },
1150    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1151       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1152        "\"google:suggestrelevance\":[1, 2]}]",
1153      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
1154      std::string() },
1155
1156    // Without suggested relevance scores, we should only allow one
1157    // navsuggest result to be be displayed.
1158    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1159       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1160      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1161      std::string() },
1162
1163    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1164    // Negative values will have no effect; the calculated value will be used.
1165    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1166                             "\"google:suggestrelevance\":[9998]}]",
1167      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
1168      std::string() },
1169    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1170                             "\"google:suggestrelevance\":[9999]}]",
1171      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1172      "1" },
1173    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1174                             "\"google:suggestrelevance\":[9999]}]",
1175      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1176      "1" },
1177    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1178                             "\"google:suggestrelevance\":[9999]}]",
1179      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1180      "1" },
1181    { "[\"a\",[\"http://a.com\"],[],[],"
1182       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1183        "\"google:verbatimrelevance\":9999,"
1184        "\"google:suggestrelevance\":[9998]}]",
1185      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1186      std::string() },
1187    { "[\"a\",[\"http://a.com\"],[],[],"
1188       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1189        "\"google:verbatimrelevance\":9998,"
1190        "\"google:suggestrelevance\":[9999]}]",
1191      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1192      ".com" },
1193    { "[\"a\",[\"http://a.com\"],[],[],"
1194       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1195        "\"google:verbatimrelevance\":0,"
1196        "\"google:suggestrelevance\":[9999]}]",
1197      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1198      ".com" },
1199    { "[\"a\",[\"http://a.com\"],[],[],"
1200       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1201        "\"google:verbatimrelevance\":-1,"
1202        "\"google:suggestrelevance\":[9999]}]",
1203      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1204      ".com" },
1205
1206    // Ensure that both types of relevance scores reorder matches together.
1207    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1208                                     "\"google:verbatimrelevance\":9998}]",
1209      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1210      "1" },
1211
1212    // Allow non-inlineable matches to be the highest-scoring match but,
1213    // if the result set lacks a single inlineable result, abandon suggested
1214    // relevance scores entirely.
1215    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1216      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1217      std::string() },
1218    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1219                            "\"google:verbatimrelevance\":0}]",
1220      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1221      std::string() },
1222    { "[\"a\",[\"http://b.com\"],[],[],"
1223       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1224        "\"google:suggestrelevance\":[9999]}]",
1225      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1226      std::string() },
1227    { "[\"a\",[\"http://b.com\"],[],[],"
1228       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1229        "\"google:suggestrelevance\":[9999],"
1230        "\"google:verbatimrelevance\":0}]",
1231      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1232      std::string() },
1233
1234    // Allow low-scoring matches.
1235    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1236      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1237      "1" },
1238    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1239      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1240      "1" },
1241    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1242                             "\"google:verbatimrelevance\":0}]",
1243      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1244      "1" },
1245    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1246                                     "\"google:verbatimrelevance\":0}]",
1247      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1248      "2" },
1249    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1250      "\"google:verbatimrelevance\":2}]",
1251      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1252      "2" },
1253    { "[\"a\",[\"http://a.com\"],[],[],"
1254       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1255        "\"google:suggestrelevance\":[1],"
1256        "\"google:verbatimrelevance\":0}]",
1257      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1258      ".com" },
1259    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1260       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1261        "\"google:suggestrelevance\":[1, 2],"
1262        "\"google:verbatimrelevance\":0}]",
1263      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1264      "2.com" },
1265
1266    // Ensure that all suggestions are considered, regardless of order.
1267    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1268       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1269      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1270      std::string() },
1271    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1272              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1273              "\"http://h.com\"],[],[],"
1274       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1275                                "\"NAVIGATION\", \"NAVIGATION\","
1276                                "\"NAVIGATION\", \"NAVIGATION\","
1277                                "\"NAVIGATION\"],"
1278        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1279      { { "a", true }, { "h.com", false }, { "g.com", false },
1280        { "f.com", false } },
1281      std::string() },
1282
1283    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1284    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1285      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1286      std::string() },
1287    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1288      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1289      std::string() },
1290    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1291       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1292        "\"google:suggestrelevance\":[1]}]",
1293      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1294      std::string() },
1295    { "[\"a\",[\"http://a1.com\"],[],[],"
1296       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1297       "\"google:suggestrelevance\":[9999, 1]}]",
1298      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1299      std::string() },
1300
1301    // Ensure that all 'verbatim' results are merged with their maximum score.
1302    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1303       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1304      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1305      "2" },
1306    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1307       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1308        "\"google:verbatimrelevance\":0}]",
1309      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1310      "2" },
1311
1312    // Ensure that verbatim is always generated without other suggestions.
1313    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1314    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1315      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1316      std::string() },
1317    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1318      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1319      std::string() },
1320  };
1321
1322  std::map<std::string, std::string> params;
1323  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1324         ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
1325  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1326      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1327  base::FieldTrialList::CreateFieldTrial(
1328      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1329
1330  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1331    QueryForInput(ASCIIToUTF16("a"), false, false);
1332    net::TestURLFetcher* fetcher =
1333        test_factory_.GetFetcherByID(
1334            SearchProvider::kDefaultProviderURLFetcherID);
1335    ASSERT_TRUE(fetcher);
1336    fetcher->set_response_code(200);
1337    fetcher->SetResponseString(cases[i].json);
1338    fetcher->delegate()->OnURLFetchComplete(fetcher);
1339    RunTillProviderDone();
1340
1341    const std::string description = "for input with json=" + cases[i].json;
1342    const ACMatches& matches = provider_->matches();
1343    // The top match must inline and score as highly as calculated verbatim.
1344    ASSERT_FALSE(matches.empty());
1345    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1346              matches[0].inline_autocompletion) << description;
1347
1348    size_t j = 0;
1349    // Ensure that the returned matches equal the expectations.
1350    for (; j < matches.size(); ++j) {
1351      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1352                matches[j].contents) << description;
1353      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1354                matches[j].allowed_to_be_default_match) << description;
1355    }
1356    // Ensure that no expected matches are missing.
1357    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1358      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1359          "Case # " << i << " " << description;
1360  }
1361}
1362
1363// Verifies that suggest results with relevance scores are added
1364// properly when using the keyword fetcher.  This is similar to the
1365// test DefaultFetcherSuggestRelevance above but this uses inputs that
1366// trigger keyword suggestions (i.e., "k a" rather than "a") and has
1367// different expectations (because now the results are a mix of
1368// keyword suggestions and default provider suggestions).  When a new
1369// test is added to this TEST_F, please consider if it would be
1370// appropriate to add to DefaultFetcherSuggestRelevance as well.
1371TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1372  struct KeywordFetcherMatch {
1373    std::string contents;
1374    bool from_keyword;
1375    bool allowed_to_be_default_match;
1376  };
1377  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1378  struct {
1379    const std::string json;
1380    const KeywordFetcherMatch matches[5];
1381    const std::string inline_autocompletion;
1382  } cases[] = {
1383    // Ensure that suggest relevance scores reorder matches and that
1384    // the keyword verbatim (lacking a suggested verbatim score) beats
1385    // the default provider verbatim.
1386    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1387      { { "a",   true,  true },
1388        { "k a", false, true },
1389        { "c",   true,  false },
1390        { "b",   true,  false },
1391        kEmptyMatch },
1392      std::string() },
1393    // Again, check that relevance scores reorder matches, just this
1394    // time with navigation matches.  This also checks that with
1395    // suggested relevance scores we allow multiple navsuggest results.
1396    // It's odd that navsuggest results that come from a keyword
1397    // provider are marked as not a keyword result.  I think this
1398    // comes from them not going to a keyword search engine).
1399    // TODO(mpearson): Investigate the implications (if any) of
1400    // tagging these results appropriately.  If so, do it because it
1401    // makes more sense.
1402    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1403       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1404       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1405      { { "a",     true,  true },
1406        { "d",     true,  false },
1407        { "c.com", false, false },
1408        { "b.com", false, false },
1409        { "k a",   false, true }, },
1410      std::string() },
1411
1412    // Without suggested relevance scores, we should only allow one
1413    // navsuggest result to be be displayed.
1414    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1415       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1416      { { "a",     true,  true },
1417        { "b.com", false, false },
1418        { "k a",   false, true },
1419        kEmptyMatch, kEmptyMatch },
1420      std::string() },
1421
1422    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1423    // Negative values will have no effect; the calculated value will be used.
1424    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1425                             "\"google:suggestrelevance\":[9998]}]",
1426      { { "a",   true,  true },
1427        { "a1",  true,  true },
1428        { "k a", false, true },
1429        kEmptyMatch, kEmptyMatch },
1430      std::string() },
1431    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1432                             "\"google:suggestrelevance\":[9999]}]",
1433      { { "a1",  true,  true },
1434        { "a",   true,  true },
1435        { "k a", false, true },
1436        kEmptyMatch, kEmptyMatch },
1437      "1" },
1438    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1439                             "\"google:suggestrelevance\":[9999]}]",
1440      { { "a1",  true,  true },
1441        { "k a", false, true },
1442        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1443      "1" },
1444    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1445                             "\"google:suggestrelevance\":[9999]}]",
1446      { { "a1",  true,  true },
1447        { "a",   true,  true },
1448        { "k a", false, true },
1449        kEmptyMatch, kEmptyMatch },
1450      "1" },
1451    { "[\"a\",[\"http://a.com\"],[],[],"
1452       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1453        "\"google:verbatimrelevance\":9999,"
1454        "\"google:suggestrelevance\":[9998]}]",
1455      { { "a",     true,  true },
1456        { "a.com", false, true },
1457        { "k a",   false, true },
1458        kEmptyMatch, kEmptyMatch },
1459      std::string() },
1460
1461    // Ensure that both types of relevance scores reorder matches together.
1462    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1463                                     "\"google:verbatimrelevance\":9998}]",
1464      { { "a1",  true,  true },
1465        { "a",   true,  true },
1466        { "a2",  true,  true },
1467        { "k a", false, true },
1468        kEmptyMatch },
1469      "1" },
1470
1471    // Ensure that only inlinable matches may be ranked as the highest result.
1472    // Ignore all suggested relevance scores if this constraint is violated.
1473    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1474      { { "a",   true,  true },
1475        { "b",   true,  false },
1476        { "k a", false, true },
1477        kEmptyMatch, kEmptyMatch },
1478      std::string() },
1479    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1480                            "\"google:verbatimrelevance\":0}]",
1481      { { "a",   true,  true },
1482        { "b",   true,  false },
1483        { "k a", false, true },
1484        kEmptyMatch, kEmptyMatch },
1485      std::string() },
1486    { "[\"a\",[\"http://b.com\"],[],[],"
1487       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1488        "\"google:suggestrelevance\":[9999]}]",
1489      { { "a",     true,  true },
1490        { "b.com", false, false },
1491        { "k a",   false, true },
1492        kEmptyMatch, kEmptyMatch },
1493      std::string() },
1494    { "[\"a\",[\"http://b.com\"],[],[],"
1495       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1496        "\"google:suggestrelevance\":[9999],"
1497        "\"google:verbatimrelevance\":0}]",
1498      { { "a",     true,  true },
1499        { "b.com", false, false },
1500        { "k a",   false, true },
1501        kEmptyMatch, kEmptyMatch },
1502      std::string() },
1503
1504    // Ensure that the top result is ranked as highly as calculated verbatim.
1505    // Ignore the suggested verbatim relevance if this constraint is violated.
1506    // Note that keyword suggestions by default (not in suggested relevance
1507    // mode) score more highly than the default verbatim.
1508    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1509      { { "a",   true,  true },
1510        { "a1",  true,  true },
1511        { "k a", false, true },
1512        kEmptyMatch, kEmptyMatch },
1513      std::string() },
1514    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1515      { { "a",   true,  true },
1516        { "a1",  true,  true },
1517        { "k a", false, true },
1518        kEmptyMatch, kEmptyMatch},
1519      std::string() },
1520    // Continuing the same category of tests, but make sure we keep the
1521    // suggested relevance scores even as we discard the verbatim relevance
1522    // scores.
1523    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1524                             "\"google:verbatimrelevance\":0}]",
1525      { { "a",   true,  true },
1526        { "k a", false, true },
1527        { "a1",   true, true },
1528        kEmptyMatch, kEmptyMatch},
1529      std::string() },
1530    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1531                                     "\"google:verbatimrelevance\":0}]",
1532      { { "a",   true,  true },
1533        { "k a", false, true },
1534        { "a2",  true,  true },
1535        { "a1",  true,  true },
1536        kEmptyMatch },
1537      std::string() },
1538    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1539      "\"google:verbatimrelevance\":2}]",
1540      { { "a",   true,  true },
1541        { "k a", false, true },
1542        { "a2",  true,  true },
1543        { "a1",  true,  true },
1544        kEmptyMatch },
1545      std::string() },
1546
1547    // Ensure that all suggestions are considered, regardless of order.
1548    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1549       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1550      { { "a",   true,  true },
1551        { "k a", false, true },
1552        { "h",   true,  false },
1553        { "g",   true,  false },
1554        { "f",   true,  false } },
1555      std::string() },
1556    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1557              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1558              "\"http://h.com\"],[],[],"
1559       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1560                                "\"NAVIGATION\", \"NAVIGATION\","
1561                                "\"NAVIGATION\", \"NAVIGATION\","
1562                                "\"NAVIGATION\"],"
1563        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1564      { { "a",     true,  true },
1565        { "k a",   false, true },
1566        { "h.com", false, false },
1567        { "g.com", false, false },
1568        { "f.com", false, false } },
1569      std::string() },
1570
1571    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1572    // Note that keyword suggestions by default (not in suggested relevance
1573    // mode) score more highly than the default verbatim.
1574    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1575      { { "a",   true,  true },
1576        { "a1",  true,  true },
1577        { "a2",  true,  true },
1578        { "k a", false, true },
1579        kEmptyMatch },
1580      std::string() },
1581    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1582      { { "a",   true,  true },
1583        { "a1",  true,  true },
1584        { "k a", false, true },
1585        kEmptyMatch, kEmptyMatch},
1586      std::string() },
1587    // In this case, ignored the suggested relevance scores means we keep
1588    // only one navsuggest result.
1589    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1590       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1591        "\"google:suggestrelevance\":[1]}]",
1592      { { "a",      true,  true },
1593        { "a1.com", false, true },
1594        { "k a",    false, true },
1595        kEmptyMatch, kEmptyMatch},
1596      std::string() },
1597    { "[\"a\",[\"http://a1.com\"],[],[],"
1598       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1599       "\"google:suggestrelevance\":[9999, 1]}]",
1600      { { "a",      true,  true },
1601        { "a1.com", false, true },
1602        { "k a",    false, true },
1603        kEmptyMatch, kEmptyMatch},
1604      std::string() },
1605
1606    // Ensure that all 'verbatim' results are merged with their maximum score.
1607    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1608       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1609      { { "a2",  true,  true },
1610        { "a",   true,  true },
1611        { "a1",  true,  true },
1612        { "k a", false, true },
1613        kEmptyMatch },
1614      "2" },
1615    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1616       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1617        "\"google:verbatimrelevance\":0}]",
1618      { { "a2",  true,  true },
1619        { "a",   true,  true },
1620        { "a1",  true,  true },
1621        { "k a", false, true },
1622        kEmptyMatch },
1623      "2" },
1624
1625    // Ensure that verbatim is always generated without other suggestions.
1626    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1627    // (except when suggested relevances are ignored).
1628    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1629      { { "a",   true,  true },
1630        { "k a", false, true },
1631        kEmptyMatch, kEmptyMatch, kEmptyMatch},
1632      std::string() },
1633    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1634      { { "a",   true,  true },
1635        { "k a", false, true },
1636        kEmptyMatch, kEmptyMatch, kEmptyMatch},
1637      std::string() },
1638
1639    // Check that navsuggestions will be demoted below queries.
1640    // (Navsuggestions are not allowed to appear first.)  In the process,
1641    // make sure the navsuggestions still remain in the same order.
1642    // First, check the situation where navsuggest scores more than verbatim
1643    // and there are no query suggestions.
1644    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1645       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1646        "\"google:verbatimrelevance\":9990,"
1647        "\"google:suggestrelevance\":[9998, 9999]}]",
1648      { { "a",      true,  true },
1649        { "a2.com", false, true },
1650        { "a1.com", false, true },
1651        { "k a",    false, true },
1652        kEmptyMatch },
1653      std::string() },
1654    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1655       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1656        "\"google:verbatimrelevance\":9990,"
1657        "\"google:suggestrelevance\":[9999, 9998]}]",
1658      { { "a",      true,  true },
1659        { "a1.com", false, true },
1660        { "a2.com", false, true },
1661        { "k a",    false, true },
1662        kEmptyMatch },
1663      std::string() },
1664    // Check when navsuggest scores more than verbatim and there is query
1665    // suggestion but it scores lower.
1666    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1667       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1668        "\"google:verbatimrelevance\":9990,"
1669        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1670      { { "a",      true,  true },
1671        { "a2.com", false, true },
1672        { "a1.com", false, true },
1673        { "a3",     true,  true },
1674        { "k a",    false, true } },
1675      std::string() },
1676    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1677       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1678        "\"google:verbatimrelevance\":9990,"
1679        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1680      { { "a",      true,  true },
1681        { "a1.com", false, true },
1682        { "a2.com", false, true },
1683        { "a3",     true,  true },
1684        { "k a",    false, true } },
1685      std::string() },
1686    // Check when navsuggest scores more than a query suggestion.  There is
1687    // a verbatim but it scores lower.
1688    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1689       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1690        "\"google:verbatimrelevance\":9990,"
1691        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1692      { { "a3",     true,  true },
1693        { "a2.com", false, true },
1694        { "a1.com", false, true },
1695        { "a",      true,  true },
1696        { "k a",    false, true } },
1697      "3" },
1698    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1699       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1700        "\"google:verbatimrelevance\":9990,"
1701        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1702      { { "a3",     true,  true },
1703        { "a1.com", false, true },
1704        { "a2.com", false, true },
1705        { "a",      true,  true },
1706        { "k a",    false, true } },
1707      "3" },
1708    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1709       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1710        "\"google:verbatimrelevance\":0,"
1711        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1712      { { "a3",     true,  true },
1713        { "a2.com", false, true },
1714        { "a1.com", false, true },
1715        { "k a",    false, true },
1716        kEmptyMatch },
1717      "3" },
1718    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1719       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1720        "\"google:verbatimrelevance\":0,"
1721        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1722      { { "a3",     true,  true },
1723        { "a1.com", false, true },
1724        { "a2.com", false, true },
1725        { "k a",    false, true },
1726        kEmptyMatch },
1727      "3" },
1728    // Check when there is neither verbatim nor a query suggestion that,
1729    // because we can't demote navsuggestions below a query suggestion,
1730    // we abandon suggested relevance scores entirely.  One consequence is
1731    // that this means we restore the keyword verbatim match.  Note
1732    // that in this case of abandoning suggested relevance scores, we still
1733    // keep the navsuggestions in the same order, but we revert to only allowing
1734    // one navigation to appear because the scores are completely local.
1735    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1736       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1737        "\"google:verbatimrelevance\":0,"
1738        "\"google:suggestrelevance\":[9998, 9999]}]",
1739      { { "a",      true,  true },
1740        { "a2.com", false, true },
1741        { "k a",    false, true },
1742        kEmptyMatch, kEmptyMatch},
1743      std::string() },
1744    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1745       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1746        "\"google:verbatimrelevance\":0,"
1747        "\"google:suggestrelevance\":[9999, 9998]}]",
1748      { { "a",      true,  true },
1749        { "a1.com", false, true },
1750        { "k a",    false, true },
1751        kEmptyMatch, kEmptyMatch},
1752      std::string() },
1753    // More checks that everything works when it's not necessary to demote.
1754    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1755       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1756        "\"google:verbatimrelevance\":9990,"
1757        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1758      { { "a3",     true,  true },
1759        { "a2.com", false, true },
1760        { "a1.com", false, true },
1761        { "a",      true,  true },
1762        { "k a",    false, true } },
1763      "3" },
1764    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1765       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1766        "\"google:verbatimrelevance\":9990,"
1767        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1768      { { "a3",     true,  true },
1769        { "a1.com", false, true },
1770        { "a2.com", false, true },
1771        { "a",      true,  true },
1772        { "k a",    false, true } },
1773      "3" },
1774  };
1775
1776  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1777    QueryForInput(ASCIIToUTF16("k a"), false, true);
1778
1779    // Set up a default fetcher with no results.
1780    net::TestURLFetcher* default_fetcher =
1781        test_factory_.GetFetcherByID(
1782            SearchProvider::kDefaultProviderURLFetcherID);
1783    ASSERT_TRUE(default_fetcher);
1784    default_fetcher->set_response_code(200);
1785    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1786    default_fetcher = NULL;
1787
1788    // Set up a keyword fetcher with provided results.
1789    net::TestURLFetcher* keyword_fetcher =
1790        test_factory_.GetFetcherByID(
1791            SearchProvider::kKeywordProviderURLFetcherID);
1792    ASSERT_TRUE(keyword_fetcher);
1793    keyword_fetcher->set_response_code(200);
1794    keyword_fetcher->SetResponseString(cases[i].json);
1795    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1796    keyword_fetcher = NULL;
1797    RunTillProviderDone();
1798
1799    const std::string description = "for input with json=" + cases[i].json;
1800    const ACMatches& matches = provider_->matches();
1801    // The top match must inline and score as highly as calculated verbatim.
1802    ASSERT_FALSE(matches.empty());
1803    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1804              matches[0].inline_autocompletion) << description;
1805    EXPECT_GE(matches[0].relevance, 1300) << description;
1806
1807    size_t j = 0;
1808    // Ensure that the returned matches equal the expectations.
1809    for (; j < matches.size(); ++j) {
1810      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1811                matches[j].contents) << description;
1812      EXPECT_EQ(cases[i].matches[j].from_keyword,
1813                matches[j].keyword == ASCIIToUTF16("k")) << description;
1814      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1815                matches[j].allowed_to_be_default_match) << description;
1816    }
1817    // Ensure that no expected matches are missing.
1818    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1819      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1820          "Case # " << i << " " << description;
1821  }
1822}
1823
1824TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
1825  // Enable Instant Extended in order to allow an increased number of
1826  // suggestions.
1827  chrome::EnableInstantExtendedAPIForTesting();
1828
1829  // We hardcode the string "term1" below, so ensure that the search term that
1830  // got added to history already is that string.
1831  ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
1832  string16 term = term1_.substr(0, term1_.length() - 1);
1833
1834  AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
1835  profile_.BlockUntilHistoryProcessesPendingRequests();
1836
1837  struct {
1838    const string16 input;
1839    const std::string json;
1840    const std::string matches[6];
1841  } cases[] = {
1842    // The history results outscore the default verbatim score.  term2 has more
1843    // visits so it outscores term1.  The suggestions are still returned since
1844    // they're server-scored.
1845    { term,
1846      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1847       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1848        "\"google:suggestrelevance\":[1, 2, 3]}]",
1849      { "term2", "term1", "term", "a3", "a2", "a1" } },
1850    // Because we already have three suggestions by the time we see the history
1851    // results, they don't get returned.
1852    { term,
1853      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1854       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1855        "\"google:verbatimrelevance\":1450,"
1856        "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
1857      { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
1858    // If we only have two suggestions, we have room for a history result.
1859    { term,
1860      "[\"term\",[\"a1\", \"a2\"],[],[],"
1861       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
1862        "\"google:verbatimrelevance\":1450,"
1863        "\"google:suggestrelevance\":[1430, 1410]}]",
1864      { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
1865    // If we have more than three suggestions, they should all be returned as
1866    // long as we have enough total space for them.
1867    { term,
1868      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1869       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1870        "\"google:verbatimrelevance\":1450,"
1871        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
1872      { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
1873    { term,
1874      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
1875       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
1876                                "\"QUERY\", \"QUERY\"],"
1877        "\"google:verbatimrelevance\":1450,"
1878        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
1879      { "term", "a1", "a2", "a3", "a4", "a5" } },
1880    { term,
1881      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1882       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1883        "\"google:verbatimrelevance\":1450,"
1884        "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
1885      { "term", "a1", "a2", "term2", "a3", "a4" } },
1886    // When the input looks like a URL, we disallow having a query as the
1887    // highest-ranking result.  If the query was provided by a suggestion, we
1888    // reset the suggest scores to enforce this (see
1889    // SearchProvider::UpdateMatches()).  Even if we reset the suggest scores,
1890    // however, we should still allow navsuggestions to be treated as
1891    // server-provided.
1892    { ASCIIToUTF16("a.com"),
1893      "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[],"
1894       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\","
1895                                "\"NAVIGATION\"],"
1896        // A verbatim query for URL-like input scores 850, so the navigation
1897        // scores here should bracket it.
1898        "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]",
1899      { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } },
1900  };
1901
1902  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1903    QueryForInput(cases[i].input, false, false);
1904    net::TestURLFetcher* fetcher =
1905        test_factory_.GetFetcherByID(
1906            SearchProvider::kDefaultProviderURLFetcherID);
1907    ASSERT_TRUE(fetcher);
1908    fetcher->set_response_code(200);
1909    fetcher->SetResponseString(cases[i].json);
1910    fetcher->delegate()->OnURLFetchComplete(fetcher);
1911    RunTillProviderDone();
1912
1913    const std::string description = "for input with json=" + cases[i].json;
1914    const ACMatches& matches = provider_->matches();
1915
1916    // Ensure no extra matches are present.
1917    ASSERT_LE(matches.size(), 6U);
1918
1919    size_t j = 0;
1920    // Ensure that the returned matches equal the expectations.
1921    for (; j < matches.size(); ++j)
1922      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
1923                matches[j].contents) << description;
1924    // Ensure that no expected matches are missing.
1925    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1926      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
1927          "Case # " << i << " " << description;
1928  }
1929}
1930
1931// Verifies suggest relevance behavior for URL input.
1932TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
1933  struct DefaultFetcherUrlInputMatch {
1934    const std::string match_contents;
1935    AutocompleteMatch::Type match_type;
1936    bool allowed_to_be_default_match;
1937  };
1938  const DefaultFetcherUrlInputMatch kEmptyMatch =
1939      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
1940  struct {
1941    const std::string input;
1942    const std::string json;
1943    const DefaultFetcherUrlInputMatch output[4];
1944  } cases[] = {
1945    // Ensure topmost NAVIGATION matches are allowed for URL input.
1946    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
1947                "{\"google:suggesttype\":[\"NAVIGATION\"],"
1948                 "\"google:suggestrelevance\":[9999]}]",
1949      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
1950        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1951        kEmptyMatch, kEmptyMatch } },
1952
1953    // Ensure topmost SUGGEST matches are not allowed for URL input.
1954    // SearchProvider disregards search and verbatim suggested relevances.
1955    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
1956                "{\"google:suggestrelevance\":[9999]}]",
1957      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1958        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1959        kEmptyMatch, kEmptyMatch } },
1960    { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
1961                "{\"google:suggestrelevance\":[9999]}]",
1962      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1963        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1964        kEmptyMatch, kEmptyMatch } },
1965
1966    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
1967    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
1968                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1969                 "\"google:suggestrelevance\":[9999, 9998]}]",
1970      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
1971        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1972        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1973        kEmptyMatch } },
1974    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
1975                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1976                 "\"google:suggestrelevance\":[9998, 9997],"
1977                 "\"google:verbatimrelevance\":9999}]",
1978      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
1979        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1980        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1981        kEmptyMatch } },
1982
1983    // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
1984    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
1985                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1986      "\"google:suggestrelevance\":[9999, 9998]}]",
1987      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1988        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
1989        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1990        kEmptyMatch } },
1991    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
1992                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1993                 "\"google:suggestrelevance\":[9998, 9997],"
1994                 "\"google:verbatimrelevance\":9999}]",
1995      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
1996        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
1997        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
1998        kEmptyMatch } },
1999  };
2000
2001  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2002    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2003    net::TestURLFetcher* fetcher =
2004        test_factory_.GetFetcherByID(
2005            SearchProvider::kDefaultProviderURLFetcherID);
2006    ASSERT_TRUE(fetcher);
2007    fetcher->set_response_code(200);
2008    fetcher->SetResponseString(cases[i].json);
2009    fetcher->delegate()->OnURLFetchComplete(fetcher);
2010    RunTillProviderDone();
2011
2012    size_t j = 0;
2013    const ACMatches& matches = provider_->matches();
2014    // Ensure that the returned matches equal the expectations.
2015    for (; j < matches.size(); ++j) {
2016      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2017                matches[j].contents);
2018      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2019      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2020                matches[j].allowed_to_be_default_match);
2021    }
2022    // Ensure that no expected matches are missing.
2023    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2024      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2025      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2026                cases[i].output[j].match_type);
2027      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2028    }
2029  }
2030}
2031
2032// A basic test that verifies the field trial triggered parsing logic.
2033TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2034  QueryForInput(ASCIIToUTF16("foo"), false, false);
2035
2036  // Make sure the default providers suggest service was queried.
2037  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2038      SearchProvider::kDefaultProviderURLFetcherID);
2039  ASSERT_TRUE(fetcher);
2040
2041  // Tell the SearchProvider the suggest query is done.
2042  fetcher->set_response_code(200);
2043  fetcher->SetResponseString(
2044      "[\"foo\",[\"foo bar\"],[\"\"],[],"
2045      "{\"google:suggesttype\":[\"QUERY\"],"
2046      "\"google:fieldtrialtriggered\":true}]");
2047  fetcher->delegate()->OnURLFetchComplete(fetcher);
2048  fetcher = NULL;
2049
2050  // Run till the history results complete.
2051  RunTillProviderDone();
2052
2053  {
2054    // Check for the match and field trial triggered bits.
2055    AutocompleteMatch match;
2056    EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2057    ProvidersInfo providers_info;
2058    provider_->AddProviderInfo(&providers_info);
2059    ASSERT_EQ(1U, providers_info.size());
2060    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2061    EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2062  }
2063  {
2064    // Reset the session and check that bits are reset.
2065    provider_->ResetSession();
2066    ProvidersInfo providers_info;
2067    provider_->AddProviderInfo(&providers_info);
2068    ASSERT_EQ(1U, providers_info.size());
2069    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2070    EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2071  }
2072}
2073
2074// Verifies inline autocompletion of navigational results.
2075TEST_F(SearchProviderTest, NavigationInline) {
2076  struct {
2077    const std::string input;
2078    const std::string url;
2079    // Test the expected fill_into_edit, which may drop "http://".
2080    // Some cases do not trim "http://" to match from the start of the scheme.
2081    const std::string fill_into_edit;
2082    const std::string inline_autocompletion;
2083    const bool allowed_to_be_default_match;
2084  } cases[] = {
2085    // Do not inline matches that do not contain the input; trim http as needed.
2086    { "x",                    "http://www.abc.com",
2087                                     "www.abc.com",  std::string(), false },
2088    { "https:",               "http://www.abc.com",
2089                                     "www.abc.com",  std::string(), false },
2090    { "abc.com/",             "http://www.abc.com",
2091                                     "www.abc.com",  std::string(), false },
2092    { "http://www.abc.com/a", "http://www.abc.com",
2093                              "http://www.abc.com",  std::string(), false },
2094    { "http://www.abc.com",   "https://www.abc.com",
2095                              "https://www.abc.com", std::string(), false },
2096    { "http://abc.com",       "ftp://abc.com",
2097                              "ftp://abc.com",       std::string(), false },
2098    { "https://www.abc.com",  "http://www.abc.com",
2099                                     "www.abc.com",  std::string(), false },
2100    { "ftp://abc.com",        "http://abc.com",
2101                                     "abc.com",      std::string(), false },
2102
2103    // Do not inline matches with invalid input prefixes; trim http as needed.
2104    { "ttp",                  "http://www.abc.com",
2105                                     "www.abc.com", std::string(), false },
2106    { "://w",                 "http://www.abc.com",
2107                                     "www.abc.com", std::string(), false },
2108    { "ww.",                  "http://www.abc.com",
2109                                     "www.abc.com", std::string(), false },
2110    { ".ab",                  "http://www.abc.com",
2111                                     "www.abc.com", std::string(), false },
2112    { "bc",                   "http://www.abc.com",
2113                                     "www.abc.com", std::string(), false },
2114    { ".com",                 "http://www.abc.com",
2115                                     "www.abc.com", std::string(), false },
2116
2117    // Do not inline matches that omit input domain labels; trim http as needed.
2118    { "www.a",                "http://a.com",
2119                                     "a.com",       std::string(), false },
2120    { "http://www.a",         "http://a.com",
2121                              "http://a.com",       std::string(), false },
2122    { "www.a",                "ftp://a.com",
2123                              "ftp://a.com",        std::string(), false },
2124    { "ftp://www.a",          "ftp://a.com",
2125                              "ftp://a.com",        std::string(), false },
2126
2127    // Input matching but with nothing to inline will not yield an offset.
2128    { "abc.com",              "http://www.abc.com",
2129                                     "www.abc.com", std::string(), false },
2130    { "http://www.abc.com",   "http://www.abc.com",
2131                              "http://www.abc.com", std::string(), false },
2132
2133    // Inline matches when the input is a leading substring of the scheme.
2134    { "h",                    "http://www.abc.com",
2135                              "http://www.abc.com", "ttp://www.abc.com", true },
2136    { "http",                 "http://www.abc.com",
2137                              "http://www.abc.com", "://www.abc.com",    true },
2138
2139    // Inline matches when the input is a leading substring of the full URL.
2140    { "http:",                "http://www.abc.com",
2141                              "http://www.abc.com", "//www.abc.com", true },
2142    { "http://w",             "http://www.abc.com",
2143                              "http://www.abc.com", "ww.abc.com",    true },
2144    { "http://www.",          "http://www.abc.com",
2145                              "http://www.abc.com", "abc.com",       true },
2146    { "http://www.ab",        "http://www.abc.com",
2147                              "http://www.abc.com", "c.com",         true },
2148    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2149                              "http://www.abc.com/path/file.htm?q=x#foo",
2150                                                  "ath/file.htm?q=x#foo",
2151                              true },
2152    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
2153                              "http://abc.com/path/file.htm?q=x#foo",
2154                                              "ath/file.htm?q=x#foo", true},
2155
2156    // Inline matches with valid URLPrefixes; only trim "http://".
2157    { "w",               "http://www.abc.com",
2158                                "www.abc.com", "ww.abc.com", true },
2159    { "www.a",           "http://www.abc.com",
2160                                "www.abc.com", "bc.com",     true },
2161    { "abc",             "http://www.abc.com",
2162                                "www.abc.com", ".com",       true },
2163    { "abc.c",           "http://www.abc.com",
2164                                "www.abc.com", "om",         true },
2165    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
2166                                "www.abc.com/path/file.htm?q=x#foo",
2167                                             "ath/file.htm?q=x#foo", true },
2168    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
2169                                "abc.com/path/file.htm?q=x#foo",
2170                                         "ath/file.htm?q=x#foo", true },
2171
2172    // Inline matches using the maximal URLPrefix components.
2173    { "h",               "http://help.com",
2174                                "help.com", "elp.com",     true },
2175    { "http",            "http://http.com",
2176                                "http.com", ".com",        true },
2177    { "h",               "http://www.help.com",
2178                                "www.help.com", "elp.com", true },
2179    { "http",            "http://www.http.com",
2180                                "www.http.com", ".com",    true },
2181    { "w",               "http://www.www.com",
2182                                "www.www.com",  "ww.com",  true },
2183
2184    // Test similar behavior for the ftp and https schemes.
2185    { "ftp://www.ab",    "ftp://www.abc.com/path/file.htm?q=x#foo",
2186                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2187                                     "c.com/path/file.htm?q=x#foo", true },
2188    { "www.ab",          "ftp://www.abc.com/path/file.htm?q=x#foo",
2189                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2190                                     "c.com/path/file.htm?q=x#foo", true },
2191    { "ab",              "ftp://www.abc.com/path/file.htm?q=x#foo",
2192                         "ftp://www.abc.com/path/file.htm?q=x#foo",
2193                                     "c.com/path/file.htm?q=x#foo", true },
2194    { "ab",              "ftp://abc.com/path/file.htm?q=x#foo",
2195                         "ftp://abc.com/path/file.htm?q=x#foo",
2196                                 "c.com/path/file.htm?q=x#foo",     true },
2197    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
2198                         "https://www.abc.com/path/file.htm?q=x#foo",
2199                                       "c.com/path/file.htm?q=x#foo", true },
2200    { "www.ab",          "https://www.abc.com/path/file.htm?q=x#foo",
2201                         "https://www.abc.com/path/file.htm?q=x#foo",
2202                                       "c.com/path/file.htm?q=x#foo", true },
2203    { "ab",              "https://www.abc.com/path/file.htm?q=x#foo",
2204                         "https://www.abc.com/path/file.htm?q=x#foo",
2205                                       "c.com/path/file.htm?q=x#foo", true },
2206    { "ab",              "https://abc.com/path/file.htm?q=x#foo",
2207                         "https://abc.com/path/file.htm?q=x#foo",
2208                                   "c.com/path/file.htm?q=x#foo", true },
2209
2210    // Forced query input should inline and retain the "?" prefix.
2211    { "?http://www.ab",  "http://www.abc.com",
2212                        "?http://www.abc.com", "c.com", true },
2213    { "?www.ab",         "http://www.abc.com",
2214                               "?www.abc.com", "c.com", true },
2215    { "?ab",             "http://www.abc.com",
2216                               "?www.abc.com", "c.com", true },
2217  };
2218
2219  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2220    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2221    AutocompleteMatch match(
2222        provider_->NavigationToMatch(SearchProvider::NavigationResult(
2223            *provider_.get(), GURL(cases[i].url), string16(), false, 0,
2224            false)));
2225    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2226              match.inline_autocompletion);
2227    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2228    EXPECT_EQ(cases[i].allowed_to_be_default_match,
2229              match.allowed_to_be_default_match);
2230  }
2231}
2232
2233// Verifies that "http://" is not trimmed for input that is a leading substring.
2234TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2235  const string16 input(ASCIIToUTF16("ht"));
2236  const string16 url(ASCIIToUTF16("http://a.com"));
2237  const SearchProvider::NavigationResult result(
2238      *provider_.get(), GURL(url), string16(), false, 0, false);
2239
2240  // Check the offset and strings when inline autocompletion is allowed.
2241  QueryForInput(input, false, false);
2242  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2243  EXPECT_EQ(url, match_inline.fill_into_edit);
2244  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2245  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2246  EXPECT_EQ(url, match_inline.contents);
2247
2248  // Check the same offset and strings when inline autocompletion is prevented.
2249  QueryForInput(input, true, false);
2250  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2251  EXPECT_EQ(url, match_prevent.fill_into_edit);
2252  EXPECT_TRUE(match_prevent.inline_autocompletion.empty());
2253  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2254  EXPECT_EQ(url, match_prevent.contents);
2255}
2256
2257// Verifies that input "w" marks a more significant domain label than "www.".
2258TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2259  QueryForInput(ASCIIToUTF16("w"), false, false);
2260  AutocompleteMatch match(
2261      provider_->NavigationToMatch(SearchProvider::NavigationResult(
2262          *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0,
2263          false)));
2264  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2265  EXPECT_TRUE(match.allowed_to_be_default_match);
2266  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2267  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2268
2269  // Ensure that the match for input "w" is marked on "wow" and not "www".
2270  ASSERT_EQ(3U, match.contents_class.size());
2271  EXPECT_EQ(0U, match.contents_class[0].offset);
2272  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2273            match.contents_class[0].style);
2274  EXPECT_EQ(4U, match.contents_class[1].offset);
2275  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2276            AutocompleteMatch::ACMatchClassification::MATCH,
2277            match.contents_class[1].style);
2278  EXPECT_EQ(5U, match.contents_class[2].offset);
2279  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2280            match.contents_class[2].style);
2281}
2282
2283TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
2284  // TODO(mpearson): Consider expanding this test to explicitly cover
2285  // testing staleness for keyword results.
2286  struct {
2287    const std::string omnibox_input;
2288    const int verbatim_relevance;
2289    // These cached suggestions should already be sorted.
2290    // The particular number 5 as the length of the array is
2291    // unimportant; it's merely enough cached results to fully test
2292    // the functioning of RemoveAllStaleResults().
2293    struct {
2294      const std::string suggestion;
2295      const bool is_navigation_result;
2296      const int relevance;
2297      // |expect_match| is true if this result should survive
2298      // RemoveAllStaleResults() filtering against |omnibox_input| below.
2299      const bool expect_match;
2300    } results[5];
2301  } cases[] = {
2302    // Simple case: multiple query suggestions and no navsuggestions.
2303    // All query suggestions score less than search-what-you-typed and
2304    // thus none should be filtered because none will appear first.
2305    { "x", 1300,
2306      { { "food",         false, 1299, true  },
2307        { "foobar",       false, 1298, true  },
2308        { "crazy",        false, 1297, true  },
2309        { "friend",       false, 1296, true  },
2310        { kNotApplicable, false, 0,    false } } },
2311
2312    // Similarly simple cases, but the query suggestion appears first.
2313    { "f", 1200,
2314      { { "food",         false, 1299, true  },
2315        { "foobar",       false, 1298, true  },
2316        { "crazy",        false, 1297, true  },
2317        { "friend",       false, 1296, true  },
2318        { kNotApplicable, false, 0,    false } } },
2319    { "c", 1200,
2320      { { "food",         false, 1299, false },
2321        { "foobar",       false, 1298, false },
2322        { "crazy",        false, 1297, true  },
2323        { "friend",       false, 1296, true  },
2324        { kNotApplicable, false, 0,    false } } },
2325    { "x", 1200,
2326      { { "food",         false, 1299, false },
2327        { "foobar",       false, 1298, false },
2328        { "crazy",        false, 1297, false },
2329        { "friend",       false, 1296, false },
2330        { kNotApplicable, false, 0,    false } } },
2331
2332    // The same sort of cases, just using a mix of queries and navsuggestions.
2333    { "x", 1300,
2334      { { "http://food.com/",   true,  1299, true },
2335        { "foobar",             false, 1298, true },
2336        { "http://crazy.com/",  true,  1297, true },
2337        { "friend",             false, 1296, true },
2338        { "http://friend.com/", true,  1295, true } } },
2339    { "f", 1200,
2340      { { "http://food.com/",   true,  1299, true },
2341        { "foobar",             false, 1298, true },
2342        { "http://crazy.com/",  true,  1297, true },
2343        { "friend",             false, 1296, true },
2344        { "http://friend.com/", true,  1295, true } } },
2345    { "c", 1200,
2346      { { "http://food.com/",   true,  1299, false },
2347        { "foobar",             false, 1298, false },
2348        { "http://crazy.com/",  true,  1297, true  },
2349        { "friend",             false, 1296, true  },
2350        { "http://friend.com/", true,  1295, true  } } },
2351    { "x", 1200,
2352      { { "http://food.com/",   true,  1299, false },
2353        { "foobar",             false, 1298, false },
2354        { "http://crazy.com/",  true,  1297, false },
2355        { "friend",             false, 1296, false },
2356        { "http://friend.com/", true,  1295, false } } },
2357
2358    // Run the three tests immediately above again, just with verbatim
2359    // suppressed.  Note that in the last case, all results are filtered.
2360    // Because verbatim is also suppressed, SearchProvider will realize
2361    // in UpdateMatches() that it needs to restore verbatim to fulfill
2362    // its constraints.  This restoration does not happen in
2363    // RemoveAllStaleResults() and hence is not tested here.  This restoration
2364    // is tested in the DefaultFetcherSuggestRelevance test.
2365    { "f", 0,
2366      { { "http://food.com/",   true,  1299, true },
2367        { "foobar",             false, 1298, true },
2368        { "http://crazy.com/",  true,  1297, true },
2369        { "friend",             false, 1296, true },
2370        { "http://friend.com/", true,  1295, true } } },
2371    { "c", 0,
2372      { { "http://food.com/",   true,  1299, false },
2373        { "foobar",             false, 1298, false },
2374        { "http://crazy.com/",  true,  1297, true  },
2375        { "friend",             false, 1296, true  },
2376        { "http://friend.com/", true,  1295, true  } } },
2377    { "x", 0,
2378      { { "http://food.com/",   true,  1299, false },
2379        { "foobar",             false, 1298, false },
2380        { "http://crazy.com/",  true,  1297, false },
2381        { "friend",             false, 1296, false },
2382        { "http://friend.com/", true,  1295, false } } },
2383
2384    // The same sort of tests again, just with verbatim with a score
2385    // that would place it in between other suggestions.
2386    { "f", 1290,
2387      { { "http://food.com/",   true,  1299, true },
2388        { "foobar",             false, 1288, true },
2389        { "http://crazy.com/",  true,  1277, true },
2390        { "friend",             false, 1266, true },
2391        { "http://friend.com/", true,  1255, true } } },
2392    { "c", 1290,
2393      { { "http://food.com/",   true,  1299, false },
2394        { "foobar",             false, 1288, true  },
2395        { "http://crazy.com/",  true,  1277, true  },
2396        { "friend",             false, 1266, true  },
2397        { "http://friend.com/", true,  1255, true  } } },
2398    { "c", 1270,
2399      { { "http://food.com/",   true,  1299, false },
2400        { "foobar",             false, 1288, false },
2401        { "http://crazy.com/",  true,  1277, true  },
2402        { "friend",             false, 1266, true  },
2403        { "http://friend.com/", true,  1255, true  } } },
2404    { "x", 1280,
2405      { { "http://food.com/",   true,  1299, false },
2406        { "foobar",             false, 1288, false },
2407        { "http://crazy.com/",  true,  1277, true  },
2408        { "friend",             false, 1266, true  },
2409        { "http://friend.com/", true,  1255, true  } } },
2410  };
2411
2412  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2413    // Initialize cached results for this test case.
2414    provider_->default_results_.verbatim_relevance =
2415        cases[i].verbatim_relevance;
2416    provider_->default_results_.navigation_results.clear();
2417    provider_->default_results_.suggest_results.clear();
2418    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2419      const std::string& suggestion = cases[i].results[j].suggestion;
2420      if (suggestion == kNotApplicable)
2421        break;
2422      if (cases[i].results[j].is_navigation_result) {
2423        provider_->default_results_.navigation_results.push_back(
2424            SearchProvider::NavigationResult(
2425                *provider_.get(), GURL(suggestion), string16(), false,
2426                cases[i].results[j].relevance, false));
2427      } else {
2428        provider_->default_results_.suggest_results.push_back(
2429            SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false,
2430                                          cases[i].results[j].relevance,
2431                                          false, false));
2432      }
2433    }
2434
2435    provider_->input_ = AutocompleteInput(
2436        ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(),
2437        GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
2438        AutocompleteInput::ALL_MATCHES);
2439    provider_->RemoveAllStaleResults();
2440
2441    // Check cached results.
2442    SearchProvider::SuggestResults::const_iterator sug_it =
2443        provider_->default_results_.suggest_results.begin();
2444    const SearchProvider::SuggestResults::const_iterator sug_end =
2445        provider_->default_results_.suggest_results.end();
2446    SearchProvider::NavigationResults::const_iterator nav_it =
2447        provider_->default_results_.navigation_results.begin();
2448    const SearchProvider::NavigationResults::const_iterator nav_end =
2449        provider_->default_results_.navigation_results.end();
2450    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2451      const std::string& suggestion = cases[i].results[j].suggestion;
2452      if (suggestion == kNotApplicable)
2453        continue;
2454      if (!cases[i].results[j].expect_match)
2455        continue;
2456      if (cases[i].results[j].is_navigation_result) {
2457        ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
2458        EXPECT_EQ(suggestion, nav_it->url().spec());
2459        ++nav_it;
2460      } else {
2461        ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
2462        EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
2463        ++sug_it;
2464      }
2465    }
2466    EXPECT_EQ(sug_end, sug_it);
2467    EXPECT_EQ(nav_end, nav_it);
2468  }
2469}
2470
2471// A basic test that verifies the prefetch metadata parsing logic.
2472TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2473  struct Match {
2474    std::string contents;
2475    bool allowed_to_be_prefetched;
2476    AutocompleteMatchType::Type type;
2477    bool from_keyword;
2478  };
2479  const Match kEmptyMatch = { kNotApplicable,
2480                              false,
2481                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2482                              false };
2483
2484  struct {
2485    const std::string input_text;
2486    bool prefer_keyword_provider_results;
2487    const std::string default_provider_response_json;
2488    const std::string keyword_provider_response_json;
2489    const Match matches[5];
2490  } cases[] = {
2491    // Default provider response does not have prefetch details. Ensure that the
2492    // suggestions are not marked as prefetch query.
2493    { "a",
2494      false,
2495      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2496      std::string(),
2497      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2498        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2499        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2500        kEmptyMatch,
2501        kEmptyMatch
2502      },
2503    },
2504    // Ensure that default provider suggest response prefetch details are
2505    // parsed and recorded in AutocompleteMatch.
2506    { "ab",
2507      false,
2508      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2509          "{\"google:clientdata\":{\"phi\": 0},"
2510          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2511          "\"google:suggestrelevance\":[999, 12, 1]}]",
2512      std::string(),
2513      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2514        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
2515        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2516        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2517        kEmptyMatch
2518      },
2519    },
2520    // Default provider suggest response has prefetch details.
2521    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2522    // the same query string. Ensure that the prefetch details from
2523    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2524    { "ab",
2525      false,
2526      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2527          "{\"google:clientdata\":{\"phi\": 0},"
2528          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2529          "\"google:suggestrelevance\":[99, 98]}]",
2530      std::string(),
2531      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2532        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2533        kEmptyMatch,
2534        kEmptyMatch,
2535        kEmptyMatch
2536      },
2537    },
2538    // Default provider response has prefetch details. We prefer keyword
2539    // provider results. Ensure that prefetch bit for a suggestion from the
2540    // default search provider does not get copied onto a higher-scoring match
2541    // for the same query string from the keyword provider.
2542    { "k a",
2543      true,
2544      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2545          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2546          "\"google:suggestrelevance\":[9, 12]}]",
2547      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2548      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2549        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2550        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2551        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2552        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2553      },
2554    }
2555  };
2556
2557  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2558    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
2559                  cases[i].prefer_keyword_provider_results);
2560
2561    // Set up a default fetcher with provided results.
2562    net::TestURLFetcher* fetcher =
2563        test_factory_.GetFetcherByID(
2564            SearchProvider::kDefaultProviderURLFetcherID);
2565    ASSERT_TRUE(fetcher);
2566    fetcher->set_response_code(200);
2567    fetcher->SetResponseString(cases[i].default_provider_response_json);
2568    fetcher->delegate()->OnURLFetchComplete(fetcher);
2569
2570    if (cases[i].prefer_keyword_provider_results) {
2571      // Set up a keyword fetcher with provided results.
2572      net::TestURLFetcher* keyword_fetcher =
2573          test_factory_.GetFetcherByID(
2574              SearchProvider::kKeywordProviderURLFetcherID);
2575      ASSERT_TRUE(keyword_fetcher);
2576      keyword_fetcher->set_response_code(200);
2577      keyword_fetcher->SetResponseString(
2578          cases[i].keyword_provider_response_json);
2579      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2580      keyword_fetcher = NULL;
2581    }
2582
2583    RunTillProviderDone();
2584
2585    const std::string description =
2586        "for input with json =" + cases[i].default_provider_response_json;
2587    const ACMatches& matches = provider_->matches();
2588    // The top match must inline and score as highly as calculated verbatim.
2589    ASSERT_FALSE(matches.empty());
2590    EXPECT_GE(matches[0].relevance, 1300);
2591
2592    // Ensure that the returned matches equal the expectations.
2593    for (size_t j = 0; j < matches.size(); ++j) {
2594      SCOPED_TRACE(description);
2595      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
2596      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2597                SearchProvider::ShouldPrefetch(matches[j]));
2598      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2599      EXPECT_EQ(cases[i].matches[j].from_keyword,
2600                matches[j].keyword == ASCIIToUTF16("k"));
2601    }
2602  }
2603}
2604