autocomplete_provider_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/omnibox/autocomplete_provider.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/message_loop/message_loop.h"
11#include "base/strings/string16.h"
12#include "base/strings/string_number_conversions.h"
13#include "base/strings/string_util.h"
14#include "base/strings/utf_string_conversions.h"
15#include "chrome/browser/autocomplete/autocomplete_controller.h"
16#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
17#include "chrome/browser/chrome_notification_types.h"
18#include "chrome/browser/search_engines/template_url_service_factory.h"
19#include "chrome/test/base/testing_browser_process.h"
20#include "chrome/test/base/testing_profile.h"
21#include "components/metrics/proto/omnibox_event.pb.h"
22#include "components/omnibox/autocomplete_input.h"
23#include "components/omnibox/autocomplete_match.h"
24#include "components/omnibox/autocomplete_provider_listener.h"
25#include "components/omnibox/keyword_provider.h"
26#include "components/omnibox/search_provider.h"
27#include "components/search_engines/search_engines_switches.h"
28#include "components/search_engines/template_url.h"
29#include "components/search_engines/template_url_service.h"
30#include "content/public/browser/notification_observer.h"
31#include "content/public/browser/notification_registrar.h"
32#include "content/public/browser/notification_source.h"
33#include "testing/gtest/include/gtest/gtest.h"
34
35static std::ostream& operator<<(std::ostream& os,
36                                const AutocompleteResult::const_iterator& it) {
37  return os << static_cast<const AutocompleteMatch*>(&(*it));
38}
39
40namespace {
41const size_t kResultsPerProvider = 3;
42const char kTestTemplateURLKeyword[] = "t";
43}
44
45// Autocomplete provider that provides known results. Note that this is
46// refcounted so that it can also be a task on the message loop.
47class TestProvider : public AutocompleteProvider {
48 public:
49  TestProvider(int relevance, const base::string16& prefix,
50               Profile* profile,
51               const base::string16 match_keyword)
52      : AutocompleteProvider(AutocompleteProvider::TYPE_SEARCH),
53        listener_(NULL),
54        profile_(profile),
55        relevance_(relevance),
56        prefix_(prefix),
57        match_keyword_(match_keyword) {
58  }
59
60  virtual void Start(const AutocompleteInput& input,
61                     bool minimal_changes) OVERRIDE;
62
63  void set_listener(AutocompleteProviderListener* listener) {
64    listener_ = listener;
65  }
66
67 private:
68  virtual ~TestProvider() {}
69
70  void Run();
71
72  void AddResults(int start_at, int num);
73  void AddResultsWithSearchTermsArgs(
74      int start_at,
75      int num,
76      AutocompleteMatch::Type type,
77      const TemplateURLRef::SearchTermsArgs& search_terms_args);
78
79  AutocompleteProviderListener* listener_;
80  Profile* profile_;
81  int relevance_;
82  const base::string16 prefix_;
83  const base::string16 match_keyword_;
84};
85
86void TestProvider::Start(const AutocompleteInput& input,
87                         bool minimal_changes) {
88  if (minimal_changes)
89    return;
90
91  matches_.clear();
92
93  // Generate 4 results synchronously, the rest later.
94  AddResults(0, 1);
95  AddResultsWithSearchTermsArgs(
96      1, 1, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
97      TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("echo")));
98  AddResultsWithSearchTermsArgs(
99      2, 1, AutocompleteMatchType::NAVSUGGEST,
100      TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("nav")));
101  AddResultsWithSearchTermsArgs(
102      3, 1, AutocompleteMatchType::SEARCH_SUGGEST,
103      TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("query")));
104
105  if (input.want_asynchronous_matches()) {
106    done_ = false;
107    base::MessageLoop::current()->PostTask(
108        FROM_HERE, base::Bind(&TestProvider::Run, this));
109  }
110}
111
112void TestProvider::Run() {
113  DCHECK_GT(kResultsPerProvider, 0U);
114  AddResults(1, kResultsPerProvider);
115  done_ = true;
116  DCHECK(listener_);
117  listener_->OnProviderUpdate(true);
118}
119
120void TestProvider::AddResults(int start_at, int num) {
121  AddResultsWithSearchTermsArgs(start_at,
122                                num,
123                                AutocompleteMatchType::URL_WHAT_YOU_TYPED,
124                                TemplateURLRef::SearchTermsArgs(
125                                    base::string16()));
126}
127
128void TestProvider::AddResultsWithSearchTermsArgs(
129    int start_at,
130    int num,
131    AutocompleteMatch::Type type,
132    const TemplateURLRef::SearchTermsArgs& search_terms_args) {
133  for (int i = start_at; i < num; i++) {
134    AutocompleteMatch match(this, relevance_ - i, false, type);
135
136    match.fill_into_edit = prefix_ + base::UTF8ToUTF16(base::IntToString(i));
137    match.destination_url = GURL(base::UTF16ToUTF8(match.fill_into_edit));
138    match.allowed_to_be_default_match = true;
139
140    match.contents = match.fill_into_edit;
141    match.contents_class.push_back(
142        ACMatchClassification(0, ACMatchClassification::NONE));
143    match.description = match.fill_into_edit;
144    match.description_class.push_back(
145        ACMatchClassification(0, ACMatchClassification::NONE));
146    match.search_terms_args.reset(
147        new TemplateURLRef::SearchTermsArgs(search_terms_args));
148    if (!match_keyword_.empty()) {
149      match.keyword = match_keyword_;
150      TemplateURLService* service =
151          TemplateURLServiceFactory::GetForProfile(profile_);
152      ASSERT_TRUE(match.GetTemplateURL(service, false) != NULL);
153    }
154
155    matches_.push_back(match);
156  }
157}
158
159class AutocompleteProviderTest : public testing::Test,
160                                 public content::NotificationObserver {
161 protected:
162  struct KeywordTestData {
163    const base::string16 fill_into_edit;
164    const base::string16 keyword;
165    const base::string16 expected_associated_keyword;
166  };
167
168  struct AssistedQueryStatsTestData {
169    const AutocompleteMatch::Type match_type;
170    const std::string expected_aqs;
171  };
172
173 protected:
174   // Registers a test TemplateURL under the given keyword.
175  void RegisterTemplateURL(const base::string16 keyword,
176                           const std::string& template_url);
177
178  // Resets |controller_| with two TestProviders.  |provider1_ptr| and
179  // |provider2_ptr| are updated to point to the new providers if non-NULL.
180  void ResetControllerWithTestProviders(bool same_destinations,
181                                        TestProvider** provider1_ptr,
182                                        TestProvider** provider2_ptr);
183
184  // Runs a query on the input "a", and makes sure both providers' input is
185  // properly collected.
186  void RunTest();
187
188  // Constructs an AutocompleteResult from |match_data|, sets the |controller_|
189  // to pretend it was running against input |input|, calls the |controller_|'s
190  // UpdateAssociatedKeywords, and checks that the matches have associated
191  // keywords as expected.
192  void RunKeywordTest(const base::string16& input,
193                      const KeywordTestData* match_data,
194                      size_t size);
195
196  void RunAssistedQueryStatsTest(
197      const AssistedQueryStatsTestData* aqs_test_data,
198      size_t size);
199
200  void RunQuery(const base::string16 query);
201
202  void ResetControllerWithKeywordAndSearchProviders();
203  void ResetControllerWithKeywordProvider();
204  void RunExactKeymatchTest(bool allow_exact_keyword_match);
205
206  void CopyResults();
207
208  // Returns match.destination_url as it would be set by
209  // AutocompleteController::UpdateMatchDestinationURL().
210  GURL GetDestinationURL(AutocompleteMatch match,
211                         base::TimeDelta query_formulation_time) const;
212
213  AutocompleteResult result_;
214  scoped_ptr<AutocompleteController> controller_;
215
216 private:
217  // content::NotificationObserver:
218  virtual void Observe(int type,
219                       const content::NotificationSource& source,
220                       const content::NotificationDetails& details) OVERRIDE;
221
222  base::MessageLoopForUI message_loop_;
223  content::NotificationRegistrar registrar_;
224  TestingProfile profile_;
225};
226
227void AutocompleteProviderTest::RegisterTemplateURL(
228    const base::string16 keyword,
229    const std::string& template_url) {
230  if (TemplateURLServiceFactory::GetForProfile(&profile_) == NULL) {
231    TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
232        &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
233  }
234  TemplateURLData data;
235  data.SetURL(template_url);
236  data.SetKeyword(keyword);
237  TemplateURL* default_t_url = new TemplateURL(data);
238  TemplateURLService* turl_model =
239      TemplateURLServiceFactory::GetForProfile(&profile_);
240  turl_model->Add(default_t_url);
241  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url);
242  turl_model->Load();
243  TemplateURLID default_provider_id = default_t_url->id();
244  ASSERT_NE(0, default_provider_id);
245}
246
247void AutocompleteProviderTest::ResetControllerWithTestProviders(
248    bool same_destinations,
249    TestProvider** provider1_ptr,
250    TestProvider** provider2_ptr) {
251  // TODO: Move it outside this method, after refactoring the existing
252  // unit tests.  Specifically:
253  //   (1) Make sure that AutocompleteMatch.keyword is set iff there is
254  //       a corresponding call to RegisterTemplateURL; otherwise the
255  //       controller flow will crash; this practically means that
256  //       RunTests/ResetControllerXXX/RegisterTemplateURL should
257  //       be coordinated with each other.
258  //   (2) Inject test arguments rather than rely on the hardcoded values, e.g.
259  //       don't rely on kResultsPerProvided and default relevance ordering
260  //       (B > A).
261  RegisterTemplateURL(base::ASCIIToUTF16(kTestTemplateURLKeyword),
262                      "http://aqs/{searchTerms}/{google:assistedQueryStats}");
263
264  AutocompleteController::Providers providers;
265
266  // Construct two new providers, with either the same or different prefixes.
267  TestProvider* provider1 = new TestProvider(
268      kResultsPerProvider,
269      base::ASCIIToUTF16("http://a"),
270      &profile_,
271      base::ASCIIToUTF16(kTestTemplateURLKeyword));
272  providers.push_back(provider1);
273
274  TestProvider* provider2 = new TestProvider(
275      kResultsPerProvider * 2,
276      same_destinations ? base::ASCIIToUTF16("http://a")
277                        : base::ASCIIToUTF16("http://b"),
278      &profile_,
279      base::string16());
280  providers.push_back(provider2);
281
282  // Reset the controller to contain our new providers.
283  controller_.reset(new AutocompleteController(
284      &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL, 0));
285  // We're going to swap the providers vector, but the old vector should be
286  // empty so no elements need to be freed at this point.
287  EXPECT_TRUE(controller_->providers_.empty());
288  controller_->providers_.swap(providers);
289  provider1->set_listener(controller_.get());
290  provider2->set_listener(controller_.get());
291
292  // The providers don't complete synchronously, so listen for "result updated"
293  // notifications.
294  registrar_.Add(this,
295                 chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
296                 content::Source<AutocompleteController>(controller_.get()));
297
298  if (provider1_ptr)
299    *provider1_ptr = provider1;
300  if (provider2_ptr)
301    *provider2_ptr = provider2;
302}
303
304void AutocompleteProviderTest::
305    ResetControllerWithKeywordAndSearchProviders() {
306  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
307      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
308
309  // Reset the default TemplateURL.
310  TemplateURLData data;
311  data.SetURL("http://defaultturl/{searchTerms}");
312  TemplateURL* default_t_url = new TemplateURL(data);
313  TemplateURLService* turl_model =
314      TemplateURLServiceFactory::GetForProfile(&profile_);
315  turl_model->Add(default_t_url);
316  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url);
317  TemplateURLID default_provider_id = default_t_url->id();
318  ASSERT_NE(0, default_provider_id);
319
320  // Create another TemplateURL for KeywordProvider.
321  TemplateURLData data2;
322  data2.short_name = base::ASCIIToUTF16("k");
323  data2.SetKeyword(base::ASCIIToUTF16("k"));
324  data2.SetURL("http://keyword/{searchTerms}");
325  TemplateURL* keyword_t_url = new TemplateURL(data2);
326  turl_model->Add(keyword_t_url);
327  ASSERT_NE(0, keyword_t_url->id());
328
329  controller_.reset(new AutocompleteController(
330      &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL,
331      AutocompleteProvider::TYPE_KEYWORD | AutocompleteProvider::TYPE_SEARCH));
332}
333
334void AutocompleteProviderTest::ResetControllerWithKeywordProvider() {
335  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
336      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
337
338  TemplateURLService* turl_model =
339      TemplateURLServiceFactory::GetForProfile(&profile_);
340
341  // Create a TemplateURL for KeywordProvider.
342  TemplateURLData data;
343  data.short_name = base::ASCIIToUTF16("foo.com");
344  data.SetKeyword(base::ASCIIToUTF16("foo.com"));
345  data.SetURL("http://foo.com/{searchTerms}");
346  TemplateURL* keyword_t_url = new TemplateURL(data);
347  turl_model->Add(keyword_t_url);
348  ASSERT_NE(0, keyword_t_url->id());
349
350  // Make a TemplateURL for KeywordProvider that a shorter version of the
351  // first.
352  data.short_name = base::ASCIIToUTF16("f");
353  data.SetKeyword(base::ASCIIToUTF16("f"));
354  data.SetURL("http://f.com/{searchTerms}");
355  keyword_t_url = new TemplateURL(data);
356  turl_model->Add(keyword_t_url);
357  ASSERT_NE(0, keyword_t_url->id());
358
359  // Create another TemplateURL for KeywordProvider.
360  data.short_name = base::ASCIIToUTF16("bar.com");
361  data.SetKeyword(base::ASCIIToUTF16("bar.com"));
362  data.SetURL("http://bar.com/{searchTerms}");
363  keyword_t_url = new TemplateURL(data);
364  turl_model->Add(keyword_t_url);
365  ASSERT_NE(0, keyword_t_url->id());
366
367  controller_.reset(new AutocompleteController(
368      &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL,
369      AutocompleteProvider::TYPE_KEYWORD));
370}
371
372void AutocompleteProviderTest::RunTest() {
373  RunQuery(base::ASCIIToUTF16("a"));
374}
375
376void AutocompleteProviderTest::RunKeywordTest(const base::string16& input,
377                                              const KeywordTestData* match_data,
378                                              size_t size) {
379  ACMatches matches;
380  for (size_t i = 0; i < size; ++i) {
381    AutocompleteMatch match;
382    match.relevance = 1000;  // Arbitrary non-zero value.
383    match.allowed_to_be_default_match = true;
384    match.fill_into_edit = match_data[i].fill_into_edit;
385    match.transition = ui::PAGE_TRANSITION_KEYWORD;
386    match.keyword = match_data[i].keyword;
387    matches.push_back(match);
388  }
389
390  AutocompleteResult result;
391  result.AppendMatches(matches);
392  controller_->input_ = AutocompleteInput(
393      input, base::string16::npos, base::string16(), GURL(),
394      metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
395      false, true, true, true, ChromeAutocompleteSchemeClassifier(&profile_));
396  controller_->UpdateAssociatedKeywords(&result);
397
398  for (size_t j = 0; j < result.size(); ++j) {
399    EXPECT_EQ(match_data[j].expected_associated_keyword,
400              result.match_at(j)->associated_keyword.get() ?
401                  result.match_at(j)->associated_keyword->keyword :
402                  base::string16());
403  }
404}
405
406void AutocompleteProviderTest::RunAssistedQueryStatsTest(
407    const AssistedQueryStatsTestData* aqs_test_data,
408    size_t size) {
409  // Prepare input.
410  const size_t kMaxRelevance = 1000;
411  ACMatches matches;
412  for (size_t i = 0; i < size; ++i) {
413    AutocompleteMatch match(NULL, kMaxRelevance - i, false,
414                            aqs_test_data[i].match_type);
415    match.allowed_to_be_default_match = true;
416    match.keyword = base::ASCIIToUTF16(kTestTemplateURLKeyword);
417    match.search_terms_args.reset(
418        new TemplateURLRef::SearchTermsArgs(base::string16()));
419    matches.push_back(match);
420  }
421  result_.Reset();
422  result_.AppendMatches(matches);
423
424  // Update AQS.
425  controller_->UpdateAssistedQueryStats(&result_);
426
427  // Verify data.
428  for (size_t i = 0; i < size; ++i) {
429    EXPECT_EQ(aqs_test_data[i].expected_aqs,
430              result_.match_at(i)->search_terms_args->assisted_query_stats);
431  }
432}
433
434void AutocompleteProviderTest::RunQuery(const base::string16 query) {
435  result_.Reset();
436  controller_->Start(AutocompleteInput(
437      query, base::string16::npos, base::string16(), GURL(),
438      metrics::OmniboxEventProto::INVALID_SPEC, true, false, true, true,
439      ChromeAutocompleteSchemeClassifier(&profile_)));
440
441  if (!controller_->done())
442    // The message loop will terminate when all autocomplete input has been
443    // collected.
444    base::MessageLoop::current()->Run();
445}
446
447void AutocompleteProviderTest::RunExactKeymatchTest(
448    bool allow_exact_keyword_match) {
449  // Send the controller input which exactly matches the keyword provider we
450  // created in ResetControllerWithKeywordAndSearchProviders().  The default
451  // match should thus be a search-other-engine match iff
452  // |allow_exact_keyword_match| is true.  Regardless, the match should
453  // be from SearchProvider.  (It provides all verbatim search matches,
454  // keyword or not.)
455  controller_->Start(AutocompleteInput(
456      base::ASCIIToUTF16("k test"), base::string16::npos, base::string16(),
457      GURL(), metrics::OmniboxEventProto::INVALID_SPEC, true, false,
458      allow_exact_keyword_match, false,
459      ChromeAutocompleteSchemeClassifier(&profile_)));
460  EXPECT_TRUE(controller_->done());
461  EXPECT_EQ(AutocompleteProvider::TYPE_SEARCH,
462      controller_->result().default_match()->provider->type());
463  EXPECT_EQ(allow_exact_keyword_match ?
464      AutocompleteMatchType::SEARCH_OTHER_ENGINE :
465      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
466      controller_->result().default_match()->type);
467}
468
469void AutocompleteProviderTest::CopyResults() {
470  result_.CopyFrom(controller_->result());
471}
472
473GURL AutocompleteProviderTest::GetDestinationURL(
474    AutocompleteMatch match,
475    base::TimeDelta query_formulation_time) const {
476  controller_->UpdateMatchDestinationURLWithQueryFormulationTime(
477      query_formulation_time, &match);
478  return match.destination_url;
479}
480
481void AutocompleteProviderTest::Observe(
482    int type,
483    const content::NotificationSource& source,
484    const content::NotificationDetails& details) {
485  if (controller_->done()) {
486    CopyResults();
487    base::MessageLoop::current()->Quit();
488  }
489}
490
491// Tests that the default selection is set properly when updating results.
492TEST_F(AutocompleteProviderTest, Query) {
493  TestProvider* provider1 = NULL;
494  TestProvider* provider2 = NULL;
495  ResetControllerWithTestProviders(false, &provider1, &provider2);
496  RunTest();
497
498  // Make sure the default match gets set to the highest relevance match.  The
499  // highest relevance matches should come from the second provider.
500  EXPECT_EQ(kResultsPerProvider * 2, result_.size());
501  ASSERT_NE(result_.end(), result_.default_match());
502  EXPECT_EQ(provider2, result_.default_match()->provider);
503}
504
505// Tests assisted query stats.
506TEST_F(AutocompleteProviderTest, AssistedQueryStats) {
507  ResetControllerWithTestProviders(false, NULL, NULL);
508  RunTest();
509
510  ASSERT_EQ(kResultsPerProvider * 2, result_.size());
511
512  // Now, check the results from the second provider, as they should not have
513  // assisted query stats set.
514  for (size_t i = 0; i < kResultsPerProvider; ++i) {
515    EXPECT_TRUE(
516        result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
517  }
518  // The first provider has a test keyword, so AQS should be non-empty.
519  for (size_t i = kResultsPerProvider; i < kResultsPerProvider * 2; ++i) {
520    EXPECT_FALSE(
521        result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
522  }
523}
524
525TEST_F(AutocompleteProviderTest, RemoveDuplicates) {
526  TestProvider* provider1 = NULL;
527  TestProvider* provider2 = NULL;
528  ResetControllerWithTestProviders(true, &provider1, &provider2);
529  RunTest();
530
531  // Make sure all the first provider's results were eliminated by the second
532  // provider's.
533  EXPECT_EQ(kResultsPerProvider, result_.size());
534  for (AutocompleteResult::const_iterator i(result_.begin());
535       i != result_.end(); ++i)
536    EXPECT_EQ(provider2, i->provider);
537}
538
539TEST_F(AutocompleteProviderTest, AllowExactKeywordMatch) {
540  ResetControllerWithKeywordAndSearchProviders();
541  RunExactKeymatchTest(true);
542  RunExactKeymatchTest(false);
543}
544
545// Ensures matches from (only) the default search provider respect any extra
546// query params set on the command line.
547TEST_F(AutocompleteProviderTest, ExtraQueryParams) {
548  ResetControllerWithKeywordAndSearchProviders();
549  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
550      switches::kExtraSearchQueryParams, "a=b");
551  RunExactKeymatchTest(true);
552  CopyResults();
553  ASSERT_EQ(2U, result_.size());
554  EXPECT_EQ("http://keyword/test",
555            result_.match_at(0)->destination_url.possibly_invalid_spec());
556  EXPECT_EQ("http://defaultturl/k%20test?a=b",
557            result_.match_at(1)->destination_url.possibly_invalid_spec());
558}
559
560// Test that redundant associated keywords are removed.
561TEST_F(AutocompleteProviderTest, RedundantKeywordsIgnoredInResult) {
562  ResetControllerWithKeywordProvider();
563
564  {
565    KeywordTestData duplicate_url[] = {
566      { base::ASCIIToUTF16("fo"), base::string16(), base::string16() },
567      { base::ASCIIToUTF16("foo.com"), base::string16(),
568        base::ASCIIToUTF16("foo.com") },
569      { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() }
570    };
571
572    SCOPED_TRACE("Duplicate url");
573    RunKeywordTest(base::ASCIIToUTF16("fo"), duplicate_url,
574                   ARRAYSIZE_UNSAFE(duplicate_url));
575  }
576
577  {
578    KeywordTestData keyword_match[] = {
579      { base::ASCIIToUTF16("foo.com"), base::ASCIIToUTF16("foo.com"),
580        base::string16() },
581      { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() }
582    };
583
584    SCOPED_TRACE("Duplicate url with keyword match");
585    RunKeywordTest(base::ASCIIToUTF16("fo"), keyword_match,
586                   ARRAYSIZE_UNSAFE(keyword_match));
587  }
588
589  {
590    KeywordTestData multiple_keyword[] = {
591      { base::ASCIIToUTF16("fo"), base::string16(), base::string16() },
592      { base::ASCIIToUTF16("foo.com"), base::string16(),
593        base::ASCIIToUTF16("foo.com") },
594      { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() },
595      { base::ASCIIToUTF16("bar.com"), base::string16(),
596        base::ASCIIToUTF16("bar.com") },
597    };
598
599    SCOPED_TRACE("Duplicate url with multiple keywords");
600    RunKeywordTest(base::ASCIIToUTF16("fo"), multiple_keyword,
601                   ARRAYSIZE_UNSAFE(multiple_keyword));
602  }
603}
604
605// Test that exact match keywords trump keywords associated with
606// the match.
607TEST_F(AutocompleteProviderTest, ExactMatchKeywords) {
608  ResetControllerWithKeywordProvider();
609
610  {
611    KeywordTestData keyword_match[] = {
612      { base::ASCIIToUTF16("foo.com"), base::string16(),
613        base::ASCIIToUTF16("foo.com") }
614    };
615
616    SCOPED_TRACE("keyword match as usual");
617    RunKeywordTest(base::ASCIIToUTF16("fo"), keyword_match,
618                   ARRAYSIZE_UNSAFE(keyword_match));
619  }
620
621  // The same result set with an input of "f" (versus "fo") should get
622  // a different associated keyword because "f" is an exact match for
623  // a keyword and that should trump the keyword normally associated with
624  // this match.
625  {
626    KeywordTestData keyword_match[] = {
627      { base::ASCIIToUTF16("foo.com"), base::string16(),
628        base::ASCIIToUTF16("f") }
629    };
630
631    SCOPED_TRACE("keyword exact match");
632    RunKeywordTest(base::ASCIIToUTF16("f"), keyword_match,
633                   ARRAYSIZE_UNSAFE(keyword_match));
634  }
635}
636
637TEST_F(AutocompleteProviderTest, UpdateAssistedQueryStats) {
638  ResetControllerWithTestProviders(false, NULL, NULL);
639
640  {
641    AssistedQueryStatsTestData test_data[] = {
642      //  MSVC doesn't support zero-length arrays, so supply some dummy data.
643      { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "" }
644    };
645    SCOPED_TRACE("No matches");
646    // Note: We pass 0 here to ignore the dummy data above.
647    RunAssistedQueryStatsTest(test_data, 0);
648  }
649
650  {
651    AssistedQueryStatsTestData test_data[] = {
652      { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "chrome..69i57" }
653    };
654    SCOPED_TRACE("One match");
655    RunAssistedQueryStatsTest(test_data, ARRAYSIZE_UNSAFE(test_data));
656  }
657
658  {
659    AssistedQueryStatsTestData test_data[] = {
660      { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
661        "chrome..69i57j69i58j5l2j0l3j69i59" },
662      { AutocompleteMatchType::URL_WHAT_YOU_TYPED,
663        "chrome..69i57j69i58j5l2j0l3j69i59" },
664      { AutocompleteMatchType::NAVSUGGEST,
665        "chrome.2.69i57j69i58j5l2j0l3j69i59" },
666      { AutocompleteMatchType::NAVSUGGEST,
667        "chrome.3.69i57j69i58j5l2j0l3j69i59" },
668      { AutocompleteMatchType::SEARCH_SUGGEST,
669        "chrome.4.69i57j69i58j5l2j0l3j69i59" },
670      { AutocompleteMatchType::SEARCH_SUGGEST,
671        "chrome.5.69i57j69i58j5l2j0l3j69i59" },
672      { AutocompleteMatchType::SEARCH_SUGGEST,
673        "chrome.6.69i57j69i58j5l2j0l3j69i59" },
674      { AutocompleteMatchType::SEARCH_HISTORY,
675        "chrome.7.69i57j69i58j5l2j0l3j69i59" },
676    };
677    SCOPED_TRACE("Multiple matches");
678    RunAssistedQueryStatsTest(test_data, ARRAYSIZE_UNSAFE(test_data));
679  }
680}
681
682TEST_F(AutocompleteProviderTest, GetDestinationURL) {
683  ResetControllerWithKeywordAndSearchProviders();
684
685  // For the destination URL to have aqs parameters for query formulation time
686  // and the field trial triggered bit, many conditions need to be satisfied.
687  AutocompleteMatch match(NULL, 1100, false,
688                          AutocompleteMatchType::SEARCH_SUGGEST);
689  GURL url(GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)));
690  EXPECT_TRUE(url.path().empty());
691
692  // The protocol needs to be https.
693  RegisterTemplateURL(base::ASCIIToUTF16(kTestTemplateURLKeyword),
694                      "https://aqs/{searchTerms}/{google:assistedQueryStats}");
695  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
696  EXPECT_TRUE(url.path().empty());
697
698  // There needs to be a keyword provider.
699  match.keyword = base::ASCIIToUTF16(kTestTemplateURLKeyword);
700  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
701  EXPECT_TRUE(url.path().empty());
702
703  // search_terms_args needs to be set.
704  match.search_terms_args.reset(
705      new TemplateURLRef::SearchTermsArgs(base::string16()));
706  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
707  EXPECT_TRUE(url.path().empty());
708
709  // assisted_query_stats needs to have been previously set.
710  match.search_terms_args->assisted_query_stats =
711      "chrome.0.69i57j69i58j5l2j0l3j69i59";
712  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
713  EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j0&", url.path());
714
715  // Test field trial triggered bit set.
716  controller_->search_provider_->field_trial_triggered_in_session_ = true;
717  EXPECT_TRUE(
718      controller_->search_provider_->field_trial_triggered_in_session());
719  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
720  EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j0&", url.path());
721
722  // Test page classification set.
723  controller_->input_.current_page_classification_ =
724      metrics::OmniboxEventProto::OTHER;
725  controller_->search_provider_->field_trial_triggered_in_session_ = false;
726  EXPECT_FALSE(
727      controller_->search_provider_->field_trial_triggered_in_session());
728  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
729  EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j4&", url.path());
730
731  // Test page classification and field trial triggered set.
732  controller_->search_provider_->field_trial_triggered_in_session_ = true;
733  EXPECT_TRUE(
734      controller_->search_provider_->field_trial_triggered_in_session());
735  url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456));
736  EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j4&", url.path());
737}
738