blob: 03c9bfacb736526d693952ecda8ac4520c7f5551 [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/user_annotations/user_annotations_service.h"
#include <memory>
#include "base/files/scoped_temp_dir.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/protobuf_matchers.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "components/autofill/core/common/form_data.h"
#include "components/optimization_guide/core/mock_optimization_guide_model_executor.h"
#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
#include "components/user_annotations/user_annotations_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace user_annotations {
namespace {
using ::base::test::EqualsProto;
using ::testing::_;
using ::testing::An;
class UserAnnotationsServiceTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
void SetUp() override {
CHECK(temp_dir_.CreateUniqueTempDir());
service_ = std::make_unique<UserAnnotationsService>(&model_executor_,
temp_dir_.GetPath());
}
virtual void InitializeFeatureList() {
base::FieldTrialParams feature_parameters;
if (ShouldPersistAnnotations()) {
feature_parameters["persist_annotations"] = "true";
}
scoped_feature_list_.InitAndEnableFeatureWithParameters(kUserAnnotations,
feature_parameters);
}
bool ShouldPersistAnnotations() const { return GetParam(); }
UserAnnotationsService* service() { return service_.get(); }
optimization_guide::MockOptimizationGuideModelExecutor* model_executor() {
return &model_executor_;
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
testing::NiceMock<optimization_guide::MockOptimizationGuideModelExecutor>
model_executor_;
std::unique_ptr<UserAnnotationsService> service_;
};
TEST_P(UserAnnotationsServiceTest, RetrieveAllEntriesNoDB) {
base::test::TestFuture<
std::vector<optimization_guide::proto::UserAnnotationsEntry>>
test_future;
service()->RetrieveAllEntries(test_future.GetCallback());
auto entries = test_future.Take();
EXPECT_TRUE(entries.empty());
}
TEST_P(UserAnnotationsServiceTest, RetrieveAllEntriesWithInsert) {
{
base::HistogramTester histogram_tester;
optimization_guide::proto::FormsAnnotationsRequest expected_request;
expected_request.mutable_page_context()
->mutable_ax_tree_data()
->mutable_tree_data()
->set_title("title");
expected_request.mutable_page_context()->set_title("title");
optimization_guide::proto::FormData* form_proto =
expected_request.mutable_form_data();
optimization_guide::proto::FormFieldData* field_proto1 =
form_proto->add_fields();
field_proto1->set_field_label("label");
field_proto1->set_field_value("whatever");
field_proto1->set_is_visible(true);
field_proto1->set_is_focusable(true);
field_proto1->set_form_control_type(
optimization_guide::proto::FORM_CONTROL_TYPE_INPUT_TEXT);
optimization_guide::proto::FormFieldData* field_proto2 =
form_proto->add_fields();
field_proto2->set_field_name("nolabel");
field_proto2->set_field_value("value");
field_proto2->set_is_visible(true);
field_proto2->set_is_focusable(true);
field_proto2->set_form_control_type(
optimization_guide::proto::FORM_CONTROL_TYPE_INPUT_TEXT);
optimization_guide::proto::FormsAnnotationsResponse response;
optimization_guide::proto::UserAnnotationsEntry* entry1 =
response.add_entries();
entry1->set_key("label");
entry1->set_value("whatever");
optimization_guide::proto::UserAnnotationsEntry* entry2 =
response.add_entries();
entry2->set_key("nolabel");
entry2->set_value("value");
optimization_guide::proto::Any any;
any.set_type_url(response.GetTypeName());
response.SerializeToString(any.mutable_value());
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations,
EqualsProto(expected_request),
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
autofill::FormFieldData form_field_data;
form_field_data.set_label(u"label");
form_field_data.set_value(u"whatever");
autofill::FormFieldData form_field_data2;
form_field_data2.set_name(u"nolabel");
form_field_data2.set_value(u"value");
autofill::FormData form_data;
form_data.set_fields({form_field_data, form_field_data2});
optimization_guide::proto::AXTreeUpdate ax_tree;
ax_tree.mutable_tree_data()->set_title("title");
service()->AddFormSubmission(ax_tree, form_data);
base::test::TestFuture<
std::vector<optimization_guide::proto::UserAnnotationsEntry>>
test_future;
service()->RetrieveAllEntries(test_future.GetCallback());
auto entries = test_future.Take();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ(entries[0].key(), "label");
EXPECT_EQ(entries[0].value(), "whatever");
EXPECT_EQ(entries[1].key(), "nolabel");
EXPECT_EQ(entries[1].value(), "value");
histogram_tester.ExpectUniqueSample("UserAnnotations.DidAddFormSubmission",
true, 1);
}
{
base::HistogramTester histogram_tester;
optimization_guide::proto::FormsAnnotationsResponse response;
optimization_guide::proto::Any any;
any.set_type_url(response.GetTypeName());
response.SerializeToString(any.mutable_value());
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations, _,
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
autofill::FormData empty_form_data;
optimization_guide::proto::AXTreeUpdate ax_tree;
service()->AddFormSubmission(ax_tree, empty_form_data);
base::test::TestFuture<
std::vector<optimization_guide::proto::UserAnnotationsEntry>>
test_future;
service()->RetrieveAllEntries(test_future.GetCallback());
// Entries should still remain.
auto entries = test_future.Take();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ(entries[0].key(), "label");
EXPECT_EQ(entries[0].value(), "whatever");
EXPECT_EQ(entries[1].key(), "nolabel");
EXPECT_EQ(entries[1].value(), "value");
histogram_tester.ExpectUniqueSample("UserAnnotations.DidAddFormSubmission",
true, 1);
}
}
TEST_F(UserAnnotationsServiceTest, ExecuteFailed) {
base::HistogramTester histogram_tester;
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations, _,
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(
base::unexpected(
optimization_guide::OptimizationGuideModelExecutionError::
FromModelExecutionError(
optimization_guide::OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
/*log_entry=*/nullptr));
autofill::FormFieldData form_field_data;
form_field_data.set_label(u"label");
form_field_data.set_value(u"whatever");
autofill::FormFieldData form_field_data2;
form_field_data2.set_name(u"nolabel");
form_field_data2.set_value(u"value");
autofill::FormData form_data;
form_data.set_fields({form_field_data, form_field_data2});
optimization_guide::proto::AXTreeUpdate ax_tree;
service()->AddFormSubmission(ax_tree, form_data);
histogram_tester.ExpectTotalCount("UserAnnotations.DidAddFormSubmission", 0);
}
TEST_F(UserAnnotationsServiceTest, UnexpectedResponseType) {
base::HistogramTester histogram_tester;
optimization_guide::proto::Any any;
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations, _,
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
autofill::FormFieldData form_field_data;
form_field_data.set_label(u"label");
form_field_data.set_value(u"whatever");
autofill::FormFieldData form_field_data2;
form_field_data2.set_name(u"nolabel");
form_field_data2.set_value(u"value");
autofill::FormData form_data;
form_data.set_fields({form_field_data, form_field_data2});
optimization_guide::proto::AXTreeUpdate ax_tree;
service()->AddFormSubmission(ax_tree, form_data);
histogram_tester.ExpectTotalCount("UserAnnotations.DidAddFormSubmission", 0);
}
INSTANTIATE_TEST_SUITE_P(All, UserAnnotationsServiceTest, ::testing::Bool());
class UserAnnotationsServiceReplaceAnnotationsTest
: public UserAnnotationsServiceTest {
public:
UserAnnotationsServiceReplaceAnnotationsTest() {
base::FieldTrialParams feature_parameters;
feature_parameters["should_replace_annotations_for_form_submissions"] =
"true";
if (ShouldPersistAnnotations()) {
feature_parameters["persist_annotations"] = "true";
}
scoped_feature_list_.InitAndEnableFeatureWithParameters(kUserAnnotations,
feature_parameters);
}
private:
void InitializeFeatureList() override {}
};
TEST_P(UserAnnotationsServiceReplaceAnnotationsTest,
RetrieveAllEntriesWithInsertShouldReplace) {
{
optimization_guide::proto::FormsAnnotationsResponse response;
optimization_guide::proto::UserAnnotationsEntry* entry1 =
response.add_entries();
entry1->set_key("label");
entry1->set_value("whatever");
optimization_guide::proto::UserAnnotationsEntry* entry2 =
response.add_entries();
entry2->set_key("nolabel");
entry2->set_value("value");
optimization_guide::proto::Any any;
any.set_type_url(response.GetTypeName());
response.SerializeToString(any.mutable_value());
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations, _,
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
autofill::FormFieldData form_field_data;
form_field_data.set_label(u"label");
form_field_data.set_value(u"whatever");
autofill::FormFieldData form_field_data2;
form_field_data2.set_name(u"nolabel");
form_field_data2.set_value(u"value");
autofill::FormData form_data;
form_data.set_fields({form_field_data, form_field_data2});
optimization_guide::proto::AXTreeUpdate ax_tree;
service()->AddFormSubmission(ax_tree, form_data);
base::test::TestFuture<
std::vector<optimization_guide::proto::UserAnnotationsEntry>>
test_future;
service()->RetrieveAllEntries(test_future.GetCallback());
auto entries = test_future.Take();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ(entries[0].key(), "label");
EXPECT_EQ(entries[0].value(), "whatever");
EXPECT_EQ(entries[1].key(), "nolabel");
EXPECT_EQ(entries[1].value(), "value");
}
{
optimization_guide::proto::FormsAnnotationsResponse response;
optimization_guide::proto::Any any;
any.set_type_url(response.GetTypeName());
response.SerializeToString(any.mutable_value());
EXPECT_CALL(
*model_executor(),
ExecuteModel(
optimization_guide::ModelBasedCapabilityKey::kFormsAnnotations, _,
An<optimization_guide::
OptimizationGuideModelExecutionResultCallback>()))
.WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
autofill::FormData empty_form_data;
optimization_guide::proto::AXTreeUpdate ax_tree;
service()->AddFormSubmission(ax_tree, empty_form_data);
base::test::TestFuture<
std::vector<optimization_guide::proto::UserAnnotationsEntry>>
test_future;
service()->RetrieveAllEntries(test_future.GetCallback());
// Entries should be cleared since there were no fields to replace with.
auto entries = test_future.Take();
EXPECT_EQ(0u, entries.size());
}
}
INSTANTIATE_TEST_SUITE_P(All,
UserAnnotationsServiceReplaceAnnotationsTest,
::testing::Bool());
} // namespace
} // namespace user_annotations