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