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