1// Copyright 2014 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/base_search_provider.h"
6
7#include "base/strings/string16.h"
8#include "base/strings/utf_string_conversions.h"
9#include "components/omnibox/autocomplete_match.h"
10#include "components/omnibox/autocomplete_match_type.h"
11#include "components/omnibox/autocomplete_provider_client.h"
12#include "components/omnibox/autocomplete_scheme_classifier.h"
13#include "components/omnibox/search_suggestion_parser.h"
14#include "components/search_engines/search_terms_data.h"
15#include "components/search_engines/template_url_service.h"
16#include "components/search_engines/template_url_service_client.h"
17#include "testing/gmock/include/gmock/gmock.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using testing::NiceMock;
21using testing::Return;
22using testing::_;
23
24class MockAutocompleteProviderClient : public AutocompleteProviderClient {
25 public:
26  MockAutocompleteProviderClient() {}
27  MOCK_METHOD0(RequestContext, net::URLRequestContextGetter*());
28  MOCK_METHOD0(IsOffTheRecord, bool());
29  MOCK_METHOD0(AcceptLanguages, std::string());
30  MOCK_METHOD0(SearchSuggestEnabled, bool());
31  MOCK_METHOD0(ShowBookmarkBar, bool());
32  MOCK_METHOD0(SchemeClassifier, const AutocompleteSchemeClassifier&());
33  MOCK_METHOD6(
34      Classify,
35      void(const base::string16& text,
36           bool prefer_keyword,
37           bool allow_exact_keyword_match,
38           metrics::OmniboxEventProto::PageClassification page_classification,
39           AutocompleteMatch* match,
40           GURL* alternate_nav_url));
41  MOCK_METHOD0(InMemoryDatabase, history::URLDatabase*());
42  MOCK_METHOD2(DeleteMatchingURLsForKeywordFromHistory,
43               void(history::KeywordID keyword_id, const base::string16& term));
44  MOCK_METHOD0(TabSyncEnabledAndUnencrypted, bool());
45  MOCK_METHOD1(PrefetchImage, void(const GURL& url));
46
47 private:
48  DISALLOW_COPY_AND_ASSIGN(MockAutocompleteProviderClient);
49};
50
51class TestBaseSearchProvider : public BaseSearchProvider {
52 public:
53  typedef BaseSearchProvider::MatchMap MatchMap;
54
55  // Note: Takes ownership of client. scoped_ptr<> would be the right way to
56  // express that, but NiceMock<> can't forward a scoped_ptr.
57  TestBaseSearchProvider(TemplateURLService* template_url_service,
58                         AutocompleteProviderClient* client,
59                         AutocompleteProvider::Type type)
60      : BaseSearchProvider(template_url_service,
61                           scoped_ptr<AutocompleteProviderClient>(client),
62                           type) {}
63  MOCK_METHOD1(DeleteMatch, void(const AutocompleteMatch& match));
64  MOCK_CONST_METHOD1(AddProviderInfo, void(ProvidersInfo* provider_info));
65  MOCK_CONST_METHOD1(GetTemplateURL, const TemplateURL*(bool is_keyword));
66  MOCK_CONST_METHOD1(GetInput, const AutocompleteInput(bool is_keyword));
67  MOCK_CONST_METHOD1(ShouldAppendExtraParams,
68                     bool(const SearchSuggestionParser::SuggestResult& result));
69  MOCK_METHOD1(RecordDeletionResult, void(bool success));
70
71  MOCK_METHOD2(Start,
72               void(const AutocompleteInput& input, bool minimal_changes));
73  void AddMatchToMap(const SearchSuggestionParser::SuggestResult& result,
74                     const std::string& metadata,
75                     int accepted_suggestion,
76                     bool mark_as_deletable,
77                     bool in_keyword_mode,
78                     MatchMap* map) {
79    BaseSearchProvider::AddMatchToMap(result,
80                                      metadata,
81                                      accepted_suggestion,
82                                      mark_as_deletable,
83                                      in_keyword_mode,
84                                      map);
85  }
86
87 protected:
88  virtual ~TestBaseSearchProvider() {}
89
90 private:
91  DISALLOW_COPY_AND_ASSIGN(TestBaseSearchProvider);
92};
93
94class BaseSearchProviderTest : public testing::Test {
95 public:
96  virtual ~BaseSearchProviderTest() {}
97
98 protected:
99  virtual void SetUp() {
100    service_.reset(
101        new TemplateURLService(NULL,
102                               scoped_ptr<SearchTermsData>(new SearchTermsData),
103                               NULL,
104                               scoped_ptr<TemplateURLServiceClient>(),
105                               NULL,
106                               NULL,
107                               base::Closure()));
108    provider_ = new NiceMock<TestBaseSearchProvider>(
109        service_.get(),
110        new NiceMock<MockAutocompleteProviderClient>,
111        AutocompleteProvider::TYPE_SEARCH);
112  }
113
114  scoped_refptr<NiceMock<TestBaseSearchProvider> > provider_;
115  scoped_ptr<TemplateURLService> service_;
116};
117
118TEST_F(BaseSearchProviderTest, PreserveAnswersWhenDeduplicating) {
119  TemplateURLData data;
120  data.SetURL("http://foo.com/url?bar={searchTerms}");
121  scoped_ptr<TemplateURL> template_url(new TemplateURL(data));
122
123  TestBaseSearchProvider::MatchMap map;
124  base::string16 query = base::ASCIIToUTF16("weather los angeles");
125  base::string16 answer_contents = base::ASCIIToUTF16("some answer content");
126  base::string16 answer_type = base::ASCIIToUTF16("2334");
127
128  EXPECT_CALL(*provider_, GetInput(_))
129      .WillRepeatedly(Return(AutocompleteInput()));
130  EXPECT_CALL(*provider_, GetTemplateURL(_))
131      .WillRepeatedly(Return(template_url.get()));
132
133  SearchSuggestionParser::SuggestResult more_relevant(
134      query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
135      base::string16(), base::string16(), base::string16(), std::string(),
136      std::string(), false, 1300, true, false, query);
137  provider_->AddMatchToMap(
138      more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
139      false, false, &map);
140
141  SearchSuggestionParser::SuggestResult less_relevant(
142      query, AutocompleteMatchType::SEARCH_SUGGEST, query, base::string16(),
143      base::string16(), answer_contents, answer_type, std::string(),
144      std::string(), false, 850, true, false, query);
145  provider_->AddMatchToMap(
146      less_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
147      false, false, &map);
148
149  ASSERT_EQ(1U, map.size());
150  AutocompleteMatch match = map.begin()->second;
151  ASSERT_EQ(1U, match.duplicate_matches.size());
152  AutocompleteMatch duplicate = match.duplicate_matches[0];
153
154  EXPECT_EQ(answer_contents, match.answer_contents);
155  EXPECT_EQ(answer_type, match.answer_type);
156  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, match.type);
157  EXPECT_EQ(1300, match.relevance);
158
159  EXPECT_EQ(answer_contents, duplicate.answer_contents);
160  EXPECT_EQ(answer_type, duplicate.answer_type);
161  EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST, duplicate.type);
162  EXPECT_EQ(850, duplicate.relevance);
163
164  // Ensure answers are not copied over existing answers.
165  map.clear();
166  base::string16 answer_contents2 = base::ASCIIToUTF16("different answer");
167  more_relevant = SearchSuggestionParser::SuggestResult(
168      query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
169      base::string16(), answer_contents2, answer_type, std::string(),
170      std::string(), false, 1300, true, false, query);
171  provider_->AddMatchToMap(
172      more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
173      false, false, &map);
174  provider_->AddMatchToMap(
175      less_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
176      false, false, &map);
177  ASSERT_EQ(1U, map.size());
178  match = map.begin()->second;
179  ASSERT_EQ(1U, match.duplicate_matches.size());
180  duplicate = match.duplicate_matches[0];
181
182  EXPECT_EQ(answer_contents2, match.answer_contents);
183  EXPECT_EQ(answer_type, match.answer_type);
184  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, match.type);
185  EXPECT_EQ(1300, match.relevance);
186
187  EXPECT_EQ(answer_contents, duplicate.answer_contents);
188  EXPECT_EQ(answer_type, duplicate.answer_type);
189  EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST, duplicate.type);
190  EXPECT_EQ(850, duplicate.relevance);
191
192}
193