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