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