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