blob: d6f62fd17ac131b81c5b5d49a5108f7c6540b9b7 [file] [log] [blame]
mattreynolds5afc01692016-08-19 22:09:561// Copyright 2016 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/browser/physical_web_provider.h"
6
7#include <memory>
8#include <string>
9
10#include "base/memory/ptr_util.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "components/metrics/proto/omnibox_event.pb.h"
15#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
16#include "components/omnibox/browser/test_scheme_classifier.h"
17#include "components/physical_web/data_source/physical_web_data_source.h"
hayesjordan1098aecd2016-08-24 02:33:1018#include "components/physical_web/data_source/physical_web_listener.h"
mattreynolds5afc01692016-08-19 22:09:5619#include "grit/components_strings.h"
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "ui/base/l10n/l10n_util.h"
23#include "url/gurl.h"
24
25namespace {
26
27// A mock implementation of the Physical Web data source that allows setting
28// metadata for nearby URLs directly.
29class MockPhysicalWebDataSource : public PhysicalWebDataSource {
30 public:
31 MockPhysicalWebDataSource() : metadata_(new base::ListValue()) {}
32 ~MockPhysicalWebDataSource() override {}
33
34 void StartDiscovery(bool network_request_enabled) override {}
35 void StopDiscovery() override {}
36
37 std::unique_ptr<base::ListValue> GetMetadata() override {
38 return metadata_->CreateDeepCopy();
39 }
40
41 bool HasUnresolvedDiscoveries() override {
42 return false;
43 }
44
hayesjordan1098aecd2016-08-24 02:33:1045 void RegisterListener(PhysicalWebListener* physical_web_listener) override {}
46
47 void UnregisterListener(
48 PhysicalWebListener* physical_web_listener) override {}
49
mattreynolds5afc01692016-08-19 22:09:5650 // for testing
51 void SetMetadata(std::unique_ptr<base::ListValue> metadata) {
52 metadata_ = std::move(metadata);
53 }
54
55 private:
56 std::unique_ptr<base::ListValue> metadata_;
57};
58
59// An autocomplete provider client that embeds the mock Physical Web data
60// source.
61class FakeAutocompleteProviderClient
62 : public testing::NiceMock<MockAutocompleteProviderClient> {
63 public:
64 FakeAutocompleteProviderClient()
65 : physical_web_data_source_(base::MakeUnique<MockPhysicalWebDataSource>())
66 {
67 }
68
69 const AutocompleteSchemeClassifier& GetSchemeClassifier() const override {
70 return scheme_classifier_;
71 }
72
73 PhysicalWebDataSource* GetPhysicalWebDataSource() override {
74 return physical_web_data_source_.get();
75 }
76
77 // Convenience method to avoid downcasts when accessing the mock data source.
78 MockPhysicalWebDataSource* GetMockPhysicalWebDataSource() {
79 return physical_web_data_source_.get();
80 }
81
82 private:
83 std::unique_ptr<MockPhysicalWebDataSource> physical_web_data_source_;
84 TestSchemeClassifier scheme_classifier_;
85};
86
87class PhysicalWebProviderTest : public testing::Test {
88 protected:
89 PhysicalWebProviderTest() : provider_(NULL) {}
90 ~PhysicalWebProviderTest() override {}
91
92 void SetUp() override {
93 client_.reset(new FakeAutocompleteProviderClient());
mattreynolds2b530de2016-09-02 18:20:1694 provider_ = PhysicalWebProvider::Create(client_.get(), nullptr);
mattreynolds5afc01692016-08-19 22:09:5695 }
96
97 void TearDown() override {
98 provider_ = NULL;
99 }
100
101 // Create a dummy metadata list with |metadata_count| items. Each item is
102 // populated with a unique scanned URL and page metadata.
103 static std::unique_ptr<base::ListValue> CreateMetadata(
104 size_t metadata_count) {
105 auto metadata_list = base::MakeUnique<base::ListValue>();
106 for (size_t i = 0; i < metadata_count; ++i) {
107 std::string item_id = base::SizeTToString(i);
108 std::string url = "https://example.com/" + item_id;
109 auto metadata_item = base::MakeUnique<base::DictionaryValue>();
110 metadata_item->SetString("scannedUrl", url);
111 metadata_item->SetString("resolvedUrl", url);
112 metadata_item->SetString("icon", url);
113 metadata_item->SetString("title", "Example title " + item_id);
114 metadata_item->SetString("description", "Example description " + item_id);
115 metadata_list->Append(std::move(metadata_item));
116 }
117 return metadata_list;
118 }
119
mattreynolds2b530de2016-09-02 18:20:16120 // Construct an AutocompleteInput to represent tapping the omnibox from the
121 // new tab page.
122 static AutocompleteInput CreateInputForNTP() {
123 return AutocompleteInput(
124 base::string16(), base::string16::npos, std::string(), GURL(),
125 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
126 false, false, true, true, true, TestSchemeClassifier());
127 }
128
129 // Construct an AutocompleteInput to represent tapping the omnibox with |url|
130 // as the current web page.
131 static AutocompleteInput CreateInputWithCurrentUrl(const std::string& url) {
132 return AutocompleteInput(base::ASCIIToUTF16(url), base::string16::npos,
133 std::string(), GURL(url),
134 metrics::OmniboxEventProto::OTHER, false, false,
135 true, true, true, TestSchemeClassifier());
136 }
137
mattreynolds5afc01692016-08-19 22:09:56138 std::unique_ptr<FakeAutocompleteProviderClient> client_;
139 scoped_refptr<PhysicalWebProvider> provider_;
140
141 private:
142 DISALLOW_COPY_AND_ASSIGN(PhysicalWebProviderTest);
143};
144
145TEST_F(PhysicalWebProviderTest, TestEmptyMetadataListCreatesNoMatches) {
146 MockPhysicalWebDataSource* data_source =
147 client_->GetMockPhysicalWebDataSource();
148 EXPECT_TRUE(data_source);
149
150 data_source->SetMetadata(CreateMetadata(0));
151
mattreynolds2b530de2016-09-02 18:20:16152 // Run the test with no text in the omnibox input to simulate NTP.
153 provider_->Start(CreateInputForNTP(), false);
154 EXPECT_TRUE(provider_->matches().empty());
mattreynolds5afc01692016-08-19 22:09:56155
mattreynolds2b530de2016-09-02 18:20:16156 // Run the test again with a URL in the omnibox input.
157 provider_->Start(CreateInputWithCurrentUrl("http://www.cnn.com"), false);
mattreynolds5afc01692016-08-19 22:09:56158 EXPECT_TRUE(provider_->matches().empty());
159}
160
161TEST_F(PhysicalWebProviderTest, TestSingleMetadataItemCreatesOneMatch) {
162 MockPhysicalWebDataSource* data_source =
163 client_->GetMockPhysicalWebDataSource();
164 EXPECT_TRUE(data_source);
165
166 // Extract the URL and title before inserting the metadata into the data
167 // source.
168 std::unique_ptr<base::ListValue> metadata_list = CreateMetadata(1);
169 base::DictionaryValue* metadata_item;
170 EXPECT_TRUE(metadata_list->GetDictionary(0, &metadata_item));
171 std::string resolved_url;
172 EXPECT_TRUE(metadata_item->GetString("resolvedUrl", &resolved_url));
173 std::string title;
174 EXPECT_TRUE(metadata_item->GetString("title", &title));
175
176 data_source->SetMetadata(std::move(metadata_list));
177
mattreynolds2b530de2016-09-02 18:20:16178 // Run the test with no text in the omnibox input to simulate NTP.
179 provider_->Start(CreateInputForNTP(), false);
mattreynolds5afc01692016-08-19 22:09:56180
181 // Check that there is only one match item and its fields are correct.
182 EXPECT_EQ(1U, provider_->matches().size());
183 const AutocompleteMatch& metadata_match = provider_->matches().front();
184 EXPECT_EQ(AutocompleteMatchType::PHYSICAL_WEB, metadata_match.type);
185 EXPECT_EQ(resolved_url, metadata_match.destination_url.spec());
186 EXPECT_EQ(resolved_url, base::UTF16ToASCII(metadata_match.contents));
187 EXPECT_EQ(title, base::UTF16ToASCII(metadata_match.description));
mattreynolds2b530de2016-09-02 18:20:16188 EXPECT_FALSE(metadata_match.allowed_to_be_default_match);
189
190 // Run the test again with a URL in the omnibox input. An additional match
191 // should be added as a default match.
192 provider_->Start(CreateInputWithCurrentUrl("http://www.cnn.com"), false);
193
194 size_t metadata_match_count = 0;
195 size_t default_match_count = 0;
196 for (const auto& match : provider_->matches()) {
197 if (match.type == AutocompleteMatchType::PHYSICAL_WEB) {
198 EXPECT_EQ(resolved_url, match.destination_url.spec());
199 EXPECT_EQ(resolved_url, base::UTF16ToASCII(match.contents));
200 EXPECT_EQ(title, base::UTF16ToASCII(match.description));
201 EXPECT_FALSE(match.allowed_to_be_default_match);
202 ++metadata_match_count;
203 } else {
204 EXPECT_TRUE(match.allowed_to_be_default_match);
205 ++default_match_count;
206 }
207 }
208 EXPECT_EQ(2U, provider_->matches().size());
209 EXPECT_EQ(1U, metadata_match_count);
210 EXPECT_EQ(1U, default_match_count);
mattreynolds5afc01692016-08-19 22:09:56211}
212
213TEST_F(PhysicalWebProviderTest, TestManyMetadataItemsCreatesOverflowItem) {
214 // This test is intended to verify that an overflow item is created when the
215 // number of nearby Physical Web URLs exceeds the maximum allowable matches
216 // for this provider. The actual limit for the PhysicalWebProvider may be
217 // changed in the future, so create enough metadata to exceed the
218 // AutocompleteProvider's limit.
219 const size_t metadata_count = AutocompleteProvider::kMaxMatches + 1;
220
221 MockPhysicalWebDataSource* data_source =
222 client_->GetMockPhysicalWebDataSource();
223 EXPECT_TRUE(data_source);
224
225 data_source->SetMetadata(CreateMetadata(metadata_count));
226
mattreynolds2b530de2016-09-02 18:20:16227 {
228 // Run the test with no text in the omnibox input to simulate NTP.
229 provider_->Start(CreateInputForNTP(), false);
mattreynolds5afc01692016-08-19 22:09:56230
mattreynolds2b530de2016-09-02 18:20:16231 const size_t match_count = provider_->matches().size();
232 EXPECT_LT(match_count, metadata_count);
mattreynolds5afc01692016-08-19 22:09:56233
mattreynolds2b530de2016-09-02 18:20:16234 // Check that the overflow item is present and its fields are correct. There
235 // may be additional match items, but none should be marked as default.
236 size_t overflow_match_count = 0;
237 for (const auto& match : provider_->matches()) {
238 if (match.type == AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW) {
239 EXPECT_EQ("chrome://physical-web/", match.destination_url.spec());
240 EXPECT_EQ("chrome://physical-web/", base::UTF16ToASCII(match.contents));
241 const size_t metadata_matches = match_count - 1;
242 std::string description = l10n_util::GetPluralStringFUTF8(
243 IDS_PHYSICAL_WEB_OVERFLOW, metadata_count - metadata_matches);
244 EXPECT_EQ(description, base::UTF16ToASCII(match.description));
245 ++overflow_match_count;
246 }
247 EXPECT_FALSE(match.allowed_to_be_default_match);
248 }
249 EXPECT_EQ(1U, overflow_match_count);
250 }
251
252 {
253 // Run the test again with a URL in the omnibox input. An additional match
254 // should be added as a default match.
255 provider_->Start(CreateInputWithCurrentUrl("http://www.cnn.com"), false);
256
257 const size_t match_count = provider_->matches().size();
258 EXPECT_LT(match_count - 1, metadata_count);
259
260 // Check that the overflow item and default match are present and their
261 // fields are correct.
262 size_t overflow_match_count = 0;
263 size_t default_match_count = 0;
264 for (const auto& match : provider_->matches()) {
265 if (match.type == AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW) {
266 EXPECT_EQ("chrome://physical-web/", match.destination_url.spec());
267 EXPECT_EQ("chrome://physical-web/", base::UTF16ToASCII(match.contents));
268 const size_t metadata_matches = match_count - 2;
269 std::string description = l10n_util::GetPluralStringFUTF8(
270 IDS_PHYSICAL_WEB_OVERFLOW, metadata_count - metadata_matches);
271 EXPECT_EQ(description, base::UTF16ToASCII(match.description));
272 EXPECT_FALSE(match.allowed_to_be_default_match);
273 ++overflow_match_count;
274 } else if (match.allowed_to_be_default_match) {
275 ++default_match_count;
276 }
277 }
278 EXPECT_EQ(1U, overflow_match_count);
279 EXPECT_EQ(1U, default_match_count);
280 }
mattreynolds5afc01692016-08-19 22:09:56281}
282
283TEST_F(PhysicalWebProviderTest, TestNoMatchesWithUserInput) {
284 MockPhysicalWebDataSource* data_source =
285 client_->GetMockPhysicalWebDataSource();
286 EXPECT_TRUE(data_source);
287
288 data_source->SetMetadata(CreateMetadata(1));
289
290 // Construct an AutocompleteInput to simulate user input in the omnibox input
291 // field. The provider should not generate any matches.
292 std::string text("user input");
293 const AutocompleteInput input(base::ASCIIToUTF16(text), text.length(),
294 std::string(), GURL(),
295 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
296 true, false, true, true, false, TestSchemeClassifier());
297 provider_->Start(input, false);
298
299 EXPECT_TRUE(provider_->matches().empty());
300}
301
302}