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