Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / autocomplete / search_provider_unittest.cc
blobabb7e7041efe79f8a3668d688f80c0355e61723b
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.
5 #include "components/omnibox/search_provider.h"
7 #include <string>
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"
56 using base::ASCIIToUTF16;
58 namespace {
60 // Returns the first match in |matches| with |allowed_to_be_default_match|
61 // set to true.
62 ACMatches::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;
69 class SuggestionDeletionHandler;
70 class SearchProviderForTest : public SearchProvider {
71 public:
72 SearchProviderForTest(AutocompleteProviderListener* listener,
73 TemplateURLService* template_url_service,
74 Profile* profile);
75 bool is_success() { return is_success_; };
77 protected:
78 virtual ~SearchProviderForTest();
80 private:
81 virtual void RecordDeletionResult(bool success) OVERRIDE;
82 bool is_success_;
83 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
86 SearchProviderForTest::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) {
96 SearchProviderForTest::~SearchProviderForTest() {
99 void SearchProviderForTest::RecordDeletionResult(bool success) {
100 is_success_ = success;
103 } // namespace
105 // SearchProviderTest ---------------------------------------------------------
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.
116 class 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) {
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) {
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;
139 struct TestData {
140 const base::string16 input;
141 const size_t num_results;
142 const ResultInfo output[3];
145 struct ExpectedMatch {
146 std::string contents;
147 bool allowed_to_be_default_match;
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();
159 // See description above class for what this registers.
160 virtual void SetUp() OVERRIDE;
161 virtual void TearDown() OVERRIDE;
163 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
165 protected:
166 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
167 scoped_ptr<base::FieldTrialList> field_trial_list_;
169 // Default values used for testing.
170 static const std::string kNotApplicable;
171 static const ExpectedMatch kEmptyExpectedMatch;
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);
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);
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);
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;
190 // Runs a nested message loop until provider_ is done. The message loop is
191 // exited by way of OnProviderUpdate.
192 void RunTillProviderDone();
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);
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);
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);
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();
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);
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);
237 void ResetFieldTrialList();
239 // Create a field trial, with ZeroSuggest activation based on |enabled|.
240 base::FieldTrial* CreateZeroSuggestFieldTrial(bool enabled);
242 void ClearAllResults();
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_;
252 content::TestBrowserThreadBundle thread_bundle_;
254 // URLFetcherFactory implementation registered.
255 net::TestURLFetcherFactory test_factory_;
257 // Profile we use.
258 TestingProfile profile_;
260 // The provider.
261 scoped_refptr<SearchProviderForTest> provider_;
263 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
264 base::RunLoop* run_loop_;
266 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
269 // static
270 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
271 const SearchProviderTest::ExpectedMatch
272 SearchProviderTest::kEmptyExpectedMatch = { kNotApplicable, false };
274 void SearchProviderTest::SetUp() {
275 // Make sure that fetchers are automatically ungregistered upon destruction.
276 test_factory_.set_remove_fetcher_on_delete(true);
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);
283 TemplateURLService* turl_model =
284 TemplateURLServiceFactory::GetForProfile(&profile_);
286 turl_model->Load();
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);
301 // Add url1, with search term term1_.
302 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
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());
313 // Add a page and search term for keyword_t_url_.
314 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
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();
321 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
322 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
324 provider_ = new SearchProviderForTest(this, turl_model, &profile_);
325 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
328 void SearchProviderTest::TearDown() {
329 base::RunLoop().RunUntilIdle();
331 // Shutdown the provider before the profile.
332 provider_ = NULL;
335 void 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);
366 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
367 if (run_loop_ && provider_->done()) {
368 run_loop_->Quit();
369 run_loop_ = NULL;
373 void SearchProviderTest::RunTillProviderDone() {
374 if (provider_->done())
375 return;
377 base::RunLoop run_loop;
378 run_loop_ = &run_loop;
379 run_loop.Run();
382 void 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);
392 // RunUntilIdle so that the task scheduled by SearchProvider to create the
393 // URLFetchers runs.
394 base::RunLoop().RunUntilIdle();
397 void 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));
415 void 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);
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);
437 RunTillProviderDone();
440 GURL 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;
459 bool 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;
468 return false;
471 bool 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;
480 return false;
483 void SearchProviderTest::FinishDefaultSuggestQuery() {
484 net::TestURLFetcher* default_fetcher =
485 test_factory_.GetFetcherByID(
486 SearchProvider::kDefaultProviderURLFetcherID);
487 ASSERT_TRUE(default_fetcher);
489 // Tell the SearchProvider the default suggest query is done.
490 default_fetcher->set_response_code(200);
491 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
494 void 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());
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;
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;
522 void 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);
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);
544 void 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();
556 base::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");
567 void SearchProviderTest::ClearAllResults() {
568 provider_->ClearAllResults();
571 // Actual Tests ---------------------------------------------------------------
573 // Make sure we query history for the default provider and a URLFetcher is
574 // created for the default provider suggest results.
575 TEST_F(SearchProviderTest, QueryDefaultProvider) {
576 base::string16 term = term1_.substr(0, term1_.length() - 1);
577 QueryForInput(term, false, false);
579 // Make sure the default providers suggest service was queried.
580 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
581 SearchProvider::kDefaultProviderURLFetcherID);
582 ASSERT_TRUE(fetcher);
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);
591 // Tell the SearchProvider the suggest query is done.
592 fetcher->set_response_code(200);
593 fetcher->delegate()->OnURLFetchComplete(fetcher);
594 fetcher = NULL;
596 // Run till the history results complete.
597 RunTillProviderDone();
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());
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());
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);
623 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
624 base::string16 term = term1_.substr(0, term1_.length() - 1);
625 QueryForInput(term, true, false);
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);
633 // Issues a query that matches the registered keyword and makes sure history
634 // is queried as well as URLFetchers getting created.
635 TEST_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);
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);
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;
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);
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);
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;
668 // Run till the history results complete.
669 RunTillProviderDone();
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));
676 // The match should have an associated keyword.
677 EXPECT_FALSE(match.keyword.empty());
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);
684 TEST_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",
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();
712 TEST_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"
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);
736 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
737 GURL url = AddSearchToHistory(default_t_url_,
738 ASCIIToUTF16("docs.google.com"), 1);
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();
746 AutocompleteMatch wyt_match;
747 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
748 &wyt_match));
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);
761 TEST_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 } },
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 } },
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 } },
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 } },
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);
809 // A multiword search with one visit should not autocomplete until multiple
810 // words are typed.
811 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
812 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
813 1));
814 profile_.BlockUntilHistoryProcessesPendingRequests();
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);
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);
835 // A multiword search with more than one visit should autocomplete immediately.
836 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
837 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
838 2));
839 profile_.BlockUntilHistoryProcessesPendingRequests();
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);
852 // Autocompletion should work at a word boundary after a space, and should
853 // offer a suggestion for the trimmed search query.
854 TEST_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();
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);
875 // Newer multiword searches should score more highly than older ones.
876 TEST_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();
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);
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.
901 TEST_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();
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);
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);
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);
956 // Non-completable multiword searches should not crowd out single-word searches.
957 TEST_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();
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);
976 // Inline autocomplete matches regardless of case differences from the input.
977 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
978 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
979 profile_.BlockUntilHistoryProcessesPendingRequests();
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);
993 // Verifies AutocompleteControllers return results (including keyword
994 // results) in the right order and set descriptions for them correctly.
995 TEST_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();
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();
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);
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);
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);
1041 TEST_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") ) } },
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.
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")) } },
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")) } },
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")) } },
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")) } }
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.
1140 // Test not in keyword mode.
1141 RunTest(cases, arraysize(cases), false);
1143 // Test in keyword mode. (Both modes should give the same result.)
1144 RunTest(cases, arraysize(cases), true);
1147 // Ensures command-line flags are reflected in the URLs the search provider
1148 // generates.
1149 TEST_F(SearchProviderTest, CommandLineOverrides) {
1150 TemplateURLService* turl_model =
1151 TemplateURLServiceFactory::GetForProfile(&profile_);
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_);
1161 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
1162 "http://www.bar.com/");
1163 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1164 switches::kExtraSearchQueryParams, "a=b");
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")) } },
1178 RunTest(cases, arraysize(cases), false);
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.
1184 TEST_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());
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));
1199 // Verifies that the most relevant suggest results are added properly.
1200 TEST_F(SearchProviderTest, SuggestRelevance) {
1201 QueryForInputAndWaitForFetcherResponses(
1202 ASCIIToUTF16("a"), false, "[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]",
1203 std::string());
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);
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.
1224 TEST_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" } },
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" } }
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);
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]);
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.
1280 TEST_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() },
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() },
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" },
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" },
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() },
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" },
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() },
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() },
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" },
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() },
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());
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());
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.
1518 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1519 struct KeywordFetcherMatch {
1520 std::string contents;
1521 bool from_keyword;
1522 bool allowed_to_be_default_match;
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() },
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() },
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() },
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" },
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() },
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" },
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() },
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() },
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" },
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() },
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" },
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);
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;
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();
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);
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);
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);
1984 TEST_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 } } },
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 } },
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 } },
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 } },
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());
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());
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());
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());
2201 TEST_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);
2207 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2208 profile_.BlockUntilHistoryProcessesPendingRequests();
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" } }
2261 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
2262 QueryForInputAndWaitForFetcherResponses(
2263 cases[i].input, false, cases[i].json, std::string());
2265 const std::string description = "for input with json=" + cases[i].json;
2266 const ACMatches& matches = provider_->matches();
2268 // Ensure no extra matches are present.
2269 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
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;
2283 // Verifies suggest relevance behavior for URL input.
2284 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2285 struct DefaultFetcherUrlInputMatch {
2286 const std::string match_contents;
2287 AutocompleteMatch::Type match_type;
2288 bool allowed_to_be_default_match;
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 } },
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 } },
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 } },
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 } },
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());
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);
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);
2404 // A basic test that verifies the field trial triggered parsing logic.
2405 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2406 QueryForInputAndWaitForFetcherResponses(
2407 ASCIIToUTF16("foo"), false,
2408 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2409 "{\"google:suggesttype\":[\"QUERY\"],"
2410 "\"google:fieldtrialtriggered\":true}]",
2411 std::string());
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());
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());
2434 // Verifies inline autocompletion of navigational results.
2435 TEST_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 },
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 },
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 },
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 },
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 },
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 },
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 },
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},
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 },
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 },
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 },
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 },
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);
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);
2630 // Verifies that "http://" is not trimmed for input that is a leading substring.
2631 TEST_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);
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);
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);
2656 // Verifies that input "w" marks a more significant domain label than "www.".
2657 TEST_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);
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);
2685 #if !defined(OS_WIN)
2686 // Verify entity suggestion parsing.
2687 TEST_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;
2695 const Match kEmptyMatch = {
2696 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
2697 AutocompleteMatchType::NUM_TYPES};
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
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
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());
2738 const ACMatches& matches = provider_->matches();
2739 ASSERT_FALSE(matches.empty());
2741 SCOPED_TRACE("for input with json = " + cases[i].response_json);
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);
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);
2770 #endif // !defined(OS_WIN)
2773 // A basic test that verifies the prefetch metadata parsing logic.
2774 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2775 struct Match {
2776 std::string contents;
2777 bool allowed_to_be_prefetched;
2778 AutocompleteMatchType::Type type;
2779 bool from_keyword;
2781 const Match kEmptyMatch = { kNotApplicable,
2782 false,
2783 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2784 false };
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
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
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
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 }
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());
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);
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"));
2889 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
2890 ClearAllResults();
2892 std::string input_str("abc");
2893 QueryForInputAndWaitForFetcherResponses(
2894 ASCIIToUTF16(input_str), false, "this is a bad non-json response",
2895 std::string());
2897 const ACMatches& matches = provider_->matches();
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);
2906 // A basic test that verifies that the XSSI guarded JSON response is parsed
2907 // correctly.
2908 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
2909 struct Match {
2910 std::string contents;
2911 AutocompleteMatchType::Type type;
2913 const Match kEmptyMatch = {
2914 kNotApplicable, AutocompleteMatchType::NUM_TYPES
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,
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,
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,
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());
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);
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);
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);
2986 // Test that deletion url gets set on an AutocompleteMatch when available for a
2987 // personalized query or a personalized URL.
2988 TEST_F(SearchProviderTest, ParseDeletionUrl) {
2989 struct Match {
2990 std::string contents;
2991 std::string deletion_url;
2992 AutocompleteMatchType::Type type;
2995 const Match kEmptyMatch = {
2996 kNotApplicable, std::string(), AutocompleteMatchType::NUM_TYPES
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",
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,
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,
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,
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());
3069 const ACMatches& matches = provider_->matches();
3070 ASSERT_FALSE(matches.empty());
3072 SCOPED_TRACE("for input with json = " + cases[i].response_json);
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"));
3084 TEST_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);
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);
3104 TEST_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);
3114 // Create field trial.
3115 CreateZeroSuggestFieldTrial(true);
3117 ChromeAutocompleteProviderClient client(&profile_);
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");
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));
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);
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));
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));
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));
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));
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));
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));
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));
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);
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));
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);
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);
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));
3235 TEST_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");
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());
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());
3270 TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
3271 GURL term_url(
3272 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
3273 profile_.BlockUntilHistoryProcessesPendingRequests();
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));
3281 size_t matches_before = provider_->matches().size();
3282 provider_->DeleteMatch(games);
3283 EXPECT_EQ(matches_before - 1, provider_->matches().size());
3285 // Process history deletions.
3286 profile_.BlockUntilHistoryProcessesPendingRequests();
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));
3295 // Verifies that duplicates are preserved in AddMatchToMap().
3296 TEST_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);
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());
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));
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());
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());
3323 EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
3326 TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
3327 CommandLine::ForCurrentProcess()->AppendSwitch(
3328 switches::kEnableAnswersInSuggest);
3330 TemplateURLService* turl_model =
3331 TemplateURLServiceFactory::GetForProfile(&profile_);
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_);
3343 base::string16 term = term1_.substr(0, term1_.length() - 1);
3344 QueryForInput(term, false, false);
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);
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());
3358 // Complete running the fetcher to clean up.
3359 fetcher->set_response_code(200);
3360 fetcher->delegate()->OnURLFetchComplete(fetcher);
3361 RunTillProviderDone();
3364 TEST_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());
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");
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_);
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);
3394 TEST_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");
3402 AutocompleteMatch non_answer_match1;
3403 non_answer_match1.fill_into_edit = base::ASCIIToUTF16("weather laguna beach");
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());
3412 // Test that DoAnswersQuery retrieves data from cache.
3413 AutocompleteInput input(base::ASCIIToUTF16("weather l"),
3414 base::string16::npos, base::string16(), GURL(),
3415 metrics::OmniboxEventProto::INVALID_SPEC, false,
3416 false, true, true,
3417 ChromeAutocompleteSchemeClassifier(&profile_));
3418 provider_->DoAnswersQuery(input);
3419 EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"),
3420 provider_->prefetch_data_.full_query_text);
3421 EXPECT_EQ(base::ASCIIToUTF16("2334"), provider_->prefetch_data_.query_type);