blob: 54fd7f156a0a75200eda189a3f941e154256b19a [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/autocomplete_controller.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/fake_autocomplete_controller.h"
#include "components/omnibox/browser/fake_autocomplete_provider_client.h"
#include "components/omnibox/browser/omnibox_feature_configs.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_starter_pack_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class AutocompleteControllerTest : public testing::Test {
public:
AutocompleteControllerTest() : controller_(&task_environment_) {}
void SetAutocompleteMatches(const std::vector<AutocompleteMatch>& matches) {
controller_.internal_result_.ClearMatches();
controller_.internal_result_.AppendMatches(matches);
}
void MaybeRemoveCompanyEntityImages() {
controller_.MaybeRemoveCompanyEntityImages(&controller_.internal_result_);
}
bool ImageURLAndImageDominantColorIsEmpty(size_t index) {
return controller_.internal_result_.match_at(index)->image_url.is_empty() &&
controller_.internal_result_.match_at(index)
->image_dominant_color.empty();
}
AutocompleteMatch CreateHistoryURLMatch(std::string destination_url) {
AutocompleteMatch match;
match.type = AutocompleteMatchType::Type::HISTORY_URL;
match.destination_url = GURL(destination_url);
return match;
}
AutocompleteMatch CreateCompanyEntityMatch(std::string website_uri) {
AutocompleteMatch match;
match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST_ENTITY;
match.website_uri = website_uri;
match.image_url = GURL("https://url");
match.image_dominant_color = "#000000";
return match;
}
AutocompleteMatch CreateSearchMatch(std::u16string contents = u"text") {
AutocompleteMatch match;
match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST;
match.contents = contents;
return match;
}
AutocompleteMatch CreateStarterPackMatch(std::u16string keyword) {
AutocompleteMatch match;
match.type = AutocompleteMatchType::Type::STARTER_PACK;
match.contents = keyword;
match.keyword = keyword;
match.associated_keyword = std::make_unique<AutocompleteMatch>(
nullptr, 1000, false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED);
match.associated_keyword->keyword = keyword;
return match;
}
AutocompleteMatch CreateFeaturedEnterpriseSearch(std::u16string keyword) {
AutocompleteMatch match;
match.type = AutocompleteMatchType::Type::FEATURED_ENTERPRISE_SEARCH;
match.contents = keyword;
match.keyword = keyword;
match.associated_keyword = std::make_unique<AutocompleteMatch>(
nullptr, 1000, false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED);
match.associated_keyword->keyword = keyword;
return match;
}
AutocompleteMatch CreateSearchMatch(std::string name,
bool allowed_to_be_default_match,
int traditional_relevance) {
auto match =
CreateAutocompleteMatch(name, AutocompleteMatchType::SEARCH_SUGGEST,
allowed_to_be_default_match, false,
traditional_relevance, std::nullopt);
match.keyword = u"keyword";
match.search_terms_args = std::make_unique<TemplateURLRef::SearchTermsArgs>(
base::UTF8ToUTF16(name));
return match;
}
AutocompleteMatch CreatePersonalizedZeroPrefixMatch(
std::string name,
int traditional_relevance) {
auto match = CreateAutocompleteMatch(
name, AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED, false, false,
traditional_relevance, std::nullopt);
match.keyword = u"keyword";
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(std::u16string());
match.suggestion_group_id = omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST;
match.subtypes.emplace(omnibox::SUBTYPE_PERSONAL);
match.subtypes.emplace(omnibox::SUBTYPE_ZERO_PREFIX);
return match;
}
AutocompleteMatch CreateHistoryUrlMlScoredMatch(
std::string name,
bool allowed_to_be_default_match,
int traditional_relevance,
float ml_output) {
return CreateMlScoredMatch(name, AutocompleteMatchType::HISTORY_URL,
allowed_to_be_default_match,
traditional_relevance, ml_output);
}
AutocompleteMatch CreateMlScoredMatch(std::string name,
AutocompleteMatchType::Type type,
bool allowed_to_be_default_match,
int traditional_relevance,
float ml_output) {
return CreateAutocompleteMatch(name, type, allowed_to_be_default_match,
false, traditional_relevance, ml_output);
}
AutocompleteMatch CreateBoostedShortcutMatch(std::string name,
int traditional_relevance,
float ml_output) {
return CreateAutocompleteMatch(name, AutocompleteMatchType::HISTORY_URL,
true, true, traditional_relevance,
ml_output);
}
AutocompleteMatch CreateKeywordHintMatch(std::string name,
int traditional_relevance) {
auto match = CreateAutocompleteMatch(
name, AutocompleteMatchType::SEARCH_SUGGEST, false, false,
traditional_relevance, std::nullopt);
match.keyword = u"keyword";
match.associated_keyword = std::make_unique<AutocompleteMatch>(
nullptr, 1000, false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED);
return match;
}
AutocompleteMatch CreateHistoryClusterMatch(std::string name,
int traditional_relevance) {
return CreateAutocompleteMatch(name, AutocompleteMatchType::HISTORY_CLUSTER,
false, false, traditional_relevance,
std::nullopt);
}
AutocompleteMatch CreateAutocompleteMatch(std::string name,
AutocompleteMatchType::Type type,
bool allowed_to_be_default_match,
bool shortcut_boosted,
int traditional_relevance,
std::optional<float> ml_output) {
AutocompleteMatch match{nullptr, traditional_relevance, false, type};
match.shortcut_boosted = shortcut_boosted;
match.allowed_to_be_default_match = allowed_to_be_default_match;
match.destination_url = GURL{"https://google.com/" + name};
match.stripped_destination_url = GURL{"https://google.com/" + name};
match.contents = base::UTF8ToUTF16(name);
match.contents_class = {{0, 1}};
if (ml_output.has_value()) {
match.scoring_signals = {{}};
match.scoring_signals->set_site_engagement(ml_output.value());
}
return match;
}
FakeAutocompleteProviderClient* provider_client() {
return static_cast<FakeAutocompleteProviderClient*>(
controller_.autocomplete_provider_client());
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
FakeAutocompleteController controller_;
};
TEST_F(AutocompleteControllerTest, RemoveCompanyEntityImage_LeastAggressive) {
// Set feature flag and param.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kCompanyEntityIconAdjustment,
{{OmniboxFieldTrial::kCompanyEntityIconAdjustmentGroup.name,
"least-aggressive"}});
std::vector<AutocompleteMatch> matches;
// In the least aggressive experiment group the historical match must be the
// first match and the company entity must be the second match to replace the
// entity's image.
matches.push_back(
CreateHistoryURLMatch(/*destination_url=*/"https://www.wellsfargo.com/"));
matches.push_back(
CreateCompanyEntityMatch(/*website_uri=*/"https://www.wellsfargo.com/"));
matches.push_back(CreateSearchMatch());
SetAutocompleteMatches(matches);
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/1));
MaybeRemoveCompanyEntityImages();
ASSERT_TRUE(ImageURLAndImageDominantColorIsEmpty(/*index=*/1));
EXPECT_TRUE(
provider_client()
->GetOmniboxTriggeredFeatureService()
->GetFeatureTriggeredInSession(
metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
}
TEST_F(AutocompleteControllerTest,
CompanyEntityImageNotRemoved_LeastAggressive) {
// Set feature flag and param.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kCompanyEntityIconAdjustment,
{{OmniboxFieldTrial::kCompanyEntityIconAdjustmentGroup.name,
"least-aggressive"}});
std::vector<AutocompleteMatch> matches;
// Entity is not the second suggestion. Entity's image should not be removed.
matches.push_back(
CreateHistoryURLMatch(/*destination_url=*/"https://www.wellsfargo.com/"));
matches.push_back(CreateSearchMatch());
matches.push_back(
CreateCompanyEntityMatch(/*website_uri=*/"https://www.wellsfargo.com/"));
SetAutocompleteMatches(matches);
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/2));
MaybeRemoveCompanyEntityImages();
// The entity's image_url should remain as is.
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/2));
EXPECT_FALSE(
provider_client()
->GetOmniboxTriggeredFeatureService()
->GetFeatureTriggeredInSession(
metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
}
TEST_F(AutocompleteControllerTest, RemoveCompanyEntityImage_Moderate) {
// Set feature flag and param.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kCompanyEntityIconAdjustment,
{{OmniboxFieldTrial::kCompanyEntityIconAdjustmentGroup.name,
"moderate"}});
std::vector<AutocompleteMatch> matches;
// In the moderate experiment group the historical match must be the first
// match and the company entity can be in any slot.
matches.push_back(
CreateHistoryURLMatch(/*destination_url=*/"https://www.wellsfargo.com/"));
matches.push_back(CreateSearchMatch());
matches.push_back(
CreateCompanyEntityMatch(/*website_uri=*/"https://www.wellsfargo.com/"));
SetAutocompleteMatches(matches);
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/2));
MaybeRemoveCompanyEntityImages();
ASSERT_TRUE(ImageURLAndImageDominantColorIsEmpty(/*index=*/2));
EXPECT_TRUE(
provider_client()
->GetOmniboxTriggeredFeatureService()
->GetFeatureTriggeredInSession(
metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
}
TEST_F(AutocompleteControllerTest, CompanyEntityImageNotRemoved_Moderate) {
// Set feature flag and param.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kCompanyEntityIconAdjustment,
{{OmniboxFieldTrial::kCompanyEntityIconAdjustmentGroup.name,
"moderate"}});
std::vector<AutocompleteMatch> matches;
// History match is not the first suggestion. Entity's image should not be
// removed.
matches.push_back(
CreateCompanyEntityMatch(/*website_uri=*/"https://www.wellsfargo.com/"));
matches.push_back(
CreateHistoryURLMatch(/*destination_url=*/"https://www.wellsfargo.com/"));
matches.push_back(CreateSearchMatch());
SetAutocompleteMatches(matches);
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/0));
MaybeRemoveCompanyEntityImages();
// The entity's image_url should remain as is.
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/0));
EXPECT_FALSE(
provider_client()
->GetOmniboxTriggeredFeatureService()
->GetFeatureTriggeredInSession(
metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
}
TEST_F(AutocompleteControllerTest, RemoveCompanyEntityImage_MostAggressive) {
// Set feature flag and param.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kCompanyEntityIconAdjustment,
{{OmniboxFieldTrial::kCompanyEntityIconAdjustmentGroup.name,
"most-aggressive"}});
std::vector<AutocompleteMatch> matches;
// In the most aggressive experiment group both the historical match and
// company entity can be in any slot.
matches.push_back(
CreateCompanyEntityMatch(/*website_uri=*/"https://www.wellsfargo.com/"));
matches.push_back(CreateSearchMatch());
matches.push_back(
CreateHistoryURLMatch(/*destination_url=*/"https://www.wellsfargo.com/"));
SetAutocompleteMatches(matches);
ASSERT_FALSE(ImageURLAndImageDominantColorIsEmpty(/*index=*/0));
MaybeRemoveCompanyEntityImages();
ASSERT_TRUE(ImageURLAndImageDominantColorIsEmpty(/*index=*/0));
EXPECT_TRUE(
provider_client()
->GetOmniboxTriggeredFeatureService()
->GetFeatureTriggeredInSession(
metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
}
// Desktop has some special handling for bare '@' inputs.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
TEST_F(AutocompleteControllerTest, FilterMatchesForInstantKeywordWithBareAt) {
SetAutocompleteMatches({
CreateSearchMatch(u"@"),
CreateCompanyEntityMatch("https://example.com"),
CreateHistoryURLMatch("https://example.com"),
CreateStarterPackMatch(u"@bookmarks"),
CreateStarterPackMatch(u"@history"),
CreateStarterPackMatch(u"@tabs"),
CreateFeaturedEnterpriseSearch(u"@work"),
});
AutocompleteInput input(u"@", 1u, metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
controller_.MaybeCleanSuggestionsForKeywordMode(
input, &controller_.internal_result_);
EXPECT_EQ(controller_.internal_result_.size(), 5u);
EXPECT_TRUE(std::all_of(
controller_.internal_result_.begin(), controller_.internal_result_.end(),
[](const auto& match) {
return match.type == AutocompleteMatchType::STARTER_PACK ||
match.type ==
AutocompleteMatchType::FEATURED_ENTERPRISE_SEARCH ||
match.contents == u"@";
}));
}
#endif
TEST_F(AutocompleteControllerTest, UpdateResult_SyncAnd2Async) {
auto sync_match = CreateSearchMatch("sync", true, 1300);
auto async_match1 = CreateSearchMatch("async_1", true, 1200);
auto async_match2 = CreateSearchMatch("async_2", true, 1250);
{
SCOPED_TRACE("Sync pass.");
EXPECT_THAT(controller_.SimulateAutocompletePass(true, false, {sync_match}),
testing::ElementsAreArray({
"sync",
}));
}
{
SCOPED_TRACE("1st async pass.");
EXPECT_THAT(controller_.SimulateAutocompletePass(
false, false, {sync_match, async_match1}),
testing::ElementsAreArray({
"sync",
"async_1",
}));
}
{
SCOPED_TRACE(
"Last async pass. Verify the correct matches are shown ranked by "
"relevance.");
EXPECT_THAT(controller_.SimulateAutocompletePass(
false, true, {async_match1, async_match2}),
testing::ElementsAreArray({
"async_2",
"async_1",
}));
}
}
TEST_F(AutocompleteControllerTest, UpdateResult_TransferringOldMatches) {
auto pass1_match1 = CreateSearchMatch("pass1_match1", true, 1300);
auto pass1_match2 = CreateSearchMatch("pass1_match2", true, 1200);
auto pass1_match3 = CreateSearchMatch("pass1_match3", true, 1100);
auto pass2_match1 = CreateSearchMatch("pass2_match1", true, 1000);
auto pass3_match2 = CreateSearchMatch("pass3_match2", true, 900);
auto pass3_match3 = CreateSearchMatch("pass3_match3", true, 800);
auto pass3_match4 = CreateSearchMatch("pass3_match4", true, 700);
auto pass4_match1 = CreateSearchMatch("pass4_match1", true, 600);
auto pass5_match1 = CreateSearchMatch("pass5_match1", true, 500);
EXPECT_THAT(controller_.SimulateAutocompletePass(
true, false, {pass1_match1, pass1_match2, pass1_match3}),
testing::ElementsAreArray({
"pass1_match1",
"pass1_match2",
"pass1_match3",
}));
// # of matches decreased from 3 to 2. So 1 match should be transferred. The
// lowest ranked match should be transferred. It should keep its score and be
// ranked above the new non-transferred match.
EXPECT_THAT(controller_.SimulateAutocompletePass(
false, false, {pass1_match1, pass2_match1}),
testing::ElementsAreArray({
"pass1_match1",
"pass1_match3",
"pass2_match1",
}));
// # of matches remained 3. So no matches should be transferred.
EXPECT_THAT(controller_.SimulateAutocompletePass(
false, false, {pass3_match2, pass3_match3, pass3_match4}),
testing::ElementsAreArray({
"pass3_match2",
"pass3_match3",
"pass3_match4",
}));
// Transferred matches should not be allowed to be default.
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, false, {pass4_match1}),
testing::ElementsAreArray({
"pass4_match1",
"pass3_match3",
"pass3_match4",
}));
// Lowest ranked match should be transferred. But old matches still present
// shouldn't count, and the next lowest match should be transferred.
// Transferred match scores should be capped to the new default, therefore,
// the transferred `pass3_match3` should be demoted to last even though it
// originally outscored `pass3_match4`.
EXPECT_THAT(controller_.SimulateAutocompletePass(
false, false, {pass4_match1, pass3_match4}),
testing::ElementsAreArray({
"pass4_match1",
"pass3_match4",
"pass3_match3",
}));
// Sync updates should also transfer old matches. Lowest ranked, not
// necessarily lowest scored, match should be transferred.
EXPECT_THAT(controller_.SimulateAutocompletePass(true, false, {pass5_match1}),
testing::ElementsAreArray({
"pass5_match1",
"pass3_match3",
"pass3_match4",
}));
// Expire updates should not transfer old matches.
EXPECT_THAT(controller_.SimulateExpirePass(), testing::ElementsAreArray({
"pass5_match1",
}));
// Async updates after the expire update should transfer matches.
EXPECT_THAT(controller_.SimulateAutocompletePass(false, false, {}),
testing::ElementsAreArray({
"pass5_match1",
}));
// The last async pass shouldn't transfer matches.
EXPECT_THAT(controller_.SimulateAutocompletePass(true, true, {}),
testing::ElementsAreArray<std::string>({}));
}
TEST_F(AutocompleteControllerTest, UpdateResult_PreservingDefault) {
auto match1 = CreateSearchMatch("match1", true, 100);
auto match2 = CreateSearchMatch("match2", true, 200);
auto match3 = CreateSearchMatch("match3", true, 300);
auto match4 = CreateSearchMatch("match4", true, 400);
auto match5 = CreateSearchMatch("match5", true, 500);
auto match6 = CreateSearchMatch("match6", true, 600);
auto match7 = CreateSearchMatch("match7", true, 700);
auto match8 = CreateSearchMatch("match8", true, 800);
auto match9 = CreateSearchMatch("match9", true, 900);
{
SCOPED_TRACE("Load a default suggestion.");
EXPECT_THAT(controller_.SimulateAutocompletePass(true, true, {match1}),
testing::ElementsAreArray({
"match1",
}));
}
{
SCOPED_TRACE("Don't preserve default on sync pass with short inputs.");
EXPECT_THAT(controller_.SimulateAutocompletePass(
true, false, {match1, match2},
FakeAutocompleteController::CreateInput(u"x")),
testing::ElementsAreArray({
"match2",
"match1",
}));
}
{
SCOPED_TRACE("Preserve default on async pass with short inputs.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, false, {match2, match3}),
testing::ElementsAreArray({
"match2",
"match3",
}));
// Preserve default on last async pass with short inputs. Preserve default
}
{
SCOPED_TRACE("across multiple passes.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, true, {match2, match4}),
testing::ElementsAreArray({
"match2",
"match4",
}));
// Preserve default on sync pass with long inputs. Preserve default across
}
{
SCOPED_TRACE("multiple inputs.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(true, false, {match2, match5}),
testing::ElementsAreArray({
"match2",
"match5",
}));
}
{
SCOPED_TRACE("Preserve default on async pass with long inputs.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, false, {match2, match6}),
testing::ElementsAreArray({
"match2",
"match6",
}));
}
{
SCOPED_TRACE("Don't preserve default if it's transferred.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, false, {match7, match8}),
testing::ElementsAreArray({
"match8",
"match7",
}));
}
{
SCOPED_TRACE("Preserve default on last async pass with long inputs.");
EXPECT_THAT(
controller_.SimulateAutocompletePass(false, true, {match8, match9}),
testing::ElementsAreArray({
"match8",
"match9",
}));
}
}
TEST_F(AutocompleteControllerTest, UpdateResult_Ranking) {
// Higher scored suggestions are ranked higher.
// Clear results between each test to avoid default preserving applying.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("500", true, 500),
CreateSearchMatch("800", true, 800),
}),
testing::ElementsAreArray({
"800",
"500",
}));
// Default suggestion must be allowed to be default.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("500", true, 500),
CreateSearchMatch("800", false, 800),
}),
testing::ElementsAreArray({
"500",
"800",
}));
// Android and iOS don't use the same grouping logic as desktop
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
// Searches should be grouped above non-shortcut-boosted URLs.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search900", true, 900),
CreateHistoryUrlMlScoredMatch("history800", true, 800, 1),
CreateSearchMatch("search700", true, 700),
CreateHistoryUrlMlScoredMatch("history600", true, 600, 1),
CreateSearchMatch("search500", true, 500),
CreateHistoryUrlMlScoredMatch("history400", true, 400, 1),
}),
testing::ElementsAreArray({
"search900",
"search700",
"search500",
"history800",
"history600",
"history400",
}));
// Default can be a non-search if it's scored higher than all the searches.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search900", true, 900),
CreateHistoryUrlMlScoredMatch("history800", true, 800, 1),
CreateSearchMatch("search700", true, 700),
CreateHistoryUrlMlScoredMatch("history600", true, 600, 1),
CreateSearchMatch("search500", true, 500),
CreateHistoryUrlMlScoredMatch("history400", true, 400, 1),
CreateHistoryUrlMlScoredMatch("history1000", true, 1000, 1),
}),
testing::ElementsAreArray({
"history1000",
"search900",
"search700",
"search500",
"history800",
"history600",
"history400",
}));
// Shortcut boosting is re-distributed when ML Scoring is enabled. That is
// tested in the `MlRanking` test below.
OmniboxFieldTrial::ScopedMLConfigForTesting scoped_config;
if (!scoped_config.GetMLConfig().ml_url_scoring) {
// Shortcut boosted suggestions should be ranked above searches, even if
// they're scored lower.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history800", true, 800, 1),
CreateHistoryUrlMlScoredMatch("history850", true, 850, 1),
CreateSearchMatch("search700", true, 700),
CreateSearchMatch("search750", true, 750),
CreateBoostedShortcutMatch("shortcut600", 600, 1),
CreateBoostedShortcutMatch("shortcut650", 650, 1),
}),
testing::ElementsAreArray({
"history850",
"shortcut650",
"shortcut600",
"search750",
"search700",
"history800",
}));
}
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
}
TEST_F(AutocompleteControllerTest, UpdateResult_ZPSEnabledAndShownInSession) {
// Populate TemplateURLService with a keyword.
TemplateURLData turl_data;
turl_data.SetShortName(u"Keyword");
turl_data.SetKeyword(u"keyword");
turl_data.SetURL("https://google.com/search?q={searchTerms}");
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(turl_data));
// Create a zero-suggest input.
auto zps_input = FakeAutocompleteController::CreateInput(u"");
zps_input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_CLOBBER);
{
SCOPED_TRACE("Zero-prefix suggestions are offered synchronously");
EXPECT_THAT(controller_.SimulateAutocompletePass(
/*sync=*/true, /*done=*/false,
{
CreatePersonalizedZeroPrefixMatch("zps_1", 1450),
CreatePersonalizedZeroPrefixMatch("zps_2", 1449),
},
zps_input),
testing::ElementsAreArray({
"zps_1",
"zps_2",
}));
// Whether zero-suggest was enabled and the number of zero-prefix
// suggestions shown in the session are updated in the internal result set.
EXPECT_TRUE(controller_.internal_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.internal_result_
.num_zero_prefix_suggestions_shown_in_session(),
2u);
// Published result set does not get the session data.
EXPECT_FALSE(
controller_.published_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.published_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
// Published matches contain the relevant session data in searchboxstats.
EXPECT_TRUE(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats.zero_prefix_enabled());
EXPECT_EQ(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats
.num_zero_prefix_suggestions_shown(),
2u);
}
{
SCOPED_TRACE("More zero-prefix suggestions are offered asynchronously");
EXPECT_THAT(controller_.SimulateAutocompletePass(
/*sync=*/false, /*done=*/false,
{
CreatePersonalizedZeroPrefixMatch("zps_1", 1450),
CreatePersonalizedZeroPrefixMatch("zps_2", 1449),
CreatePersonalizedZeroPrefixMatch("zps_3", 1448),
CreatePersonalizedZeroPrefixMatch("zps_4", 1447),
},
zps_input),
testing::ElementsAreArray({
"zps_1",
"zps_2",
"zps_3",
"zps_4",
}));
// If zero-prefix suggestions are offered multiple times in the session, the
// most recent count is logged.
EXPECT_TRUE(controller_.internal_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.internal_result_
.num_zero_prefix_suggestions_shown_in_session(),
4u);
// Published result set does not get the session data.
EXPECT_FALSE(
controller_.published_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.published_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
// Published matches contain the relevant session data in searchboxstats.
EXPECT_TRUE(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats.zero_prefix_enabled());
EXPECT_EQ(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats
.num_zero_prefix_suggestions_shown(),
4u);
}
{
SCOPED_TRACE("Stop with clear_result=false is called due to user idleness");
controller_.Stop(/*clear_result=*/false);
// Stop with clear_result=false does not clear the internal result set and
// does not notify `OnResultChanged()`.
EXPECT_FALSE(controller_.internal_result_.empty());
EXPECT_FALSE(controller_.published_result_.empty());
// Whether zero-suggest was enabled and the number of zero-prefix
// suggestions shown in the session are unchanged in the internal result
// set.
EXPECT_TRUE(controller_.internal_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.internal_result_
.num_zero_prefix_suggestions_shown_in_session(),
4u);
// Published result set does not get the session data.
EXPECT_FALSE(
controller_.published_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.published_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
// Published matches contain the relevant session data in searchboxstats.
EXPECT_TRUE(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats.zero_prefix_enabled());
EXPECT_EQ(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats
.num_zero_prefix_suggestions_shown(),
4u);
}
{
SCOPED_TRACE("Prefix suggestions are offered synchronously");
EXPECT_THAT(controller_.SimulateAutocompletePass(
/*sync=*/true, /*done=*/true,
{
CreateSearchMatch("search_1", true, 900),
}),
testing::ElementsAreArray({
"search_1",
}));
// Whether zero-suggest was enabled and the number of zero-prefix
// suggestions shown in the session are unchanged in the internal result
// set.
EXPECT_TRUE(controller_.internal_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.internal_result_
.num_zero_prefix_suggestions_shown_in_session(),
4u);
// Published result set does not get the session data.
EXPECT_FALSE(
controller_.published_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.published_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
// Published matches contain the relevant session data in searchboxstats.
EXPECT_TRUE(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats.zero_prefix_enabled());
EXPECT_EQ(controller_.published_result_.match_at(0)
->search_terms_args->searchbox_stats
.num_zero_prefix_suggestions_shown(),
4u);
}
{
SCOPED_TRACE("Stop with clear_result=true is called due to popup closing");
controller_.Stop(/*clear_result=*/true);
// Stop with clear_result=true clears the internal result set and notifies
// `OnResultChanged()`.
EXPECT_TRUE(controller_.internal_result_.empty());
EXPECT_TRUE(controller_.published_result_.empty());
// Whether zero-suggest was enabled and the number of zero-prefix
// suggestions shown in the session are reset in the internal result set.
EXPECT_FALSE(controller_.internal_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.internal_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
// Published result set does not get the session data.
EXPECT_FALSE(
controller_.published_result_.zero_prefix_enabled_in_session());
EXPECT_EQ(controller_.published_result_
.num_zero_prefix_suggestions_shown_in_session(),
0u);
}
}
// Android and iOS aren't ready for ML and won't pass this test because they
// have their own grouping code.
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_ANDROID) && \
!BUILDFLAG(IS_IOS)
TEST_F(AutocompleteControllerTest, MlRanking) {
OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
scoped_ml_config.GetMLConfig().ml_url_scoring = true;
scoped_ml_config.GetMLConfig().url_scoring_model = true;
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({}),
testing::ElementsAre());
// Even if ML ranks a URL 0, it should still use traditional scores.
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history", true, 1400, 0),
CreateSearchMatch("search", true, 1300),
}),
testing::ElementsAreArray({
"history",
"search",
}));
// Simple case of redistributing ranking among only URLs.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history 1350 .5", true, 1350, .5),
CreateSearchMatch("search 1400", false, 1400),
CreateSearchMatch("search 800", true, 800),
CreateSearchMatch("search 600", false, 600),
CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
CreateHistoryUrlMlScoredMatch("history 1100 .1", false, 1100, .1),
CreateHistoryUrlMlScoredMatch("history 500 .2", true, 500, .2),
}),
testing::ElementsAreArray({
"history 1200 .9",
"search 1400",
"search 800",
"search 600",
"history 1350 .5",
"history 500 .2",
"history 1100 .1",
}));
// When multiple URL suggestions have been assigned the same score by the ML
// model, those suggestions which were top-ranked according to legacy scoring
// should continue to be top-ranked once ML scoring has run.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history A 1350 .2", true, 1350, .2),
CreateHistoryUrlMlScoredMatch("history B 1200 .2", true, 1200, .2),
CreateHistoryUrlMlScoredMatch("history C 1100 .2", false, 1100, .2),
CreateHistoryUrlMlScoredMatch("history D 300 .2", true, 300, .2),
CreateHistoryUrlMlScoredMatch("history E 200 .2", true, 200, .2),
CreateHistoryUrlMlScoredMatch("history F 100 .2", true, 100, .2),
}),
testing::ElementsAreArray({
"history A 1350 .2",
"history B 1200 .2",
"history C 1100 .2",
"history D 300 .2",
"history E 200 .2",
"history F 100 .2",
}));
// Can change the default suggestion from 1 history to another.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history 1400 .5", true, 1400, .5),
CreateSearchMatch("search", true, 1300),
CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
}),
testing::ElementsAreArray({
"history 1200 .9",
"search",
"history 1400 .5",
}));
// Can change the default from search to history.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1300", true, 1300),
CreateHistoryUrlMlScoredMatch("history 1400 .5", false, 1400, .5),
CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
}),
testing::ElementsAreArray({
"history 1200 .9",
"search 1300",
"history 1400 .5",
}));
// Can change the default from history to search.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history 1400 .5", true, 1400, .5),
CreateSearchMatch("search 1300", true, 1300),
CreateHistoryUrlMlScoredMatch("history 1200 .9", false, 1200, .9),
}),
testing::ElementsAreArray({
"search 1300",
"history 1200 .9",
"history 1400 .5",
}));
// Can redistribute shortcut boosting to non-shortcuts.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1300", true, 1300),
CreateBoostedShortcutMatch("shortcut 1000 .1", 1000, .1),
CreateSearchMatch("search 1200", true, 1200),
CreateHistoryUrlMlScoredMatch("history 1400 .9", false, 1400, .9),
CreateHistoryUrlMlScoredMatch("history 1100 .5", true, 1100, .5),
}),
testing::ElementsAreArray({
"search 1300",
"history 1400 .9",
"search 1200",
"history 1100 .5",
"shortcut 1000 .1",
}));
// Can 'consume' shortcut boosting by assigning it to a match that's becoming
// default anyways.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1300", true, 1300),
CreateBoostedShortcutMatch("shortcut 1000 .1", 1000, .1),
CreateSearchMatch("search 1200", true, 1200),
CreateHistoryUrlMlScoredMatch("history 1400 .5", false, 1400, .5),
CreateHistoryUrlMlScoredMatch("history 1100 .9", true, 1100, .9),
}),
testing::ElementsAreArray({
"history 1100 .9",
"search 1300",
"search 1200",
"history 1400 .5",
"shortcut 1000 .1",
}));
// Can increase the number of URLs above searches.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1300", true, 1300),
CreateBoostedShortcutMatch("shortcut 1000 .7", 1000, .7),
CreateSearchMatch("search 1200", true, 1200),
CreateHistoryUrlMlScoredMatch("history 1400 .5", false, 1400, .5),
CreateHistoryUrlMlScoredMatch("history 1350 .2", false, 1350, .2),
CreateHistoryUrlMlScoredMatch("history 1100 .8", true, 1100, .8),
CreateHistoryUrlMlScoredMatch("history 1050 .9", false, 1050, .9),
}),
testing::ElementsAreArray({
"history 1100 .8",
"history 1050 .9",
"search 1300",
"search 1200",
"shortcut 1000 .7",
"history 1400 .5",
"history 1350 .2",
}));
// Can increase the number of URLs above searches even when the default was a
// URL.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateBoostedShortcutMatch("shortcut 1450 .7", 1450, .7),
CreateSearchMatch("search 1200", true, 1200),
CreateHistoryUrlMlScoredMatch("history 1400 .9", false, 1400, .9),
}),
testing::ElementsAreArray({
"shortcut 1450 .7",
"history 1400 .9",
"search 1200",
}));
// Can decrease the number of URLs above searches.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history 1400 .5", true, 1400, .5),
CreateBoostedShortcutMatch("shortcut 1000 .1", 1000, .1),
CreateSearchMatch("search 1300", true, 1300),
CreateSearchMatch("search 1200", true, 1200),
CreateHistoryUrlMlScoredMatch("history 1100 .9", true, 1100, .9),
}),
testing::ElementsAreArray({
"history 1100 .9",
"search 1300",
"search 1200",
"history 1400 .5",
"shortcut 1000 .1",
}));
// When transferring matches, culls the lowest ML ranked matches, rather than
// the lowest traditional ranked matches.
controller_.internal_result_.ClearMatches();
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, false,
{
CreateSearchMatch("search 1270", true, 1270),
CreateSearchMatch("search 1260", true, 1260),
CreateSearchMatch("search 1250", true, 1250),
CreateSearchMatch("search 1240", true, 1240),
CreateSearchMatch("search 1230", true, 1230),
CreateSearchMatch("search 1220", true, 1220),
CreateSearchMatch("search 1210", true, 1210),
CreateHistoryUrlMlScoredMatch("history 1100 .5", true, 1100, .5),
CreateHistoryUrlMlScoredMatch("history 1000 .9", true, 1000, .9),
}),
testing::ElementsAreArray({
"search 1270",
"search 1260",
"search 1250",
"search 1240",
"search 1230",
"search 1220",
"search 1210",
"history 1000 .9",
}));
// When not transferring matches, like above, culls the lowest ML ranked
// matches, rather than the lowest traditional ranked matches.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1270", true, 1270),
CreateSearchMatch("search 1260", true, 1260),
CreateSearchMatch("search 1250", true, 1250),
CreateSearchMatch("search 1240", true, 1240),
CreateSearchMatch("search 1230", true, 1230),
CreateSearchMatch("search 1220", true, 1220),
CreateSearchMatch("search 1210", true, 1210),
CreateHistoryUrlMlScoredMatch("history 1100 .5", true, 1100, .5),
CreateHistoryUrlMlScoredMatch("history 1000 .9", true, 1000, .9),
}),
testing::ElementsAreArray({
"search 1270",
"search 1260",
"search 1250",
"search 1240",
"search 1230",
"search 1220",
"search 1210",
"history 1000 .9",
}));
}
TEST_F(AutocompleteControllerTest, MlRanking_MappedSearchBlending) {
OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
scoped_ml_config.GetMLConfig().ml_url_scoring = true;
scoped_ml_config.GetMLConfig().url_scoring_model = true;
scoped_ml_config.GetMLConfig().mapped_search_blending = true;
scoped_ml_config.GetMLConfig().mapped_search_blending_min = 600;
scoped_ml_config.GetMLConfig().mapped_search_blending_max = 2800;
scoped_ml_config.GetMLConfig().mapped_search_blending_grouping_threshold =
1400;
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({}),
testing::ElementsAre());
// If ML ranks a URL 0, then the final relevance score should be set to the
// value of `mapped_search_blending_min` (since ML scores are mapped using the
// formula "final_score = min + ml_score * (max - min))".
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateHistoryUrlMlScoredMatch("history", true, 1400, 0),
CreateSearchMatch("search", true, 1300),
}),
testing::ElementsAreArray({
"search",
"history",
}));
// If a (remote) document suggestion has a traditional score of zero, then the
// final relevance score should remain zero (instead of using the formula
// "final_score = min + ml_score * (max - min)" to overwrite the score). This
// will result in the document suggestion getting culled from the final list
// of suggestions.
const auto type = AutocompleteMatchType::DOCUMENT_SUGGESTION;
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
// Final score: 1150 (== 600 + 0.25 * (2800 - 600))
CreateMlScoredMatch("document 1400 0.25", type, false, 1400, 0.25),
// Final score: 0 (!= 600 + 0.95 * (2800 - 600))
CreateMlScoredMatch("document 0 0.95", type, false, 0, 0.95),
// Final score: 2250 (== 600 + 0.75 * (2800 - 600))
CreateMlScoredMatch("document 1200 0.75", type, false, 1200, 0.75),
}),
testing::ElementsAreArray({
"document 1200 0.75",
"document 1400 0.25",
}));
// Simple case of ranking with linear score mapping.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
// Final score: 1700 (== 600 + 0.5 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1350 .5", true, 1350, .5),
// Final score: 2580 (== 600 + 0.9 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
// Final score: 820 (== 600 + 0.1 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1100 .1", false, 1100, .1),
// Final score: 1040 (== 600 + 0.2 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 500 .2", true, 500, .2),
}),
testing::ElementsAreArray({
"history 1200 .9",
"history 1350 .5",
"history 500 .2",
"history 1100 .1",
}));
// Verify that URLs are grouped above searches if their final score is
// greater than `grouping_threshold` (i.e. "shortcut boosting").
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
// Final score: 1700 (== 600 + 0.5 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1350 .5", true, 1350, .5),
CreateSearchMatch("search 1400", false, 1400),
CreateSearchMatch("search 800", true, 800),
CreateSearchMatch("search 600", false, 600),
// Final score: 2580 (== 600 + 0.9 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
// Final score: 820 (== 600 + 0.1 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1100 .1", false, 1100, .1),
// Final score: 1040 (== 600 + 0.2 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 500 .2", true, 500, .2),
}),
testing::ElementsAreArray({
"history 1200 .9",
"history 1350 .5",
"search 1400",
"search 800",
"search 600",
"history 500 .2",
"history 1100 .1",
}));
// When multiple URL suggestions have been assigned the same score by the ML
// model, those suggestions which were top-ranked according to legacy scoring
// should continue to be top-ranked once ML scoring has run.
EXPECT_THAT(
// Each of the below URL suggestions are assigned an initial relevance
// score of 1040 (== 600 + 0.2 * (2800 - 600)). After initial assignment,
// score adjustment logic is applied in order to generate the final
// relevance scores (which are guaranteed to be distinct).
controller_.SimulateCleanAutocompletePass({
// Final score: 1039
CreateHistoryUrlMlScoredMatch("history B 1200 .2", true, 1200, .2),
// Final score: 1036
CreateHistoryUrlMlScoredMatch("history E 200 .2", true, 200, .2),
// Final score: 1040
CreateHistoryUrlMlScoredMatch("history A 1350 .2", true, 1350, .2),
// Final score: 1037
CreateHistoryUrlMlScoredMatch("history D 300 .2", true, 300, .2),
// Final score: 1038
CreateHistoryUrlMlScoredMatch("history C 1100 .2", false, 1100, .2),
// Final score: 1035
CreateHistoryUrlMlScoredMatch("history F 100 .2", true, 100, .2),
}),
testing::ElementsAreArray({
"history A 1350 .2",
"history B 1200 .2",
"history C 1100 .2",
"history D 300 .2",
"history E 200 .2",
"history F 100 .2",
}));
// Can change the default suggestion from 1 history to another.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
// Final score: 1040 (== 600 + 0.2 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1400 .2", true, 1400, .2),
CreateSearchMatch("search", true, 1100),
// Final score: 1260 (== 600 + 0.3 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1200 .3", true, 1200, .3),
}),
testing::ElementsAreArray({
"history 1200 .3",
"search",
"history 1400 .2",
}));
// Can change the default from search to history.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1200", true, 1200),
// Final score: 1040 (== 600 + 0.2 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1400 .2", false, 1400, .2),
// Final score: 1260 (== 600 + 0.3 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1100 .3", true, 1100, .3),
}),
testing::ElementsAreArray({
"history 1100 .3",
"search 1200",
"history 1400 .2",
}));
// Can change the default from history to search.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
// Final score: 1040 (== 600 + 0.2 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1400 .2", true, 1400, .2),
CreateSearchMatch("search 1300", true, 1300),
// Final score: 820 (== 600 + 0.1 * (2800 - 600))
CreateHistoryUrlMlScoredMatch("history 1200 .1", false, 1200, .1),
}),
testing::ElementsAreArray({
"search 1300",
"history 1400 .2",
"history 1200 .1",
}));
// When transferring matches, culls the lowest ML ranked matches, rather than
// the lowest traditional ranked matches.
controller_.internal_result_.Reset();
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, false,
{
CreateSearchMatch("search 1270", true, 1270),
CreateSearchMatch("search 1260", true, 1260),
CreateSearchMatch("search 1250", true, 1250),
CreateSearchMatch("search 1240", true, 1240),
CreateSearchMatch("search 1230", true, 1230),
CreateSearchMatch("search 1220", true, 1220),
CreateSearchMatch("search 1210", true, 1210),
CreateHistoryUrlMlScoredMatch("history 1100 .1", true, 1100, .1),
CreateHistoryUrlMlScoredMatch("history 1000 .2", true, 1000, .2),
}),
testing::ElementsAreArray({
"search 1270",
"search 1260",
"search 1250",
"search 1240",
"search 1230",
"search 1220",
"search 1210",
"history 1000 .2",
}));
// When not transferring matches, like above, culls the lowest ML ranked
// matches, rather than the lowest traditional ranked matches.
EXPECT_THAT(
controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search 1270", true, 1270),
CreateSearchMatch("search 1260", true, 1260),
CreateSearchMatch("search 1250", true, 1250),
CreateSearchMatch("search 1240", true, 1240),
CreateSearchMatch("search 1230", true, 1230),
CreateSearchMatch("search 1220", true, 1220),
CreateSearchMatch("search 1210", true, 1210),
CreateHistoryUrlMlScoredMatch("history 1100 .1", true, 1100, .1),
CreateHistoryUrlMlScoredMatch("history 1000 .2", true, 1000, .2),
}),
testing::ElementsAreArray({
"search 1270",
"search 1260",
"search 1250",
"search 1240",
"search 1230",
"search 1220",
"search 1210",
"history 1000 .2",
}));
}
TEST_F(AutocompleteControllerTest, UpdateResult_MLRanking_PreserveDefault) {
OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
scoped_ml_config.GetMLConfig().ml_url_scoring = true;
scoped_ml_config.GetMLConfig().url_scoring_model = true;
scoped_ml_config.GetMLConfig().mapped_search_blending = true;
scoped_ml_config.GetMLConfig().mapped_search_blending_min = 600;
scoped_ml_config.GetMLConfig().mapped_search_blending_max = 2800;
scoped_ml_config.GetMLConfig().mapped_search_blending_grouping_threshold =
1400;
// ML ranking should preserve search defaults.
// In other words, if the autocomplete controller starts off by listing
// "search 1300" as the (top-ranked) default suggestion, then "search 1300"
// should remain the default suggestion (even though "history 1400" has been
// added to the list of suggestions).
EXPECT_THAT(controller_.SimulateAutocompletePass(
true, true,
{
CreateSearchMatch("search 1300", true, 1300),
}),
testing::ElementsAreArray({
"search 1300",
}));
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, true,
{
CreateHistoryUrlMlScoredMatch("history 1400", true, 1400, 1),
CreateSearchMatch("search 1300", true, 1300),
}),
testing::ElementsAreArray({
"search 1300",
"history 1400",
}));
// ML ranking should preserve non-search defaults.
// In other words, if the autocomplete controller starts off by listing
// "history 1300" as the (top-ranked) default suggestion, then "history 1300"
// should remain the default suggestion (even though "history 1500" and
// "search 1400" have been added to the list of suggestions).
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, true,
{
CreateHistoryUrlMlScoredMatch("history 1300", true, 1300, 1),
}),
testing::ElementsAreArray({
"history 1300",
}));
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, true,
{
CreateHistoryUrlMlScoredMatch("history 1500", true, 1500, 1),
CreateSearchMatch("search 1400", true, 1400),
CreateHistoryUrlMlScoredMatch("history 1300", true, 1300, .1),
}),
testing::ElementsAreArray({
"history 1300",
"history 1500",
"search 1400",
}));
}
TEST_F(AutocompleteControllerTest, UpdateResult_MLRanking_AllMatches) {
OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
scoped_ml_config.GetMLConfig().ml_url_scoring = true;
scoped_ml_config.GetMLConfig().url_scoring_model = true;
scoped_ml_config.GetMLConfig().mapped_search_blending = true;
scoped_ml_config.GetMLConfig().mapped_search_blending_min = 600;
scoped_ml_config.GetMLConfig().mapped_search_blending_max = 2800;
scoped_ml_config.GetMLConfig().mapped_search_blending_grouping_threshold =
1400;
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, false,
{
CreateHistoryUrlMlScoredMatch("history 100", true, 100, 1),
CreateHistoryUrlMlScoredMatch("history 200", true, 200, 1),
CreateHistoryUrlMlScoredMatch("history 300", true, 300, 1),
CreateHistoryUrlMlScoredMatch("history 400", true, 400, 1),
CreateHistoryUrlMlScoredMatch("history 500", true, 500, 1),
CreateHistoryUrlMlScoredMatch("history 600", true, 600, 1),
CreateHistoryUrlMlScoredMatch("history 700", true, 700, 1),
CreateHistoryUrlMlScoredMatch("history 800", true, 800, 1),
CreateHistoryUrlMlScoredMatch("history 900", true, 900, 1),
CreateHistoryUrlMlScoredMatch("history 1000", true, 1000, 1),
}),
testing::ElementsAreArray({
"history 1000",
"history 900",
"history 800",
"history 700",
"history 600",
"history 500",
"history 400",
"history 300",
}));
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, false,
{
CreateHistoryUrlMlScoredMatch("history 100 .9", true, 100, .9),
CreateHistoryUrlMlScoredMatch("history 200 .8", true, 200, .8),
CreateHistoryUrlMlScoredMatch("history 300 .7", true, 300, .7),
CreateHistoryUrlMlScoredMatch("history 400 .6", true, 400, .6),
CreateHistoryUrlMlScoredMatch("history 500 .5", true, 500, .5),
CreateHistoryUrlMlScoredMatch("history 600 .4", true, 600, .4),
CreateHistoryUrlMlScoredMatch("history 700 .3", true, 700, .3),
CreateHistoryUrlMlScoredMatch("history 800 .2", true, 800, .2),
CreateHistoryUrlMlScoredMatch("history 900 .1", true, 900, .1),
CreateHistoryUrlMlScoredMatch("history 1000 0", true, 1000, 0),
}),
testing::ElementsAreArray({
"history 100 .9",
"history 200 .8",
"history 300 .7",
"history 400 .6",
"history 500 .5",
"history 600 .4",
"history 700 .3",
"history 800 .2",
}));
}
#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_ANDROID) &&
// !BUILDFLAG(IS_IOS)
TEST_F(AutocompleteControllerTest, UpdateResult_NotifyingAndTimers) {
{
SCOPED_TRACE("Expect immediate notification after sync pass.");
controller_.GetFakeProvider().done_ = false;
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPass);
}
{
SCOPED_TRACE("Expect debounced notification after async pass.");
controller_.GetFakeProvider().done_ = false;
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
controller_.ExpectOnResultChanged(
200, AutocompleteController::UpdateType::kAsyncPass);
}
{
SCOPED_TRACE("Expect debouncing to reset after each async passes.");
controller_.GetFakeProvider().done_ = false;
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
task_environment_.FastForwardBy(base::Milliseconds(150));
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
task_environment_.FastForwardBy(base::Milliseconds(150));
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
controller_.ExpectOnResultChanged(
200, AutocompleteController::UpdateType::kAsyncPass);
}
{
SCOPED_TRACE("Expect delayed notification after expiration.");
controller_.UpdateResult(AutocompleteController::UpdateType::kExpirePass);
controller_.ExpectOnResultChanged(
200, AutocompleteController::UpdateType::kExpirePass);
}
{
SCOPED_TRACE("Expect immediate notification after the last async pass.");
controller_.GetFakeProvider().done_ = true;
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kLastAsyncPass);
}
{
SCOPED_TRACE("Expect no stop update after the last async pass.");
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE(
"Expect immediate notification after the last async pass, even if a "
"debounced notification is pending.");
controller_.GetFakeProvider().done_ = false;
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPass);
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
controller_.GetFakeProvider().done_ = true;
task_environment_.FastForwardBy(base::Milliseconds(10));
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kLastAsyncPass);
}
{
SCOPED_TRACE("Expect no stop update after the last async pass (2).");
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE("Expect no stop update after a sync only pass.");
controller_.GetFakeProvider().done_ = true;
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPassOnly);
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE(
"Expect a stop update if the async passes takes too long. Expect no "
"notification.");
controller_.GetFakeProvider().done_ = false;
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPass);
controller_.ExpectStopAfter(1500);
}
{
SCOPED_TRACE(
"Expect a stop update to flush any pending notification for completed "
"non-final async passes.");
controller_.GetFakeProvider().done_ = false;
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPass);
for (size_t i = 0; i < 9; ++i) {
task_environment_.FastForwardBy(base::Milliseconds(150));
controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
}
controller_.ExpectStopAfter(150);
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE("Expect debounced expire notification.");
controller_.GetFakeProvider().done_ = false;
AutocompleteMatch transferred_match{
nullptr, 1000, false, AutocompleteMatchType::URL_WHAT_YOU_TYPED};
transferred_match.from_previous = true;
controller_.GetFakeProvider().matches_ = {transferred_match};
controller_.Start(FakeAutocompleteController::CreateInput(u"test"));
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kSyncPass);
// Expire timer is 500ms. Debounce delay is 200ms.
controller_.ExpectOnResultChanged(
700, AutocompleteController::UpdateType::kExpirePass);
controller_.ExpectStopAfter(800);
controller_.ExpectNoNotificationOrStop();
}
}
TEST_F(AutocompleteControllerTest, ExplicitStop) {
// Besides the `Stop()` fired by the timer, which is tested in
// `UpdateResult_NotifyingAndTimers`, there's also user triggered `Stop()`s
// tests here.
auto matches = {CreateSearchMatch("search", true, 900)};
{
SCOPED_TRACE(
"Stop with clear_result=false and no pending changes should not notify"
"`OnResultChanged()` - there's no change to notify of.");
controller_.SimulateAutocompletePass(true, false, matches);
controller_.Stop(false);
controller_.ExpectStopAfter(0);
EXPECT_FALSE(controller_.published_result_.empty());
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE(
"Stop with clear_result=false and pending changes should not notify"
"`OnResultChanged()` - the last pending change should be abandoned to "
"avoid changes as the user's e.g. down arrowing..");
controller_.SimulateAutocompletePass(true, false, matches);
controller_.SimulateAutocompletePass(false, false, matches);
controller_.Stop(false);
EXPECT_FALSE(controller_.published_result_.empty());
controller_.ExpectStopAfter(0);
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE(
"Stop with clear_result=true and no pending notifications should "
"notify `OnResultChanged()` - observers should know the results were "
"cleared.");
controller_.SimulateAutocompletePass(true, false, matches);
controller_.observer_->last_default_match_changed = true;
controller_.Stop(true);
EXPECT_TRUE(controller_.published_result_.empty());
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kStop);
EXPECT_FALSE(controller_.observer_->last_default_match_changed);
controller_.ExpectNoNotificationOrStop();
}
{
SCOPED_TRACE(
"Stop with clear_result=true and pending notifications should notify "
"`OnResultChanged()` - observers should know the results were "
"cleared.");
controller_.SimulateAutocompletePass(true, false, matches);
controller_.SimulateAutocompletePass(false, false, matches);
controller_.observer_->last_default_match_changed = true;
controller_.Stop(true);
EXPECT_TRUE(controller_.published_result_.empty());
controller_.ExpectOnResultChanged(
0, AutocompleteController::UpdateType::kStop);
EXPECT_FALSE(controller_.observer_->last_default_match_changed);
controller_.ExpectNoNotificationOrStop();
}
}
TEST_F(AutocompleteControllerTest, UpdateResult_ForceAllowedToBeDefault) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(omnibox::kGroupingFrameworkForNonZPS);
auto set_feature = [](bool enabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatureState(
omnibox_feature_configs::ForceAllowedToBeDefault::
kForceAllowedToBeDefault,
enabled);
return omnibox_feature_configs::ScopedConfigForTesting<
omnibox_feature_configs::ForceAllowedToBeDefault>();
};
{
// When disabled, a not-defaultable history match should not be default.
SCOPED_TRACE("Disabled");
auto disabled_config = set_feature(false);
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search", true, 200),
CreateHistoryUrlMlScoredMatch("history", false, 1400, .5),
}),
testing::ElementsAreArray({
"search",
"history",
}));
}
{
// An initially not-defaultable history match can be made defaultable.
SCOPED_TRACE("Enabled");
auto enabled_config = set_feature(true);
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search", true, 200),
CreateHistoryUrlMlScoredMatch("history", false, 1400, .5),
}),
testing::ElementsAreArray({
"history",
"search",
}));
}
{
// Initially defaultable matches should not be made non-defaultable even if
// they don't qualify for forcing defaultable.
SCOPED_TRACE("Enabled defaultable");
auto enabled_config = set_feature(true);
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search", true, 200),
CreateHistoryUrlMlScoredMatch("history", true, 300, .5),
}),
testing::ElementsAreArray({
"history",
"search",
}));
}
{
// Keyword matches shouldn't be made defaultable.
SCOPED_TRACE("Enabled keyword");
auto enabled_config = set_feature(true);
EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
CreateSearchMatch("search", true, 200),
CreateKeywordHintMatch("keyword", 1000),
}),
testing::ElementsAreArray({
"search",
"keyword",
}));
}
{
// Should not force default when `prevent_inline_autocomplete_` is true.
SCOPED_TRACE("Enabled prevent inline autocomplete");
auto enabled_config = set_feature(true);
controller_.internal_result_.ClearMatches();
EXPECT_THAT(
controller_.SimulateAutocompletePass(
true, true,
{
CreateSearchMatch("search", true, 200),
CreateAutocompleteMatch("history",
AutocompleteMatchType::HISTORY_CLUSTER,
false, false, 1000, std::nullopt),
},
FakeAutocompleteController::CreateInput(u"test", false, true)),
testing::ElementsAreArray({
"search",
"history",
}));
}
}
TEST_F(AutocompleteControllerTest, ExtraHeaders) {
// Populate TemplateURLService with a keyword.
{
TemplateURLData turl_data;
turl_data.SetShortName(u"Keyword");
turl_data.SetKeyword(u"keyword");
turl_data.SetURL("https://google.com/search?q={searchTerms}");
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(turl_data));
}
// Populate template URL service with starter pack entries.
for (auto& turl_data : TemplateURLStarterPackData::GetStarterPackEngines()) {
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(std::move(*turl_data)));
}
{
SCOPED_TRACE("@gemini starter pack match gets an extra header.");
auto match = CreateStarterPackMatch(u"@gemini");
// search_terms_args need to have been set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(u"search term");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers, "X-Omnibox-Gemini:search%20term");
EXPECT_EQ(match.destination_url, "https://gemini.google.com/prompt");
}
{
SCOPED_TRACE("@gemini starter pack match with url override");
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
omnibox::kStarterPackExpansion,
{{"StarterPackGeminiUrlOverride", "https://example.com/"}});
auto match = CreateStarterPackMatch(u"@gemini");
// search_terms_args need to have been set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(u"search term?");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers, "X-Omnibox-Gemini:search%20term%3F");
EXPECT_EQ(match.destination_url, "https://example.com/");
}
{
SCOPED_TRACE("@gemini starter pack with invalid non-encoded input");
auto match = CreateStarterPackMatch(u"@gemini");
// search_terms_args need to have been set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(u"search term\n");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers, "X-Omnibox-Gemini:search%20term%0A");
EXPECT_EQ(match.destination_url, "https://gemini.google.com/prompt");
}
{
SCOPED_TRACE("@gemini starter pack with url in the input");
auto match = CreateStarterPackMatch(u"@gemini");
// search_terms_args need to have been set.
match.search_terms_args = std::make_unique<TemplateURLRef::SearchTermsArgs>(
u"what is http://example.com for?");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers,
"X-Omnibox-Gemini:what%20is%20http%3A%2F%2Fexample.com%20for%3F");
EXPECT_EQ(match.destination_url, "https://gemini.google.com/prompt");
}
{
SCOPED_TRACE("@gemini starter pack with non-ascii input");
auto match = CreateStarterPackMatch(u"@gemini");
// search_terms_args need to have been set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(u"こんにちは\n");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(
match.extra_headers,
"X-Omnibox-Gemini:%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%0A");
EXPECT_EQ(match.destination_url, "https://gemini.google.com/prompt");
}
{
SCOPED_TRACE("@bookmarks starter pack match does not get an extra header.");
auto match = CreateStarterPackMatch(u"@bookmarks");
// search_terms_args need to have been set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(u"search term");
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers, "");
EXPECT_EQ(match.destination_url, "chrome://bookmarks/?q=search+term");
}
{
SCOPED_TRACE("search match does not get an extra header.");
auto match = CreateSearchMatch("search term", true, 1300);
controller_.SetMatchDestinationURL(&match);
EXPECT_EQ(match.extra_headers, "");
EXPECT_EQ(match.destination_url, "https://google.com/search?q=search+term");
}
}
TEST_F(AutocompleteControllerTest, ShouldRunProvider) {
// Disable LimitKeywordModeSuggestions flag.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureState(
omnibox_feature_configs::LimitKeywordModeSuggestions::
kLimitKeywordModeSuggestions,
false);
omnibox_feature_configs::ScopedConfigForTesting<
omnibox_feature_configs::LimitKeywordModeSuggestions>
scoped_config;
std::set<AutocompleteProvider::Type> expected_provider_types;
AutocompleteInput input(u"a", 1u, metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
controller_.input_ = input;
// Populate template URL service with starter pack entries.
std::vector<std::unique_ptr<TemplateURLData>> turls =
TemplateURLStarterPackData::GetStarterPackEngines();
for (auto& turl : turls) {
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(std::move(*turl)));
}
// Not in keyword mode, run all providers except open tab provider.
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
provider->type() != AutocompleteProvider::TYPE_OPEN_TAB)
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// In keyword mode but not starter pack, LimitkeywordModeSuggestions disabled,
// run all providers except open tab provider.
controller_.input_.set_keyword_mode_entry_method(
metrics::OmniboxEventProto_KeywordModeEntryMethod_TAB);
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
provider->type() != AutocompleteProvider::TYPE_OPEN_TAB)
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// In @tabs, run search, keyword, and open tab provider only.
controller_.input_.UpdateText(u"@tabs", 0, {});
expected_provider_types = {AutocompleteProvider::TYPE_KEYWORD,
AutocompleteProvider::TYPE_SEARCH,
AutocompleteProvider::TYPE_OPEN_TAB};
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// In @bookmarks, run search, keyword, and bookmarks only.
controller_.input_.UpdateText(u"@bookmarks", 0, {});
expected_provider_types = {AutocompleteProvider::TYPE_KEYWORD,
AutocompleteProvider::TYPE_SEARCH,
AutocompleteProvider::TYPE_BOOKMARK};
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// In @history, run search, keyword, and history providers only.
controller_.input_.UpdateText(u"@history", 0, {});
expected_provider_types = {AutocompleteProvider::TYPE_KEYWORD,
AutocompleteProvider::TYPE_SEARCH,
AutocompleteProvider::TYPE_HISTORY_QUICK,
AutocompleteProvider::TYPE_HISTORY_URL};
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
}
TEST_F(AutocompleteControllerTest,
ShouldRunProvider_LimitKeywordModeSuggestions) {
// Enable LimitKeywordModeSuggestions flag.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureState(
omnibox_feature_configs::LimitKeywordModeSuggestions::
kLimitKeywordModeSuggestions,
true);
omnibox_feature_configs::ScopedConfigForTesting<
omnibox_feature_configs::LimitKeywordModeSuggestions>
scoped_config;
std::set<AutocompleteProvider::Type> excluded_provider_types;
AutocompleteInput input(u"a", 1u, metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
controller_.input_ = input;
// Populate template URL service with an entry for drive.google.com (to test
// document provider) and a generic keyword entry.
TemplateURLData drive_turl_data;
drive_turl_data.SetShortName(u"Google Drive");
drive_turl_data.SetKeyword(u"drive.google.com");
drive_turl_data.SetURL("https://drive.google.com/search?q={searchTerms}");
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(drive_turl_data));
TemplateURLData turl_data;
turl_data.SetShortName(u"Test Keyword");
turl_data.SetKeyword(u"keyword");
turl_data.SetURL("https://google.com/search?q={searchTerms}");
controller_.template_url_service_->Add(
std::make_unique<TemplateURL>(turl_data));
// Not in keyword mode, run all providers except open tab provider.
excluded_provider_types = {AutocompleteProvider::TYPE_OPEN_TAB};
for (auto& provider : controller_.providers()) {
EXPECT_NE(controller_.ShouldRunProvider(provider.get()),
excluded_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// In keyword mode, all limit provider params on by default, limit document
// and history cluster suggestions as well.
controller_.input_.UpdateText(u"keyword", 0, {});
controller_.input_.set_keyword_mode_entry_method(
metrics::OmniboxEventProto_KeywordModeEntryMethod_TAB);
excluded_provider_types = {
AutocompleteProvider::TYPE_OPEN_TAB,
AutocompleteProvider::TYPE_HISTORY_CLUSTER_PROVIDER,
AutocompleteProvider::TYPE_DOCUMENT,
AutocompleteProvider::TYPE_ON_DEVICE_HEAD};
for (auto& provider : controller_.providers()) {
EXPECT_NE(controller_.ShouldRunProvider(provider.get()),
excluded_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// For drive.google.com, run document provider.
controller_.input_.UpdateText(u"drive.google.com", 0, {});
excluded_provider_types = {
AutocompleteProvider::TYPE_OPEN_TAB,
AutocompleteProvider::TYPE_HISTORY_CLUSTER_PROVIDER,
AutocompleteProvider::TYPE_ON_DEVICE_HEAD};
for (auto& provider : controller_.providers()) {
EXPECT_NE(controller_.ShouldRunProvider(provider.get()),
excluded_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// Turn off param to limit history cluster and document suggestions, ensure
// they're run.
scoped_feature_list.Reset();
scoped_feature_list.InitAndEnableFeatureWithParameters(
omnibox_feature_configs::LimitKeywordModeSuggestions::
kLimitKeywordModeSuggestions,
{{"LimitHistoryClusterSuggestions", "false"}});
scoped_config.Reset();
controller_.input_.UpdateText(u"keyword", 0, {});
excluded_provider_types = {AutocompleteProvider::TYPE_OPEN_TAB,
AutocompleteProvider::TYPE_DOCUMENT};
for (auto& provider : controller_.providers()) {
EXPECT_NE(controller_.ShouldRunProvider(provider.get()),
excluded_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
}
TEST_F(AutocompleteControllerTest, ShouldRunProvider_LensSearchbox) {
// Run all providers except open tab provider.
std::set<AutocompleteProvider::Type> excluded_provider_types = {
AutocompleteProvider::TYPE_OPEN_TAB};
controller_.input_ = AutocompleteInput(
u"a", 1u, metrics::OmniboxEventProto::OTHER, TestSchemeClassifier());
for (auto& provider : controller_.providers()) {
EXPECT_NE(controller_.ShouldRunProvider(provider.get()),
excluded_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
// For Lens searchboxes, run search provider only.
std::set<AutocompleteProvider::Type> expected_provider_types = {
AutocompleteProvider::TYPE_SEARCH};
controller_.input_ = AutocompleteInput(
u"a", 1u, metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX,
TestSchemeClassifier());
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
controller_.input_ = AutocompleteInput(
u"a", 1u, metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX,
TestSchemeClassifier());
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
controller_.input_ = AutocompleteInput(
u"a", 1u, metrics::OmniboxEventProto::LENS_SIDE_PANEL_SEARCHBOX,
TestSchemeClassifier());
for (auto& provider : controller_.providers()) {
EXPECT_EQ(controller_.ShouldRunProvider(provider.get()),
expected_provider_types.contains(provider->type()))
<< "Provider Type: "
<< AutocompleteProvider::TypeToString(provider->type());
}
}