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