Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 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 "chrome/browser/ui/app_list/search/app_search_provider.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <memory> | |
| 10 #include <string> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/macros.h" | |
| 14 #include "base/metrics/field_trial.h" | |
| 15 #include "base/metrics/field_trial_params.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "base/test/scoped_feature_list.h" | |
| 18 #include "chrome/browser/ui/app_list/app_list_test_util.h" | |
| 19 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provi der.h" | |
| 20 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h" | |
| 21 #include "chrome/test/base/testing_profile.h" | |
| 22 #include "content/test/mock_navigation_handle.h" | |
| 23 #include "net/http/http_response_headers.h" | |
| 24 #include "testing/gtest/include/gtest/gtest.h" | |
| 25 #include "ui/app_list/app_list_features.h" | |
| 26 #include "ui/app_list/app_list_model.h" | |
| 27 #include "ui/app_list/search_result.h" | |
| 28 | |
| 29 using ::testing::_; | |
| 30 using ::testing::Return; | |
| 31 using ::testing::ReturnRef; | |
| 32 | |
| 33 namespace app_list { | |
| 34 namespace test { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 views::View* const kView = reinterpret_cast<views::View*>(123456); | |
|
xiyuan
2017/06/22 16:43:21
Can we create a real views::View owned by AnswerCa
vadimt
2017/06/22 19:48:03
Done.
| |
| 39 constexpr char kCatCardId[] = "http://meow.org/meow"; | |
| 40 constexpr char kCatCardTitle[] = "Cat is a furry beast."; | |
| 41 | |
| 42 class MockAnswerCardContents : public AnswerCardContents { | |
| 43 public: | |
| 44 MockAnswerCardContents() {} | |
| 45 | |
| 46 // AnswerCardContents overrides: | |
| 47 MOCK_METHOD1(LoadURL, void(const GURL& url)); | |
| 48 MOCK_CONST_METHOD0(IsLoading, bool()); | |
| 49 MOCK_METHOD0(GetView, views::View*()); | |
| 50 | |
| 51 private: | |
| 52 DISALLOW_COPY_AND_ASSIGN(MockAnswerCardContents); | |
| 53 }; | |
| 54 | |
| 55 gfx::Size GetMaxValidCardSize() { | |
| 56 return gfx::Size(features::AnswerCardMaxWidth(), | |
| 57 features::AnswerCardMaxHeight()); | |
| 58 } | |
| 59 | |
| 60 } // namespace | |
| 61 | |
| 62 class AnswerCardSearchProviderTest : public AppListTestBase { | |
| 63 public: | |
| 64 AnswerCardSearchProviderTest() : field_trial_list_(nullptr) {} | |
| 65 | |
| 66 void SetMockHeaders(std::string has_result, | |
| 67 std::string open_url, | |
| 68 std::string title) { | |
| 69 headers_->RemoveHeader("SearchAnswer-HasResult"); | |
| 70 headers_->RemoveHeader("SearchAnswer-OpenResultUrl"); | |
| 71 headers_->RemoveHeader("SearchAnswer-Title"); | |
| 72 | |
| 73 if (!has_result.empty()) | |
| 74 headers_->AddHeader("SearchAnswer-HasResult: " + has_result); | |
| 75 if (!open_url.empty()) | |
| 76 headers_->AddHeader("SearchAnswer-OpenResultUrl: " + open_url); | |
| 77 if (!title.empty()) | |
| 78 headers_->AddHeader("SearchAnswer-Title: " + title); | |
| 79 } | |
| 80 | |
| 81 void TestHeadersParsing(std::string has_result, | |
| 82 std::string open_url, | |
| 83 std::string title, | |
| 84 std::size_t expected_result_count) { | |
| 85 SetMockHeaders(has_result, open_url, title); | |
| 86 | |
| 87 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
| 88 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 89 | |
| 90 GURL url("http://beasts.org/search?q=cat"); | |
| 91 EXPECT_CALL(*navigation_handle_.get(), GetURL()).WillOnce(ReturnRef(url)); | |
| 92 provider()->DidFinishNavigation(navigation_handle_.get()); | |
| 93 | |
| 94 provider()->DidStopLoading(); | |
| 95 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 96 | |
| 97 EXPECT_EQ(expected_result_count, results().size()); | |
|
xiyuan
2017/06/22 16:43:21
We might need a
testing::Mock::VerifyAndClearE
vadimt
2017/06/22 19:48:03
Done.
| |
| 98 } | |
| 99 | |
| 100 AppListModel* model() const { return model_.get(); } | |
| 101 | |
| 102 const SearchProvider::Results& results() { return provider()->results(); } | |
| 103 | |
| 104 MockAnswerCardContents* contents() const { return contents_; } | |
| 105 | |
| 106 AnswerCardSearchProvider* provider() const { return provider_.get(); } | |
| 107 | |
| 108 content::MockNavigationHandle* navigation_handle() const { | |
| 109 return navigation_handle_.get(); | |
| 110 } | |
| 111 | |
| 112 // AppListTestBase overrides: | |
| 113 void SetUp() override { | |
| 114 AppListTestBase::SetUp(); | |
| 115 | |
| 116 model_ = base::MakeUnique<app_list::AppListModel>(); | |
| 117 model_->SetSearchEngineIsGoogle(true); | |
| 118 | |
| 119 controller_ = base::MakeUnique<::test::TestAppListControllerDelegate>(); | |
| 120 | |
| 121 // Set up card server URL. | |
| 122 std::map<std::string, std::string> params; | |
| 123 params["ServerUrl"] = "http://beasts.org/search"; | |
| 124 base::AssociateFieldTrialParams("TestTrial", "TestGroup", params); | |
| 125 scoped_refptr<base::FieldTrial> trial = | |
| 126 base::FieldTrialList::CreateFieldTrial("TestTrial", "TestGroup"); | |
| 127 std::unique_ptr<base::FeatureList> feature_list = | |
| 128 base::MakeUnique<base::FeatureList>(); | |
| 129 feature_list->RegisterFieldTrialOverride( | |
| 130 features::kEnableAnswerCard.name, | |
| 131 base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get()); | |
| 132 scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); | |
| 133 | |
| 134 contents_ = new MockAnswerCardContents; | |
| 135 std::unique_ptr<AnswerCardContents> contents(contents_); | |
|
xiyuan
2017/06/22 16:43:22
Would presubmit script complains about using std::
vadimt
2017/06/22 19:48:03
No, presubmit is fine.
| |
| 136 // Provider will own the MockAnswerCardContents instance. | |
| 137 provider_ = base::MakeUnique<AnswerCardSearchProvider>( | |
| 138 profile_.get(), model_.get(), nullptr, std::move(contents)); | |
| 139 | |
| 140 headers_ = new net::HttpResponseHeaders(""); | |
| 141 SetMockHeaders("true", kCatCardId, kCatCardTitle); | |
| 142 | |
| 143 navigation_handle_ = base::MakeUnique<content::MockNavigationHandle>(); | |
| 144 ON_CALL(*navigation_handle_.get(), HasCommitted()) | |
| 145 .WillByDefault(Return(true)); | |
| 146 ON_CALL(*navigation_handle_.get(), IsErrorPage()) | |
| 147 .WillByDefault(Return(false)); | |
| 148 ON_CALL(*navigation_handle_.get(), IsInMainFrame()) | |
| 149 .WillByDefault(Return(true)); | |
| 150 ON_CALL(*navigation_handle_.get(), GetResponseHeaders()) | |
| 151 .WillByDefault(Return(headers_.get())); | |
| 152 | |
| 153 ON_CALL(*contents_, GetView()).WillByDefault(Return(kView)); | |
| 154 } | |
| 155 | |
| 156 private: | |
| 157 std::unique_ptr<app_list::AppListModel> model_; | |
| 158 std::unique_ptr<AnswerCardSearchProvider> provider_; | |
| 159 std::unique_ptr<::test::TestAppListControllerDelegate> controller_; | |
| 160 MockAnswerCardContents* contents_ = nullptr; // Unowned. | |
| 161 base::FieldTrialList field_trial_list_; | |
| 162 base::test::ScopedFeatureList scoped_feature_list_; | |
| 163 scoped_refptr<net::HttpResponseHeaders> headers_; | |
| 164 std::unique_ptr<content::MockNavigationHandle> navigation_handle_; | |
| 165 | |
| 166 DISALLOW_COPY_AND_ASSIGN(AnswerCardSearchProviderTest); | |
| 167 }; | |
| 168 | |
| 169 // Basic event sequence. | |
| 170 TEST_F(AnswerCardSearchProviderTest, Basic) { | |
| 171 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
| 172 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 173 GURL url("http://beasts.org/search?q=cat"); | |
| 174 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 175 provider()->DidFinishNavigation(navigation_handle()); | |
| 176 provider()->DidStopLoading(); | |
| 177 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 178 | |
| 179 EXPECT_EQ(1UL, results().size()); | |
| 180 SearchResult* result = results()[0].get(); | |
| 181 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
| 182 EXPECT_EQ(kCatCardId, result->id()); | |
| 183 EXPECT_EQ(1, result->relevance()); | |
| 184 EXPECT_EQ(kView, result->view()); | |
| 185 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
| 186 } | |
| 187 | |
| 188 // Voice queries are ignored. | |
| 189 TEST_F(AnswerCardSearchProviderTest, VoiceQuery) { | |
| 190 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
| 191 provider()->Start(true, base::UTF8ToUTF16("cat")); | |
| 192 } | |
| 193 | |
| 194 // Queries to non-Google search engines are ignored. | |
| 195 TEST_F(AnswerCardSearchProviderTest, NotGoogle) { | |
| 196 model()->SetSearchEngineIsGoogle(false); | |
| 197 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
| 198 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 199 } | |
| 200 | |
| 201 // Zero-query is ignored. | |
| 202 TEST_F(AnswerCardSearchProviderTest, EmptyQuery) { | |
| 203 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
| 204 provider()->Start(false, base::UTF8ToUTF16("")); | |
| 205 } | |
| 206 | |
| 207 // Two queries, the second produces a card of exactly same size, so | |
| 208 // UpdatePreferredSize() doesn't come. The second query should still produce a | |
| 209 // result. | |
| 210 TEST_F(AnswerCardSearchProviderTest, TwoResultsSameSize) { | |
| 211 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
| 212 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 213 GURL url("http://beasts.org/search?q=cat"); | |
| 214 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 215 provider()->DidFinishNavigation(navigation_handle()); | |
| 216 provider()->DidStopLoading(); | |
| 217 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 218 | |
| 219 EXPECT_EQ(1UL, results().size()); | |
| 220 SearchResult* result = results()[0].get(); | |
| 221 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
| 222 EXPECT_EQ(kCatCardId, result->id()); | |
| 223 EXPECT_EQ(1, result->relevance()); | |
| 224 EXPECT_EQ(kView, result->view()); | |
| 225 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
| 226 | |
| 227 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=dog"))); | |
| 228 provider()->Start(false, base::UTF8ToUTF16("dog")); | |
| 229 EXPECT_EQ(0UL, results().size()); | |
| 230 | |
| 231 url = GURL("http://beasts.org/search?q=dog"); | |
| 232 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 233 SetMockHeaders("true", "http://woof.org/woof", "Dog is a friendly beast."); | |
| 234 provider()->DidFinishNavigation(navigation_handle()); | |
| 235 provider()->DidStopLoading(); | |
| 236 // No UpdatePreferredSize(). | |
| 237 | |
| 238 EXPECT_EQ(1UL, results().size()); | |
| 239 result = results()[0].get(); | |
| 240 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
| 241 EXPECT_EQ("http://woof.org/woof", result->id()); | |
| 242 EXPECT_EQ(1, result->relevance()); | |
| 243 EXPECT_EQ(kView, result->view()); | |
| 244 EXPECT_EQ(base::UTF8ToUTF16("Dog is a friendly beast."), result->title()); | |
| 245 } | |
| 246 | |
| 247 // User enters a query character by character, so that each next query generates | |
| 248 // a web request while the previous one is still in progress. Only the last | |
| 249 // query should produce a result. | |
| 250 TEST_F(AnswerCardSearchProviderTest, InterruptedRequest) { | |
| 251 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=c"))); | |
| 252 provider()->Start(false, base::UTF8ToUTF16("c")); | |
| 253 EXPECT_EQ(0UL, results().size()); | |
| 254 | |
| 255 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=ca"))); | |
| 256 provider()->Start(false, base::UTF8ToUTF16("ca")); | |
| 257 EXPECT_EQ(0UL, results().size()); | |
| 258 | |
| 259 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
| 260 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 261 EXPECT_EQ(0UL, results().size()); | |
| 262 | |
| 263 SetMockHeaders("true", "http://c-meow", "Title c"); | |
| 264 GURL url = GURL("http://beasts.org/search?q=c"); | |
| 265 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 266 provider()->DidFinishNavigation(navigation_handle()); | |
| 267 provider()->DidStopLoading(); | |
| 268 EXPECT_EQ(0UL, results().size()); | |
| 269 | |
| 270 SetMockHeaders("true", "http://ca-meow", "Title ca"); | |
| 271 url = GURL("http://beasts.org/search?q=ca"); | |
| 272 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 273 provider()->DidFinishNavigation(navigation_handle()); | |
| 274 provider()->DidStopLoading(); | |
| 275 provider()->UpdatePreferredSize(gfx::Size(1, 1)); | |
| 276 EXPECT_EQ(0UL, results().size()); | |
| 277 | |
| 278 SetMockHeaders("true", kCatCardId, kCatCardTitle); | |
| 279 url = GURL("http://beasts.org/search?q=cat"); | |
| 280 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 281 provider()->DidFinishNavigation(navigation_handle()); | |
| 282 provider()->DidStopLoading(); | |
| 283 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 284 EXPECT_EQ(1UL, results().size()); | |
| 285 | |
| 286 SearchResult* result = results()[0].get(); | |
| 287 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
| 288 EXPECT_EQ(kCatCardId, result->id()); | |
| 289 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
| 290 } | |
| 291 | |
| 292 // Due to, for example, JS activity in the card, it can change its size after | |
| 293 // loading. We should hide the result while its size if larger than the allowed | |
| 294 // maximum. | |
| 295 TEST_F(AnswerCardSearchProviderTest, ChangingSize) { | |
| 296 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
| 297 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
| 298 GURL url("http://beasts.org/search?q=cat"); | |
| 299 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
| 300 provider()->DidFinishNavigation(navigation_handle()); | |
| 301 provider()->UpdatePreferredSize(gfx::Size(features::AnswerCardMaxWidth() + 1, | |
| 302 features::AnswerCardMaxHeight())); | |
| 303 provider()->DidStopLoading(); | |
| 304 EXPECT_EQ(0UL, results().size()); | |
| 305 | |
| 306 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 307 EXPECT_EQ(1UL, results().size()); | |
| 308 | |
| 309 provider()->UpdatePreferredSize(gfx::Size( | |
| 310 features::AnswerCardMaxWidth(), features::AnswerCardMaxHeight() + 1)); | |
| 311 EXPECT_EQ(0UL, results().size()); | |
| 312 | |
| 313 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
| 314 EXPECT_EQ(1UL, results().size()); | |
| 315 } | |
| 316 | |
| 317 // The result is generated only when all headers exist, and indicate presence of | |
| 318 // the result. | |
| 319 TEST_F(AnswerCardSearchProviderTest, Headers) { | |
| 320 TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); | |
| 321 TestHeadersParsing("false", kCatCardId, kCatCardTitle, 0UL); | |
| 322 TestHeadersParsing("", kCatCardId, kCatCardTitle, 0UL); | |
| 323 TestHeadersParsing("true", "", kCatCardTitle, 0UL); | |
| 324 TestHeadersParsing("true", kCatCardId, "", 0UL); | |
| 325 TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); | |
| 326 } | |
| 327 | |
| 328 } // namespace test | |
| 329 } // namespace app_list | |
| OLD | NEW |