#include "ash/quick_insert/search/quick_insert_search_controller.h"
#include <array>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/quick_insert/model/quick_insert_search_results_section.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/views/quick_insert_view_delegate.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/branding_buildflags.h"
#include "chromeos/ash/components/emoji/grit/emoji.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/image_model.h"
#include "ui/base/resource/mock_resource_bundle_delegate.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
namespace ash {
namespace {
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::IsSupersetOf;
using ::testing::NiceMock;
using ::testing::Not;
using ::testing::Property;
using ::testing::VariantWith;
constexpr base::TimeDelta kBurnInPeriod = base::Milliseconds(400);
constexpr base::TimeDelta kBeforeBurnIn = base::Milliseconds(300);
static_assert(kBeforeBurnIn < kBurnInPeriod);
constexpr base::TimeDelta kAfterBurnIn = base::Milliseconds(700);
static_assert(kBurnInPeriod < kAfterBurnIn);
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,
});
MATCHER_P(LastElement, matcher, "") {
return !arg.empty() &&
ExplainMatchResult(matcher, arg.back(), result_listener);
}
using MockSearchResultsCallback =
::testing::MockFunction<QuickInsertViewDelegate::SearchResultsCallback>;
using MockEmojiSearchResultsCallback = ::testing::MockFunction<
QuickInsertViewDelegate::EmojiSearchResultsCallback>;
class QuickInsertSearchControllerTest : public testing::Test {
protected:
base::test::SingleThreadTaskEnvironment& task_environment() {
return task_environment_;
}
MockSearchQuickInsertClient& client() { return client_; }
TestingPrefServiceSimple& prefs_service() { return prefs_service_; }
private:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
NiceMock<MockSearchQuickInsertClient> client_;
TestingPrefServiceSimple prefs_service_;
};
struct FakeResource {
int resource;
std::string data;
};
class ScopedFakeResourceBundleDelegate {
public:
explicit ScopedFakeResourceBundleDelegate(
base::span<const FakeResource> resources) {
for (const auto& [resource, data] : resources) {
ON_CALL(delegate_, LoadDataResourceString(resource))
.WillByDefault(testing::Return(data));
}
}
private:
testing::NiceMock<ui::MockResourceBundleDelegate> delegate_;
ui::ResourceBundle resource_bundle_with_mock_delegate_{&delegate_};
ui::ResourceBundle::SharedInstanceSwapperForTesting resource_bundle_swapper_{
&resource_bundle_with_mock_delegate_};
};
TEST_F(QuickInsertSearchControllerTest, SendsQueryToCrosSearchImmediately) {
NiceMock<MockSearchResultsCallback> search_results_callback;
EXPECT_CALL(client(), StartCrosSearch(Eq(u"cat"), _, _)).Times(1);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
}
TEST_F(QuickInsertSearchControllerTest, DoesNotPublishResultsDuringBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(0);
QuickInsertSearchController controller(
base::Milliseconds(100));
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(
AppListSearchResultType::kOmnibox,
{QuickInsertBrowsingHistoryResult(
GURL("https://www.google.com/search?q=cat"), u"cat - Google Search",
ui::ImageModel())});
task_environment().FastForwardBy(base::Milliseconds(99));
}
TEST_F(QuickInsertSearchControllerTest, ShowsResultsFromOmniboxSearch) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(
search_results_callback,
Call(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks),
Property(
"results", &QuickInsertSearchResultsSection::results,
ElementsAre(VariantWith<QuickInsertBrowsingHistoryResult>(
Field("url", &QuickInsertBrowsingHistoryResult::url,
Property("spec", &GURL::spec,
"https://www.google.com/search?q=cat")))))))))
.Times(AtLeast(1));
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(
AppListSearchResultType::kOmnibox,
{QuickInsertBrowsingHistoryResult(
GURL("https://www.google.com/search?q=cat"), u"cat - Google Search",
ui::ImageModel())});
task_environment().FastForwardBy(kBurnInPeriod);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotFlashEmptyResultsFromOmniboxSearch) {
NiceMock<MockSearchResultsCallback> first_search_results_callback;
NiceMock<MockSearchResultsCallback> second_search_results_callback;
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);
});
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(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks),
Property("results", &QuickInsertSearchResultsSection::results,
IsEmpty())))))
.Times(0)
.After(after_start_search_call);
EXPECT_CALL(second_search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(second_search_results_callback,
Call(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks),
Property("results", &QuickInsertSearchResultsSection::results,
IsEmpty())))))
.Times(0);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&first_search_results_callback)));
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())});
controller.StartSearch(
&client(), u"dog", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&second_search_results_callback)));
}
TEST_F(QuickInsertSearchControllerTest, RecordsOmniboxMetricsBeforeBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
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", kBeforeBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest, RecordsOmniboxMetricsAfterBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kAfterBurnIn);
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", kAfterBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordOmniboxMetricsIfNoOmniboxResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 0);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordOmniboxMetricsIfOtherCrosSearchResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kFileSearch,
{QuickInsertTextResult(u"monorail_cat.jpg")});
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 0);
}
TEST_F(
QuickInsertSearchControllerTest,
DoesNotRecordOmniboxMetricsTwiceIfSearchResultsArePublishedAfterStopSearch) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.WillRepeatedly([&search_started, this]() {
if (search_started) {
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{});
}
search_started = false;
});
EXPECT_CALL(client(), StartCrosSearch)
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(
AppListSearchResultType::kOmnibox,
{QuickInsertBrowsingHistoryResult(
GURL("https://www.google.com/search?q=cat"), u"cat - Google Search",
ui::ImageModel())});
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.OmniboxProvider.QueryTime", 1);
}
TEST_F(QuickInsertSearchControllerTest, ShowsResultsFromFileSearch) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(search_results_callback,
Call(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLocalFiles),
Property("results", &QuickInsertSearchResultsSection::results,
ElementsAre(VariantWith<QuickInsertTextResult>(Field(
"text", &QuickInsertTextResult::primary_text,
u"monorail_cat.jpg"))))))))
.Times(AtLeast(1));
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(
AppListSearchResultType::kFileSearch,
{QuickInsertTextResult(u"monorail_cat.jpg")});
task_environment().FastForwardBy(kBurnInPeriod);
}
TEST_F(QuickInsertSearchControllerTest, RecordsFileMetricsBeforeBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kFileSearch,
{QuickInsertTextResult(u"monorail_cat.jpg")});
histogram.ExpectUniqueTimeSample("Ash.Picker.Search.FileProvider.QueryTime",
kBeforeBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest, RecordsFileMetricsAfterBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kAfterBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kFileSearch,
{QuickInsertTextResult(u"monorail_cat.jpg")});
histogram.ExpectUniqueTimeSample("Ash.Picker.Search.FileProvider.QueryTime",
kAfterBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordFileMetricsIfNoFileResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.FileProvider.QueryTime", 0);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordFileMetricsIfOtherCrosSearchResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kOmnibox,
{QuickInsertBrowsingHistoryResult(
GURL("https://www.google.com/search?q=cat"), u"cat - Google Search",
ui::ImageModel())});
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.FileProvider.QueryTime", 0);
}
TEST_F(QuickInsertSearchControllerTest, ShowsResultsFromDriveSearch) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(search_results_callback,
Call(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kDriveFiles),
Property("results", &QuickInsertSearchResultsSection::results,
ElementsAre(VariantWith<QuickInsertTextResult>(Field(
"text", &QuickInsertTextResult::primary_text,
u"catrbug_135117.jpg"))))))))
.Times(AtLeast(1));
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(
AppListSearchResultType::kDriveSearch,
{QuickInsertTextResult(u"catrbug_135117.jpg")});
task_environment().FastForwardBy(kBurnInPeriod);
}
TEST_F(QuickInsertSearchControllerTest, RecordsDriveMetricsBeforeBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kDriveSearch,
{QuickInsertTextResult(u"catrbug_135117.jpg")});
histogram.ExpectUniqueTimeSample("Ash.Picker.Search.DriveProvider.QueryTime",
kBeforeBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest, RecordsDriveMetricsAfterBurnIn) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kAfterBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kDriveSearch,
{QuickInsertTextResult(u"catrbug_135117.jpg")});
histogram.ExpectUniqueTimeSample("Ash.Picker.Search.DriveProvider.QueryTime",
kAfterBurnIn, 1);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordDriveMetricsIfNoDriveResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.DriveProvider.QueryTime", 0);
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotRecordDriveMetricsIfOtherCrosSearchResponse) {
base::HistogramTester histogram;
NiceMock<MockSearchResultsCallback> search_results_callback;
bool search_started = false;
EXPECT_CALL(client(), StopCrosQuery)
.Times(AtLeast(2))
.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);
});
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(
AppListSearchResultType::kOmnibox,
{QuickInsertBrowsingHistoryResult(
GURL("https://www.google.com/search?q=cat"), u"cat - Google Search",
ui::ImageModel())});
controller.StopSearch();
histogram.ExpectTotalCount("Ash.Picker.Search.DriveProvider.QueryTime", 0);
}
TEST_F(QuickInsertSearchControllerTest, CombinesSearchResults) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(
search_results_callback,
Call(IsSupersetOf({
AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks),
Property("results", &QuickInsertSearchResultsSection::results,
Contains(VariantWith<QuickInsertTextResult>(Field(
"primary_text", &QuickInsertTextResult::primary_text,
u"omnibox"))))),
AllOf(Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLocalFiles),
Property("results", &QuickInsertSearchResultsSection::results,
Contains(VariantWith<QuickInsertTextResult>(Field(
"primary_text",
&QuickInsertTextResult::primary_text, u"file"))))),
AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kDriveFiles),
Property("results", &QuickInsertSearchResultsSection::results,
Contains(VariantWith<QuickInsertTextResult>(Field(
"primary_text", &QuickInsertTextResult::primary_text,
u"drive"))))),
})))
.Times(AtLeast(1));
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"omnibox")});
client().cros_search_callback().Run(AppListSearchResultType::kFileSearch,
{QuickInsertTextResult(u"file")});
client().cros_search_callback().Run(AppListSearchResultType::kDriveSearch,
{QuickInsertTextResult(u"drive")});
task_environment().FastForwardBy(kBurnInPeriod - kBeforeBurnIn);
}
TEST_F(QuickInsertSearchControllerTest, DoNotShowEmptySectionsDuringBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(0);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"zz", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {});
task_environment().FastForwardBy(kBurnInPeriod);
}
TEST_F(QuickInsertSearchControllerTest, DoNotShowEmptySectionsAfterBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(0);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"zz", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBurnInPeriod);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox, {});
}
TEST_F(QuickInsertSearchControllerTest, ShowResultsEvenAfterBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(
search_results_callback,
Call(Contains(AllOf(
Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks),
Property("results", &QuickInsertSearchResultsSection::results,
Contains(VariantWith<QuickInsertTextResult>(AllOf(Field(
"primary_text", &QuickInsertTextResult::primary_text,
u"test")))))))))
.Times(AtLeast(1));
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBurnInPeriod);
std::move(client().cros_search_callback())
.Run(AppListSearchResultType::kOmnibox, {QuickInsertTextResult(u"test")});
}
TEST_F(QuickInsertSearchControllerTest,
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);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(&client(), u"ant", QuickInsertCategory::kLinks,
kAllCategories, false, false, base::DoNothing());
controller.StartSearch(&client(), u"bat", QuickInsertCategory::kDriveFiles,
kAllCategories, false, false, base::DoNothing());
controller.StartSearch(&client(), u"cat", QuickInsertCategory::kLocalFiles,
kAllCategories, false, false, base::DoNothing());
}
TEST_F(QuickInsertSearchControllerTest,
PublishesEmptyResultsAfterResultsOnceDoneDuringBurnIn) {
MockSearchResultsCallback search_results_callback;
{
::testing::InSequence seq;
EXPECT_CALL(
search_results_callback,
Call(Contains(Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks))))
.Times(1);
EXPECT_CALL(search_results_callback, Call(IsEmpty())).Times(1);
}
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt,
base::span_from_ref(QuickInsertCategory::kLinks), false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
}
TEST_F(QuickInsertSearchControllerTest,
PublishesEmptyResultsAfterResultsOnceDoneAfterDoneAfterBurnIn) {
MockSearchResultsCallback search_results_callback;
{
::testing::InSequence seq;
EXPECT_CALL(
search_results_callback,
Call(Contains(Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks))))
.Times(1);
EXPECT_CALL(search_results_callback, Call(IsEmpty())).Times(1);
}
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt,
base::span_from_ref(QuickInsertCategory::kLinks), false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBurnInPeriod);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotPublishResultsWhenInterruptedDuringBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(
search_results_callback,
Call(Contains(Property("type", &QuickInsertSearchResultsSection::type,
QuickInsertSectionType::kLinks))))
.Times(0);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
controller.StopSearch();
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotPublishEmptyResultsWhenInterruptedDuringBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(search_results_callback, Call(IsEmpty())).Times(0);
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBeforeBurnIn);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
controller.StopSearch();
}
TEST_F(QuickInsertSearchControllerTest,
DoesNotPublishEmptyResultsWhenInterruptedAfterBurnIn) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(AnyNumber());
EXPECT_CALL(search_results_callback, Call(IsEmpty())).Times(0);
NiceMock<MockSearchResultsCallback> second_search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
task_environment().FastForwardBy(kBurnInPeriod);
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
controller.StopSearch();
}
TEST_F(QuickInsertSearchControllerTest,
StopSearchDoesNotCallOldCallbackAfterwards) {
MockSearchResultsCallback search_results_callback;
EXPECT_CALL(search_results_callback, Call).Times(0);
MockSearchResultsCallback second_search_results_callback;
QuickInsertSearchController controller(kBurnInPeriod);
controller.StartSearch(
&client(), u"cat", std::nullopt, kAllCategories, false, false,
base::BindRepeating(&MockSearchResultsCallback::Call,
base::Unretained(&search_results_callback)));
client().cros_search_callback().Run(AppListSearchResultType::kOmnibox,
{QuickInsertTextResult(u"cat")});
controller.StopSearch();
task_environment().FastForwardBy(kBurnInPeriod);
}
TEST_F(QuickInsertSearchControllerTest, LoadsEmojiDataInAllLanguages) {
ScopedFakeResourceBundleDelegate mock_resource_delegate(
{{FakeResource{
IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
R"([{"emoji":[{"base":{"string":"😀en","name":"grinning face",
"keywords":["face","grin","grinning face",":D","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
R"([])"},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
FakeResource{IDR_EMOJI_PICKER_EN_INTERNAL, R"([])"},
#endif
FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow"}}]}])"},
FakeResource{IDR_EMOJI_PICKER_SYMBOL_JA,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow","keywords":["矢印"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
R"-([{"group":"Classic","emoji":[
{"base":{"string":":-)","name":"smiley face "}}]}])-"},
FakeResource{
IDR_EMOJI_PICKER_JA_START,
R"([{"emoji":[{"base":{"string":"😀jp","name":"grinning face",
"keywords":["笑顔","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_JA_REMAINING, R"([])"}}});
prefs_service().registry()->RegisterStringPref(
language::prefs::kApplicationLocale, "");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguageCurrentInputMethod,
"_comp_ime_jkghodnilhceideoidjikpgommlajknknacl_mozc_jp");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguagePreloadEngines,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng,"
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:jp::jpn,"
"_comp_ime_jkghodnilhceideoidjikpgommlajknknacl_mozc_jp,"
"_comp_ime_jkghodnilhceideoidjikpgommlajknknacl_mozc_us");
prefs_service().registry()->RegisterDictionaryPref(
prefs::kEmojiPickerPreferences, base::Value::Dict());
MockEmojiSearchResultsCallback results_callback;
EXPECT_CALL(
results_callback,
Call(ElementsAre(
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀jp")),
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀en")),
Field("text", &QuickInsertEmojiResult::text, Eq(u":-)")))))
.Times(1);
QuickInsertSearchController controller(
base::Milliseconds(100));
controller.LoadEmojiLanguagesFromPrefs(&prefs_service());
controller.StartEmojiSearch(
&prefs_service(), u"smile",
base::BindRepeating(&MockEmojiSearchResultsCallback::Call,
base::Unretained(&results_callback)));
}
TEST_F(QuickInsertSearchControllerTest,
LoadsEmojiDataInDefaultEnglishIfNoSupportedLanguage) {
ScopedFakeResourceBundleDelegate mock_resource_delegate(
{{FakeResource{
IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
R"([{"emoji":[{"base":{"string":"😀en","name":"grinning face",
"keywords":["face","grin","grinning face",":D","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
R"([])"},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
FakeResource{IDR_EMOJI_PICKER_EN_INTERNAL, R"([])"},
#endif
FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow"}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
R"-([{"group":"Classic","emoji":[
{"base":{"string":":-)","name":"smiley face "}}]}])-"}}});
prefs_service().registry()->RegisterStringPref(
language::prefs::kApplicationLocale, "en-US");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguageCurrentInputMethod,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:notareallanguage");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguagePreloadEngines,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:notareallanguage"
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:someotherfakelanguage");
prefs_service().registry()->RegisterDictionaryPref(
prefs::kEmojiPickerPreferences, base::Value::Dict());
MockEmojiSearchResultsCallback results_callback;
EXPECT_CALL(results_callback,
Call(ElementsAre(
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀en")),
Field("text", &QuickInsertEmojiResult::text, Eq(u":-)")))))
.Times(1);
QuickInsertSearchController controller(
base::Milliseconds(100));
controller.LoadEmojiLanguagesFromPrefs(&prefs_service());
controller.StartEmojiSearch(
&prefs_service(), u"smile",
base::BindRepeating(&MockEmojiSearchResultsCallback::Call,
base::Unretained(&results_callback)));
}
TEST_F(QuickInsertSearchControllerTest, LoadsEmojiDataOnPrefsChange) {
ScopedFakeResourceBundleDelegate mock_resource_delegate(
{{FakeResource{
IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
R"([{"emoji":[{"base":{"string":"😀en","name":"grinning face",
"keywords":["face","grin","grinning face",":D","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
R"([])"},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
FakeResource{IDR_EMOJI_PICKER_EN_INTERNAL, R"([])"},
#endif
FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow"}}]}])"},
FakeResource{IDR_EMOJI_PICKER_SYMBOL_JA,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow","keywords":["矢印"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
R"-([{"group":"Classic","emoji":[
{"base":{"string":":-)","name":"smiley face "}}]}])-"},
FakeResource{
IDR_EMOJI_PICKER_JA_START,
R"([{"emoji":[{"base":{"string":"😀jp","name":"grinning face",
"keywords":["笑顔","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_JA_REMAINING, R"([])"}}});
prefs_service().registry()->RegisterStringPref(
language::prefs::kApplicationLocale, "");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguageCurrentInputMethod,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguagePreloadEngines,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng");
prefs_service().registry()->RegisterDictionaryPref(
prefs::kEmojiPickerPreferences, base::Value::Dict());
QuickInsertSearchController controller(
base::Milliseconds(100));
controller.LoadEmojiLanguagesFromPrefs(&prefs_service());
MockEmojiSearchResultsCallback results_callback;
EXPECT_CALL(results_callback,
Call(ElementsAre(
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀en")),
Field("text", &QuickInsertEmojiResult::text, Eq(u":-)")))))
.Times(1);
controller.StartEmojiSearch(
&prefs_service(), u"smile",
base::BindRepeating(&MockEmojiSearchResultsCallback::Call,
base::Unretained(&results_callback)));
prefs_service().SetUserPref(
prefs::kLanguagePreloadEngines,
base::Value("_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng,"
"_comp_ime_jkghodnilhceideoidjikpgommlajknknacl_mozc_jp,"));
MockEmojiSearchResultsCallback results_callback_jp;
EXPECT_CALL(results_callback_jp,
Call(ElementsAre(
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀en")),
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀jp")),
Field("text", &QuickInsertEmojiResult::text, Eq(u":-)")))))
.Times(1);
controller.StartEmojiSearch(
&prefs_service(), u"smile",
base::BindRepeating(&MockEmojiSearchResultsCallback::Call,
base::Unretained(&results_callback_jp)));
}
TEST_F(QuickInsertSearchControllerTest, LoadsEmojiDataForJapaneseUiLocale) {
ScopedFakeResourceBundleDelegate mock_resource_delegate(
{{FakeResource{
IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
R"([{"emoji":[{"base":{"string":"😀en","name":"grinning face",
"keywords":["face","grin","grinning face",":D","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
R"([])"},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
FakeResource{IDR_EMOJI_PICKER_EN_INTERNAL, R"([])"},
#endif
FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow"}}]}])"},
FakeResource{IDR_EMOJI_PICKER_SYMBOL_JA,
R"([{"group":"Arrows","emoji":[{"base":
{"string":"←","name":"leftwards arrow","keywords":["矢印"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
R"-([{"group":"Classic","emoji":[
{"base":{"string":":-)","name":"smiley face "}}]}])-"},
FakeResource{
IDR_EMOJI_PICKER_JA_START,
R"([{"emoji":[{"base":{"string":"😀jp","name":"grinning face",
"keywords":["笑顔","smile"]}}]}])"},
FakeResource{IDR_EMOJI_PICKER_JA_REMAINING, R"([])"}}});
prefs_service().registry()->RegisterStringPref(
language::prefs::kApplicationLocale, "ja-JP");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguageCurrentInputMethod,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng");
prefs_service().registry()->RegisterStringPref(
prefs::kLanguagePreloadEngines,
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng");
prefs_service().registry()->RegisterDictionaryPref(
prefs::kEmojiPickerPreferences, base::Value::Dict());
QuickInsertSearchController controller(
base::Milliseconds(100));
controller.LoadEmojiLanguagesFromPrefs(&prefs_service());
MockEmojiSearchResultsCallback results_callback_jp;
EXPECT_CALL(results_callback_jp,
Call(ElementsAre(
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀en")),
Field("text", &QuickInsertEmojiResult::text, Eq(u"😀jp")),
Field("text", &QuickInsertEmojiResult::text, Eq(u":-)")))))
.Times(1);
controller.StartEmojiSearch(
&prefs_service(), u"smile",
base::BindRepeating(&MockEmojiSearchResultsCallback::Call,
base::Unretained(&results_callback_jp)));
}
}
}