OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/omnibox/browser/physical_web_provider.h" | 5 #include "components/omnibox/browser/physical_web_provider.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
11 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
13 #include "base/values.h" | 13 #include "base/values.h" |
14 #include "components/metrics/proto/omnibox_event.pb.h" | 14 #include "components/metrics/proto/omnibox_event.pb.h" |
15 #include "components/omnibox/browser/mock_autocomplete_provider_client.h" | 15 #include "components/omnibox/browser/mock_autocomplete_provider_client.h" |
16 #include "components/omnibox/browser/test_scheme_classifier.h" | 16 #include "components/omnibox/browser/test_scheme_classifier.h" |
17 #include "components/physical_web/data_source/physical_web_data_source.h" | 17 #include "components/physical_web/data_source/physical_web_data_source.h" |
18 #include "components/physical_web/data_source/physical_web_listener.h" | 18 #include "components/physical_web/data_source/physical_web_listener.h" |
19 #include "grit/components_strings.h" | 19 #include "grit/components_strings.h" |
20 #include "testing/gmock/include/gmock/gmock.h" | 20 #include "testing/gmock/include/gmock/gmock.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
22 #include "ui/base/l10n/l10n_util.h" | 22 #include "ui/base/l10n/l10n_util.h" |
23 #include "ui/gfx/text_elider.h" | |
23 #include "url/gurl.h" | 24 #include "url/gurl.h" |
24 | 25 |
25 namespace { | 26 namespace { |
26 | 27 |
27 // A mock implementation of the Physical Web data source that allows setting | 28 // A mock implementation of the Physical Web data source that allows setting |
28 // metadata for nearby URLs directly. | 29 // metadata for nearby URLs directly. |
29 class MockPhysicalWebDataSource : public PhysicalWebDataSource { | 30 class MockPhysicalWebDataSource : public PhysicalWebDataSource { |
30 public: | 31 public: |
31 MockPhysicalWebDataSource() : metadata_(new base::ListValue()) {} | 32 MockPhysicalWebDataSource() : metadata_(new base::ListValue()) {} |
32 ~MockPhysicalWebDataSource() override {} | 33 ~MockPhysicalWebDataSource() override {} |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
77 // Convenience method to avoid downcasts when accessing the mock data source. | 78 // Convenience method to avoid downcasts when accessing the mock data source. |
78 MockPhysicalWebDataSource* GetMockPhysicalWebDataSource() { | 79 MockPhysicalWebDataSource* GetMockPhysicalWebDataSource() { |
79 return physical_web_data_source_.get(); | 80 return physical_web_data_source_.get(); |
80 } | 81 } |
81 | 82 |
82 private: | 83 private: |
83 std::unique_ptr<MockPhysicalWebDataSource> physical_web_data_source_; | 84 std::unique_ptr<MockPhysicalWebDataSource> physical_web_data_source_; |
84 TestSchemeClassifier scheme_classifier_; | 85 TestSchemeClassifier scheme_classifier_; |
85 }; | 86 }; |
86 | 87 |
88 } // namespace | |
89 | |
87 class PhysicalWebProviderTest : public testing::Test { | 90 class PhysicalWebProviderTest : public testing::Test { |
88 protected: | 91 protected: |
89 PhysicalWebProviderTest() : provider_(NULL) {} | 92 PhysicalWebProviderTest() : provider_(NULL) {} |
90 ~PhysicalWebProviderTest() override {} | 93 ~PhysicalWebProviderTest() override {} |
91 | 94 |
92 void SetUp() override { | 95 void SetUp() override { |
93 client_.reset(new FakeAutocompleteProviderClient()); | 96 client_.reset(new FakeAutocompleteProviderClient()); |
94 provider_ = PhysicalWebProvider::Create(client_.get(), nullptr); | 97 provider_ = PhysicalWebProvider::Create(client_.get(), nullptr); |
95 } | 98 } |
96 | 99 |
(...skipping 25 matching lines...) Expand all Loading... | |
122 static AutocompleteInput CreateInputForNTP() { | 125 static AutocompleteInput CreateInputForNTP() { |
123 return AutocompleteInput( | 126 return AutocompleteInput( |
124 base::string16(), base::string16::npos, std::string(), GURL(), | 127 base::string16(), base::string16::npos, std::string(), GURL(), |
125 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, | 128 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, |
126 false, false, true, true, true, TestSchemeClassifier()); | 129 false, false, true, true, true, TestSchemeClassifier()); |
127 } | 130 } |
128 | 131 |
129 // Construct an AutocompleteInput to represent tapping the omnibox with |url| | 132 // Construct an AutocompleteInput to represent tapping the omnibox with |url| |
130 // as the current web page. | 133 // as the current web page. |
131 static AutocompleteInput CreateInputWithCurrentUrl(const std::string& url) { | 134 static AutocompleteInput CreateInputWithCurrentUrl(const std::string& url) { |
132 return AutocompleteInput(base::ASCIIToUTF16(url), base::string16::npos, | 135 return AutocompleteInput(base::UTF8ToUTF16(url), base::string16::npos, |
Mark P
2016/09/13 23:34:43
side comment: URLs are in ASCII as far as I know.
mattreynolds
2016/09/14 19:04:41
Acknowledged.
| |
133 std::string(), GURL(url), | 136 std::string(), GURL(url), |
134 metrics::OmniboxEventProto::OTHER, false, false, | 137 metrics::OmniboxEventProto::OTHER, false, false, |
135 true, true, true, TestSchemeClassifier()); | 138 true, true, true, TestSchemeClassifier()); |
136 } | 139 } |
137 | 140 |
141 // For a given |match|, check that the destination URL, contents string, | |
142 // description string, and default match state agree with the values specified | |
143 // in |url|, |contents|, |description|, and |allowed_to_be_default_match|. | |
144 static void ValidateMatch(const AutocompleteMatch& match, | |
145 const std::string& url, | |
146 const std::string& contents, | |
147 const std::string& description, | |
148 bool allowed_to_be_default_match) { | |
149 EXPECT_EQ(url, match.destination_url.spec()); | |
150 EXPECT_EQ(contents, base::UTF16ToUTF8(match.contents)); | |
151 EXPECT_EQ(description, base::UTF16ToUTF8(match.description)); | |
152 EXPECT_EQ(allowed_to_be_default_match, match.allowed_to_be_default_match); | |
153 } | |
154 | |
155 // Construct the contents string for an overflow item. Use |truncated_title| | |
Mark P
2016/09/13 23:34:43
nit: Constructs -> Returns
mattreynolds
2016/09/14 19:04:41
Done.
| |
156 // as the title of the first match, |match_count_without_default| as the | |
157 // total number of matches (not counting the default match), and | |
158 // |metadata_count| as the number of nearby Physical Web URLs for which we | |
159 // have metadata. | |
160 static std::string ConstructOverflowItemContents( | |
161 const std::string& truncated_title, | |
162 size_t match_count_without_default, | |
163 size_t metadata_count) { | |
164 // Don't treat the overflow item as a metadata match. | |
165 const size_t metadata_match_count = match_count_without_default - 1; | |
166 // Determine how many URLs we didn't create match items for. | |
167 const size_t additional_url_count = metadata_count - metadata_match_count; | |
168 | |
169 // Build the contents string. | |
170 if (truncated_title.empty()) { | |
171 return l10n_util::GetPluralStringFUTF8( | |
172 IDS_PHYSICAL_WEB_OVERFLOW_EMPTY_TITLE, additional_url_count); | |
173 } else { | |
174 // Subtract one from the additional URL count because the first item is | |
175 // represented by its title. | |
176 std::string contents_suffix = l10n_util::GetPluralStringFUTF8( | |
177 IDS_PHYSICAL_WEB_OVERFLOW, additional_url_count - 1); | |
178 return truncated_title + " " + contents_suffix; | |
179 } | |
180 } | |
181 | |
182 // Run a test case using |input| as the simulated state of the omnibox input | |
183 // field, |metadata_list| as the list of simulated Physical Web metadata, | |
184 // and |title_truncated| as the truncated title of the first match. This will | |
185 // check for the presence of default matches and overflow items, as well as | |
186 // checking the fields of the overflow item. Metadata matches are not checked. | |
187 void OverflowItemTestCase(const AutocompleteInput& input, | |
Mark P
2016/09/13 23:34:43
I'm a bit nervous about this function. Rather tha
mattreynolds
2016/09/14 19:04:41
I removed the logic for computing the number of ma
| |
188 std::unique_ptr<base::ListValue> metadata_list, | |
189 const std::string& title_truncated) { | |
190 const size_t metadata_count = metadata_list->GetSize(); | |
191 | |
192 const bool should_expect_default_match = !input.text().empty(); | |
193 | |
194 // The actual threshold for creating an overflow item may be lower than | |
195 // kMaxMatches, but for testing we always create enough metadata items to | |
196 // exceed the AutocompleteProvider's limit to ensure the overflow item is | |
197 // created. | |
198 const bool should_expect_overflow_item = | |
199 (metadata_count > AutocompleteProvider::kMaxMatches); | |
200 | |
201 MockPhysicalWebDataSource* data_source = | |
202 client_->GetMockPhysicalWebDataSource(); | |
203 EXPECT_TRUE(data_source); | |
204 | |
205 data_source->SetMetadata(std::move(metadata_list)); | |
206 | |
207 provider_->Start(input, false); | |
208 | |
209 const size_t match_count = provider_->matches().size(); | |
210 const size_t match_count_without_default = | |
211 should_expect_default_match ? match_count - 1 : match_count; | |
212 | |
213 if (should_expect_overflow_item) { | |
214 EXPECT_LT(match_count_without_default, metadata_count); | |
215 } else { | |
216 EXPECT_EQ(match_count_without_default, metadata_count); | |
217 } | |
218 | |
219 size_t overflow_match_count = 0; | |
220 size_t default_match_count = 0; | |
221 for (const auto& match : provider_->matches()) { | |
222 if (match.type == AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW) { | |
223 std::string contents = ConstructOverflowItemContents( | |
224 title_truncated, match_count_without_default, metadata_count); | |
225 ValidateMatch( | |
226 match, "chrome://physical-web/", contents, | |
227 l10n_util::GetStringUTF8(IDS_PHYSICAL_WEB_OVERFLOW_DESCRIPTION), | |
228 false); | |
229 ++overflow_match_count; | |
230 } else if (match.allowed_to_be_default_match) { | |
231 ++default_match_count; | |
232 } | |
233 } | |
234 EXPECT_EQ(should_expect_overflow_item ? 1U : 0U, overflow_match_count); | |
235 EXPECT_EQ(should_expect_default_match ? 1U : 0U, default_match_count); | |
236 } | |
237 | |
138 std::unique_ptr<FakeAutocompleteProviderClient> client_; | 238 std::unique_ptr<FakeAutocompleteProviderClient> client_; |
139 scoped_refptr<PhysicalWebProvider> provider_; | 239 scoped_refptr<PhysicalWebProvider> provider_; |
140 | 240 |
141 private: | 241 private: |
142 DISALLOW_COPY_AND_ASSIGN(PhysicalWebProviderTest); | 242 DISALLOW_COPY_AND_ASSIGN(PhysicalWebProviderTest); |
143 }; | 243 }; |
144 | 244 |
145 TEST_F(PhysicalWebProviderTest, TestEmptyMetadataListCreatesNoMatches) { | 245 TEST_F(PhysicalWebProviderTest, TestEmptyMetadataListCreatesNoMatches) { |
146 MockPhysicalWebDataSource* data_source = | 246 MockPhysicalWebDataSource* data_source = |
147 client_->GetMockPhysicalWebDataSource(); | 247 client_->GetMockPhysicalWebDataSource(); |
(...skipping 27 matching lines...) Expand all Loading... | |
175 | 275 |
176 data_source->SetMetadata(std::move(metadata_list)); | 276 data_source->SetMetadata(std::move(metadata_list)); |
177 | 277 |
178 // Run the test with no text in the omnibox input to simulate NTP. | 278 // Run the test with no text in the omnibox input to simulate NTP. |
179 provider_->Start(CreateInputForNTP(), false); | 279 provider_->Start(CreateInputForNTP(), false); |
180 | 280 |
181 // Check that there is only one match item and its fields are correct. | 281 // Check that there is only one match item and its fields are correct. |
182 EXPECT_EQ(1U, provider_->matches().size()); | 282 EXPECT_EQ(1U, provider_->matches().size()); |
183 const AutocompleteMatch& metadata_match = provider_->matches().front(); | 283 const AutocompleteMatch& metadata_match = provider_->matches().front(); |
184 EXPECT_EQ(AutocompleteMatchType::PHYSICAL_WEB, metadata_match.type); | 284 EXPECT_EQ(AutocompleteMatchType::PHYSICAL_WEB, metadata_match.type); |
185 EXPECT_EQ(resolved_url, metadata_match.destination_url.spec()); | 285 ValidateMatch(metadata_match, resolved_url, resolved_url, title, false); |
186 EXPECT_EQ(resolved_url, base::UTF16ToASCII(metadata_match.contents)); | |
187 EXPECT_EQ(title, base::UTF16ToASCII(metadata_match.description)); | |
188 EXPECT_FALSE(metadata_match.allowed_to_be_default_match); | |
189 | 286 |
190 // Run the test again with a URL in the omnibox input. An additional match | 287 // Run the test again with a URL in the omnibox input. An additional match |
191 // should be added as a default match. | 288 // should be added as a default match. |
192 provider_->Start(CreateInputWithCurrentUrl("http://www.cnn.com"), false); | 289 provider_->Start(CreateInputWithCurrentUrl("http://www.cnn.com"), false); |
193 | 290 |
194 size_t metadata_match_count = 0; | 291 size_t metadata_match_count = 0; |
195 size_t default_match_count = 0; | 292 size_t default_match_count = 0; |
196 for (const auto& match : provider_->matches()) { | 293 for (const auto& match : provider_->matches()) { |
197 if (match.type == AutocompleteMatchType::PHYSICAL_WEB) { | 294 if (match.type == AutocompleteMatchType::PHYSICAL_WEB) { |
198 EXPECT_EQ(resolved_url, match.destination_url.spec()); | 295 ValidateMatch(match, resolved_url, resolved_url, title, false); |
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; | 296 ++metadata_match_count; |
203 } else { | 297 } else { |
204 EXPECT_TRUE(match.allowed_to_be_default_match); | 298 EXPECT_TRUE(match.allowed_to_be_default_match); |
205 ++default_match_count; | 299 ++default_match_count; |
206 } | 300 } |
207 } | 301 } |
208 EXPECT_EQ(2U, provider_->matches().size()); | 302 EXPECT_EQ(2U, provider_->matches().size()); |
209 EXPECT_EQ(1U, metadata_match_count); | 303 EXPECT_EQ(1U, metadata_match_count); |
210 EXPECT_EQ(1U, default_match_count); | 304 EXPECT_EQ(1U, default_match_count); |
211 } | 305 } |
212 | 306 |
213 TEST_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 | |
227 { | |
228 // Run the test with no text in the omnibox input to simulate NTP. | |
229 provider_->Start(CreateInputForNTP(), false); | |
230 | |
231 const size_t match_count = provider_->matches().size(); | |
232 EXPECT_LT(match_count, metadata_count); | |
233 | |
234 // 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 } | |
281 } | |
282 | |
283 TEST_F(PhysicalWebProviderTest, TestNoMatchesWithUserInput) { | 307 TEST_F(PhysicalWebProviderTest, TestNoMatchesWithUserInput) { |
284 MockPhysicalWebDataSource* data_source = | 308 MockPhysicalWebDataSource* data_source = |
285 client_->GetMockPhysicalWebDataSource(); | 309 client_->GetMockPhysicalWebDataSource(); |
286 EXPECT_TRUE(data_source); | 310 EXPECT_TRUE(data_source); |
287 | 311 |
288 data_source->SetMetadata(CreateMetadata(1)); | 312 data_source->SetMetadata(CreateMetadata(1)); |
289 | 313 |
290 // Construct an AutocompleteInput to simulate user input in the omnibox input | 314 // Construct an AutocompleteInput to simulate user input in the omnibox input |
291 // field. The provider should not generate any matches. | 315 // field. The provider should not generate any matches. |
292 std::string text("user input"); | 316 std::string text("user input"); |
293 const AutocompleteInput input(base::ASCIIToUTF16(text), text.length(), | 317 const AutocompleteInput input( |
294 std::string(), GURL(), | 318 base::UTF8ToUTF16(text), text.length(), std::string(), GURL(), |
295 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, | 319 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, |
296 true, false, true, true, false, TestSchemeClassifier()); | 320 true, false, true, true, false, TestSchemeClassifier()); |
297 provider_->Start(input, false); | 321 provider_->Start(input, false); |
298 | 322 |
299 EXPECT_TRUE(provider_->matches().empty()); | 323 EXPECT_TRUE(provider_->matches().empty()); |
300 } | 324 } |
301 | 325 |
326 TEST_F(PhysicalWebProviderTest, TestManyMetadataItemsCreatesOverflowItem) { | |
327 // Create enough metadata to guarantee an overflow item will be created. | |
328 const size_t metadata_count = AutocompleteProvider::kMaxMatches + 1; | |
329 | |
330 // Run the test with no text in the omnibox input to simulate NTP. | |
331 OverflowItemTestCase(CreateInputForNTP(), CreateMetadata(metadata_count), | |
332 "Example title 0"); | |
333 | |
334 // Run the test again with a URL in the omnibox input. An additional match | |
335 // should be added as a default match. | |
336 OverflowItemTestCase(CreateInputWithCurrentUrl("http://www.cnn.com"), | |
337 CreateMetadata(metadata_count), "Example title 0"); | |
302 } | 338 } |
339 | |
340 TEST_F(PhysicalWebProviderTest, TestLongPageTitleIsTruncatedInOverflowItem) { | |
341 // Set a long title for the first item. The page title for this item will | |
342 // appear in the overflow item's content string. | |
343 auto metadata_list = CreateMetadata(AutocompleteProvider::kMaxMatches + 1); | |
344 base::DictionaryValue* metadata_item; | |
345 EXPECT_TRUE(metadata_list->GetDictionary(0, &metadata_item)); | |
346 metadata_item->SetString("title", "Extra long example title 0"); | |
347 | |
348 OverflowItemTestCase(CreateInputForNTP(), std::move(metadata_list), | |
349 "Extra long exa" + std::string(gfx::kEllipsis)); | |
350 } | |
351 | |
352 TEST_F(PhysicalWebProviderTest, TestEmptyPageTitleInOverflowItem) { | |
353 // Set an empty title for the first item. Because the title is empty, we will | |
354 // display an alternate string in the overflow item's contents. | |
355 auto metadata_list = CreateMetadata(AutocompleteProvider::kMaxMatches + 1); | |
356 base::DictionaryValue* metadata_item; | |
357 EXPECT_TRUE(metadata_list->GetDictionary(0, &metadata_item)); | |
358 metadata_item->SetString("title", ""); | |
359 | |
360 OverflowItemTestCase(CreateInputForNTP(), std::move(metadata_list), ""); | |
361 } | |
362 | |
363 TEST_F(PhysicalWebProviderTest, TestRTLPageTitleInOverflowItem) { | |
364 // Set a Hebrew title for the first item. | |
365 auto metadata_list = CreateMetadata(AutocompleteProvider::kMaxMatches + 1); | |
366 base::DictionaryValue* metadata_item; | |
367 EXPECT_TRUE(metadata_list->GetDictionary(0, &metadata_item)); | |
368 metadata_item->SetString("title", "ויקיפדיה"); | |
369 | |
370 OverflowItemTestCase(CreateInputForNTP(), std::move(metadata_list), | |
371 "ויקיפדיה"); | |
372 } | |
OLD | NEW |