blob: a658d385d956a2f78942829d4903bff1cbff605a [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/history_embeddings_provider.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/url_row.h"
#include "components/history/core/test/history_service_test_util.h"
#include "components/history_embeddings/answerer.h"
#include "components/history_embeddings/history_embeddings_features.h"
#include "components/history_embeddings/history_embeddings_service.h"
#include "components/history_embeddings/mock_history_embeddings_service.h"
#include "components/omnibox/browser/autocomplete_enums.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/fake_autocomplete_provider_client.h"
#include "components/omnibox/browser/omnibox_triggered_feature_service.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "components/optimization_guide/proto/features/history_answer.pb.h"
#include "components/os_crypt/async/browser/os_crypt_async.h"
#include "components/os_crypt/async/browser/test_utils.h"
#include "components/passage_embeddings/passage_embeddings_test_util.h"
#include "components/search_engines/template_url.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/constants/chromeos_features.h"
#endif // BUILDFLAG(IS_CHROMEOS)
using testing::AllOf;
using testing::ElementsAre;
using testing::Field;
using enum history_embeddings::ComputeAnswerStatus;
namespace {
AutocompleteInput CreateAutocompleteInput(const std::u16string input) {
return {input, metrics::OmniboxEventProto::OTHER, TestSchemeClassifier()};
}
history_embeddings::ScoredUrlRow CreateScoredUrlRow(
float score,
const std::string& url,
const std::u16string& title) {
history_embeddings::ScoredUrlRow scored_url_row(
history_embeddings::ScoredUrl(0, 0, {}, score, 0));
scored_url_row.row = history::URLRow{GURL{url}};
scored_url_row.row.set_title(title);
scored_url_row.passages_embeddings.passages.add_passages("passage");
scored_url_row.passages_embeddings.embeddings.emplace_back(
std::vector<float>(768, 1.0f));
scored_url_row.scores.push_back(score);
return scored_url_row;
}
history_embeddings::SearchResult CreateSearchResult(
const std::string& query,
const std::u16string& title) {
history_embeddings::SearchResult result;
result.query = query;
result.scored_url_rows = {
CreateScoredUrlRow(.5, "https://url.com/", title),
};
return result;
}
TemplateURL CreateTemplateUrl() {
TemplateURLData template_url_data;
template_url_data.SetShortName(u"shortname");
template_url_data.SetKeyword(u"keyword");
template_url_data.SetURL("https://url.com");
return TemplateURL{template_url_data};
}
} // namespace
class FakeHistoryEmbeddingsProvider : public HistoryEmbeddingsProvider {
public:
using HistoryEmbeddingsProvider::HistoryEmbeddingsProvider;
using HistoryEmbeddingsProvider::OnReceivedSearchResult;
using HistoryEmbeddingsProvider::done_;
using HistoryEmbeddingsProvider::input_;
using HistoryEmbeddingsProvider::matches_;
using HistoryEmbeddingsProvider::starter_pack_engine_;
private:
~FakeHistoryEmbeddingsProvider() override = default;
};
class HistoryEmbeddingsProviderTest : public testing::Test,
public AutocompleteProviderListener {
public:
void SetUp() override {
testing::Test::SetUp();
os_crypt_ = os_crypt_async::GetTestOSCryptAsyncForTesting(
/*is_sync_for_unittests=*/true);
auto feature_parameters = history_embeddings::GetFeatureParameters();
feature_parameters.use_ml_answerer = false;
history_embeddings::SetFeatureParametersForTesting(feature_parameters);
CHECK(history_dir_.CreateUniqueTempDir());
client_ = std::make_unique<FakeAutocompleteProviderClient>();
client_->set_history_service(
history::CreateHistoryService(history_dir_.GetPath(), true));
client_->set_history_embeddings_service(
std::make_unique<testing::NiceMock<
history_embeddings::MockHistoryEmbeddingsService>>(
os_crypt_.get(), client_->GetHistoryService(),
passage_embeddings_test_env_.embedder_metadata_provider(),
passage_embeddings_test_env_.embedder()));
history_embeddings_service_ = static_cast<
testing::NiceMock<history_embeddings::MockHistoryEmbeddingsService>*>(
client_->GetHistoryEmbeddingsService());
history_embeddings_provider_ =
new FakeHistoryEmbeddingsProvider(client_.get(), this);
// When `Search()` is called, pushes a callback to `search_callbacks_` that
// can be ran to simulate `Search()` responding asyncly.
ON_CALL(*history_embeddings_service_,
Search(testing::_, testing::_, testing::_, testing::_, testing::_,
testing::_))
.WillByDefault(
[&](history_embeddings::SearchResult* previous_search_result,
std::string query, std::optional<base::Time> time_range_start,
size_t count, bool skip_answering,
history_embeddings::SearchResultCallback callback) {
search_callbacks_.push_back(base::BindOnce(
[](history_embeddings::SearchResultCallback callback,
std::string query, std::u16string response) {
std::move(callback).Run(
CreateSearchResult(query, response));
},
std::move(callback)));
return history_embeddings::SearchResult();
});
}
// AutocompleteProviderListener:
void OnProviderUpdate(bool updated_matches,
const AutocompleteProvider* provider) override {
last_update_matches_ = history_embeddings_provider_->matches_;
}
base::ScopedTempDir history_dir_;
std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
base::test::TaskEnvironment task_environment_;
passage_embeddings::TestEnvironment passage_embeddings_test_env_;
std::unique_ptr<FakeAutocompleteProviderClient> client_;
raw_ptr<testing::NiceMock<history_embeddings::MockHistoryEmbeddingsService>>
history_embeddings_service_;
scoped_refptr<FakeHistoryEmbeddingsProvider> history_embeddings_provider_;
// Callbacks created when `Search()` is called. Running a callback with
// `(query, response)` will simulate `Search()` responding with a result with
// query `query` and 1 row with title `response`.
std::vector<base::OnceCallback<void(std::string, std::u16string)>>
search_callbacks_;
// The last set of matches the provider gave the autocomplete controller.
ACMatches last_update_matches_;
};
TEST_F(HistoryEmbeddingsProviderTest, Start) {
OmniboxTriggeredFeatureService* trigger_service =
client_->GetOmniboxTriggeredFeatureService();
OmniboxTriggeredFeatureService::Feature trigger_feature =
metrics::OmniboxEventProto_Feature_HISTORY_EMBEDDINGS_FEATURE;
AutocompleteInput short_input = CreateAutocompleteInput(u"query");
AutocompleteInput sync_long_input =
CreateAutocompleteInput(u"query query query");
sync_long_input.set_omit_asynchronous_matches(true);
AutocompleteInput long_input = CreateAutocompleteInput(u"query query query");
// When the feature is disabled, should early exit.
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillOnce(testing::Return(false));
EXPECT_CALL(*history_embeddings_service_,
Search(testing::_, testing::_, testing::_, testing::_, testing::_,
testing::_))
.Times(0);
history_embeddings_provider_->Start(long_input, false);
EXPECT_FALSE(trigger_service->GetFeatureTriggeredInSession(trigger_feature));
// Short queries should be blocked.
auto feature_parameters = history_embeddings::GetFeatureParameters();
feature_parameters.search_query_minimum_word_count = 3;
history_embeddings::SetFeatureParametersForTesting(feature_parameters);
base::test::ScopedFeatureList enabled_feature;
enabled_feature.InitWithFeaturesAndParameters(
{{history_embeddings::kHistoryEmbeddings, {}},
#if BUILDFLAG(IS_CHROMEOS)
{chromeos::features::kFeatureManagementHistoryEmbedding, {{}}}
#endif // BUILDFLAG(IS_CHROMEOS)
},
/*disabled_features=*/{});
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*history_embeddings_service_,
Search(testing::_, testing::_, testing::_, testing::_, testing::_,
testing::_))
.Times(0);
history_embeddings_provider_->Start(short_input, false);
EXPECT_FALSE(trigger_service->GetFeatureTriggeredInSession(trigger_feature));
trigger_service->ResetSession();
// Sync queries should be blocked.
EXPECT_CALL(*history_embeddings_service_,
Search(testing::_, testing::_, testing::_, testing::_, testing::_,
testing::_))
.Times(0);
history_embeddings_provider_->Start(sync_long_input, false);
EXPECT_FALSE(trigger_service->GetFeatureTriggeredInSession(trigger_feature));
trigger_service->ResetSession();
// Long queries should pass.
EXPECT_CALL(*history_embeddings_service_,
Search(testing::_, "query query query",
std::optional<base::Time>{}, 3u, false, testing::_))
.Times(1);
history_embeddings_provider_->Start(long_input, false);
EXPECT_TRUE(trigger_service->GetFeatureTriggeredInSession(trigger_feature));
}
TEST_F(HistoryEmbeddingsProviderTest, Start_MultipleSequentialSearches) {
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
// Start 1st search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Check results are populated when 1st search responds.
std::move(search_callbacks_[0]).Run("1 1 1", u"1");
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"1")));
// Start 2nd search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"2 2 2"), false);
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"1")));
// Check results are populated when 2nd search responds.
std::move(search_callbacks_[1]).Run("2 2 2", u"2");
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"2")));
}
TEST_F(HistoryEmbeddingsProviderTest, Start_MultipleParallelSearches) {
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
// Start 1st search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Start 2nd search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"2 2 2"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Check results are not populated when 1st search responds.
std::move(search_callbacks_[0]).Run("1 1 1", u"1");
EXPECT_TRUE(last_update_matches_.empty());
// Check results are populated when 2nd search responds.
std::move(search_callbacks_[1]).Run("2 2 2", u"2");
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"2")));
}
TEST_F(HistoryEmbeddingsProviderTest,
Start_MultipleParallelSearchesWithSameQuery) {
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
// Start 1st search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Start 2nd search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Check results are populated when 1st search responds. Even though the
// provider usually only cares about the most recent `Search()`, since the
// input didn't change, it can use the 1st `Search()`.
std::move(search_callbacks_[0]).Run("1 1 1", u"1");
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"1")));
// Check results aren't replaced when 2nd search responds. The provider
// already reported `done_ = true` and it would break autocompletion to send
// an update after doing so.
std::move(search_callbacks_[1]).Run("1 1 1", u"2");
EXPECT_THAT(last_update_matches_,
testing::ElementsAre(
testing::Field(&AutocompleteMatch::description, u"1")));
}
TEST_F(HistoryEmbeddingsProviderTest,
Start_MultipleParallelSearchesWithIneligibleQuery) {
auto feature_parameters = history_embeddings::GetFeatureParameters();
feature_parameters.search_query_minimum_word_count = 3;
history_embeddings::SetFeatureParametersForTesting(feature_parameters);
base::test::ScopedFeatureList enabled_feature;
enabled_feature.InitWithFeaturesAndParameters(
{{history_embeddings::kHistoryEmbeddings, {}},
#if BUILDFLAG(IS_CHROMEOS)
{chromeos::features::kFeatureManagementHistoryEmbedding, {{}}}
#endif // BUILDFLAG(IS_CHROMEOS)
},
/*disabled_features=*/{});
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
// Start 1st search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Start 2nd search. It's too short.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"2 2"), false);
EXPECT_TRUE(last_update_matches_.empty());
// Ensure a stale search doesn't populate matches.
std::move(search_callbacks_[0]).Run("1 1 1", u"1");
EXPECT_TRUE(last_update_matches_.empty());
// Ensure a 2nd search wasn't made.
EXPECT_EQ(search_callbacks_.size(), 1u);
}
TEST_F(HistoryEmbeddingsProviderTest, Start_Stop_SearchCompletesAfterStop) {
EXPECT_CALL(*client_, IsHistoryEmbeddingsEnabled())
.WillRepeatedly(testing::Return(true));
// Start search.
history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
EXPECT_TRUE(last_update_matches_.empty());
history_embeddings_provider_->Stop(AutocompleteStopReason::kClobbered);
// Results returned after `Stop()` should be discarded.
std::move(search_callbacks_[0]).Run("1 1 1", u"1");
EXPECT_TRUE(last_update_matches_.empty());
}
TEST_F(HistoryEmbeddingsProviderTest, Stop) {
history_embeddings_provider_->done_ = false;
// TODO(crbug.com/364303536) Temporarily allow history embeddings provider to
// ignore `Stop()`.
history_embeddings_provider_->Stop(AutocompleteStopReason::kInactivity);
EXPECT_FALSE(history_embeddings_provider_->done_);
history_embeddings_provider_->Stop(AutocompleteStopReason::kClobbered);
EXPECT_TRUE(history_embeddings_provider_->done_);
}
TEST_F(HistoryEmbeddingsProviderTest, DeleteMatch) {
AutocompleteMatch match(history_embeddings_provider_.get(), 1000, true,
AutocompleteMatchType::HISTORY_EMBEDDINGS);
match.destination_url = GURL{"https://en.wikipedia.org/wiki/Matenadaran"};
history_embeddings_provider_->matches_.push_back(match);
history_embeddings_provider_->DeleteMatch(match);
EXPECT_TRUE(history_embeddings_provider_->matches_.empty());
}
TEST_F(HistoryEmbeddingsProviderTest,
OnReceivedSearchResult_CreatesAutocompleteMatches) {
history_embeddings::SearchResult result;
result.query = "query";
result.scored_url_rows = {
CreateScoredUrlRow(.5, "https://url.com/", u"title"),
};
history_embeddings_provider_->done_ = false;
history_embeddings_provider_->input_ = CreateAutocompleteInput(u"query");
history_embeddings_provider_->OnReceivedSearchResult(std::move(result));
EXPECT_TRUE(history_embeddings_provider_->done_);
ASSERT_EQ(history_embeddings_provider_->matches_.size(), 1u);
EXPECT_EQ(history_embeddings_provider_->matches_[0].provider.get(),
history_embeddings_provider_.get());
EXPECT_EQ(history_embeddings_provider_->matches_[0].relevance, 500);
EXPECT_EQ(history_embeddings_provider_->matches_[0].deletable, true);
EXPECT_EQ(history_embeddings_provider_->matches_[0].type,
AutocompleteMatchType::HISTORY_EMBEDDINGS);
EXPECT_EQ(history_embeddings_provider_->matches_[0].destination_url.spec(),
"https://url.com/");
EXPECT_EQ(history_embeddings_provider_->matches_[0].description, u"title");
EXPECT_EQ(history_embeddings_provider_->matches_[0].contents,
u"https://url.com/");
EXPECT_EQ(history_embeddings_provider_->matches_[0].keyword, u"");
EXPECT_TRUE(PageTransitionCoreTypeIs(
history_embeddings_provider_->matches_[0].transition,
ui::PAGE_TRANSITION_TYPED));
}
TEST_F(HistoryEmbeddingsProviderTest,
OnReceivedSearchResult_CreatesScopedAutocompleteMatches) {
// Verifies autocomplete match is created correctly when the user is in
// keyword mode.
TemplateURL template_url = CreateTemplateUrl();
history_embeddings_provider_->starter_pack_engine_ = &template_url;
history_embeddings::SearchResult result;
result.query = "query";
result.scored_url_rows = {
CreateScoredUrlRow(.5, "https://url.com/", u"title"),
};
history_embeddings_provider_->done_ = false;
history_embeddings_provider_->input_ = CreateAutocompleteInput(u"query");
history_embeddings_provider_->OnReceivedSearchResult(std::move(result));
EXPECT_TRUE(history_embeddings_provider_->done_);
ASSERT_EQ(history_embeddings_provider_->matches_.size(), 1u);
EXPECT_EQ(history_embeddings_provider_->matches_[0].provider.get(),
history_embeddings_provider_.get());
EXPECT_EQ(history_embeddings_provider_->matches_[0].relevance, 500);
EXPECT_EQ(history_embeddings_provider_->matches_[0].deletable, true);
EXPECT_EQ(history_embeddings_provider_->matches_[0].type,
AutocompleteMatchType::HISTORY_EMBEDDINGS);
EXPECT_EQ(history_embeddings_provider_->matches_[0].destination_url.spec(),
"https://url.com/");
EXPECT_EQ(history_embeddings_provider_->matches_[0].description, u"title");
EXPECT_EQ(history_embeddings_provider_->matches_[0].contents,
u"https://url.com/");
EXPECT_EQ(history_embeddings_provider_->matches_[0].keyword, u"keyword");
EXPECT_TRUE(PageTransitionCoreTypeIs(
history_embeddings_provider_->matches_[0].transition,
ui::PAGE_TRANSITION_KEYWORD));
}
TEST_F(HistoryEmbeddingsProviderTest,
OnReceivedSearchResult_CreatesScopedAutocompleteAnswerMatches) {
auto feature_parameters = history_embeddings::GetFeatureParameters();
feature_parameters.answers_in_omnibox_scoped = true;
feature_parameters.trim_after_host_in_results = true;
history_embeddings::SetFeatureParametersForTesting(feature_parameters);
base::test::ScopedFeatureList enabled_feature;
enabled_feature.InitWithFeaturesAndParameters(
{{history_embeddings::kHistoryEmbeddings, {}},
#if BUILDFLAG(IS_CHROMEOS)
{chromeos::features::kFeatureManagementHistoryEmbedding, {{}}}
#endif // BUILDFLAG(IS_CHROMEOS)
},
/*disabled_features=*/{});
TemplateURL template_url = CreateTemplateUrl();
history_embeddings_provider_->starter_pack_engine_ = &template_url;
// 1st response with status `kUnspecified`.
history_embeddings::SearchResult result;
result.query = "query";
result.scored_url_rows = {
CreateScoredUrlRow(.75, "https://url1.com/", u"title"),
CreateScoredUrlRow(.50, "https://url2.com/path?key=value", u"title"),
CreateScoredUrlRow(.25, "https://url3.com/", u"title"),
};
base::Time time;
EXPECT_TRUE(base::Time::FromLocalExploded(
{.year = 2025, .month = 3, .day_of_month = 23}, &time));
result.scored_url_rows[1].row.set_last_visit(time);
history_embeddings_provider_->done_ = false;
history_embeddings_provider_->input_ = CreateAutocompleteInput(u"query");
history_embeddings_provider_->input_.set_keyword_mode_entry_method(
metrics::OmniboxEventProto_KeywordModeEntryMethod_TAB);
history_embeddings_provider_->OnReceivedSearchResult(result.Clone());
// Set up expected matches.
auto expected_match_1 =
AllOf(Field(&AutocompleteMatch::relevance, 750),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS),
Field(&AutocompleteMatch::destination_url, "https://url1.com/"));
auto expected_match_2 =
AllOf(Field(&AutocompleteMatch::relevance, 500),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS),
Field(&AutocompleteMatch::destination_url,
"https://url2.com/path?key=value"));
auto expected_match_3 =
AllOf(Field(&AutocompleteMatch::relevance, 250),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS),
Field(&AutocompleteMatch::destination_url, "https://url3.com/"));
// Expect only non-answer matches.
EXPECT_FALSE(history_embeddings_provider_->done_);
EXPECT_THAT(
history_embeddings_provider_->matches_,
ElementsAre(expected_match_1, expected_match_2, expected_match_3));
// 2nd response with status `kLoading`.
auto result_loading = result.Clone();
result_loading.answerer_result.status = kLoading;
result_loading.answerer_result.query = "query";
history_embeddings_provider_->OnReceivedSearchResult(
std::move(result_loading));
// Expect the same non-answer matches as well as a 'loading' answer match,
// scored 1 less than the top match.
EXPECT_FALSE(history_embeddings_provider_->done_);
EXPECT_THAT(
history_embeddings_provider_->matches_,
ElementsAre(
expected_match_1, expected_match_2, expected_match_3,
AllOf(Field(&AutocompleteMatch::relevance, 749),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS_ANSWER),
Field(&AutocompleteMatch::destination_url, ""),
Field(&AutocompleteMatch::history_embeddings_answer_header_text,
u"Searching for a summary\x2026"),
Field(&AutocompleteMatch::description, u""))));
// 3rd response with status `kSuccess`.
auto result_success = result.Clone();
result_success.answerer_result.status = kSuccess;
result_success.answerer_result.query = "query";
result_success.answerer_result.answer.set_score(1);
result_success.answerer_result.answer.set_text("answer text");
result_success.answerer_result.url = "https://url2.com/path?key=value";
history_embeddings_provider_->OnReceivedSearchResult(
std::move(result_success));
// Expect the same non-answer matches as well as a flushed out answer match,
// scored 1 less than the corresponding match.
EXPECT_TRUE(history_embeddings_provider_->done_);
EXPECT_THAT(
history_embeddings_provider_->matches_,
ElementsAre(
expected_match_1, expected_match_2, expected_match_3,
AllOf(Field(&AutocompleteMatch::relevance, 499),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS_ANSWER),
Field(&AutocompleteMatch::destination_url,
"chrome://history/?q=query"),
Field(&AutocompleteMatch::history_embeddings_answer_header_text,
u"Summary"),
Field(&AutocompleteMatch::description, u"answer text"),
Field(&AutocompleteMatch::contents,
u"url2.com • Visited Mar 23, 2025"))));
// Test error cases.
std::u16string execution_error =
u"Something went wrong. Please try again later.";
std::u16string non_temporary_error = u"Sorry, I can't help you with that.";
for (const auto& [status, expected_answer_text] : std::vector<
std::pair<history_embeddings::ComputeAnswerStatus, std::u16string>>{
{kUnanswerable, u""},
{kModelUnavailable, u""},
{kExecutionFailure, execution_error},
{kExecutionCancelled, u""},
{kFiltered, u""}}) {
SCOPED_TRACE("Testing status: " +
base::NumberToString(static_cast<int>(status)));
auto result_error = result.Clone();
result_error.answerer_result.status = status;
history_embeddings_provider_->done_ = false;
history_embeddings_provider_->OnReceivedSearchResult(
std::move(result_error));
EXPECT_TRUE(history_embeddings_provider_->done_);
if (expected_answer_text.empty()) {
EXPECT_THAT(
history_embeddings_provider_->matches_,
ElementsAre(expected_match_1, expected_match_2, expected_match_3));
} else {
EXPECT_THAT(
history_embeddings_provider_->matches_,
ElementsAre(
expected_match_1, expected_match_2, expected_match_3,
AllOf(
Field(&AutocompleteMatch::relevance, 749),
Field(&AutocompleteMatch::type,
AutocompleteMatchType::HISTORY_EMBEDDINGS_ANSWER),
Field(&AutocompleteMatch::destination_url, ""),
Field(
&AutocompleteMatch::history_embeddings_answer_header_text,
u"Summary"),
Field(&AutocompleteMatch::description, expected_answer_text),
Field(&AutocompleteMatch::contents, u""))));
}
}
}