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