search_provider_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 <string>
8
9#include "base/command_line.h"
10#include "base/metrics/field_trial.h"
11#include "base/prefs/pref_service.h"
12#include "base/run_loop.h"
13#include "base/strings/string16.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/time/time.h"
18#include "build/build_config.h"
19#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20#include "chrome/browser/autocomplete/autocomplete_controller.h"
21#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
22#include "chrome/browser/autocomplete/history_url_provider.h"
23#include "chrome/browser/history/history_service.h"
24#include "chrome/browser/history/history_service_factory.h"
25#include "chrome/browser/search_engines/template_url_service_factory.h"
26#include "chrome/browser/signin/signin_manager_factory.h"
27#include "chrome/browser/sync/profile_sync_service.h"
28#include "chrome/browser/sync/profile_sync_service_factory.h"
29#include "chrome/common/chrome_switches.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 "components/google/core/browser/google_switches.h"
34#include "components/metrics/proto/omnibox_event.pb.h"
35#include "components/omnibox/autocomplete_input.h"
36#include "components/omnibox/autocomplete_match.h"
37#include "components/omnibox/autocomplete_provider.h"
38#include "components/omnibox/autocomplete_provider_listener.h"
39#include "components/omnibox/omnibox_field_trial.h"
40#include "components/omnibox/omnibox_switches.h"
41#include "components/search_engines/search_engine_type.h"
42#include "components/search_engines/search_engines_switches.h"
43#include "components/search_engines/search_terms_data.h"
44#include "components/search_engines/template_url.h"
45#include "components/search_engines/template_url_service.h"
46#include "components/signin/core/browser/signin_manager.h"
47#include "components/sync_driver/pref_names.h"
48#include "components/variations/entropy_provider.h"
49#include "components/variations/variations_associated_data.h"
50#include "content/public/test/test_browser_thread_bundle.h"
51#include "net/url_request/test_url_fetcher_factory.h"
52#include "net/url_request/url_request_status.h"
53#include "testing/gtest/include/gtest/gtest.h"
54
55using base::ASCIIToUTF16;
56
57namespace {
58
59// Returns the first match in |matches| with |allowed_to_be_default_match|
60// set to true.
61ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
62  ACMatches::const_iterator it = matches.begin();
63  while ((it != matches.end()) && !it->allowed_to_be_default_match)
64    ++it;
65  return it;
66}
67
68class SuggestionDeletionHandler;
69class SearchProviderForTest : public SearchProvider {
70 public:
71  SearchProviderForTest(AutocompleteProviderListener* listener,
72                        TemplateURLService* template_url_service,
73                        Profile* profile);
74  bool is_success() { return is_success_; };
75
76 protected:
77  virtual ~SearchProviderForTest();
78
79 private:
80  virtual void RecordDeletionResult(bool success) OVERRIDE;
81  bool is_success_;
82  DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
83};
84
85SearchProviderForTest::SearchProviderForTest(
86    AutocompleteProviderListener* listener,
87    TemplateURLService* template_url_service,
88    Profile* profile)
89    : SearchProvider(listener, template_url_service, profile),
90      is_success_(false) {
91}
92
93SearchProviderForTest::~SearchProviderForTest() {
94}
95
96void SearchProviderForTest::RecordDeletionResult(bool success) {
97  is_success_ = success;
98}
99
100} // namespace
101
102// SearchProviderTest ---------------------------------------------------------
103
104// The following environment is configured for these tests:
105// . The TemplateURL default_t_url_ is set as the default provider.
106// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
107//   TemplateURL has a valid suggest and search URL.
108// . The URL created by using the search term term1_ with default_t_url_ is
109//   added to history.
110// . The URL created by using the search term keyword_term_ with keyword_t_url_
111//   is added to history.
112// . test_factory_ is set as the URLFetcherFactory.
113class SearchProviderTest : public testing::Test,
114                           public AutocompleteProviderListener {
115 public:
116  struct ResultInfo {
117    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
118                   allowed_to_be_default_match(false) {
119    }
120    ResultInfo(GURL gurl,
121               AutocompleteMatch::Type result_type,
122               bool allowed_to_be_default_match,
123               base::string16 fill_into_edit)
124      : gurl(gurl),
125        result_type(result_type),
126        allowed_to_be_default_match(allowed_to_be_default_match),
127        fill_into_edit(fill_into_edit) {
128    }
129
130    const GURL gurl;
131    const AutocompleteMatch::Type result_type;
132    const bool allowed_to_be_default_match;
133    const base::string16 fill_into_edit;
134  };
135
136  struct TestData {
137    const base::string16 input;
138    const size_t num_results;
139    const ResultInfo output[3];
140  };
141
142  SearchProviderTest()
143      : default_t_url_(NULL),
144        term1_(ASCIIToUTF16("term1")),
145        keyword_t_url_(NULL),
146        keyword_term_(ASCIIToUTF16("keyword")),
147        run_loop_(NULL) {
148    ResetFieldTrialList();
149  }
150
151  // See description above class for what this registers.
152  virtual void SetUp() OVERRIDE;
153  virtual void TearDown() OVERRIDE;
154
155  void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
156
157 protected:
158  // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
159  scoped_ptr<base::FieldTrialList> field_trial_list_;
160
161  // Default value used for testing.
162  static const std::string kNotApplicable;
163
164  // Adds a search for |term|, using the engine |t_url| to the history, and
165  // returns the URL for that search.
166  GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
167
168  // Looks for a match in |provider_| with |contents| equal to |contents|.
169  // Sets |match| to it if found.  Returns whether |match| was set.
170  bool FindMatchWithContents(const base::string16& contents,
171                             AutocompleteMatch* match);
172
173  // Looks for a match in |provider_| with destination |url|.  Sets |match| to
174  // it if found.  Returns whether |match| was set.
175  bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
176
177  // AutocompleteProviderListener:
178  // If we're waiting for the provider to finish, this exits the message loop.
179  virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
180
181  // Runs a nested message loop until provider_ is done. The message loop is
182  // exited by way of OnProviderUpdate.
183  void RunTillProviderDone();
184
185  // Invokes Start on provider_, then runs all pending tasks.
186  void QueryForInput(const base::string16& text,
187                     bool prevent_inline_autocomplete,
188                     bool prefer_keyword);
189
190  // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
191  // non-NULL, sets it to the "what you typed" entry for |text|.
192  void QueryForInputAndSetWYTMatch(const base::string16& text,
193                                   AutocompleteMatch* wyt_match);
194
195  // Notifies the URLFetcher for the suggest query corresponding to the default
196  // search provider that it's done.
197  // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
198  void FinishDefaultSuggestQuery();
199
200  // Runs SearchProvider on |input|, for which the suggest server replies
201  // with |json|, and expects that the resulting matches' contents equals
202  // that in |matches|.  An empty entry in |matches| means no match should
203  // be returned in that position.  Reports any errors with a message that
204  // includes |error_description|.
205  void ForcedQueryTestHelper(const std::string& input,
206                             const std::string& json,
207                             const std::string matches[3],
208                             const std::string& error_description);
209
210  void ResetFieldTrialList();
211
212  // Create a field trial, with ZeroSuggest activation based on |enabled|.
213  base::FieldTrial* CreateZeroSuggestFieldTrial(bool enabled);
214
215  void ClearAllResults();
216
217  // See description above class for details of these fields.
218  TemplateURL* default_t_url_;
219  const base::string16 term1_;
220  GURL term1_url_;
221  TemplateURL* keyword_t_url_;
222  const base::string16 keyword_term_;
223  GURL keyword_url_;
224
225  content::TestBrowserThreadBundle thread_bundle_;
226
227  // URLFetcherFactory implementation registered.
228  net::TestURLFetcherFactory test_factory_;
229
230  // Profile we use.
231  TestingProfile profile_;
232
233  // The provider.
234  scoped_refptr<SearchProviderForTest> provider_;
235
236  // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
237  base::RunLoop* run_loop_;
238
239  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
240};
241
242// static
243const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
244
245void SearchProviderTest::SetUp() {
246  // Make sure that fetchers are automatically ungregistered upon destruction.
247  test_factory_.set_remove_fetcher_on_delete(true);
248
249  // We need both the history service and template url model loaded.
250  ASSERT_TRUE(profile_.CreateHistoryService(true, false));
251  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
252      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
253
254  TemplateURLService* turl_model =
255      TemplateURLServiceFactory::GetForProfile(&profile_);
256
257  turl_model->Load();
258
259  // Reset the default TemplateURL.
260  TemplateURLData data;
261  data.short_name = ASCIIToUTF16("t");
262  data.SetURL("http://defaultturl/{searchTerms}");
263  data.suggestions_url = "http://defaultturl2/{searchTerms}";
264  data.instant_url = "http://does/not/exist?strk=1";
265  data.search_terms_replacement_key = "strk";
266  default_t_url_ = new TemplateURL(data);
267  turl_model->Add(default_t_url_);
268  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
269  TemplateURLID default_provider_id = default_t_url_->id();
270  ASSERT_NE(0, default_provider_id);
271
272  // Add url1, with search term term1_.
273  term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
274
275  // Create another TemplateURL.
276  data.short_name = ASCIIToUTF16("k");
277  data.SetKeyword(ASCIIToUTF16("k"));
278  data.SetURL("http://keyword/{searchTerms}");
279  data.suggestions_url = "http://suggest_keyword/{searchTerms}";
280  keyword_t_url_ = new TemplateURL(data);
281  turl_model->Add(keyword_t_url_);
282  ASSERT_NE(0, keyword_t_url_->id());
283
284  // Add a page and search term for keyword_t_url_.
285  keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
286
287  // Keywords are updated by the InMemoryHistoryBackend only after the message
288  // has been processed on the history thread. Block until history processes all
289  // requests to ensure the InMemoryDatabase is the state we expect it.
290  profile_.BlockUntilHistoryProcessesPendingRequests();
291
292  provider_ = new SearchProviderForTest(this, turl_model, &profile_);
293  provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
294}
295
296void SearchProviderTest::TearDown() {
297  base::RunLoop().RunUntilIdle();
298
299  // Shutdown the provider before the profile.
300  provider_ = NULL;
301}
302
303void SearchProviderTest::RunTest(TestData* cases,
304                                 int num_cases,
305                                 bool prefer_keyword) {
306  ACMatches matches;
307  for (int i = 0; i < num_cases; ++i) {
308    AutocompleteInput input(cases[i].input, base::string16::npos,
309                            base::string16(), GURL(),
310                            metrics::OmniboxEventProto::INVALID_SPEC, false,
311                            prefer_keyword, true, true,
312                            ChromeAutocompleteSchemeClassifier(&profile_));
313    provider_->Start(input, false);
314    matches = provider_->matches();
315    base::string16 diagnostic_details =
316        ASCIIToUTF16("Input was: ") +
317        cases[i].input +
318        ASCIIToUTF16("; prefer_keyword was: ") +
319        (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
320    EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
321    if (matches.size() == cases[i].num_results) {
322      for (size_t j = 0; j < cases[i].num_results; ++j) {
323        EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
324            diagnostic_details;
325        EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
326            diagnostic_details;
327        EXPECT_EQ(cases[i].output[j].fill_into_edit,
328                  matches[j].fill_into_edit) << diagnostic_details;
329        EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
330                  matches[j].allowed_to_be_default_match) << diagnostic_details;
331      }
332    }
333  }
334}
335
336void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
337  if (run_loop_ && provider_->done()) {
338    run_loop_->Quit();
339    run_loop_ = NULL;
340  }
341}
342
343void SearchProviderTest::RunTillProviderDone() {
344  if (provider_->done())
345    return;
346
347  base::RunLoop run_loop;
348  run_loop_ = &run_loop;
349  run_loop.Run();
350}
351
352void SearchProviderTest::QueryForInput(const base::string16& text,
353                                       bool prevent_inline_autocomplete,
354                                       bool prefer_keyword) {
355  // Start a query.
356  AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(),
357                          metrics::OmniboxEventProto::INVALID_SPEC,
358                          prevent_inline_autocomplete, prefer_keyword, true,
359                          true, ChromeAutocompleteSchemeClassifier(&profile_));
360  provider_->Start(input, false);
361
362  // RunUntilIdle so that the task scheduled by SearchProvider to create the
363  // URLFetchers runs.
364  base::RunLoop().RunUntilIdle();
365}
366
367void SearchProviderTest::QueryForInputAndSetWYTMatch(
368    const base::string16& text,
369    AutocompleteMatch* wyt_match) {
370  QueryForInput(text, false, false);
371  profile_.BlockUntilHistoryProcessesPendingRequests();
372  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
373  if (!wyt_match)
374    return;
375  ASSERT_GE(provider_->matches().size(), 1u);
376  EXPECT_TRUE(FindMatchWithDestination(
377      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
378          TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace(
379              text, false)),
380          TemplateURLServiceFactory::GetForProfile(
381              &profile_)->search_terms_data())),
382      wyt_match));
383}
384
385GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
386                                            base::string16 term,
387                                            int visit_count) {
388  HistoryService* history =
389      HistoryServiceFactory::GetForProfile(&profile_,
390                                           Profile::EXPLICIT_ACCESS);
391  GURL search(t_url->url_ref().ReplaceSearchTerms(
392      TemplateURLRef::SearchTermsArgs(term),
393      TemplateURLServiceFactory::GetForProfile(
394          &profile_)->search_terms_data()));
395  static base::Time last_added_time;
396  last_added_time = std::max(base::Time::Now(),
397      last_added_time + base::TimeDelta::FromMicroseconds(1));
398  history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
399      last_added_time, false, history::SOURCE_BROWSED);
400  history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
401  return search;
402}
403
404bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
405                                               AutocompleteMatch* match) {
406  for (ACMatches::const_iterator i = provider_->matches().begin();
407       i != provider_->matches().end(); ++i) {
408    if (i->contents == contents) {
409      *match = *i;
410      return true;
411    }
412  }
413  return false;
414}
415
416bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
417                                                  AutocompleteMatch* match) {
418  for (ACMatches::const_iterator i = provider_->matches().begin();
419       i != provider_->matches().end(); ++i) {
420    if (i->destination_url == url) {
421      *match = *i;
422      return true;
423    }
424  }
425  return false;
426}
427
428void SearchProviderTest::FinishDefaultSuggestQuery() {
429  net::TestURLFetcher* default_fetcher =
430      test_factory_.GetFetcherByID(
431          SearchProvider::kDefaultProviderURLFetcherID);
432  ASSERT_TRUE(default_fetcher);
433
434  // Tell the SearchProvider the default suggest query is done.
435  default_fetcher->set_response_code(200);
436  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
437}
438
439void SearchProviderTest::ForcedQueryTestHelper(
440    const std::string& input,
441    const std::string& json,
442    const std::string expected_matches[3],
443    const std::string& error_description) {
444  QueryForInput(ASCIIToUTF16(input), false, false);
445  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
446      SearchProvider::kDefaultProviderURLFetcherID);
447  ASSERT_TRUE(fetcher);
448  fetcher->set_response_code(200);
449  fetcher->SetResponseString(json);
450  fetcher->delegate()->OnURLFetchComplete(fetcher);
451  RunTillProviderDone();
452
453  const ACMatches& matches = provider_->matches();
454  ASSERT_LE(matches.size(), 3u);
455  size_t i = 0;
456  // Ensure that the returned matches equal the expectations.
457  for (; i < matches.size(); ++i) {
458    EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
459        error_description;
460  }
461  // Ensure that no expected matches are missing.
462  for (; i < 3u; ++i) {
463    EXPECT_EQ(std::string(), expected_matches[i]) <<
464        "Case #" << i << ": " << error_description;
465  }
466}
467
468void SearchProviderTest::ResetFieldTrialList() {
469  // Destroy the existing FieldTrialList before creating a new one to avoid
470  // a DCHECK.
471  field_trial_list_.reset();
472  field_trial_list_.reset(new base::FieldTrialList(
473      new metrics::SHA1EntropyProvider("foo")));
474  variations::testing::ClearAllVariationParams();
475  base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
476      "AutocompleteDynamicTrial_0", "DefaultGroup");
477  trial->group();
478}
479
480base::FieldTrial* SearchProviderTest::CreateZeroSuggestFieldTrial(
481    bool enabled) {
482  std::map<std::string, std::string> params;
483  params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = enabled ?
484      "true" : "false";
485  variations::AssociateVariationParams(
486      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
487  return base::FieldTrialList::CreateFieldTrial(
488      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
489}
490
491void SearchProviderTest::ClearAllResults() {
492  provider_->ClearAllResults();
493}
494
495// Actual Tests ---------------------------------------------------------------
496
497// Make sure we query history for the default provider and a URLFetcher is
498// created for the default provider suggest results.
499TEST_F(SearchProviderTest, QueryDefaultProvider) {
500  base::string16 term = term1_.substr(0, term1_.length() - 1);
501  QueryForInput(term, false, false);
502
503  // Make sure the default providers suggest service was queried.
504  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
505      SearchProvider::kDefaultProviderURLFetcherID);
506  ASSERT_TRUE(fetcher);
507
508  // And the URL matches what we expected.
509  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
510      TemplateURLRef::SearchTermsArgs(term),
511      TemplateURLServiceFactory::GetForProfile(
512          &profile_)->search_terms_data()));
513  ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
514
515  // Tell the SearchProvider the suggest query is done.
516  fetcher->set_response_code(200);
517  fetcher->delegate()->OnURLFetchComplete(fetcher);
518  fetcher = NULL;
519
520  // Run till the history results complete.
521  RunTillProviderDone();
522
523  // The SearchProvider is done. Make sure it has a result for the history
524  // term term1.
525  AutocompleteMatch term1_match;
526  EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
527  // Term1 should not have a description, it's set later.
528  EXPECT_TRUE(term1_match.description.empty());
529
530  AutocompleteMatch wyt_match;
531  EXPECT_TRUE(FindMatchWithDestination(
532      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
533          TemplateURLRef::SearchTermsArgs(term),
534          TemplateURLServiceFactory::GetForProfile(
535              &profile_)->search_terms_data())),
536      &wyt_match));
537  EXPECT_TRUE(wyt_match.description.empty());
538
539  // The match for term1 should be more relevant than the what you typed match.
540  EXPECT_GT(term1_match.relevance, wyt_match.relevance);
541  // This longer match should be inlineable.
542  EXPECT_TRUE(term1_match.allowed_to_be_default_match);
543  // The what you typed match should be too, of course.
544  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
545}
546
547TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
548  base::string16 term = term1_.substr(0, term1_.length() - 1);
549  QueryForInput(term, true, false);
550
551  ASSERT_FALSE(provider_->matches().empty());
552  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
553            provider_->matches()[0].type);
554  EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
555}
556
557// Issues a query that matches the registered keyword and makes sure history
558// is queried as well as URLFetchers getting created.
559TEST_F(SearchProviderTest, QueryKeywordProvider) {
560  base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
561  QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
562                false,
563                false);
564
565  // Make sure the default providers suggest service was queried.
566  net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
567      SearchProvider::kDefaultProviderURLFetcherID);
568  ASSERT_TRUE(default_fetcher);
569
570  // Tell the SearchProvider the default suggest query is done.
571  default_fetcher->set_response_code(200);
572  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
573  default_fetcher = NULL;
574
575  // Make sure the keyword providers suggest service was queried.
576  net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
577      SearchProvider::kKeywordProviderURLFetcherID);
578  ASSERT_TRUE(keyword_fetcher);
579
580  // And the URL matches what we expected.
581  GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
582      TemplateURLRef::SearchTermsArgs(term),
583      TemplateURLServiceFactory::GetForProfile(
584          &profile_)->search_terms_data()));
585  ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
586
587  // Tell the SearchProvider the keyword suggest query is done.
588  keyword_fetcher->set_response_code(200);
589  keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
590  keyword_fetcher = NULL;
591
592  // Run till the history results complete.
593  RunTillProviderDone();
594
595  // The SearchProvider is done. Make sure it has a result for the history
596  // term keyword.
597  AutocompleteMatch match;
598  EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
599
600  // The match should have an associated keyword.
601  EXPECT_FALSE(match.keyword.empty());
602
603  // The fill into edit should contain the keyword.
604  EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
605            match.fill_into_edit);
606}
607
608TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
609  // None of the following input strings should be sent to the suggest server,
610  // because they may contain private data.
611  const char* inputs[] = {
612    "username:password",
613    "http://username:password",
614    "https://username:password",
615    "username:password@hostname",
616    "http://username:password@hostname/",
617    "file://filename",
618    "data://data",
619    "unknownscheme:anything",
620    "http://hostname/?query=q",
621    "http://hostname/path#ref",
622    "http://hostname/path #ref",
623    "https://hostname/path",
624  };
625
626  for (size_t i = 0; i < arraysize(inputs); ++i) {
627    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
628    // Make sure the default provider's suggest service was not queried.
629    ASSERT_TRUE(test_factory_.GetFetcherByID(
630        SearchProvider::kDefaultProviderURLFetcherID) == NULL);
631    // Run till the history results complete.
632    RunTillProviderDone();
633  }
634}
635
636TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
637  // All of the following input strings should be sent to the suggest server,
638  // because they should not get caught by the private data checks.
639  const char* inputs[] = {
640    "query",
641    "query with spaces",
642    "http://hostname",
643    "http://hostname/path",
644    "http://hostname #ref",
645    "www.hostname.com #ref",
646    "https://hostname",
647    "#hashtag",
648    "foo https://hostname/path"
649  };
650
651  profile_.BlockUntilHistoryProcessesPendingRequests();
652  for (size_t i = 0; i < arraysize(inputs); ++i) {
653    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
654    // Make sure the default provider's suggest service was queried.
655    ASSERT_TRUE(test_factory_.GetFetcherByID(
656        SearchProvider::kDefaultProviderURLFetcherID) != NULL);
657  }
658}
659
660TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
661  AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
662      &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
663  GURL url = AddSearchToHistory(default_t_url_,
664                                ASCIIToUTF16("docs.google.com"), 1);
665
666  // Add the term as a url.
667  HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
668      AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1,
669                         base::Time::Now(), false, history::SOURCE_BROWSED);
670  profile_.BlockUntilHistoryProcessesPendingRequests();
671
672  AutocompleteMatch wyt_match;
673  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
674                                                      &wyt_match));
675
676  // There should be two matches, one for what you typed, the other for
677  // 'docs.google.com'. The search term should have a lower priority than the
678  // what you typed match.
679  ASSERT_EQ(2u, provider_->matches().size());
680  AutocompleteMatch term_match;
681  EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
682  EXPECT_GT(wyt_match.relevance, term_match.relevance);
683  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
684  EXPECT_TRUE(term_match.allowed_to_be_default_match);
685}
686
687TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
688  const std::string kEmptyMatch;
689  struct {
690    const std::string json;
691    const std::string matches_in_default_mode[3];
692    const std::string matches_in_forced_query_mode[3];
693  } cases[] = {
694    // Without suggested relevance scores.
695    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
696       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
697      { "a", "a1.com", "a2" },
698      { "a", "a2", kEmptyMatch } },
699
700    // With suggested relevance scores in a situation where navsuggest would
701    // go second.
702    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
703       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
704        "\"google:suggestrelevance\":[1250, 1200]}]",
705      { "a", "a1.com", "a2" },
706      { "a", "a2", kEmptyMatch } },
707
708    // With suggested relevance scores in a situation where navsuggest
709    // would go first.
710    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
711       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
712        "\"google:suggestrelevance\":[1350, 1250]}]",
713      { "a1.com", "a", "a2" },
714      { "a", "a2", kEmptyMatch } },
715
716    // With suggested relevance scores in a situation where navsuggest
717    // would go first only because verbatim has been demoted.
718    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
719       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
720        "\"google:suggestrelevance\":[1450, 1400],"
721        "\"google:verbatimrelevance\":1350}]",
722      { "a1.com", "a2", "a" },
723      { "a2", "a", kEmptyMatch } },
724  };
725
726  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
727    ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
728                           "regular input with json=" + cases[i].json);
729    ForcedQueryTestHelper("?a", cases[i].json,
730                          cases[i].matches_in_forced_query_mode,
731                          "forced query input with json=" + cases[i].json);
732  }
733}
734
735// A multiword search with one visit should not autocomplete until multiple
736// words are typed.
737TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
738  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
739                                   1));
740  profile_.BlockUntilHistoryProcessesPendingRequests();
741
742  AutocompleteMatch wyt_match;
743  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
744                                                      &wyt_match));
745  ASSERT_EQ(2u, provider_->matches().size());
746  AutocompleteMatch term_match;
747  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
748  EXPECT_GT(wyt_match.relevance, term_match.relevance);
749  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
750  EXPECT_TRUE(term_match.allowed_to_be_default_match);
751
752  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
753                                                      &wyt_match));
754  ASSERT_EQ(2u, provider_->matches().size());
755  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
756  EXPECT_GT(term_match.relevance, wyt_match.relevance);
757  EXPECT_TRUE(term_match.allowed_to_be_default_match);
758  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
759}
760
761// A multiword search with more than one visit should autocomplete immediately.
762TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
763  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
764                                   2));
765  profile_.BlockUntilHistoryProcessesPendingRequests();
766
767  AutocompleteMatch wyt_match;
768  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
769                                                      &wyt_match));
770  ASSERT_EQ(2u, provider_->matches().size());
771  AutocompleteMatch term_match;
772  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
773  EXPECT_GT(term_match.relevance, wyt_match.relevance);
774  EXPECT_TRUE(term_match.allowed_to_be_default_match);
775  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
776}
777
778// Autocompletion should work at a word boundary after a space, and should
779// offer a suggestion for the trimmed search query.
780TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
781  AddSearchToHistory(default_t_url_, ASCIIToUTF16("two  searches "), 2);
782  GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms(
783      TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")),
784      TemplateURLServiceFactory::GetForProfile(
785          &profile_)->search_terms_data()));
786  profile_.BlockUntilHistoryProcessesPendingRequests();
787
788  AutocompleteMatch wyt_match;
789  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
790                                                      &wyt_match));
791  ASSERT_EQ(2u, provider_->matches().size());
792  AutocompleteMatch term_match;
793  EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match));
794  EXPECT_GT(term_match.relevance, wyt_match.relevance);
795  EXPECT_TRUE(term_match.allowed_to_be_default_match);
796  EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion);
797  EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit);
798  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
799}
800
801// Newer multiword searches should score more highly than older ones.
802TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
803  GURL term_url_a(AddSearchToHistory(default_t_url_,
804                                     ASCIIToUTF16("three searches aaa"), 1));
805  GURL term_url_b(AddSearchToHistory(default_t_url_,
806                                     ASCIIToUTF16("three searches bbb"), 1));
807  profile_.BlockUntilHistoryProcessesPendingRequests();
808
809  AutocompleteMatch wyt_match;
810  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
811                                                      &wyt_match));
812  ASSERT_EQ(3u, provider_->matches().size());
813  AutocompleteMatch term_match_a;
814  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
815  AutocompleteMatch term_match_b;
816  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
817  EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
818  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
819  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
820  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
821  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
822}
823
824// An autocompleted multiword search should not be replaced by a different
825// autocompletion while the user is still typing a valid prefix unless the
826// user has typed the prefix as a query before.
827TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
828  GURL term_url_a(AddSearchToHistory(default_t_url_,
829                                     ASCIIToUTF16("four searches aaa"), 3));
830  GURL term_url_b(AddSearchToHistory(default_t_url_,
831                                     ASCIIToUTF16("four searches bbb"), 1));
832  GURL term_url_c(AddSearchToHistory(default_t_url_,
833                                     ASCIIToUTF16("four searches"), 1));
834  profile_.BlockUntilHistoryProcessesPendingRequests();
835
836  AutocompleteMatch wyt_match;
837  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
838                                                      &wyt_match));
839  ASSERT_EQ(4u, provider_->matches().size());
840  AutocompleteMatch term_match_a;
841  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
842  AutocompleteMatch term_match_b;
843  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
844  AutocompleteMatch term_match_c;
845  EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
846  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
847  // We don't care about the relative order of b and c.
848  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
849  EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
850  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
851  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
852  EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
853  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
854
855  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
856                                                      &wyt_match));
857  ASSERT_EQ(4u, provider_->matches().size());
858  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
859  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
860  EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
861  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
862  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
863  EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
864  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
865  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
866  EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
867  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
868
869  // For the exact previously-issued query, the what-you-typed match should win.
870  ASSERT_NO_FATAL_FAILURE(
871      QueryForInputAndSetWYTMatch(ASCIIToUTF16("four searches"), &wyt_match));
872  ASSERT_EQ(3u, provider_->matches().size());
873  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
874  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
875  EXPECT_GT(wyt_match.relevance, term_match_a.relevance);
876  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
877  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
878  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
879  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
880}
881
882// Non-completable multiword searches should not crowd out single-word searches.
883TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
884  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
885  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
886  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
887  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
888  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
889  profile_.BlockUntilHistoryProcessesPendingRequests();
890
891  AutocompleteMatch wyt_match;
892  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
893                                                      &wyt_match));
894  ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
895  AutocompleteMatch term_match;
896  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
897  EXPECT_GT(term_match.relevance, wyt_match.relevance);
898  EXPECT_TRUE(term_match.allowed_to_be_default_match);
899  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
900}
901
902// Inline autocomplete matches regardless of case differences from the input.
903TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
904  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
905  profile_.BlockUntilHistoryProcessesPendingRequests();
906
907  AutocompleteMatch wyt_match;
908  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
909                                                      &wyt_match));
910  ASSERT_EQ(2u, provider_->matches().size());
911  AutocompleteMatch term_match;
912  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
913  EXPECT_GT(term_match.relevance, wyt_match.relevance);
914  EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
915  EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
916  EXPECT_TRUE(term_match.allowed_to_be_default_match);
917}
918
919// Verifies AutocompleteControllers return results (including keyword
920// results) in the right order and set descriptions for them correctly.
921TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
922  // Add an entry that corresponds to a keyword search with 'term2'.
923  AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
924  profile_.BlockUntilHistoryProcessesPendingRequests();
925
926  AutocompleteController controller(&profile_,
927      TemplateURLServiceFactory::GetForProfile(&profile_),
928      NULL, AutocompleteProvider::TYPE_SEARCH);
929  controller.Start(AutocompleteInput(
930      ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(),
931      metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true,
932      ChromeAutocompleteSchemeClassifier(&profile_)));
933  const AutocompleteResult& result = controller.result();
934
935  // There should be three matches, one for the keyword history, one for
936  // keyword provider's what-you-typed, and one for the default provider's
937  // what you typed, in that order.
938  ASSERT_EQ(3u, result.size());
939  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
940  EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
941            result.match_at(1).type);
942  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
943            result.match_at(2).type);
944  EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
945  EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
946  EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
947  EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
948  EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
949
950  // The two keyword results should come with the keyword we expect.
951  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
952  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
953  // The default provider has a different keyword.  (We don't explicitly
954  // set it during this test, so all we do is assert that it's different.)
955  EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
956
957  // The top result will always have a description.  The third result,
958  // coming from a different provider than the first two, should also.
959  // Whether the second result has one doesn't matter much.  (If it was
960  // missing, people would infer that it's the same search provider as
961  // the one above it.)
962  EXPECT_FALSE(result.match_at(0).description.empty());
963  EXPECT_FALSE(result.match_at(2).description.empty());
964  EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
965}
966
967TEST_F(SearchProviderTest, KeywordVerbatim) {
968  TestData cases[] = {
969    // Test a simple keyword input.
970    { ASCIIToUTF16("k foo"), 2,
971      { ResultInfo(GURL("http://keyword/foo"),
972                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
973                   true,
974                   ASCIIToUTF16("k foo")),
975        ResultInfo(GURL("http://defaultturl/k%20foo"),
976                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
977                   false,
978                   ASCIIToUTF16("k foo") ) } },
979
980    // Make sure extra whitespace after the keyword doesn't change the
981    // keyword verbatim query.  Also verify that interior consecutive
982    // whitespace gets trimmed.
983    { ASCIIToUTF16("k   foo"), 2,
984      { ResultInfo(GURL("http://keyword/foo"),
985                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
986                   true,
987                   ASCIIToUTF16("k foo")),
988        ResultInfo(GURL("http://defaultturl/k%20foo"),
989                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
990                   false,
991                   ASCIIToUTF16("k foo")) } },
992    // Leading whitespace should be stripped before SearchProvider gets the
993    // input; hence there are no tests here about how it handles those inputs.
994
995    // Verify that interior consecutive whitespace gets trimmed in either case.
996    { ASCIIToUTF16("k  foo  bar"), 2,
997      { ResultInfo(GURL("http://keyword/foo%20bar"),
998                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
999                   true,
1000                   ASCIIToUTF16("k foo bar")),
1001        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1002                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1003                   false,
1004                   ASCIIToUTF16("k foo bar")) } },
1005
1006    // Verify that trailing whitespace gets trimmed.
1007    { ASCIIToUTF16("k foo bar  "), 2,
1008      { ResultInfo(GURL("http://keyword/foo%20bar"),
1009                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1010                   true,
1011                   ASCIIToUTF16("k foo bar")),
1012        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1013                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1014                   false,
1015                   ASCIIToUTF16("k foo bar")) } },
1016
1017    // Keywords can be prefixed by certain things that should get ignored
1018    // when constructing the keyword match.
1019    { ASCIIToUTF16("www.k foo"), 2,
1020      { ResultInfo(GURL("http://keyword/foo"),
1021                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1022                   true,
1023                   ASCIIToUTF16("k foo")),
1024        ResultInfo(GURL("http://defaultturl/www.k%20foo"),
1025                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1026                   false,
1027                   ASCIIToUTF16("www.k foo")) } },
1028    { ASCIIToUTF16("http://k foo"), 2,
1029      { ResultInfo(GURL("http://keyword/foo"),
1030                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1031                   true,
1032                   ASCIIToUTF16("k foo")),
1033        ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
1034                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1035                   false,
1036                   ASCIIToUTF16("http://k foo")) } },
1037    { ASCIIToUTF16("http://www.k foo"), 2,
1038      { ResultInfo(GURL("http://keyword/foo"),
1039                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1040                   true,
1041                   ASCIIToUTF16("k foo")),
1042        ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
1043                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1044                   false,
1045                   ASCIIToUTF16("http://www.k foo")) } },
1046
1047    // A keyword with no remaining input shouldn't get a keyword
1048    // verbatim match.
1049    { ASCIIToUTF16("k"), 1,
1050      { ResultInfo(GURL("http://defaultturl/k"),
1051                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1052                   true,
1053                   ASCIIToUTF16("k")) } },
1054    // Ditto.  Trailing whitespace shouldn't make a difference.
1055    { ASCIIToUTF16("k "), 1,
1056      { ResultInfo(GURL("http://defaultturl/k"),
1057                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1058                   true,
1059                   ASCIIToUTF16("k")) } }
1060
1061    // The fact that verbatim queries to keyword are handled by KeywordProvider
1062    // not SearchProvider is tested in
1063    // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
1064  };
1065
1066  // Test not in keyword mode.
1067  RunTest(cases, arraysize(cases), false);
1068
1069  // Test in keyword mode.  (Both modes should give the same result.)
1070  RunTest(cases, arraysize(cases), true);
1071}
1072
1073// Ensures command-line flags are reflected in the URLs the search provider
1074// generates.
1075TEST_F(SearchProviderTest, CommandLineOverrides) {
1076  TemplateURLService* turl_model =
1077      TemplateURLServiceFactory::GetForProfile(&profile_);
1078
1079  TemplateURLData data;
1080  data.short_name = ASCIIToUTF16("default");
1081  data.SetKeyword(data.short_name);
1082  data.SetURL("{google:baseURL}{searchTerms}");
1083  default_t_url_ = new TemplateURL(data);
1084  turl_model->Add(default_t_url_);
1085  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
1086
1087  CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
1088                                                      "http://www.bar.com/");
1089  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1090      switches::kExtraSearchQueryParams, "a=b");
1091
1092  TestData cases[] = {
1093    { ASCIIToUTF16("k a"), 2,
1094      { ResultInfo(GURL("http://keyword/a"),
1095                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1096                   true,
1097                   ASCIIToUTF16("k a")),
1098        ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1099                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1100                   false,
1101                   ASCIIToUTF16("k a")) } },
1102  };
1103
1104  RunTest(cases, arraysize(cases), false);
1105}
1106
1107// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1108// Also verifies that just the *first* navigational result is listed as a match
1109// if suggested relevance scores were not sent.
1110TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1111  QueryForInput(ASCIIToUTF16("a.c"), false, false);
1112
1113  // Make sure the default providers suggest service was queried.
1114  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1115      SearchProvider::kDefaultProviderURLFetcherID);
1116  ASSERT_TRUE(fetcher);
1117
1118  // Tell the SearchProvider the suggest query is done.
1119  fetcher->set_response_code(200);
1120  fetcher->SetResponseString(
1121      "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1122      "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
1123  fetcher->delegate()->OnURLFetchComplete(fetcher);
1124  fetcher = NULL;
1125
1126  // Run till the history results complete.
1127  RunTillProviderDone();
1128
1129  // Make sure the only match is 'a.com' and it doesn't have a template_url.
1130  AutocompleteMatch nav_match;
1131  EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1132  EXPECT_TRUE(nav_match.keyword.empty());
1133  EXPECT_TRUE(nav_match.allowed_to_be_default_match);
1134  EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1135}
1136
1137// Verifies that the most relevant suggest results are added properly.
1138TEST_F(SearchProviderTest, SuggestRelevance) {
1139  QueryForInput(ASCIIToUTF16("a"), false, false);
1140
1141  // Make sure the default provider's suggest service was queried.
1142  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1143      SearchProvider::kDefaultProviderURLFetcherID);
1144  ASSERT_TRUE(fetcher);
1145
1146  // Tell the SearchProvider the suggest query is done.
1147  fetcher->set_response_code(200);
1148  fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
1149  fetcher->delegate()->OnURLFetchComplete(fetcher);
1150  fetcher = NULL;
1151
1152  // Run till the history results complete.
1153  RunTillProviderDone();
1154
1155  // Check the expected verbatim and (first 3) suggestions' relative relevances.
1156  AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1157  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1158  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1159  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1160  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1161  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1162  EXPECT_GT(verbatim.relevance, match_a1.relevance);
1163  EXPECT_GT(match_a1.relevance, match_a2.relevance);
1164  EXPECT_GT(match_a2.relevance, match_a3.relevance);
1165  EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1166  EXPECT_TRUE(match_a1.allowed_to_be_default_match);
1167  EXPECT_TRUE(match_a2.allowed_to_be_default_match);
1168  EXPECT_TRUE(match_a3.allowed_to_be_default_match);
1169}
1170
1171// Verifies that the default provider abandons suggested relevance scores
1172// when in keyword mode.  This should happen regardless of whether the
1173// keyword provider returns suggested relevance scores.
1174TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1175  struct {
1176    const std::string default_provider_json;
1177    const std::string keyword_provider_json;
1178    const std::string matches[5];
1179  } cases[] = {
1180    // First, try an input where the keyword provider does not deliver
1181    // suggested relevance scores.
1182    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1183      "{\"google:verbatimrelevance\":9700,"
1184      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1185      "\"google:suggestrelevance\":[9900, 9800]}]",
1186      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1187      { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1188
1189    // Now try with keyword provider suggested relevance scores.
1190    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1191      "{\"google:verbatimrelevance\":9700,"
1192      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1193      "\"google:suggestrelevance\":[9900, 9800]}]",
1194      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1195      "\"google:verbatimrelevance\":9500,"
1196      "\"google:suggestrelevance\":[9600]}]",
1197      { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1198  };
1199
1200  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1201    QueryForInput(ASCIIToUTF16("k a"), false, true);
1202    net::TestURLFetcher* default_fetcher =
1203        test_factory_.GetFetcherByID(
1204            SearchProvider::kDefaultProviderURLFetcherID);
1205    ASSERT_TRUE(default_fetcher);
1206    default_fetcher->set_response_code(200);
1207    default_fetcher->SetResponseString(cases[i].default_provider_json);
1208    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1209    net::TestURLFetcher* keyword_fetcher =
1210        test_factory_.GetFetcherByID(
1211            SearchProvider::kKeywordProviderURLFetcherID);
1212    ASSERT_TRUE(keyword_fetcher);
1213    keyword_fetcher->set_response_code(200);
1214    keyword_fetcher->SetResponseString(cases[i].keyword_provider_json);
1215    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1216    RunTillProviderDone();
1217
1218    const std::string description = "for input with default_provider_json=" +
1219        cases[i].default_provider_json + " and keyword_provider_json=" +
1220        cases[i].keyword_provider_json;
1221    const ACMatches& matches = provider_->matches();
1222    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1223    size_t j = 0;
1224    // Ensure that the returned matches equal the expectations.
1225    for (; j < matches.size(); ++j) {
1226      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) <<
1227          description;
1228    }
1229    // Ensure that no expected matches are missing.
1230    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1231      EXPECT_EQ(std::string(), cases[i].matches[j]) << description;
1232  }
1233}
1234
1235// Verifies that suggest results with relevance scores are added
1236// properly when using the default fetcher.  When adding a new test
1237// case to this test, please consider adding it to the tests in
1238// KeywordFetcherSuggestRelevance below.
1239TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1240  struct DefaultFetcherMatch {
1241    std::string contents;
1242    bool allowed_to_be_default_match;
1243  };
1244  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1245  struct {
1246    const std::string json;
1247    const DefaultFetcherMatch matches[6];
1248    const std::string inline_autocompletion;
1249  } cases[] = {
1250    // Ensure that suggestrelevance scores reorder matches.
1251    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1252      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch,
1253        kEmptyMatch },
1254      std::string() },
1255    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1256       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1257        "\"google:suggestrelevance\":[1, 2]}]",
1258      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1259        kEmptyMatch, kEmptyMatch },
1260      std::string() },
1261
1262    // Without suggested relevance scores, we should only allow one
1263    // navsuggest result to be be displayed.
1264    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1265       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1266      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1267        kEmptyMatch, kEmptyMatch },
1268      std::string() },
1269
1270    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1271    // Negative values will have no effect; the calculated value will be used.
1272    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1273                             "\"google:suggestrelevance\":[9998]}]",
1274      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1275        kEmptyMatch },
1276      std::string() },
1277    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1278                             "\"google:suggestrelevance\":[9999]}]",
1279      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1280        kEmptyMatch },
1281      "1" },
1282    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1283                             "\"google:suggestrelevance\":[9999]}]",
1284      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1285        kEmptyMatch },
1286      "1" },
1287    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1288                             "\"google:suggestrelevance\":[9999]}]",
1289      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1290        kEmptyMatch },
1291      "1" },
1292    { "[\"a\",[\"http://a.com\"],[],[],"
1293       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1294        "\"google:verbatimrelevance\":9999,"
1295        "\"google:suggestrelevance\":[9998]}]",
1296      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1297        kEmptyMatch },
1298      std::string() },
1299    { "[\"a\",[\"http://a.com\"],[],[],"
1300       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1301        "\"google:verbatimrelevance\":9998,"
1302        "\"google:suggestrelevance\":[9999]}]",
1303      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1304        kEmptyMatch },
1305      ".com" },
1306    { "[\"a\",[\"http://a.com\"],[],[],"
1307       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1308        "\"google:verbatimrelevance\":0,"
1309        "\"google:suggestrelevance\":[9999]}]",
1310      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1311        kEmptyMatch },
1312      ".com" },
1313    { "[\"a\",[\"http://a.com\"],[],[],"
1314       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1315        "\"google:verbatimrelevance\":-1,"
1316        "\"google:suggestrelevance\":[9999]}]",
1317      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1318        kEmptyMatch },
1319      ".com" },
1320
1321    // Ensure that both types of relevance scores reorder matches together.
1322    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1323                                     "\"google:verbatimrelevance\":9998}]",
1324      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1325        kEmptyMatch },
1326      "1" },
1327
1328    // Allow non-inlineable matches to be the highest-scoring match but,
1329    // if the result set lacks a single inlineable result, abandon suggested
1330    // relevance scores entirely.
1331    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1332      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1333        kEmptyMatch },
1334      std::string() },
1335    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1336                            "\"google:verbatimrelevance\":0}]",
1337      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1338        kEmptyMatch },
1339      std::string() },
1340    { "[\"a\",[\"http://b.com\"],[],[],"
1341       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1342        "\"google:suggestrelevance\":[9999]}]",
1343      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch,
1344        kEmptyMatch, kEmptyMatch },
1345      std::string() },
1346    { "[\"a\",[\"http://b.com\"],[],[],"
1347       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1348        "\"google:suggestrelevance\":[9999],"
1349        "\"google:verbatimrelevance\":0}]",
1350      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1351        kEmptyMatch, kEmptyMatch },
1352      std::string() },
1353
1354    // Allow low-scoring matches.
1355    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1356      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1357        kEmptyMatch },
1358      "1" },
1359    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1360      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1361        kEmptyMatch },
1362      "1" },
1363    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1364                             "\"google:verbatimrelevance\":0}]",
1365      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1366        kEmptyMatch },
1367      "1" },
1368    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1369                                     "\"google:verbatimrelevance\":0}]",
1370      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1371        kEmptyMatch },
1372      "2" },
1373    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1374      "\"google:verbatimrelevance\":2}]",
1375      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1376        kEmptyMatch },
1377      "2" },
1378    { "[\"a\",[\"http://a.com\"],[],[],"
1379       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1380        "\"google:suggestrelevance\":[1],"
1381        "\"google:verbatimrelevance\":0}]",
1382      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1383        kEmptyMatch },
1384      ".com" },
1385    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1386       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1387        "\"google:suggestrelevance\":[1, 2],"
1388        "\"google:verbatimrelevance\":0}]",
1389      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1390        kEmptyMatch, kEmptyMatch },
1391      "2.com" },
1392
1393    // Ensure that all suggestions are considered, regardless of order.
1394    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1395       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1396      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1397        { "e", false }, { "d", false } },
1398      std::string() },
1399    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1400              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1401              "\"http://h.com\"],[],[],"
1402       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1403                                "\"NAVIGATION\", \"NAVIGATION\","
1404                                "\"NAVIGATION\", \"NAVIGATION\","
1405                                "\"NAVIGATION\"],"
1406        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1407      { { "a", true }, { "h.com", false }, { "g.com", false },
1408        { "f.com", false }, { "e.com", false }, { "d.com", false } },
1409      std::string() },
1410
1411    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1412    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1413      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1414        kEmptyMatch },
1415      std::string() },
1416    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1417      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1418        kEmptyMatch },
1419      std::string() },
1420    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1421       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1422        "\"google:suggestrelevance\":[1]}]",
1423      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1424        kEmptyMatch, kEmptyMatch },
1425      std::string() },
1426    { "[\"a\",[\"http://a1.com\"],[],[],"
1427       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1428       "\"google:suggestrelevance\":[9999, 1]}]",
1429      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1430        kEmptyMatch, kEmptyMatch },
1431      std::string() },
1432
1433    // Ensure that all 'verbatim' results are merged with their maximum score.
1434    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1435       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1436      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1437        kEmptyMatch },
1438      "2" },
1439    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1440       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1441        "\"google:verbatimrelevance\":0}]",
1442      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1443        kEmptyMatch },
1444      "2" },
1445
1446    // Ensure that verbatim is always generated without other suggestions.
1447    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1448    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1449      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1450        kEmptyMatch },
1451      std::string() },
1452    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1453      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1454        kEmptyMatch },
1455      std::string() },
1456  };
1457
1458  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1459    QueryForInput(ASCIIToUTF16("a"), false, false);
1460    net::TestURLFetcher* fetcher =
1461        test_factory_.GetFetcherByID(
1462            SearchProvider::kDefaultProviderURLFetcherID);
1463    ASSERT_TRUE(fetcher);
1464    fetcher->set_response_code(200);
1465    fetcher->SetResponseString(cases[i].json);
1466    fetcher->delegate()->OnURLFetchComplete(fetcher);
1467    RunTillProviderDone();
1468
1469    const std::string description = "for input with json=" + cases[i].json;
1470    const ACMatches& matches = provider_->matches();
1471    ASSERT_FALSE(matches.empty());
1472    // Find the first match that's allowed to be the default match and check
1473    // its inline_autocompletion.
1474    ACMatches::const_iterator it = FindDefaultMatch(matches);
1475    ASSERT_NE(matches.end(), it);
1476    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1477              it->inline_autocompletion) << description;
1478
1479    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1480    size_t j = 0;
1481    // Ensure that the returned matches equal the expectations.
1482    for (; j < matches.size(); ++j) {
1483      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1484                matches[j].contents) << description;
1485      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1486                matches[j].allowed_to_be_default_match) << description;
1487    }
1488    // Ensure that no expected matches are missing.
1489    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1490      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1491          "Case # " << i << " " << description;
1492  }
1493}
1494
1495// Verifies that suggest results with relevance scores are added
1496// properly when using the keyword fetcher.  This is similar to the
1497// test DefaultFetcherSuggestRelevance above but this uses inputs that
1498// trigger keyword suggestions (i.e., "k a" rather than "a") and has
1499// different expectations (because now the results are a mix of
1500// keyword suggestions and default provider suggestions).  When a new
1501// test is added to this TEST_F, please consider if it would be
1502// appropriate to add to DefaultFetcherSuggestRelevance as well.
1503TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1504  struct KeywordFetcherMatch {
1505    std::string contents;
1506    bool from_keyword;
1507    bool allowed_to_be_default_match;
1508  };
1509  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1510  struct {
1511    const std::string json;
1512    const KeywordFetcherMatch matches[6];
1513    const std::string inline_autocompletion;
1514  } cases[] = {
1515    // Ensure that suggest relevance scores reorder matches and that
1516    // the keyword verbatim (lacking a suggested verbatim score) beats
1517    // the default provider verbatim.
1518    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1519      { { "a",   true,  true },
1520        { "k a", false, false },
1521        { "c",   true,  false },
1522        { "b",   true,  false },
1523        kEmptyMatch, kEmptyMatch },
1524      std::string() },
1525    // Again, check that relevance scores reorder matches, just this
1526    // time with navigation matches.  This also checks that with
1527    // suggested relevance scores we allow multiple navsuggest results.
1528    // Note that navsuggest results that come from a keyword provider
1529    // are marked as not a keyword result.  (They don't go to a
1530    // keyword search engine.)
1531    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1532       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1533       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1534      { { "a",     true,  true },
1535        { "d",     true,  false },
1536        { "c.com", false, false },
1537        { "b.com", false, false },
1538        { "k a",   false, false },
1539        kEmptyMatch },
1540      std::string() },
1541
1542    // Without suggested relevance scores, we should only allow one
1543    // navsuggest result to be be displayed.
1544    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1545       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1546      { { "a",     true,  true },
1547        { "b.com", false, false },
1548        { "k a",   false, false },
1549        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1550      std::string() },
1551
1552    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1553    // Negative values will have no effect; the calculated value will be used.
1554    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1555                             "\"google:suggestrelevance\":[9998]}]",
1556      { { "a",   true,  true },
1557        { "a1",  true,  true },
1558        { "k a", false, false },
1559        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1560      std::string() },
1561    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1562                             "\"google:suggestrelevance\":[9999]}]",
1563      { { "a1",  true,  true },
1564        { "a",   true,  true },
1565        { "k a", false, false },
1566        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1567      "1" },
1568    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1569                             "\"google:suggestrelevance\":[9999]}]",
1570      { { "a1",  true,  true },
1571        { "k a", false, false },
1572        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1573      "1" },
1574    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1575                             "\"google:suggestrelevance\":[9999]}]",
1576      { { "a1",  true,  true },
1577        { "a",   true,  true },
1578        { "k a", false, false },
1579        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1580      "1" },
1581    { "[\"a\",[\"http://a.com\"],[],[],"
1582       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1583        "\"google:verbatimrelevance\":9999,"
1584        "\"google:suggestrelevance\":[9998]}]",
1585      { { "a",     true,  true },
1586        { "a.com", false, false },
1587        { "k a",   false, false },
1588        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1589      std::string() },
1590
1591    // Ensure that both types of relevance scores reorder matches together.
1592    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1593                                     "\"google:verbatimrelevance\":9998}]",
1594      { { "a1",  true,  true },
1595        { "a",   true,  true },
1596        { "a2",  true,  true },
1597        { "k a", false, false },
1598        kEmptyMatch, kEmptyMatch },
1599      "1" },
1600
1601    // Check that non-inlinable matches may be ranked as the highest result
1602    // if there is at least one inlineable match.
1603    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1604      { { "b",   true,  false },
1605        { "a",   true,  true },
1606        { "k a", false, false },
1607        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1608      std::string() },
1609    { "[\"a\",[\"http://b.com\"],[],[],"
1610       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1611        "\"google:suggestrelevance\":[9999]}]",
1612      { { "b.com", false, false },
1613        { "a",     true,  true },
1614        { "k a",   false, false },
1615        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1616      std::string() },
1617    // On the other hand, if there is no inlineable match, restore
1618    // the keyword verbatim score.
1619    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1620                            "\"google:verbatimrelevance\":0}]",
1621      { { "b",   true,  false },
1622        { "a",   true,  true },
1623        { "k a", false, false },
1624        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1625      std::string() },
1626    { "[\"a\",[\"http://b.com\"],[],[],"
1627       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1628        "\"google:suggestrelevance\":[9999],"
1629        "\"google:verbatimrelevance\":0}]",
1630      { { "b.com", false, false },
1631        { "a",     true,  true },
1632        { "k a",   false, false },
1633        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1634      std::string() },
1635
1636    // The top result does not have to score as highly as calculated
1637    // verbatim.  i.e., there are no minimum score restrictions in
1638    // this provider.
1639    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1640      { { "a1",  true,  true },
1641        { "k a", false, false },
1642        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1643      "1" },
1644    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1645      { { "a1",  true,  true },
1646        { "k a", false, false },
1647        { "a",   true,  true },
1648        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1649      "1" },
1650    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1651                             "\"google:verbatimrelevance\":0}]",
1652      { { "k a", false, false },
1653        { "a1",   true, true },
1654        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1655      "1" },
1656    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1657                                     "\"google:verbatimrelevance\":0}]",
1658      {
1659        { "k a", false, false },
1660        { "a2",  true,  true },
1661        { "a1",  true,  true },
1662        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1663      "2" },
1664    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1665      "\"google:verbatimrelevance\":2}]",
1666      { { "k a", false, false },
1667        { "a2",  true,  true },
1668        { "a",   true,  true },
1669        { "a1",  true,  true },
1670        kEmptyMatch, kEmptyMatch },
1671      "2" },
1672
1673    // Ensure that all suggestions are considered, regardless of order.
1674    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1675       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1676      { { "a",   true,  true },
1677        { "k a", false, false },
1678        { "h",   true,  false },
1679        { "g",   true,  false },
1680        { "f",   true,  false },
1681        { "e",   true,  false } },
1682      std::string() },
1683    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1684              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1685              "\"http://h.com\"],[],[],"
1686       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1687                                "\"NAVIGATION\", \"NAVIGATION\","
1688                                "\"NAVIGATION\", \"NAVIGATION\","
1689                                "\"NAVIGATION\"],"
1690        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1691      { { "a",     true,  true },
1692        { "k a",   false, false },
1693        { "h.com", false, false },
1694        { "g.com", false, false },
1695        { "f.com", false, false },
1696        { "e.com", false, false } },
1697      std::string() },
1698
1699    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1700    // Note that keyword suggestions by default (not in suggested relevance
1701    // mode) score more highly than the default verbatim.
1702    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1703      { { "a",   true,  true },
1704        { "a1",  true,  true },
1705        { "a2",  true,  true },
1706        { "k a", false, false },
1707        kEmptyMatch, kEmptyMatch },
1708      std::string() },
1709    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1710      { { "a",   true,  true },
1711        { "a1",  true,  true },
1712        { "k a", false, false },
1713        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1714      std::string() },
1715    // In this case, ignoring the suggested relevance scores means we keep
1716    // only one navsuggest result.
1717    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1718       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1719        "\"google:suggestrelevance\":[1]}]",
1720      { { "a",      true,  true },
1721        { "a1.com", false, false },
1722        { "k a",    false, false },
1723        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1724      std::string() },
1725    { "[\"a\",[\"http://a1.com\"],[],[],"
1726       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1727       "\"google:suggestrelevance\":[9999, 1]}]",
1728      { { "a",      true,  true },
1729        { "a1.com", false, false },
1730        { "k a",    false, false },
1731        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1732      std::string() },
1733
1734    // Ensure that all 'verbatim' results are merged with their maximum score.
1735    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1736       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1737      { { "a2",  true,  true },
1738        { "a",   true,  true },
1739        { "a1",  true,  true },
1740        { "k a", false, false },
1741        kEmptyMatch, kEmptyMatch },
1742      "2" },
1743    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1744       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1745        "\"google:verbatimrelevance\":0}]",
1746      { { "a2",  true,  true },
1747        { "a",   true,  true },
1748        { "a1",  true,  true },
1749        { "k a", false, false },
1750        kEmptyMatch, kEmptyMatch },
1751      "2" },
1752
1753    // Ensure that verbatim is always generated without other suggestions.
1754    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1755    // (except when suggested relevances are ignored).
1756    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1757      { { "k a", false, false },
1758        { "a",   true,  true },
1759        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1760      std::string() },
1761    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1762      { { "a",   true,  true },
1763        { "k a", false, false },
1764        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1765      std::string() },
1766
1767    // In reorder mode, navsuggestions will not need to be demoted (because
1768    // they are marked as not allowed to be default match and will be
1769    // reordered as necessary).
1770    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1771       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1772        "\"google:verbatimrelevance\":9990,"
1773        "\"google:suggestrelevance\":[9998, 9999]}]",
1774      { { "a2.com", false, false },
1775        { "a1.com", false, false },
1776        { "a",      true,  true },
1777        { "k a",    false, false },
1778        kEmptyMatch, kEmptyMatch },
1779      std::string() },
1780    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1781       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1782        "\"google:verbatimrelevance\":9990,"
1783        "\"google:suggestrelevance\":[9999, 9998]}]",
1784      { { "a1.com", false, false },
1785        { "a2.com", false, false },
1786        { "a",      true,  true },
1787        { "k a",    false, false },
1788        kEmptyMatch, kEmptyMatch },
1789      std::string() },
1790    { "[\"a\",[\"https://a/\"],[],[],"
1791       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1792        "\"google:suggestrelevance\":[9999]}]",
1793      { { "https://a", false, false },
1794        { "a",         true,  true },
1795        { "k a",       false, false },
1796        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1797      std::string() },
1798    // Check when navsuggest scores more than verbatim and there is query
1799    // suggestion but it scores lower.
1800    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1801       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1802        "\"google:verbatimrelevance\":9990,"
1803        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1804      { { "a2.com", false, false },
1805        { "a1.com", false, false },
1806        { "a",      true,  true },
1807        { "a3",     true,  true },
1808        { "k a",    false, false },
1809        kEmptyMatch },
1810      std::string() },
1811    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1812       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1813        "\"google:verbatimrelevance\":9990,"
1814        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1815      { { "a1.com", false, false },
1816        { "a2.com", false, false },
1817        { "a",      true,  true },
1818        { "a3",     true,  true },
1819        { "k a",    false, false },
1820        kEmptyMatch },
1821      std::string() },
1822    // Check when navsuggest scores more than a query suggestion.  There is
1823    // a verbatim but it scores lower.
1824    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1825       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1826        "\"google:verbatimrelevance\":9990,"
1827        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1828      { { "a2.com", false, false },
1829        { "a1.com", false, false },
1830        { "a3",     true,  true },
1831        { "a",      true,  true },
1832        { "k a",    false, false },
1833        kEmptyMatch },
1834      "3" },
1835    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1836       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1837        "\"google:verbatimrelevance\":9990,"
1838        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1839      { { "a1.com", false, false },
1840        { "a2.com", false, false },
1841        { "a3",     true,  true },
1842        { "a",      true,  true },
1843        { "k a",    false, false },
1844        kEmptyMatch },
1845      "3" },
1846    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1847       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1848        "\"google:verbatimrelevance\":0,"
1849        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1850      { { "a2.com", false, false },
1851        { "a1.com", false, false },
1852        { "a3",     true,  true },
1853        { "k a",    false, false },
1854        kEmptyMatch, kEmptyMatch },
1855      "3" },
1856    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1857       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1858        "\"google:verbatimrelevance\":0,"
1859        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1860      { { "a1.com", false, false },
1861        { "a2.com", false, false },
1862        { "a3",     true,  true },
1863        { "k a",    false, false },
1864        kEmptyMatch, kEmptyMatch },
1865      "3" },
1866    // Check when there is neither verbatim nor a query suggestion that,
1867    // because we can't demote navsuggestions below a query suggestion,
1868    // we restore the keyword verbatim score.
1869    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1870       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1871        "\"google:verbatimrelevance\":0,"
1872        "\"google:suggestrelevance\":[9998, 9999]}]",
1873      { { "a2.com", false, false },
1874        { "a1.com", false, false },
1875        { "a",      true,  true },
1876        { "k a",    false, false },
1877        kEmptyMatch, kEmptyMatch },
1878      std::string() },
1879    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1880       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1881        "\"google:verbatimrelevance\":0,"
1882        "\"google:suggestrelevance\":[9999, 9998]}]",
1883      { { "a1.com", false, false },
1884        { "a2.com", false, false },
1885        { "a",      true,  true },
1886        { "k a",    false, false },
1887        kEmptyMatch, kEmptyMatch },
1888      std::string() },
1889    // More checks that everything works when it's not necessary to demote.
1890    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1891       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1892        "\"google:verbatimrelevance\":9990,"
1893        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1894      { { "a3",     true,  true },
1895        { "a2.com", false, false },
1896        { "a1.com", false, false },
1897        { "a",      true,  true },
1898        { "k a",    false, false },
1899        kEmptyMatch },
1900      "3" },
1901    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1902       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1903        "\"google:verbatimrelevance\":9990,"
1904        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1905      { { "a3",     true,  true },
1906        { "a1.com", false, false },
1907        { "a2.com", false, false },
1908        { "a",      true,  true },
1909        { "k a",    false, false },
1910        kEmptyMatch },
1911      "3" },
1912  };
1913
1914  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1915    QueryForInput(ASCIIToUTF16("k a"), false, true);
1916
1917    // Set up a default fetcher with no results.
1918    net::TestURLFetcher* default_fetcher =
1919        test_factory_.GetFetcherByID(
1920            SearchProvider::kDefaultProviderURLFetcherID);
1921    ASSERT_TRUE(default_fetcher);
1922    default_fetcher->set_response_code(200);
1923    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1924    default_fetcher = NULL;
1925
1926    // Set up a keyword fetcher with provided results.
1927    net::TestURLFetcher* keyword_fetcher =
1928        test_factory_.GetFetcherByID(
1929            SearchProvider::kKeywordProviderURLFetcherID);
1930    ASSERT_TRUE(keyword_fetcher);
1931    keyword_fetcher->set_response_code(200);
1932    keyword_fetcher->SetResponseString(cases[i].json);
1933    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1934    keyword_fetcher = NULL;
1935    RunTillProviderDone();
1936
1937    const std::string description = "for input with json=" + cases[i].json;
1938    const ACMatches& matches = provider_->matches();
1939    ASSERT_FALSE(matches.empty());
1940    // Find the first match that's allowed to be the default match and check
1941    // its inline_autocompletion.
1942    ACMatches::const_iterator it = FindDefaultMatch(matches);
1943    ASSERT_NE(matches.end(), it);
1944    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1945              it->inline_autocompletion) << description;
1946
1947    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1948    size_t j = 0;
1949    // Ensure that the returned matches equal the expectations.
1950    for (; j < matches.size(); ++j) {
1951      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1952                matches[j].contents) << description;
1953      EXPECT_EQ(cases[i].matches[j].from_keyword,
1954                matches[j].keyword == ASCIIToUTF16("k")) << description;
1955      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1956                matches[j].allowed_to_be_default_match) << description;
1957    }
1958    // Ensure that no expected matches are missing.
1959    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1960      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1961          "Case # " << i << " " << description;
1962  }
1963}
1964
1965TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
1966  // We hardcode the string "term1" below, so ensure that the search term that
1967  // got added to history already is that string.
1968  ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
1969  base::string16 term = term1_.substr(0, term1_.length() - 1);
1970
1971  AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
1972  profile_.BlockUntilHistoryProcessesPendingRequests();
1973
1974  struct {
1975    const base::string16 input;
1976    const std::string json;
1977    const std::string matches[6];
1978  } cases[] = {
1979    // The history results outscore the default verbatim score.  term2 has more
1980    // visits so it outscores term1.  The suggestions are still returned since
1981    // they're server-scored.
1982    { term,
1983      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1984       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1985        "\"google:suggestrelevance\":[1, 2, 3]}]",
1986      { "term2", "term1", "term", "a3", "a2", "a1" } },
1987    // Because we already have three suggestions by the time we see the history
1988    // results, they don't get returned.
1989    { term,
1990      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1991       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1992        "\"google:verbatimrelevance\":1450,"
1993        "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
1994      { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
1995    // If we only have two suggestions, we have room for a history result.
1996    { term,
1997      "[\"term\",[\"a1\", \"a2\"],[],[],"
1998       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
1999        "\"google:verbatimrelevance\":1450,"
2000        "\"google:suggestrelevance\":[1430, 1410]}]",
2001      { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2002    // If we have more than three suggestions, they should all be returned as
2003    // long as we have enough total space for them.
2004    { term,
2005      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2006       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2007        "\"google:verbatimrelevance\":1450,"
2008        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2009      { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2010    { term,
2011      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2012       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2013                                "\"QUERY\", \"QUERY\"],"
2014        "\"google:verbatimrelevance\":1450,"
2015        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2016      { "term", "a1", "a2", "a3", "a4", "a5" } },
2017    { term,
2018      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2019       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2020        "\"google:verbatimrelevance\":1450,"
2021        "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2022      { "term", "a1", "a2", "term2", "a3", "a4" } }
2023  };
2024
2025  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2026    QueryForInput(cases[i].input, false, false);
2027    net::TestURLFetcher* fetcher =
2028        test_factory_.GetFetcherByID(
2029            SearchProvider::kDefaultProviderURLFetcherID);
2030    ASSERT_TRUE(fetcher);
2031    fetcher->set_response_code(200);
2032    fetcher->SetResponseString(cases[i].json);
2033    fetcher->delegate()->OnURLFetchComplete(fetcher);
2034    RunTillProviderDone();
2035
2036    const std::string description = "for input with json=" + cases[i].json;
2037    const ACMatches& matches = provider_->matches();
2038
2039    // Ensure no extra matches are present.
2040    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2041
2042    size_t j = 0;
2043    // Ensure that the returned matches equal the expectations.
2044    for (; j < matches.size(); ++j)
2045      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2046                matches[j].contents) << description;
2047    // Ensure that no expected matches are missing.
2048    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2049      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2050          "Case # " << i << " " << description;
2051  }
2052}
2053
2054// Verifies suggest relevance behavior for URL input.
2055TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2056  struct DefaultFetcherUrlInputMatch {
2057    const std::string match_contents;
2058    AutocompleteMatch::Type match_type;
2059    bool allowed_to_be_default_match;
2060  };
2061  const DefaultFetcherUrlInputMatch kEmptyMatch =
2062      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2063  struct {
2064    const std::string input;
2065    const std::string json;
2066    const DefaultFetcherUrlInputMatch output[4];
2067  } cases[] = {
2068    // Ensure NAVIGATION matches are allowed to be listed first for URL
2069    // input regardless of whether the match is inlineable.  Note that
2070    // non-inlineable matches should not be allowed to be the default match.
2071    { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2072                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2073                 "\"google:suggestrelevance\":[9999]}]",
2074      { { "b.com",   AutocompleteMatchType::NAVSUGGEST,            false },
2075        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2076        kEmptyMatch, kEmptyMatch } },
2077    { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2078                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2079                 "\"google:suggestrelevance\":[9999]}]",
2080      { { "https://b.com", AutocompleteMatchType::NAVSUGGEST,           false },
2081        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2082        kEmptyMatch, kEmptyMatch } },
2083    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2084                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2085                 "\"google:suggestrelevance\":[9999]}]",
2086      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
2087        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2088        kEmptyMatch, kEmptyMatch } },
2089    { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2090                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2091                 "\"google:suggestrelevance\":[9999]}]",
2092      { { "https://a.com", AutocompleteMatchType::NAVSUGGEST,            true },
2093        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2094        kEmptyMatch, kEmptyMatch } },
2095
2096    // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2097    // input.  SearchProvider disregards search and verbatim suggested
2098    // relevances.
2099    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2100                "{\"google:suggestrelevance\":[9999]}]",
2101      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2102        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2103        kEmptyMatch, kEmptyMatch } },
2104    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2105                "{\"google:suggestrelevance\":[9999]}]",
2106      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,   true },
2107        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2108        kEmptyMatch, kEmptyMatch } },
2109
2110    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2111    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2112                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2113                 "\"google:suggestrelevance\":[9999, 9998]}]",
2114      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2115        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2116        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2117        kEmptyMatch } },
2118    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2119                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2120                 "\"google:suggestrelevance\":[9998, 9997],"
2121                 "\"google:verbatimrelevance\":9999}]",
2122      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2123        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2124        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2125        kEmptyMatch } },
2126
2127    // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
2128    // input assuming the top inlineable match is not a query (i.e., is a
2129    // NAVSUGGEST).
2130    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2131                "{\"google:suggestrelevance\":[9999]}]",
2132      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2133        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2134        kEmptyMatch, kEmptyMatch } },
2135    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2136                "{\"google:suggestrelevance\":[9999]}]",
2137      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2138        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2139        kEmptyMatch, kEmptyMatch } },
2140  };
2141
2142  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2143    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2144    net::TestURLFetcher* fetcher =
2145        test_factory_.GetFetcherByID(
2146            SearchProvider::kDefaultProviderURLFetcherID);
2147    ASSERT_TRUE(fetcher);
2148    fetcher->set_response_code(200);
2149    fetcher->SetResponseString(cases[i].json);
2150    fetcher->delegate()->OnURLFetchComplete(fetcher);
2151    RunTillProviderDone();
2152
2153    size_t j = 0;
2154    const ACMatches& matches = provider_->matches();
2155    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2156    // Ensure that the returned matches equal the expectations.
2157    for (; j < matches.size(); ++j) {
2158      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2159                matches[j].contents);
2160      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2161      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2162                matches[j].allowed_to_be_default_match);
2163    }
2164    // Ensure that no expected matches are missing.
2165    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2166      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2167      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2168                cases[i].output[j].match_type);
2169      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2170    }
2171  }
2172}
2173
2174// A basic test that verifies the field trial triggered parsing logic.
2175TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2176  QueryForInput(ASCIIToUTF16("foo"), false, false);
2177
2178  // Make sure the default providers suggest service was queried.
2179  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2180      SearchProvider::kDefaultProviderURLFetcherID);
2181  ASSERT_TRUE(fetcher);
2182
2183  // Tell the SearchProvider the suggest query is done.
2184  fetcher->set_response_code(200);
2185  fetcher->SetResponseString(
2186      "[\"foo\",[\"foo bar\"],[\"\"],[],"
2187      "{\"google:suggesttype\":[\"QUERY\"],"
2188      "\"google:fieldtrialtriggered\":true}]");
2189  fetcher->delegate()->OnURLFetchComplete(fetcher);
2190  fetcher = NULL;
2191
2192  // Run till the history results complete.
2193  RunTillProviderDone();
2194
2195  {
2196    // Check for the match and field trial triggered bits.
2197    AutocompleteMatch match;
2198    EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2199    ProvidersInfo providers_info;
2200    provider_->AddProviderInfo(&providers_info);
2201    ASSERT_EQ(1U, providers_info.size());
2202    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2203    EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2204  }
2205  {
2206    // Reset the session and check that bits are reset.
2207    provider_->ResetSession();
2208    ProvidersInfo providers_info;
2209    provider_->AddProviderInfo(&providers_info);
2210    ASSERT_EQ(1U, providers_info.size());
2211    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2212    EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2213  }
2214}
2215
2216// Verifies inline autocompletion of navigational results.
2217TEST_F(SearchProviderTest, NavigationInline) {
2218  struct {
2219    const std::string input;
2220    const std::string url;
2221    // Test the expected fill_into_edit, which may drop "http://".
2222    // Some cases do not trim "http://" to match from the start of the scheme.
2223    const std::string fill_into_edit;
2224    const std::string inline_autocompletion;
2225    const bool allowed_to_be_default_match_in_regular_mode;
2226    const bool allowed_to_be_default_match_in_prevent_inline_mode;
2227  } cases[] = {
2228    // Do not inline matches that do not contain the input; trim http as needed.
2229    { "x",                 "http://www.abc.com",
2230                                  "www.abc.com",  std::string(), false, false },
2231    { "https:",            "http://www.abc.com",
2232                                  "www.abc.com",  std::string(), false, false },
2233    { "http://www.abc.com/a", "http://www.abc.com",
2234                              "http://www.abc.com",  std::string(), false,
2235                                                                    false },
2236
2237    // Do not inline matches with invalid input prefixes; trim http as needed.
2238    { "ttp",              "http://www.abc.com",
2239                                 "www.abc.com", std::string(), false, false },
2240    { "://w",             "http://www.abc.com",
2241                                 "www.abc.com", std::string(), false, false },
2242    { "ww.",              "http://www.abc.com",
2243                                 "www.abc.com", std::string(), false, false },
2244    { ".ab",              "http://www.abc.com",
2245                                 "www.abc.com", std::string(), false, false },
2246    { "bc",               "http://www.abc.com",
2247                                 "www.abc.com", std::string(), false, false },
2248    { ".com",             "http://www.abc.com",
2249                                 "www.abc.com", std::string(), false, false },
2250
2251    // Do not inline matches that omit input domain labels; trim http as needed.
2252    { "www.a",            "http://a.com",
2253                                 "a.com",       std::string(), false, false },
2254    { "http://www.a",     "http://a.com",
2255                          "http://a.com",       std::string(), false, false },
2256    { "www.a",            "ftp://a.com",
2257                          "ftp://a.com",        std::string(), false, false },
2258    { "ftp://www.a",      "ftp://a.com",
2259                          "ftp://a.com",        std::string(), false, false },
2260
2261    // Input matching but with nothing to inline will not yield an offset, but
2262    // will be allowed to be default.
2263    { "abc.com",             "http://www.abc.com",
2264                                    "www.abc.com", std::string(), true, true },
2265    { "abc.com/",            "http://www.abc.com",
2266                                    "www.abc.com", std::string(), true, true },
2267    { "http://www.abc.com",  "http://www.abc.com",
2268                             "http://www.abc.com", std::string(), true, true },
2269    { "http://www.abc.com/", "http://www.abc.com",
2270                             "http://www.abc.com", std::string(), true, true },
2271
2272    // Inputs with trailing whitespace should inline when possible.
2273    { "abc.com ",      "http://www.abc.com",
2274                              "www.abc.com",      std::string(), true,  true },
2275    { "abc.com/ ",     "http://www.abc.com",
2276                              "www.abc.com",      std::string(), true,  true },
2277    { "abc.com ",      "http://www.abc.com/bar",
2278                              "www.abc.com/bar",  "/bar",        false, false },
2279
2280    // A suggestion that's equivalent to what the input gets fixed up to
2281    // should be inlined.
2282    { "abc.com:",      "http://abc.com/",
2283                              "abc.com",      "", true, true },
2284    { "abc.com:",      "http://www.abc.com",
2285                              "www.abc.com",  "", true, true },
2286
2287    // Inline matches when the input is a leading substring of the scheme.
2288    { "h",             "http://www.abc.com",
2289                       "http://www.abc.com", "ttp://www.abc.com", true, false },
2290    { "http",          "http://www.abc.com",
2291                       "http://www.abc.com", "://www.abc.com",    true, false },
2292
2293    // Inline matches when the input is a leading substring of the full URL.
2294    { "http:",             "http://www.abc.com",
2295                           "http://www.abc.com", "//www.abc.com", true, false },
2296    { "http://w",          "http://www.abc.com",
2297                           "http://www.abc.com", "ww.abc.com",    true, false },
2298    { "http://www.",       "http://www.abc.com",
2299                           "http://www.abc.com", "abc.com",       true, false },
2300    { "http://www.ab",     "http://www.abc.com",
2301                           "http://www.abc.com", "c.com",         true, false },
2302    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2303                              "http://www.abc.com/path/file.htm?q=x#foo",
2304                                                  "ath/file.htm?q=x#foo",
2305                                                                  true, false },
2306    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
2307                              "http://abc.com/path/file.htm?q=x#foo",
2308                                              "ath/file.htm?q=x#foo",
2309                                                                  true, false},
2310
2311    // Inline matches with valid URLPrefixes; only trim "http://".
2312    { "w",               "http://www.abc.com",
2313                                "www.abc.com", "ww.abc.com", true, false },
2314    { "www.a",           "http://www.abc.com",
2315                                "www.abc.com", "bc.com",     true, false },
2316    { "abc",             "http://www.abc.com",
2317                                "www.abc.com", ".com",       true, false },
2318    { "abc.c",           "http://www.abc.com",
2319                                "www.abc.com", "om",         true, false },
2320    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
2321                                "www.abc.com/path/file.htm?q=x#foo",
2322                                             "ath/file.htm?q=x#foo",
2323                                                             true, false },
2324    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
2325                                "abc.com/path/file.htm?q=x#foo",
2326                                         "ath/file.htm?q=x#foo",
2327                                                             true, false },
2328
2329    // Inline matches using the maximal URLPrefix components.
2330    { "h",               "http://help.com",
2331                                "help.com", "elp.com",     true, false },
2332    { "http",            "http://http.com",
2333                                "http.com", ".com",        true, false },
2334    { "h",               "http://www.help.com",
2335                                "www.help.com", "elp.com", true, false },
2336    { "http",            "http://www.http.com",
2337                                "www.http.com", ".com",    true, false },
2338    { "w",               "http://www.www.com",
2339                                "www.www.com",  "ww.com",  true, false },
2340
2341    // Test similar behavior for the ftp and https schemes.
2342    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
2343                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2344                                  "c.com/path/file.htm?q=x#foo",  true, false },
2345    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
2346                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2347                                   "c.com/path/file.htm?q=x#foo", true, false },
2348    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
2349                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2350                                   "c.com/path/file.htm?q=x#foo", true, false },
2351    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
2352                       "ftp://abc.com/path/file.htm?q=x#foo",
2353                               "c.com/path/file.htm?q=x#foo",     true, false },
2354    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
2355                         "https://www.abc.com/path/file.htm?q=x#foo",
2356                                       "c.com/path/file.htm?q=x#foo",
2357                                                                  true, false },
2358    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
2359                     "https://www.abc.com/path/file.htm?q=x#foo",
2360                                   "c.com/path/file.htm?q=x#foo", true, false },
2361    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
2362                     "https://www.abc.com/path/file.htm?q=x#foo",
2363                                   "c.com/path/file.htm?q=x#foo", true, false },
2364    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
2365                     "https://abc.com/path/file.htm?q=x#foo",
2366                               "c.com/path/file.htm?q=x#foo",     true, false },
2367
2368    // Forced query input should inline and retain the "?" prefix.
2369    { "?http://www.ab",  "http://www.abc.com",
2370                        "?http://www.abc.com", "c.com", true, false },
2371    { "?www.ab",         "http://www.abc.com",
2372                               "?www.abc.com", "c.com", true, false },
2373    { "?ab",             "http://www.abc.com",
2374                               "?www.abc.com", "c.com", true, false },
2375    { "?abc.com",        "http://www.abc.com",
2376                               "?www.abc.com", "",      true, true },
2377  };
2378
2379  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2380    // First test regular mode.
2381    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2382    AutocompleteMatch match(
2383        provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
2384            ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2385            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2386            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
2387    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2388              match.inline_autocompletion);
2389    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2390    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
2391              match.allowed_to_be_default_match);
2392
2393    // Then test prevent-inline-autocomplete mode.
2394    QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
2395    AutocompleteMatch match_prevent_inline(
2396        provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
2397            ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2398            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2399            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
2400    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2401              match_prevent_inline.inline_autocompletion);
2402    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
2403              match_prevent_inline.fill_into_edit);
2404    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
2405              match_prevent_inline.allowed_to_be_default_match);
2406  }
2407}
2408
2409// Verifies that "http://" is not trimmed for input that is a leading substring.
2410TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2411  const base::string16 input(ASCIIToUTF16("ht"));
2412  const base::string16 url(ASCIIToUTF16("http://a.com"));
2413  const SearchSuggestionParser::NavigationResult result(
2414      ChromeAutocompleteSchemeClassifier(&profile_), GURL(url),
2415      AutocompleteMatchType::NAVSUGGEST,
2416      base::string16(), std::string(), false, 0, false, input, std::string());
2417
2418  // Check the offset and strings when inline autocompletion is allowed.
2419  QueryForInput(input, false, false);
2420  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2421  EXPECT_EQ(url, match_inline.fill_into_edit);
2422  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2423  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2424  EXPECT_EQ(url, match_inline.contents);
2425
2426  // Check the same strings when inline autocompletion is prevented.
2427  QueryForInput(input, true, false);
2428  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2429  EXPECT_EQ(url, match_prevent.fill_into_edit);
2430  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2431  EXPECT_EQ(url, match_prevent.contents);
2432}
2433
2434// Verifies that input "w" marks a more significant domain label than "www.".
2435TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2436  QueryForInput(ASCIIToUTF16("w"), false, false);
2437  AutocompleteMatch match(
2438      provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
2439          ChromeAutocompleteSchemeClassifier(&profile_),
2440          GURL("http://www.wow.com"),
2441          AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2442          false, 0, false, ASCIIToUTF16("w"), std::string())));
2443  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2444  EXPECT_TRUE(match.allowed_to_be_default_match);
2445  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2446  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2447
2448  // Ensure that the match for input "w" is marked on "wow" and not "www".
2449  ASSERT_EQ(3U, match.contents_class.size());
2450  EXPECT_EQ(0U, match.contents_class[0].offset);
2451  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2452            match.contents_class[0].style);
2453  EXPECT_EQ(4U, match.contents_class[1].offset);
2454  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2455            AutocompleteMatch::ACMatchClassification::MATCH,
2456            match.contents_class[1].style);
2457  EXPECT_EQ(5U, match.contents_class[2].offset);
2458  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2459            match.contents_class[2].style);
2460}
2461
2462#if !defined(OS_WIN)
2463// Verify entity suggestion parsing.
2464TEST_F(SearchProviderTest, ParseEntitySuggestion) {
2465  struct Match {
2466    std::string contents;
2467    std::string description;
2468    std::string query_params;
2469    std::string fill_into_edit;
2470    AutocompleteMatchType::Type type;
2471  };
2472  const Match kEmptyMatch = {
2473    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
2474    AutocompleteMatchType::NUM_TYPES};
2475
2476  struct {
2477    const std::string input_text;
2478    const std::string response_json;
2479    const Match matches[5];
2480  } cases[] = {
2481    // A query and an entity suggestion with different search terms.
2482    { "x",
2483      "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
2484      " {\"google:suggestdetail\":[{},"
2485      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2486      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2487      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2488        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2489        { "xy", "A", "p=v", "yy",
2490          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2491        kEmptyMatch,
2492        kEmptyMatch
2493      },
2494    },
2495    // A query and an entity suggestion with same search terms.
2496    { "x",
2497      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2498      " {\"google:suggestdetail\":[{},"
2499      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2500      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2501      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2502        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2503        { "xy", "A", "p=v", "xy",
2504          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2505        kEmptyMatch,
2506        kEmptyMatch
2507      },
2508    },
2509  };
2510  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2511    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2512
2513    // Set up a default fetcher with provided results.
2514    net::TestURLFetcher* fetcher =
2515        test_factory_.GetFetcherByID(
2516            SearchProvider::kDefaultProviderURLFetcherID);
2517    ASSERT_TRUE(fetcher);
2518    fetcher->set_response_code(200);
2519    fetcher->SetResponseString(cases[i].response_json);
2520    fetcher->delegate()->OnURLFetchComplete(fetcher);
2521
2522    RunTillProviderDone();
2523
2524    const ACMatches& matches = provider_->matches();
2525    ASSERT_FALSE(matches.empty());
2526
2527    SCOPED_TRACE("for input with json = " + cases[i].response_json);
2528
2529    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2530    size_t j = 0;
2531    // Ensure that the returned matches equal the expectations.
2532    for (; j < matches.size(); ++j) {
2533      const Match& match = cases[i].matches[j];
2534      SCOPED_TRACE(" and match index: " + base::IntToString(j));
2535      EXPECT_EQ(match.contents,
2536                base::UTF16ToUTF8(matches[j].contents));
2537      EXPECT_EQ(match.description,
2538                base::UTF16ToUTF8(matches[j].description));
2539      EXPECT_EQ(match.query_params,
2540                matches[j].search_terms_args->suggest_query_params);
2541      EXPECT_EQ(match.fill_into_edit,
2542                base::UTF16ToUTF8(matches[j].fill_into_edit));
2543      EXPECT_EQ(match.type, matches[j].type);
2544    }
2545    // Ensure that no expected matches are missing.
2546    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2547      SCOPED_TRACE(" and match index: " + base::IntToString(j));
2548      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2549      EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
2550      EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2551      EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2552      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2553    }
2554  }
2555}
2556#endif  // !defined(OS_WIN)
2557
2558
2559// A basic test that verifies the prefetch metadata parsing logic.
2560TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2561  struct Match {
2562    std::string contents;
2563    bool allowed_to_be_prefetched;
2564    AutocompleteMatchType::Type type;
2565    bool from_keyword;
2566  };
2567  const Match kEmptyMatch = { kNotApplicable,
2568                              false,
2569                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2570                              false };
2571
2572  struct {
2573    const std::string input_text;
2574    bool prefer_keyword_provider_results;
2575    const std::string default_provider_response_json;
2576    const std::string keyword_provider_response_json;
2577    const Match matches[5];
2578  } cases[] = {
2579    // Default provider response does not have prefetch details. Ensure that the
2580    // suggestions are not marked as prefetch query.
2581    { "a",
2582      false,
2583      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2584      std::string(),
2585      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2586        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2587        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2588        kEmptyMatch,
2589        kEmptyMatch
2590      },
2591    },
2592    // Ensure that default provider suggest response prefetch details are
2593    // parsed and recorded in AutocompleteMatch.
2594    { "ab",
2595      false,
2596      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2597          "{\"google:clientdata\":{\"phi\": 0},"
2598          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2599          "\"google:suggestrelevance\":[999, 12, 1]}]",
2600      std::string(),
2601      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2602        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
2603        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2604        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2605        kEmptyMatch
2606      },
2607    },
2608    // Default provider suggest response has prefetch details.
2609    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2610    // the same query string. Ensure that the prefetch details from
2611    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2612    { "ab",
2613      false,
2614      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2615          "{\"google:clientdata\":{\"phi\": 0},"
2616          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2617          "\"google:suggestrelevance\":[99, 98]}]",
2618      std::string(),
2619      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2620        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2621        kEmptyMatch,
2622        kEmptyMatch,
2623        kEmptyMatch
2624      },
2625    },
2626    // Default provider response has prefetch details. We prefer keyword
2627    // provider results. Ensure that prefetch bit for a suggestion from the
2628    // default search provider does not get copied onto a higher-scoring match
2629    // for the same query string from the keyword provider.
2630    { "k a",
2631      true,
2632      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2633          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2634          "\"google:suggestrelevance\":[9, 12]}]",
2635      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2636      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2637        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2638        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2639        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2640        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2641      },
2642    }
2643  };
2644
2645  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2646    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
2647                  cases[i].prefer_keyword_provider_results);
2648
2649    // Set up a default fetcher with provided results.
2650    net::TestURLFetcher* fetcher =
2651        test_factory_.GetFetcherByID(
2652            SearchProvider::kDefaultProviderURLFetcherID);
2653    ASSERT_TRUE(fetcher);
2654    fetcher->set_response_code(200);
2655    fetcher->SetResponseString(cases[i].default_provider_response_json);
2656    fetcher->delegate()->OnURLFetchComplete(fetcher);
2657
2658    if (cases[i].prefer_keyword_provider_results) {
2659      // Set up a keyword fetcher with provided results.
2660      net::TestURLFetcher* keyword_fetcher =
2661          test_factory_.GetFetcherByID(
2662              SearchProvider::kKeywordProviderURLFetcherID);
2663      ASSERT_TRUE(keyword_fetcher);
2664      keyword_fetcher->set_response_code(200);
2665      keyword_fetcher->SetResponseString(
2666          cases[i].keyword_provider_response_json);
2667      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2668      keyword_fetcher = NULL;
2669    }
2670
2671    RunTillProviderDone();
2672
2673    const std::string description =
2674        "for input with json =" + cases[i].default_provider_response_json;
2675    const ACMatches& matches = provider_->matches();
2676    // The top match must inline and score as highly as calculated verbatim.
2677    ASSERT_FALSE(matches.empty());
2678    EXPECT_GE(matches[0].relevance, 1300);
2679
2680    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2681    // Ensure that the returned matches equal the expectations.
2682    for (size_t j = 0; j < matches.size(); ++j) {
2683      SCOPED_TRACE(description);
2684      EXPECT_EQ(cases[i].matches[j].contents,
2685                base::UTF16ToUTF8(matches[j].contents));
2686      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2687                SearchProvider::ShouldPrefetch(matches[j]));
2688      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2689      EXPECT_EQ(cases[i].matches[j].from_keyword,
2690                matches[j].keyword == ASCIIToUTF16("k"));
2691    }
2692  }
2693}
2694
2695TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
2696  ClearAllResults();
2697
2698  std::string input_str("abc");
2699  QueryForInput(ASCIIToUTF16(input_str), false, false);
2700
2701  // Set up a default fetcher with provided results.
2702  net::TestURLFetcher* fetcher =
2703      test_factory_.GetFetcherByID(
2704          SearchProvider::kDefaultProviderURLFetcherID);
2705  ASSERT_TRUE(fetcher);
2706  fetcher->set_response_code(200);
2707  fetcher->SetResponseString("this is a bad non-json response");
2708  fetcher->delegate()->OnURLFetchComplete(fetcher);
2709
2710  RunTillProviderDone();
2711
2712  const ACMatches& matches = provider_->matches();
2713
2714  // Should have exactly one "search what you typed" match
2715  ASSERT_TRUE(matches.size() == 1);
2716  EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
2717  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2718            matches[0].type);
2719}
2720
2721// A basic test that verifies that the XSSI guarded JSON response is parsed
2722// correctly.
2723TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
2724  struct Match {
2725    std::string contents;
2726    AutocompleteMatchType::Type type;
2727  };
2728  const Match kEmptyMatch = {
2729      kNotApplicable, AutocompleteMatchType::NUM_TYPES
2730  };
2731
2732  struct {
2733    const std::string input_text;
2734    const std::string default_provider_response_json;
2735    const Match matches[4];
2736  } cases[] = {
2737    // No XSSI guard.
2738    { "a",
2739      "[\"a\",[\"b\", \"c\"],[],[],"
2740      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2741      "\"google:suggestrelevance\":[1, 2]}]",
2742      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2743        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2744        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2745        kEmptyMatch,
2746      },
2747    },
2748    // Standard XSSI guard - )]}'\n.
2749    { "a",
2750      ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
2751      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2752      "\"google:suggestrelevance\":[1, 2]}]",
2753      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2754        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2755        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2756        kEmptyMatch,
2757      },
2758    },
2759    // Modified XSSI guard - contains "[".
2760    { "a",
2761      ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
2762      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2763      "\"google:suggestrelevance\":[1, 2]}]",
2764      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2765        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2766        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2767        kEmptyMatch,
2768      },
2769    },
2770  };
2771
2772  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2773    ClearAllResults();
2774    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2775
2776    // Set up a default fetcher with provided results.
2777    net::TestURLFetcher* fetcher =
2778        test_factory_.GetFetcherByID(
2779            SearchProvider::kDefaultProviderURLFetcherID);
2780    ASSERT_TRUE(fetcher);
2781    fetcher->set_response_code(200);
2782    fetcher->SetResponseString(cases[i].default_provider_response_json);
2783    fetcher->delegate()->OnURLFetchComplete(fetcher);
2784
2785    RunTillProviderDone();
2786
2787    const ACMatches& matches = provider_->matches();
2788    // The top match must inline and score as highly as calculated verbatim.
2789    ASSERT_FALSE(matches.empty());
2790    EXPECT_GE(matches[0].relevance, 1300);
2791
2792    SCOPED_TRACE("for case: " + base::IntToString(i));
2793    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2794    size_t j = 0;
2795    // Ensure that the returned matches equal the expectations.
2796    for (; j < matches.size(); ++j) {
2797      SCOPED_TRACE("and match: " + base::IntToString(j));
2798      EXPECT_EQ(cases[i].matches[j].contents,
2799                base::UTF16ToUTF8(matches[j].contents));
2800      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2801    }
2802    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2803      SCOPED_TRACE("and match: " + base::IntToString(j));
2804      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2805      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2806    }
2807  }
2808}
2809
2810// Test that deletion url gets set on an AutocompleteMatch when available for a
2811// personalized query or a personalized URL.
2812TEST_F(SearchProviderTest, ParseDeletionUrl) {
2813   struct Match {
2814     std::string contents;
2815     std::string deletion_url;
2816     AutocompleteMatchType::Type type;
2817   };
2818
2819   const Match kEmptyMatch = {
2820       kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
2821   };
2822
2823   const char* url[] = {
2824    "http://defaultturl/complete/deleteitems"
2825       "?delq=ab&client=chrome&deltok=xsrf124",
2826    "http://defaultturl/complete/deleteitems"
2827       "?delq=www.amazon.com&client=chrome&deltok=xsrf123",
2828     };
2829
2830   struct {
2831       const std::string input_text;
2832       const std::string response_json;
2833       const Match matches[5];
2834     } cases[] = {
2835       // A deletion URL on a personalized query should be reflected in the
2836       // resulting AutocompleteMatch.
2837       { "a",
2838         "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[],"
2839         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2840          "\"PERSONALIZED_NAVIGATION\"],"
2841         "\"google:suggestrelevance\":[3, 2, 1],"
2842         "\"google:suggestdetail\":[{\"du\":"
2843         "\"/complete/deleteitems?delq=ab&client=chrome"
2844          "&deltok=xsrf124\"}, {}, {\"du\":"
2845         "\"/complete/deleteitems?delq=www.amazon.com&"
2846         "client=chrome&deltok=xsrf123\"}]}]",
2847         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2848           { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST },
2849           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2850           { "www.amazon.com", url[1],
2851              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2852           kEmptyMatch,
2853         },
2854       },
2855       // Personalized queries or a personalized URL without deletion URLs
2856       // shouldn't cause errors.
2857       { "a",
2858         "[\"a\",[\"ab\", \"ac\"],[],[],"
2859         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2860         "\"PERSONALIZED_NAVIGATION\"],"
2861         "\"google:suggestrelevance\":[1, 2],"
2862         "\"google:suggestdetail\":[{}, {}]}]",
2863         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2864           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2865           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
2866           { "www.amazon.com", "",
2867              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2868           kEmptyMatch,
2869         },
2870       },
2871       // Personalized queries or a personalized URL without
2872       // google:suggestdetail shouldn't cause errors.
2873       { "a",
2874         "[\"a\",[\"ab\", \"ac\"],[],[],"
2875         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2876         "\"PERSONALIZED_NAVIGATION\"],"
2877         "\"google:suggestrelevance\":[1, 2]}]",
2878         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2879           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2880           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
2881           { "www.amazon.com", "",
2882              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2883           kEmptyMatch,
2884         },
2885       },
2886     };
2887
2888     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2889       QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2890
2891       net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2892           SearchProvider::kDefaultProviderURLFetcherID);
2893       ASSERT_TRUE(fetcher);
2894       fetcher->set_response_code(200);
2895       fetcher->SetResponseString(cases[i].response_json);
2896       fetcher->delegate()->OnURLFetchComplete(fetcher);
2897
2898       RunTillProviderDone();
2899
2900       const ACMatches& matches = provider_->matches();
2901       ASSERT_FALSE(matches.empty());
2902
2903       SCOPED_TRACE("for input with json = " + cases[i].response_json);
2904
2905       for (size_t j = 0; j < matches.size(); ++j) {
2906         const Match& match = cases[i].matches[j];
2907         SCOPED_TRACE(" and match index: " + base::IntToString(j));
2908         EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
2909         EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
2910             "deletion_url"));
2911       }
2912     }
2913}
2914
2915TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
2916  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
2917  base::string16 term = term1_.substr(0, term1_.length() - 1);
2918  QueryForInput(term, true, false);
2919  ASSERT_FALSE(provider_->matches().empty());
2920  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2921            provider_->matches()[0].type);
2922  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2923  EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
2924
2925  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
2926  term = term1_.substr(0, term1_.length() - 1);
2927  QueryForInput(term, true, false);
2928  ASSERT_FALSE(provider_->matches().empty());
2929  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2930            provider_->matches()[0].type);
2931  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2932  EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
2933}
2934
2935TEST_F(SearchProviderTest, CanSendURL) {
2936  TemplateURLData template_url_data;
2937  template_url_data.short_name = ASCIIToUTF16("t");
2938  template_url_data.SetURL("http://www.google.com/{searchTerms}");
2939  template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
2940  template_url_data.instant_url = "http://does/not/exist?strk=1";
2941  template_url_data.search_terms_replacement_key = "strk";
2942  template_url_data.id = SEARCH_ENGINE_GOOGLE;
2943  TemplateURL google_template_url(template_url_data);
2944
2945  // Create field trial.
2946  CreateZeroSuggestFieldTrial(true);
2947
2948  // Not signed in.
2949  EXPECT_FALSE(SearchProvider::CanSendURL(
2950      GURL("http://www.google.com/search"),
2951      GURL("https://www.google.com/complete/search"), &google_template_url,
2952      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
2953  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
2954  signin->SetAuthenticatedUsername("test");
2955
2956  // All conditions should be met.
2957  EXPECT_TRUE(SearchProvider::CanSendURL(
2958      GURL("http://www.google.com/search"),
2959      GURL("https://www.google.com/complete/search"), &google_template_url,
2960      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
2961
2962  // Not in field trial.
2963  ResetFieldTrialList();
2964  CreateZeroSuggestFieldTrial(false);
2965  EXPECT_FALSE(SearchProvider::CanSendURL(
2966      GURL("http://www.google.com/search"),
2967      GURL("https://www.google.com/complete/search"), &google_template_url,
2968      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
2969  ResetFieldTrialList();
2970  CreateZeroSuggestFieldTrial(true);
2971
2972  // Invalid page URL.
2973  EXPECT_FALSE(SearchProvider::CanSendURL(
2974      GURL("badpageurl"),
2975      GURL("https://www.google.com/complete/search"), &google_template_url,
2976      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
2977
2978  // Invalid page classification.
2979  EXPECT_FALSE(SearchProvider::CanSendURL(
2980      GURL("http://www.google.com/search"),
2981      GURL("https://www.google.com/complete/search"), &google_template_url,
2982      metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
2983      SearchTermsData(), &profile_));
2984
2985  // Invalid page classification.
2986  EXPECT_FALSE(SearchProvider::CanSendURL(
2987      GURL("http://www.google.com/search"),
2988      GURL("https://www.google.com/complete/search"), &google_template_url,
2989      metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
2990      SearchTermsData(), &profile_));
2991
2992  // HTTPS page URL on same domain as provider.
2993  EXPECT_TRUE(SearchProvider::CanSendURL(
2994      GURL("https://www.google.com/search"),
2995      GURL("https://www.google.com/complete/search"),
2996      &google_template_url, metrics::OmniboxEventProto::OTHER,
2997      SearchTermsData(), &profile_));
2998
2999  // Non-HTTP[S] page URL on same domain as provider.
3000  EXPECT_FALSE(SearchProvider::CanSendURL(
3001      GURL("ftp://www.google.com/search"),
3002      GURL("https://www.google.com/complete/search"), &google_template_url,
3003      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3004
3005  // Non-HTTP page URL on different domain.
3006  EXPECT_FALSE(SearchProvider::CanSendURL(
3007      GURL("https://www.notgoogle.com/search"),
3008      GURL("https://www.google.com/complete/search"), &google_template_url,
3009      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3010
3011  // Non-HTTPS provider.
3012  EXPECT_FALSE(SearchProvider::CanSendURL(
3013      GURL("http://www.google.com/search"),
3014      GURL("http://www.google.com/complete/search"), &google_template_url,
3015      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3016
3017  // Suggest disabled.
3018  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3019  EXPECT_FALSE(SearchProvider::CanSendURL(
3020      GURL("http://www.google.com/search"),
3021      GURL("https://www.google.com/complete/search"), &google_template_url,
3022      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3023  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3024
3025  // Incognito.
3026  EXPECT_FALSE(SearchProvider::CanSendURL(
3027      GURL("http://www.google.com/search"),
3028      GURL("https://www.google.com/complete/search"), &google_template_url,
3029      metrics::OmniboxEventProto::OTHER, SearchTermsData(),
3030      profile_.GetOffTheRecordProfile()));
3031
3032  // Tab sync not enabled.
3033  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced,
3034                                  false);
3035  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false);
3036  EXPECT_FALSE(SearchProvider::CanSendURL(
3037      GURL("http://www.google.com/search"),
3038      GURL("https://www.google.com/complete/search"), &google_template_url,
3039      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3040  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true);
3041
3042  // Tab sync is encrypted.
3043  ProfileSyncService* service =
3044      ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
3045  syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
3046  encrypted_types.Put(syncer::SESSIONS);
3047  service->OnEncryptedTypesChanged(encrypted_types, false);
3048  EXPECT_FALSE(SearchProvider::CanSendURL(
3049      GURL("http://www.google.com/search"),
3050      GURL("https://www.google.com/complete/search"), &google_template_url,
3051      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3052  encrypted_types.Remove(syncer::SESSIONS);
3053  service->OnEncryptedTypesChanged(encrypted_types, false);
3054
3055  // Check that there were no side effects from previous tests.
3056  EXPECT_TRUE(SearchProvider::CanSendURL(
3057      GURL("http://www.google.com/search"),
3058      GURL("https://www.google.com/complete/search"), &google_template_url,
3059      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
3060}
3061
3062TEST_F(SearchProviderTest, TestDeleteMatch) {
3063  AutocompleteMatch match(provider_, 0, true,
3064                          AutocompleteMatchType::SEARCH_SUGGEST);
3065  match.RecordAdditionalInfo(
3066      SearchProvider::kDeletionUrlKey,
3067      "https://www.google.com/complete/deleteitem?q=foo");
3068
3069  // Test a successful deletion request.
3070  provider_->matches_.push_back(match);
3071  provider_->DeleteMatch(match);
3072  EXPECT_FALSE(provider_->deletion_handlers_.empty());
3073  EXPECT_TRUE(provider_->matches_.empty());
3074  // Set up a default fetcher with provided results.
3075  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3076      SearchProvider::kDeletionURLFetcherID);
3077  ASSERT_TRUE(fetcher);
3078  fetcher->set_response_code(200);
3079  fetcher->delegate()->OnURLFetchComplete(fetcher);
3080  EXPECT_TRUE(provider_->deletion_handlers_.empty());
3081  EXPECT_TRUE(provider_->is_success());
3082
3083  // Test a failing deletion request.
3084  provider_->matches_.push_back(match);
3085  provider_->DeleteMatch(match);
3086  EXPECT_FALSE(provider_->deletion_handlers_.empty());
3087  // Set up a default fetcher with provided results.
3088  fetcher = test_factory_.GetFetcherByID(
3089      SearchProvider::kDeletionURLFetcherID);
3090  ASSERT_TRUE(fetcher);
3091  fetcher->set_response_code(500);
3092  fetcher->delegate()->OnURLFetchComplete(fetcher);
3093  EXPECT_TRUE(provider_->deletion_handlers_.empty());
3094  EXPECT_FALSE(provider_->is_success());
3095}
3096
3097TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
3098  GURL term_url(
3099      AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
3100  profile_.BlockUntilHistoryProcessesPendingRequests();
3101
3102  AutocompleteMatch games;
3103  QueryForInput(ASCIIToUTF16("fla"), false, false);
3104  profile_.BlockUntilHistoryProcessesPendingRequests();
3105  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3106  ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3107
3108  size_t matches_before = provider_->matches().size();
3109  provider_->DeleteMatch(games);
3110  EXPECT_EQ(matches_before - 1, provider_->matches().size());
3111
3112  // Process history deletions.
3113  profile_.BlockUntilHistoryProcessesPendingRequests();
3114
3115  // Check that the match is gone.
3116  QueryForInput(ASCIIToUTF16("fla"), false, false);
3117  profile_.BlockUntilHistoryProcessesPendingRequests();
3118  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3119  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3120}
3121
3122// Verifies that duplicates are preserved in AddMatchToMap().
3123TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
3124  AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
3125  AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
3126  AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
3127
3128  profile_.BlockUntilHistoryProcessesPendingRequests();
3129  QueryForInput(ASCIIToUTF16("a"), false, false);
3130
3131  // Make sure the default provider's suggest service was queried.
3132  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3133      SearchProvider::kDefaultProviderURLFetcherID);
3134  ASSERT_TRUE(fetcher);
3135
3136  // Tell the SearchProvider the suggest query is done.
3137  fetcher->set_response_code(200);
3138  fetcher->SetResponseString(
3139      "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
3140      "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
3141      "\"google:verbatimrelevance\":1350}]");
3142  fetcher->delegate()->OnURLFetchComplete(fetcher);
3143  fetcher = NULL;
3144
3145  // Run till the history results complete.
3146  RunTillProviderDone();
3147
3148  AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
3149  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
3150  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
3151  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
3152  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
3153
3154  // Verbatim match duplicates are added such that each one has a higher
3155  // relevance than the previous one.
3156  EXPECT_EQ(2U, verbatim.duplicate_matches.size());
3157
3158  // Other match duplicates are added in descending relevance order.
3159  EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
3160  EXPECT_EQ(1U, match_avid.duplicate_matches.size());
3161
3162  EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
3163}
3164
3165TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
3166  CommandLine::ForCurrentProcess()->AppendSwitch(
3167      switches::kEnableAnswersInSuggest);
3168
3169  TemplateURLService* turl_model =
3170      TemplateURLServiceFactory::GetForProfile(&profile_);
3171
3172  TemplateURLData data;
3173  data.short_name = ASCIIToUTF16("default");
3174  data.SetKeyword(data.short_name);
3175  data.SetURL("http://example/{searchTerms}{google:sessionToken}");
3176  data.suggestions_url =
3177      "http://suggest/?q={searchTerms}&{google:sessionToken}";
3178  default_t_url_ = new TemplateURL(data);
3179  turl_model->Add(default_t_url_);
3180  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
3181
3182  base::string16 term = term1_.substr(0, term1_.length() - 1);
3183  QueryForInput(term, false, false);
3184
3185  // Make sure the default provider's suggest service was queried.
3186  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3187      SearchProvider::kDefaultProviderURLFetcherID);
3188  ASSERT_TRUE(fetcher);
3189
3190  // And the URL matches what we expected.
3191  TemplateURLRef::SearchTermsArgs search_terms_args(term);
3192  search_terms_args.session_token = provider_->current_token_;
3193  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
3194      search_terms_args, turl_model->search_terms_data()));
3195  EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec());
3196
3197  // Complete running the fetcher to clean up.
3198  fetcher->set_response_code(200);
3199  fetcher->delegate()->OnURLFetchComplete(fetcher);
3200  RunTillProviderDone();
3201}
3202
3203TEST_F(SearchProviderTest, SessionToken) {
3204  // Subsequent calls always get the same token.
3205  std::string token = provider_->GetSessionToken();
3206  std::string token2 = provider_->GetSessionToken();
3207  EXPECT_EQ(token, token2);
3208  EXPECT_FALSE(token.empty());
3209
3210  // Calls do not regenerate a token.
3211  provider_->current_token_ = "PRE-EXISTING TOKEN";
3212  token = provider_->GetSessionToken();
3213  EXPECT_EQ(token, "PRE-EXISTING TOKEN");
3214
3215  // ... unless the token has expired.
3216  provider_->current_token_.clear();
3217  const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1);
3218  provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta;
3219  token = provider_->GetSessionToken();
3220  EXPECT_FALSE(token.empty());
3221  EXPECT_EQ(token, provider_->current_token_);
3222
3223  // The expiration time is always updated.
3224  provider_->GetSessionToken();
3225  base::TimeTicks expiration_time_1 = provider_->token_expiration_time_;
3226  base::PlatformThread::Sleep(kSmallDelta);
3227  provider_->GetSessionToken();
3228  base::TimeTicks expiration_time_2 = provider_->token_expiration_time_;
3229  EXPECT_GT(expiration_time_2, expiration_time_1);
3230  EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta);
3231}
3232
3233TEST_F(SearchProviderTest, AnswersCache) {
3234  AutocompleteResult result;
3235  ACMatches matches;
3236  AutocompleteMatch match1;
3237  match1.answer_contents = base::ASCIIToUTF16("m1");
3238  match1.answer_type = base::ASCIIToUTF16("2334");
3239  match1.fill_into_edit = base::ASCIIToUTF16("weather los angeles");
3240
3241  AutocompleteMatch non_answer_match1;
3242  non_answer_match1.fill_into_edit = base::ASCIIToUTF16("weather laguna beach");
3243
3244  // Test that an answer in the first slot populates the cache.
3245  matches.push_back(match1);
3246  matches.push_back(non_answer_match1);
3247  result.AppendMatches(matches);
3248  provider_->RegisterDisplayedAnswers(result);
3249  ASSERT_FALSE(provider_->answers_cache_.empty());
3250
3251  // Test that DoAnswersQuery retrieves data from cache.
3252  AutocompleteInput input(base::ASCIIToUTF16("weather l"),
3253                          base::string16::npos, base::string16(), GURL(),
3254                          metrics::OmniboxEventProto::INVALID_SPEC, false,
3255                          false, true, true,
3256                          ChromeAutocompleteSchemeClassifier(&profile_));
3257  provider_->DoAnswersQuery(input);
3258  EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"),
3259            provider_->prefetch_data_.full_query_text);
3260  EXPECT_EQ(base::ASCIIToUTF16("2334"), provider_->prefetch_data_.query_type);
3261}
3262