Chromium Code Reviews| Index: chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc |
| diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f375d217a5e5bfd9c9b25fb6466c6e7203acccb6 |
| --- /dev/null |
| +++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc |
| @@ -0,0 +1,329 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/ui/app_list/search/app_search_provider.h" |
| + |
| +#include <stddef.h> |
| + |
| +#include <memory> |
| +#include <string> |
| +#include <utility> |
| + |
| +#include "base/macros.h" |
| +#include "base/metrics/field_trial.h" |
| +#include "base/metrics/field_trial_params.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/test/scoped_feature_list.h" |
| +#include "chrome/browser/ui/app_list/app_list_test_util.h" |
| +#include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider.h" |
| +#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h" |
| +#include "chrome/test/base/testing_profile.h" |
| +#include "content/test/mock_navigation_handle.h" |
| +#include "net/http/http_response_headers.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "ui/app_list/app_list_features.h" |
| +#include "ui/app_list/app_list_model.h" |
| +#include "ui/app_list/search_result.h" |
| + |
| +using ::testing::_; |
| +using ::testing::Return; |
| +using ::testing::ReturnRef; |
| + |
| +namespace app_list { |
| +namespace test { |
| + |
| +namespace { |
| + |
| +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.
|
| +constexpr char kCatCardId[] = "http://meow.org/meow"; |
| +constexpr char kCatCardTitle[] = "Cat is a furry beast."; |
| + |
| +class MockAnswerCardContents : public AnswerCardContents { |
| + public: |
| + MockAnswerCardContents() {} |
| + |
| + // AnswerCardContents overrides: |
| + MOCK_METHOD1(LoadURL, void(const GURL& url)); |
| + MOCK_CONST_METHOD0(IsLoading, bool()); |
| + MOCK_METHOD0(GetView, views::View*()); |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(MockAnswerCardContents); |
| +}; |
| + |
| +gfx::Size GetMaxValidCardSize() { |
| + return gfx::Size(features::AnswerCardMaxWidth(), |
| + features::AnswerCardMaxHeight()); |
| +} |
| + |
| +} // namespace |
| + |
| +class AnswerCardSearchProviderTest : public AppListTestBase { |
| + public: |
| + AnswerCardSearchProviderTest() : field_trial_list_(nullptr) {} |
| + |
| + void SetMockHeaders(std::string has_result, |
| + std::string open_url, |
| + std::string title) { |
| + headers_->RemoveHeader("SearchAnswer-HasResult"); |
| + headers_->RemoveHeader("SearchAnswer-OpenResultUrl"); |
| + headers_->RemoveHeader("SearchAnswer-Title"); |
| + |
| + if (!has_result.empty()) |
| + headers_->AddHeader("SearchAnswer-HasResult: " + has_result); |
| + if (!open_url.empty()) |
| + headers_->AddHeader("SearchAnswer-OpenResultUrl: " + open_url); |
| + if (!title.empty()) |
| + headers_->AddHeader("SearchAnswer-Title: " + title); |
| + } |
| + |
| + void TestHeadersParsing(std::string has_result, |
| + std::string open_url, |
| + std::string title, |
| + std::size_t expected_result_count) { |
| + SetMockHeaders(has_result, open_url, title); |
| + |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| + |
| + GURL url("http://beasts.org/search?q=cat"); |
| + EXPECT_CALL(*navigation_handle_.get(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle_.get()); |
| + |
| + provider()->DidStopLoading(); |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + |
| + 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.
|
| + } |
| + |
| + AppListModel* model() const { return model_.get(); } |
| + |
| + const SearchProvider::Results& results() { return provider()->results(); } |
| + |
| + MockAnswerCardContents* contents() const { return contents_; } |
| + |
| + AnswerCardSearchProvider* provider() const { return provider_.get(); } |
| + |
| + content::MockNavigationHandle* navigation_handle() const { |
| + return navigation_handle_.get(); |
| + } |
| + |
| + // AppListTestBase overrides: |
| + void SetUp() override { |
| + AppListTestBase::SetUp(); |
| + |
| + model_ = base::MakeUnique<app_list::AppListModel>(); |
| + model_->SetSearchEngineIsGoogle(true); |
| + |
| + controller_ = base::MakeUnique<::test::TestAppListControllerDelegate>(); |
| + |
| + // Set up card server URL. |
| + std::map<std::string, std::string> params; |
| + params["ServerUrl"] = "http://beasts.org/search"; |
| + base::AssociateFieldTrialParams("TestTrial", "TestGroup", params); |
| + scoped_refptr<base::FieldTrial> trial = |
| + base::FieldTrialList::CreateFieldTrial("TestTrial", "TestGroup"); |
| + std::unique_ptr<base::FeatureList> feature_list = |
| + base::MakeUnique<base::FeatureList>(); |
| + feature_list->RegisterFieldTrialOverride( |
| + features::kEnableAnswerCard.name, |
| + base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get()); |
| + scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); |
| + |
| + contents_ = new MockAnswerCardContents; |
| + 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.
|
| + // Provider will own the MockAnswerCardContents instance. |
| + provider_ = base::MakeUnique<AnswerCardSearchProvider>( |
| + profile_.get(), model_.get(), nullptr, std::move(contents)); |
| + |
| + headers_ = new net::HttpResponseHeaders(""); |
| + SetMockHeaders("true", kCatCardId, kCatCardTitle); |
| + |
| + navigation_handle_ = base::MakeUnique<content::MockNavigationHandle>(); |
| + ON_CALL(*navigation_handle_.get(), HasCommitted()) |
| + .WillByDefault(Return(true)); |
| + ON_CALL(*navigation_handle_.get(), IsErrorPage()) |
| + .WillByDefault(Return(false)); |
| + ON_CALL(*navigation_handle_.get(), IsInMainFrame()) |
| + .WillByDefault(Return(true)); |
| + ON_CALL(*navigation_handle_.get(), GetResponseHeaders()) |
| + .WillByDefault(Return(headers_.get())); |
| + |
| + ON_CALL(*contents_, GetView()).WillByDefault(Return(kView)); |
| + } |
| + |
| + private: |
| + std::unique_ptr<app_list::AppListModel> model_; |
| + std::unique_ptr<AnswerCardSearchProvider> provider_; |
| + std::unique_ptr<::test::TestAppListControllerDelegate> controller_; |
| + MockAnswerCardContents* contents_ = nullptr; // Unowned. |
| + base::FieldTrialList field_trial_list_; |
| + base::test::ScopedFeatureList scoped_feature_list_; |
| + scoped_refptr<net::HttpResponseHeaders> headers_; |
| + std::unique_ptr<content::MockNavigationHandle> navigation_handle_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AnswerCardSearchProviderTest); |
| +}; |
| + |
| +// Basic event sequence. |
| +TEST_F(AnswerCardSearchProviderTest, Basic) { |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| + GURL url("http://beasts.org/search?q=cat"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + |
| + EXPECT_EQ(1UL, results().size()); |
| + SearchResult* result = results()[0].get(); |
| + EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); |
| + EXPECT_EQ(kCatCardId, result->id()); |
| + EXPECT_EQ(1, result->relevance()); |
| + EXPECT_EQ(kView, result->view()); |
| + EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); |
| +} |
| + |
| +// Voice queries are ignored. |
| +TEST_F(AnswerCardSearchProviderTest, VoiceQuery) { |
| + EXPECT_CALL(*contents(), LoadURL(_)).Times(0); |
| + provider()->Start(true, base::UTF8ToUTF16("cat")); |
| +} |
| + |
| +// Queries to non-Google search engines are ignored. |
| +TEST_F(AnswerCardSearchProviderTest, NotGoogle) { |
| + model()->SetSearchEngineIsGoogle(false); |
| + EXPECT_CALL(*contents(), LoadURL(_)).Times(0); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| +} |
| + |
| +// Zero-query is ignored. |
| +TEST_F(AnswerCardSearchProviderTest, EmptyQuery) { |
| + EXPECT_CALL(*contents(), LoadURL(_)).Times(0); |
| + provider()->Start(false, base::UTF8ToUTF16("")); |
| +} |
| + |
| +// Two queries, the second produces a card of exactly same size, so |
| +// UpdatePreferredSize() doesn't come. The second query should still produce a |
| +// result. |
| +TEST_F(AnswerCardSearchProviderTest, TwoResultsSameSize) { |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| + GURL url("http://beasts.org/search?q=cat"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + |
| + EXPECT_EQ(1UL, results().size()); |
| + SearchResult* result = results()[0].get(); |
| + EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); |
| + EXPECT_EQ(kCatCardId, result->id()); |
| + EXPECT_EQ(1, result->relevance()); |
| + EXPECT_EQ(kView, result->view()); |
| + EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); |
| + |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=dog"))); |
| + provider()->Start(false, base::UTF8ToUTF16("dog")); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + url = GURL("http://beasts.org/search?q=dog"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + SetMockHeaders("true", "http://woof.org/woof", "Dog is a friendly beast."); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + // No UpdatePreferredSize(). |
| + |
| + EXPECT_EQ(1UL, results().size()); |
| + result = results()[0].get(); |
| + EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); |
| + EXPECT_EQ("http://woof.org/woof", result->id()); |
| + EXPECT_EQ(1, result->relevance()); |
| + EXPECT_EQ(kView, result->view()); |
| + EXPECT_EQ(base::UTF8ToUTF16("Dog is a friendly beast."), result->title()); |
| +} |
| + |
| +// User enters a query character by character, so that each next query generates |
| +// a web request while the previous one is still in progress. Only the last |
| +// query should produce a result. |
| +TEST_F(AnswerCardSearchProviderTest, InterruptedRequest) { |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=c"))); |
| + provider()->Start(false, base::UTF8ToUTF16("c")); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=ca"))); |
| + provider()->Start(false, base::UTF8ToUTF16("ca")); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + SetMockHeaders("true", "http://c-meow", "Title c"); |
| + GURL url = GURL("http://beasts.org/search?q=c"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + SetMockHeaders("true", "http://ca-meow", "Title ca"); |
| + url = GURL("http://beasts.org/search?q=ca"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + provider()->UpdatePreferredSize(gfx::Size(1, 1)); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + SetMockHeaders("true", kCatCardId, kCatCardTitle); |
| + url = GURL("http://beasts.org/search?q=cat"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->DidStopLoading(); |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + EXPECT_EQ(1UL, results().size()); |
| + |
| + SearchResult* result = results()[0].get(); |
| + EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); |
| + EXPECT_EQ(kCatCardId, result->id()); |
| + EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); |
| +} |
| + |
| +// Due to, for example, JS activity in the card, it can change its size after |
| +// loading. We should hide the result while its size if larger than the allowed |
| +// maximum. |
| +TEST_F(AnswerCardSearchProviderTest, ChangingSize) { |
| + EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); |
| + provider()->Start(false, base::UTF8ToUTF16("cat")); |
| + GURL url("http://beasts.org/search?q=cat"); |
| + EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); |
| + provider()->DidFinishNavigation(navigation_handle()); |
| + provider()->UpdatePreferredSize(gfx::Size(features::AnswerCardMaxWidth() + 1, |
| + features::AnswerCardMaxHeight())); |
| + provider()->DidStopLoading(); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + EXPECT_EQ(1UL, results().size()); |
| + |
| + provider()->UpdatePreferredSize(gfx::Size( |
| + features::AnswerCardMaxWidth(), features::AnswerCardMaxHeight() + 1)); |
| + EXPECT_EQ(0UL, results().size()); |
| + |
| + provider()->UpdatePreferredSize(GetMaxValidCardSize()); |
| + EXPECT_EQ(1UL, results().size()); |
| +} |
| + |
| +// The result is generated only when all headers exist, and indicate presence of |
| +// the result. |
| +TEST_F(AnswerCardSearchProviderTest, Headers) { |
| + TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); |
| + TestHeadersParsing("false", kCatCardId, kCatCardTitle, 0UL); |
| + TestHeadersParsing("", kCatCardId, kCatCardTitle, 0UL); |
| + TestHeadersParsing("true", "", kCatCardTitle, 0UL); |
| + TestHeadersParsing("true", kCatCardId, "", 0UL); |
| + TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); |
| +} |
| + |
| +} // namespace test |
| +} // namespace app_list |