| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/quick_insert/search/quick_insert_search_request.h" |
| |
| #include <array> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/clipboard/clipboard_history_item.h" |
| #include "ash/clipboard/test_support/clipboard_history_item_builder.h" |
| #include "ash/clipboard/test_support/mock_clipboard_history_controller.h" |
| #include "ash/public/cpp/app_list/app_list_types.h" |
| #include "ash/public/cpp/ash_web_view.h" |
| #include "ash/public/cpp/clipboard_history_controller.h" |
| #include "ash/quick_insert/quick_insert_category.h" |
| #include "ash/quick_insert/quick_insert_search_result.h" |
| #include "ash/quick_insert/search/mock_search_quick_insert_client.h" |
| #include "ash/quick_insert/search/quick_insert_search_request.h" |
| #include "ash/quick_insert/search/quick_insert_search_source.h" |
| #include "ash/quick_insert/views/quick_insert_view_delegate.h" |
| #include "base/containers/span.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_future.h" |
| #include "base/time/clock.h" |
| #include "base/time/time.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/models/image_model.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "url/gurl.h" |
| |
| namespace ash { |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::AnyNumber; |
| using ::testing::AtLeast; |
| using ::testing::ElementsAre; |
| using ::testing::Eq; |
| using ::testing::Field; |
| using ::testing::FieldsAre; |
| using ::testing::IsEmpty; |
| using ::testing::IsSupersetOf; |
| using ::testing::Ne; |
| using ::testing::NiceMock; |
| using ::testing::Optional; |
| using ::testing::Property; |
| using ::testing::VariantWith; |
| |
| constexpr base::TimeDelta kMetricMetricTime = base::Milliseconds(300); |
| |
| constexpr auto kAllCategories = std::to_array({ |
| QuickInsertCategory::kEditorWrite, |
| QuickInsertCategory::kEditorRewrite, |
| QuickInsertCategory::kLinks, |
| QuickInsertCategory::kEmojisGifs, |
| QuickInsertCategory::kEmojis, |
| QuickInsertCategory::kClipboard, |
| QuickInsertCategory::kDriveFiles, |
| QuickInsertCategory::kLocalFiles, |
| QuickInsertCategory::kDatesTimes, |
| QuickInsertCategory::kUnitsMaths, |
| }); |
| |
| using MockSearchResultsCallback = |
| ::testing::MockFunction<QuickInsertSearchRequest::SearchResultsCallback>; |
| |
| class QuickInsertSearchRequestTest : public testing::Test { |
| protected: |
| base::test::TaskEnvironment& task_environment() { return task_environment_; } |
| |
| MockSearchQuickInsertClient& client() { return client_; } |
| |
| private: |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| network::TestURLLoaderFactory test_factory_; |
| NiceMock<MockSearchQuickInsertClient> client_; |
| }; |
| |
| TEST_F(QuickInsertSearchRequestTest, SendsQueryToCrosSearchImmediately) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| EXPECT_CALL(client(), StartCrosSearch(Eq(u"cat"), _, _)).Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotSendQueryToCrosSearchIfNotAvailableNoCategory) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| EXPECT_CALL(client(), StartCrosSearch(_, _, _)).Times(0); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client()); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotSendQueryToCrosSearchIfNotAvailableWithCategory) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| EXPECT_CALL(client(), StartCrosSearch(_, _, _)).Times(0); |
| |
| QuickInsertSearchRequest request( |
| u"cat", {QuickInsertCategory::kLinks}, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client()); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, ShowsResultsFromOmniboxSearch) { |
| MockSearchResultsCallback search_results_callback; |
| // Catch-all to prevent unexpected gMock call errors. See |
| // https://google.github.io/googletest/gmock_cook_book.html#uninteresting-vs-unexpected |
| // for more details. |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kOmnibox, |
| ElementsAre(VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| Property("spec", &GURL::spec, |
| "https://www.google.com/search?q=cat")))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, TruncatesOmniboxResults) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call( |
| QuickInsertSearchSource::kOmnibox, |
| ElementsAre(VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3"))), |
| /*has_more_results=*/true)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertTextResult(u"1"), QuickInsertTextResult(u"2"), |
| QuickInsertTextResult(u"3"), QuickInsertTextResult(u"4")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoesNotTruncateOmniboxOnlyResults) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call( |
| QuickInsertSearchSource::kOmnibox, |
| ElementsAre(VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"4"))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", QuickInsertCategory::kLinks, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertTextResult(u"1"), QuickInsertTextResult(u"2"), |
| QuickInsertTextResult(u"3"), QuickInsertTextResult(u"4")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DeduplicatesGoogleCorpGoLinks) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, |
| Call(Ne(QuickInsertSearchSource::kOmnibox), _, _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kOmnibox, |
| ElementsAre(VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| GURL("https://example.com"))), |
| VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| GURL("http://go/link"))), |
| VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| GURL("https://example.com/2"))), |
| VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| GURL("https://goto2.corp.google.com/link2"))), |
| VariantWith<QuickInsertBrowsingHistoryResult>( |
| Field("url", &QuickInsertBrowsingHistoryResult::url, |
| GURL("https://example.com/3")))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", QuickInsertCategory::kLinks, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| { |
| QuickInsertBrowsingHistoryResult(GURL("https://example.com"), u"", |
| {}), |
| QuickInsertBrowsingHistoryResult(GURL("http://go/link"), u"", {}), |
| QuickInsertBrowsingHistoryResult(GURL("https://example.com/2"), u"", |
| {}), |
| QuickInsertBrowsingHistoryResult(GURL("https://goto.google.com/link"), |
| u"", {}), |
| QuickInsertBrowsingHistoryResult( |
| GURL("https://goto2.corp.google.com/link2"), u"", {}), |
| QuickInsertBrowsingHistoryResult(GURL("https://example.com/3"), u"", |
| {}), |
| QuickInsertBrowsingHistoryResult( |
| GURL("https://goto.corp.google.com/link2"), u"", {}), |
| }); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotFlashEmptyResultsFromOmniboxSearch) { |
| NiceMock<MockSearchResultsCallback> first_search_results_callback; |
| NiceMock<MockSearchResultsCallback> second_search_results_callback; |
| // CrOS search calls `StopSearch()` automatically on starting a search. |
| // If `StopSearch` actually stops a search, some providers such as the omnibox |
| // automatically call the search result callback from the _last_ search with |
| // an empty vector. |
| // Ensure that we don't flash empty results if this happens - i.e. that we |
| // call `StopSearch` before starting a new search, and calling `StopSearch` |
| // does not trigger a search callback call with empty CrOS search results. |
| bool search_started = false; |
| ON_CALL(client(), StopCrosQuery).WillByDefault([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| ON_CALL(client(), StartCrosSearch) |
| .WillByDefault( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| // Function only used for the below `EXPECT_CALL` to ensure that we don't call |
| // the search callback with an empty callback after the initial state. |
| testing::MockFunction<void()> after_start_search; |
| testing::Expectation after_start_search_call = |
| EXPECT_CALL(after_start_search, Call).Times(1); |
| EXPECT_CALL(first_search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(first_search_results_callback, |
| Call(QuickInsertSearchSource::kOmnibox, IsEmpty(), |
| /*has_more_results=*/_)) |
| .Times(0) |
| .After(after_start_search_call); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&first_search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| after_start_search.Call(); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsOmniboxMetrics) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| task_environment().FastForwardBy(kMetricMetricTime); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| |
| histogram.ExpectUniqueTimeSample( |
| "Ash.Picker.Search.OmniboxProvider.QueryTime", kMetricMetricTime, 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotRecordOmniboxMetricsIfNoOmniboxResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 0); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotRecordOmniboxMetricsIfOtherCrosSearchResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kFileSearch, |
| {QuickInsertTextResult(u"monorail_cat.jpg")}); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 0); |
| } |
| |
| TEST_F( |
| QuickInsertSearchRequestTest, |
| DoesNotRecordOmniboxMetricsTwiceIfSearchResultsArePublishedAfterStopSearch) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> first_search_results_callback; |
| NiceMock<MockSearchResultsCallback> second_search_results_callback; |
| // CrOS search calls `StopSearch()` automatically on starting a search. |
| // If `StopSearch` actually stops a search, some providers such as the omnibox |
| // automatically call the search result callback from the _last_ search with |
| // an empty vector. |
| // Ensure that we don't record metrics twice if this happens. |
| bool search_started = false; |
| ON_CALL(client(), StopCrosQuery).WillByDefault([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| ON_CALL(client(), StartCrosSearch) |
| .WillByDefault( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&first_search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, ShowsResultsFromFileSearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kLocalFile, |
| ElementsAre(VariantWith<QuickInsertTextResult>( |
| Field("text", &QuickInsertTextResult::primary_text, |
| u"monorail_cat.jpg"))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kFileSearch, |
| {QuickInsertTextResult(u"monorail_cat.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, TruncatesResultsFromFileSearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kLocalFile, |
| ElementsAre( |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2.jpg")), |
| |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3.jpg"))), |
| /*has_more_results=*/true)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kFileSearch, |
| {QuickInsertTextResult(u"1.jpg"), QuickInsertTextResult(u"2.jpg"), |
| QuickInsertTextResult(u"3.jpg"), QuickInsertTextResult(u"4.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoesNotTruncateResultsFromFileOnlySearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kLocalFile, |
| ElementsAre( |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3.jpg")), |
| |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"4.jpg"))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", QuickInsertCategory::kLocalFiles, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kFileSearch, |
| {QuickInsertTextResult(u"1.jpg"), QuickInsertTextResult(u"2.jpg"), |
| QuickInsertTextResult(u"3.jpg"), QuickInsertTextResult(u"4.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsFileMetrics) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| task_environment().FastForwardBy(kMetricMetricTime); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kFileSearch, |
| {QuickInsertTextResult(u"monorail_cat.jpg")}); |
| |
| histogram.ExpectUniqueTimeSample("Ash.Picker.Search.FileProvider.QueryTime", |
| kMetricMetricTime, 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoesNotRecordFileMetricsIfNoFileResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.FileProvider.QueryTime", 0); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotRecordFileMetricsIfOtherCrosSearchResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.FileProvider.QueryTime", 0); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, ShowsResultsFromDriveSearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kDrive, |
| ElementsAre(VariantWith<QuickInsertTextResult>( |
| Field("text", &QuickInsertTextResult::primary_text, |
| u"catrbug_135117.jpg"))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kDriveSearch, |
| {QuickInsertTextResult(u"catrbug_135117.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, TruncatesResultsFromDriveSearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kDrive, |
| ElementsAre( |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2.jpg")), |
| |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3.jpg"))), |
| /*has_more_results=*/true)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kDriveSearch, |
| {QuickInsertTextResult(u"1.jpg"), QuickInsertTextResult(u"2.jpg"), |
| QuickInsertTextResult(u"3.jpg"), QuickInsertTextResult(u"4.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotTruncateResultsFromDriveOnlySearch) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kDrive, |
| ElementsAre( |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"1.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"2.jpg")), |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"3.jpg")), |
| |
| VariantWith<QuickInsertTextResult>(Field( |
| "text", &QuickInsertTextResult::primary_text, u"4.jpg"))), |
| /*has_more_results=*/false)) |
| .Times(AtLeast(1)); |
| |
| QuickInsertSearchRequest request( |
| u"cat", /*category=*/QuickInsertCategory::kDriveFiles, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kDriveSearch, |
| {QuickInsertTextResult(u"1.jpg"), QuickInsertTextResult(u"2.jpg"), |
| QuickInsertTextResult(u"3.jpg"), QuickInsertTextResult(u"4.jpg")}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsDriveMetrics) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| task_environment().FastForwardBy(kMetricMetricTime); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kDriveSearch, |
| {QuickInsertTextResult(u"catrbug_135117.jpg")}); |
| |
| histogram.ExpectUniqueTimeSample("Ash.Picker.Search.DriveProvider.QueryTime", |
| kMetricMetricTime, 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotRecordDriveMetricsIfNoDriveResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.DriveProvider.QueryTime", 0); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoesNotRecordDriveMetricsIfOtherCrosSearchResponse) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| bool search_started = false; |
| EXPECT_CALL(client(), StopCrosQuery) |
| .Times(AtLeast(1)) |
| .WillRepeatedly([&search_started, this]() { |
| if (search_started) { |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, |
| {}); |
| } |
| search_started = false; |
| }); |
| EXPECT_CALL(client(), StartCrosSearch) |
| .Times(1) |
| .WillRepeatedly( |
| [&search_started, this]( |
| const std::u16string& query, |
| std::optional<QuickInsertCategory> category, |
| QuickInsertClient::CrosSearchResultsCallback callback) { |
| client().StopCrosQuery(); |
| search_started = true; |
| client().cros_search_callback() = std::move(callback); |
| }); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| client().cros_search_callback().Run( |
| AppListSearchResultType::kOmnibox, |
| {QuickInsertBrowsingHistoryResult( |
| GURL("https://www.google.com/search?q=cat"), u"cat - Google Search", |
| ui::ImageModel())}); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.DriveProvider.QueryTime", 0); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, PublishesDateResultsOnlyOnce) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kDate, _, /*has_more_results=*/_)) |
| .Times(1); |
| // Fast forward the clock to a Sunday (day_of_week = 0). |
| base::Time::Exploded exploded; |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| task_environment().AdvanceClock(base::Days(7 - exploded.day_of_week)); |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| ASSERT_EQ(0, exploded.day_of_week); |
| |
| QuickInsertSearchRequest request( |
| u"next Friday", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsDateMetricsOnlyOnce) { |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| // Fast forward the clock to a Sunday (day_of_week = 0). |
| base::Time::Exploded exploded; |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| task_environment().AdvanceClock(base::Days(7 - exploded.day_of_week)); |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| ASSERT_EQ(0, exploded.day_of_week); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"next Friday", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.DateProvider.QueryTime", 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| PublishesDateResultsWhenDateCategorySelected) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kDate, _, /*has_more_results=*/_)) |
| .Times(1); |
| // Fast forward the clock to a Sunday (day_of_week = 0). |
| base::Time::Exploded exploded; |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| task_environment().AdvanceClock(base::Days(7 - exploded.day_of_week)); |
| task_environment().GetMockClock()->Now().LocalExplode(&exploded); |
| ASSERT_EQ(0, exploded.day_of_week); |
| |
| QuickInsertSearchRequest request( |
| u"next Friday", QuickInsertCategory::kDatesTimes, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, PublishesMathResultsOnlyOnce) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kMath, _, /*has_more_results=*/_)) |
| .Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"1 + 1", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsMathMetricsOnlyOnce) { |
| base::HistogramTester histogram; |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kMath, _, /*has_more_results=*/_)) |
| .Times(1); |
| |
| { |
| QuickInsertSearchRequest request( |
| u"1 + 1", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.MathProvider.QueryTime", 1); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| PublishesMathResultsWhenMathCategorySelected) { |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kMath, _, /*has_more_results=*/_)) |
| .Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"1 + 1", QuickInsertCategory::kUnitsMaths, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, OnlyStartCrosSearchForCertainCategories) { |
| EXPECT_CALL(client(), |
| StartCrosSearch(Eq(u"ant"), Eq(QuickInsertCategory::kLinks), _)) |
| .Times(1); |
| EXPECT_CALL( |
| client(), |
| StartCrosSearch(Eq(u"bat"), Eq(QuickInsertCategory::kDriveFiles), _)) |
| .Times(1); |
| EXPECT_CALL( |
| client(), |
| StartCrosSearch(Eq(u"cat"), Eq(QuickInsertCategory::kLocalFiles), _)) |
| .Times(1); |
| |
| { |
| QuickInsertSearchRequest request(u"ant", QuickInsertCategory::kLinks, |
| base::DoNothing(), base::DoNothing(), |
| &client(), kAllCategories); |
| } |
| { |
| QuickInsertSearchRequest request(u"bat", QuickInsertCategory::kDriveFiles, |
| base::DoNothing(), base::DoNothing(), |
| &client(), kAllCategories); |
| } |
| { |
| QuickInsertSearchRequest request(u"cat", QuickInsertCategory::kLocalFiles, |
| base::DoNothing(), base::DoNothing(), |
| &client(), kAllCategories); |
| } |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, ShowsResultsFromClipboardSearch) { |
| testing::StrictMock<MockClipboardHistoryController> mock_clipboard; |
| EXPECT_CALL(mock_clipboard, GetHistoryValues) |
| .WillOnce( |
| [](ClipboardHistoryController::GetHistoryValuesCallback callback) { |
| ClipboardHistoryItemBuilder builder; |
| std::move(callback).Run( |
| {builder.SetFormat(ui::ClipboardInternalFormat::kText) |
| .SetText("cat") |
| .Build()}); |
| }); |
| |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kClipboard, |
| ElementsAre(VariantWith<QuickInsertClipboardResult>( |
| FieldsAre(_, QuickInsertClipboardResult::DisplayFormat::kText, |
| /*file_count=*/0, u"cat", std::nullopt, true))), |
| /*has_more_results=*/false)) |
| .Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, RecordsClipboardMetrics) { |
| testing::StrictMock<MockClipboardHistoryController> mock_clipboard; |
| EXPECT_CALL(mock_clipboard, GetHistoryValues) |
| .WillOnce( |
| [this]( |
| ClipboardHistoryController::GetHistoryValuesCallback callback) { |
| task_environment().FastForwardBy(kMetricMetricTime); |
| std::move(callback).Run({}); |
| }); |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| |
| histogram.ExpectUniqueTimeSample( |
| "Ash.Picker.Search.ClipboardProvider.QueryTime", kMetricMetricTime, 1); |
| } |
| |
| class QuickInsertSearchRequestEditorTest |
| : public QuickInsertSearchRequestTest, |
| public testing::WithParamInterface< |
| std::pair<QuickInsertCategory, QuickInsertSearchSource>> {}; |
| |
| TEST_P(QuickInsertSearchRequestEditorTest, ShowsResultsFromEditorSearch) { |
| const auto& [category, source] = GetParam(); |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(source, ElementsAre(VariantWith<QuickInsertEditorResult>(_)), |
| /*has_more_results=*/false)) |
| .Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), base::span_from_ref(category)); |
| } |
| |
| TEST_P(QuickInsertSearchRequestEditorTest, |
| DoNotShowResultsFromEditorSearchIfNotAvailable) { |
| const auto& [category, source] = GetParam(); |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, Call(source, _, _)).Times(0); |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client()); |
| } |
| |
| TEST_P(QuickInsertSearchRequestEditorTest, RecordsEditorMetrics) { |
| const auto& [category, source] = GetParam(); |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), base::span_from_ref(category)); |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.EditorProvider.QueryTime", 1); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| QuickInsertSearchRequestEditorTest, |
| testing::Values(std::make_pair(QuickInsertCategory::kEditorWrite, |
| QuickInsertSearchSource::kEditorWrite), |
| std::make_pair(QuickInsertCategory::kEditorRewrite, |
| QuickInsertSearchSource::kEditorRewrite))); |
| |
| class QuickInsertSearchRequestLobsterTest |
| : public QuickInsertSearchRequestTest, |
| public testing::WithParamInterface< |
| std::pair<QuickInsertCategory, QuickInsertSearchSource>> {}; |
| |
| TEST_P(QuickInsertSearchRequestLobsterTest, ShowsResultsFromLobsterSearch) { |
| const auto& [category, source] = GetParam(); |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(source, ElementsAre(VariantWith<QuickInsertLobsterResult>(_)), |
| /*has_more_results=*/false)) |
| .Times(1); |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), base::span_from_ref(category)); |
| } |
| |
| TEST_P(QuickInsertSearchRequestLobsterTest, |
| DoNotShowResultsFromLobsterSearchIfNotAvailable) { |
| const auto& [category, source] = GetParam(); |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, Call(source, _, _)).Times(0); |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client()); |
| } |
| |
| TEST_P(QuickInsertSearchRequestLobsterTest, RecordsLobsterMetrics) { |
| const auto& [category, source] = GetParam(); |
| base::HistogramTester histogram; |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| |
| QuickInsertSearchRequest request( |
| u"quick brown fox jumped over lazy dog", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), base::span_from_ref(category)); |
| |
| histogram.ExpectTotalCount("Ash.Picker.Search.LobsterProvider.QueryTime", 1); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| QuickInsertSearchRequestLobsterTest, |
| testing::Values( |
| std::make_pair(QuickInsertCategory::kLobsterWithNoSelectedText, |
| QuickInsertSearchSource::kLobsterWithNoSelectedText), |
| std::make_pair(QuickInsertCategory::kLobsterWithSelectedText, |
| QuickInsertSearchSource::kLobsterWithSelectedText))); |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| PublishesGifResultsWhenGifCategorySelected) { |
| network::TestURLLoaderFactory test_url_loader_factory; |
| ON_CALL(client(), GetSharedURLLoaderFactory) |
| .WillByDefault([&test_url_loader_factory]() { |
| return test_url_loader_factory.GetSafeWeakWrapper(); |
| }); |
| base::test::TestFuture<void> future; |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, Call).Times(AnyNumber()); |
| EXPECT_CALL( |
| search_results_callback, |
| Call(QuickInsertSearchSource::kGifs, |
| ElementsAre(VariantWith<QuickInsertGifResult>(AllOf( |
| Field("preview_url", &QuickInsertGifResult::preview_url, |
| GURL("https://tenor.com/preview.gif")), |
| Field("preview_image_url", |
| &QuickInsertGifResult::preview_image_url, |
| GURL("https://tenor.com/preview.png")), |
| Field("preview_dimensions", |
| &QuickInsertGifResult::preview_dimensions, |
| gfx::Size(100, 50)), |
| Field("full_url", &QuickInsertGifResult::full_url, |
| GURL("https://tenor.com/full.gif")), |
| Field("full_dimensions", &QuickInsertGifResult::full_dimensions, |
| gfx::Size(200, 100)), |
| Field("content_description", |
| &QuickInsertGifResult::content_description, u"a cat")))), |
| /*has_more_results=*/false)) |
| .WillOnce([&future]() { future.SetValue(); }); |
| |
| QuickInsertSearchRequest request( |
| u"cat", QuickInsertCategory::kGifs, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| base::DoNothing(), &client(), kAllCategories); |
| test_url_loader_factory.SimulateResponseForPendingRequest( |
| GURL(), network::URLLoaderCompletionStatus(net::OK), |
| network::CreateURLResponseHead(net::HTTP_OK), R"json({ |
| "results": [ |
| { |
| "id": "0", |
| "content_description": "a cat", |
| "media_formats": { |
| "gif": { |
| "dims": [200, 100], |
| "url": "https://tenor.com/full.gif" |
| }, |
| "tinygif": { |
| "dims": [100, 50], |
| "url": "https://tenor.com/preview.gif" |
| }, |
| "tinygifpreview": { |
| "url": "https://tenor.com/preview.png" |
| } |
| } |
| } |
| ] |
| })json", |
| static_cast<network::TestURLLoaderFactory::ResponseMatchFlags>( |
| network::TestURLLoaderFactory::kUrlMatchPrefix | |
| network::TestURLLoaderFactory::kWaitForRequest)); |
| ASSERT_TRUE(future.Wait()); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoneClosureCalledImmediatelyWhenNoSearch) { |
| // This actually calls category search. |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client()); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledImmediatelyWhenSynchronous) { |
| // This actually calls category search. |
| MockSearchResultsCallback search_results_callback; |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kAction, _, _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(search_results_callback, |
| Call(QuickInsertSearchSource::kMath, _, _)) |
| .Times(1); |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"1+1", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| base::span_from_ref(QuickInsertCategory::kUnitsMaths)); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoneClosureNotCalledWhenAsynchronous) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| // We expect there to be at least one asynchronous source. |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), kAllCategories); |
| |
| EXPECT_FALSE(done_callback.IsReady()); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoneClosureCalledAfterClipboard) { |
| testing::StrictMock<MockClipboardHistoryController> mock_clipboard; |
| base::test::TestFuture<ClipboardHistoryController::GetHistoryValuesCallback> |
| get_history_values_future; |
| EXPECT_CALL(mock_clipboard, GetHistoryValues) |
| .WillOnce( |
| [&](ClipboardHistoryController::GetHistoryValuesCallback callback) { |
| get_history_values_future.SetValue(std::move(callback)); |
| }); |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| base::span_from_ref(QuickInsertCategory::kClipboard)); |
| EXPECT_FALSE(done_callback.IsReady()); |
| ClipboardHistoryController::GetHistoryValuesCallback get_history_values = |
| get_history_values_future.Take(); |
| ClipboardHistoryItemBuilder builder; |
| std::move(get_history_values) |
| .Run({builder.SetFormat(ui::ClipboardInternalFormat::kText) |
| .SetText("cat") |
| .Build()}); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledAfterSingleCrosSearchSource) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| base::span_from_ref(QuickInsertCategory::kLinks)); |
| EXPECT_FALSE(done_callback.IsReady()); |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {}); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledAfterMultipleCrosSearchSources) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| {QuickInsertCategory::kLinks, QuickInsertCategory::kDriveFiles, |
| QuickInsertCategory::kLocalFiles}); |
| EXPECT_FALSE(done_callback.IsReady()); |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {}); |
| EXPECT_FALSE(done_callback.IsReady()); |
| client().cros_search_callback().Run(AppListSearchResultType::kDriveSearch, |
| {}); |
| EXPECT_FALSE(done_callback.IsReady()); |
| client().cros_search_callback().Run(AppListSearchResultType::kFileSearch, {}); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledAfterClipboardAndOmnibox) { |
| testing::StrictMock<MockClipboardHistoryController> mock_clipboard; |
| base::test::TestFuture<ClipboardHistoryController::GetHistoryValuesCallback> |
| get_history_values_future; |
| EXPECT_CALL(mock_clipboard, GetHistoryValues) |
| .WillOnce( |
| [&](ClipboardHistoryController::GetHistoryValuesCallback callback) { |
| get_history_values_future.SetValue(std::move(callback)); |
| }); |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| {QuickInsertCategory::kClipboard, QuickInsertCategory::kLinks}); |
| EXPECT_FALSE(done_callback.IsReady()); |
| ClipboardHistoryController::GetHistoryValuesCallback get_history_values = |
| get_history_values_future.Take(); |
| ClipboardHistoryItemBuilder builder; |
| std::move(get_history_values) |
| .Run({builder.SetFormat(ui::ClipboardInternalFormat::kText) |
| .SetText("cat") |
| .Build()}); |
| EXPECT_FALSE(done_callback.IsReady()); |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {}); |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_FALSE(interrupted); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledAfterSearchCallbackSynchronous) { |
| MockSearchResultsCallback search_results_callback; |
| base::MockOnceCallback<void(bool)> done_callback; |
| { |
| ::testing::InSequence seq; |
| EXPECT_CALL(search_results_callback, Call(_, _, _)).Times(AtLeast(1)); |
| EXPECT_CALL(done_callback, Run(/*interrupted=*/false)).Times(1); |
| } |
| |
| QuickInsertSearchRequest request( |
| u"1+1", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.Get(), &client(), |
| base::span_from_ref(QuickInsertCategory::kUnitsMaths)); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, |
| DoneClosureCalledAfterSearchCallbackAsynchronous) { |
| MockSearchResultsCallback search_results_callback; |
| base::MockOnceCallback<void(bool)> done_callback; |
| { |
| ::testing::InSequence seq; |
| EXPECT_CALL(search_results_callback, Call(_, _, _)).Times(AtLeast(1)); |
| EXPECT_CALL(done_callback, Run(/*interrupted*/ false)).Times(1); |
| } |
| |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.Get(), &client(), |
| base::span_from_ref(QuickInsertCategory::kLinks)); |
| client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {}); |
| } |
| |
| TEST_F(QuickInsertSearchRequestTest, DoneClosureCalledWhenDestructed) { |
| NiceMock<MockSearchResultsCallback> search_results_callback; |
| base::test::TestFuture<bool> done_callback; |
| |
| { |
| QuickInsertSearchRequest request( |
| u"cat", std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&search_results_callback)), |
| done_callback.GetCallback(), &client(), |
| base::span_from_ref(QuickInsertCategory::kLinks)); |
| EXPECT_FALSE(done_callback.IsReady()); |
| } |
| |
| bool interrupted = done_callback.Get(); |
| EXPECT_TRUE(interrupted); |
| } |
| |
| } // namespace |
| } // namespace ash |