chromium/chrome/browser/ash/input_method/editor_feedback_unittest.cc

// 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 "chrome/browser/ash/input_method/editor_feedback.h"

#include "ash/constants/ash_features.h"
#include "base/test/gtest_util.h"
#include "base/test/protobuf_matchers.h"
#include "base/test/scoped_chromeos_version_info.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "chrome/browser/feedback/feedback_uploader_factory_chrome.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/common/channel_info.h"
#include "chrome/test/base/testing_profile.h"
#include "components/feedback/feedback_uploader.h"
#include "components/feedback/proto/extension.pb.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::input_method {
namespace {

class MockUploader : public feedback::FeedbackUploader {
 public:
  using OnReportSentCallback =
      base::RepeatingCallback<void(userfeedback::ExtensionSubmit)>;

  explicit MockUploader(OnReportSentCallback on_report_sent)
      : FeedbackUploader(
            /*is_off_the_record=*/false,
            /*state_path=*/{},
            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                &test_url_loader_factory_)),
        on_report_sent_(std::move(on_report_sent)) {}

  MockUploader(const MockUploader&) = delete;
  MockUploader& operator=(const MockUploader&) = delete;

  // feedback::FeedbackUploader:
  void QueueReport(std::unique_ptr<std::string> data,
                   bool has_email,
                   int product_id) override {
    if (data != nullptr) {
      userfeedback::ExtensionSubmit feedback_data;
      feedback_data.ParseFromString(*data);
      on_report_sent_.Run(feedback_data);
    }
  }

  base::WeakPtr<FeedbackUploader> AsWeakPtr() override {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  network::TestURLLoaderFactory test_url_loader_factory_;
  OnReportSentCallback on_report_sent_;
  base::WeakPtrFactory<MockUploader> weak_ptr_factory_{this};
};

std::unique_ptr<KeyedService> CreateMockUploader(
    MockUploader::OnReportSentCallback on_report_sent,
    content::BrowserContext* context) {
  return std::make_unique<MockUploader>(std::move(on_report_sent));
}

std::unique_ptr<TestingProfile> CreateTestingProfile(
    const std::string& email,
    MockUploader::OnReportSentCallback on_report_sent_callback) {
  std::unique_ptr<TestingProfile> profile =
      TestingProfile::Builder()
          .AddTestingFactory(
              feedback::FeedbackUploaderFactoryChrome::GetInstance(),
              base::BindRepeating(CreateMockUploader,
                                  std::move(on_report_sent_callback)))
          .Build();
  signin::IdentityManager* identity_manager =
      IdentityManagerFactory::GetForProfile(profile.get());
  signin::MakePrimaryAccountAvailable(identity_manager, email,
                                      signin::ConsentLevel::kSignin);
  return profile;
}

TEST(EditorFeedback, SendFeedbackDoesNotSendEmail) {
  content::BrowserTaskEnvironment task_environment;
  base::test::TestFuture<userfeedback::ExtensionSubmit> on_report_sent_future;
  std::unique_ptr<TestingProfile> profile = CreateTestingProfile(
      "[email protected]", on_report_sent_future.GetRepeatingCallback());

  EXPECT_TRUE(SendEditorFeedback(profile.get(), "test description"));

  auto feedback_data =
      on_report_sent_future.Get<userfeedback::ExtensionSubmit>();
  EXPECT_EQ(feedback_data.common_data().user_email(), "");
}

TEST(EditorFeedback, SendFeedbackRedactsDescription) {
  content::BrowserTaskEnvironment task_environment;
  base::test::TestFuture<userfeedback::ExtensionSubmit> on_report_sent_future;
  std::unique_ptr<TestingProfile> profile = CreateTestingProfile(
      "[email protected]", on_report_sent_future.GetRepeatingCallback());

  EXPECT_TRUE(SendEditorFeedback(
      profile.get(), "http://www.google.com [email protected] 111.222.3.4"));

  auto feedback_data =
      on_report_sent_future.Get<userfeedback::ExtensionSubmit>();
  EXPECT_EQ(feedback_data.common_data().description(),
            "(URL: 1) (email: 1) (IPv4: 1)");
}

// A change-detector test to ensure that the feedback only contains allowed
// information.
TEST(EditorFeedback, SendFeedbackOnlyContainsNecessaryInformation) {
  content::BrowserTaskEnvironment task_environment;
  base::test::ScopedChromeOSVersionInfo scoped_version_info(
      "CHROMEOS_RELEASE_VERSION=42", base::Time::Now());
  base::test::TestFuture<userfeedback::ExtensionSubmit> on_report_sent_future;
  std::unique_ptr<TestingProfile> profile = CreateTestingProfile(
      "[email protected]", on_report_sent_future.GetRepeatingCallback());

  EXPECT_TRUE(SendEditorFeedback(profile.get(), "test description"));

  auto feedback_data =
      on_report_sent_future.Get<userfeedback::ExtensionSubmit>();

  userfeedback::ExtensionSubmit expected_feedback_data;
  expected_feedback_data.set_product_id(5314436);
  expected_feedback_data.set_type_id(0);
  expected_feedback_data.mutable_common_data()->set_gaia_id(0);
  expected_feedback_data.mutable_common_data()->set_user_email("");
  expected_feedback_data.mutable_common_data()->set_description(
      "test description");
  expected_feedback_data.mutable_common_data()->set_source_description_language(
      "");
  expected_feedback_data.mutable_web_data()
      ->mutable_navigator()
      ->set_user_agent("");
  userfeedback::ProductSpecificData chrome_version_data;
  chrome_version_data.set_key("CHROME VERSION");
  chrome_version_data.set_value(
      chrome::GetVersionString(chrome::WithExtendedStable(true)));
  userfeedback::ProductSpecificData chromeos_version_data;
  chromeos_version_data.set_key("CHROMEOS_RELEASE_VERSION");
  chromeos_version_data.set_value("42");
  *expected_feedback_data.mutable_web_data()->add_product_specific_data() =
      chrome_version_data;
  *expected_feedback_data.mutable_web_data()->add_product_specific_data() =
      chromeos_version_data;

  EXPECT_THAT(feedback_data, base::test::EqualsProto(expected_feedback_data));
}

}  // namespace
}  // namespace ash::input_method