910e62b5创建于 1月15日历史提交
// 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 "chrome/services/speech/cros_speech_recognition_recognizer_impl.h"

#include <cstdint>
#include <utility>
#include <vector>

#include "ash/constants/ash_features.h"
#include "base/functional/callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
#include "chromeos/services/machine_learning/public/mojom/soda.mojom.h"
#include "components/soda/mock_soda_installer.h"
#include "content/browser/speech/fake_speech_recognition_manager_delegate.h"
#include "media/mojo/mojom/audio_data.mojom.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "media/mojo/mojom/speech_recognition_recognition_context.h"
#include "media/mojo/mojom/speech_recognition_result.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace speech {
namespace {

class CrosSpeechRecognitionRecognizerImplTest : public testing::Test {
  void SetUp() override {}
};

TEST_F(CrosSpeechRecognitionRecognizerImplTest, EmptyLangs) {
  chromeos::machine_learning::mojom::SodaMultilangConfigPtr expected =
      chromeos::machine_learning::mojom::SodaMultilangConfig::New();
  auto actual =
      CrosSpeechRecognitionRecognizerImpl::AddLiveCaptionLanguagesToConfig(
          "en-US", base::flat_map<std::string, base::FilePath>(),
          {"en-US", "en-AU"});
  EXPECT_EQ(expected, actual);
}

TEST_F(CrosSpeechRecognitionRecognizerImplTest, FilledLangs) {
  chromeos::machine_learning::mojom::SodaMultilangConfigPtr expected =
      chromeos::machine_learning::mojom::SodaMultilangConfig::New();
  base::flat_map<std::string, base::FilePath> config_paths;
  config_paths["en-AU"] = base::FilePath::FromASCII("/fake/path/aus");
  config_paths["en-US"] = base::FilePath::FromASCII("/fake/path/usa");
  config_paths["es-US"] = base::FilePath::FromASCII("/fake/path/espusa");
  config_paths["es-ES"] = base::FilePath::FromASCII("/fake/path/espesp");
  config_paths["fr-FR"] = base::FilePath::FromASCII("/fake/path/frafra");

  auto actual =
      CrosSpeechRecognitionRecognizerImpl::AddLiveCaptionLanguagesToConfig(
          "en-US", config_paths, {"en-US", "es-US", "fr-FR"});
  expected->locale_to_language_pack_map["es-US"] = "/fake/path/espusa";
  expected->locale_to_language_pack_map["fr-FR"] = "/fake/path/frafra";
  EXPECT_EQ(expected, actual);
}

class FakeSpeechRecognitionRecognizerClient
    : public media::mojom::SpeechRecognitionRecognizerClient {
 public:
  FakeSpeechRecognitionRecognizerClient() = default;

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

  ~FakeSpeechRecognitionRecognizerClient() override = default;

  // media::mojom::SpeechRecognitionRecognizerClient:
  void OnSpeechRecognitionRecognitionEvent(
      const media::SpeechRecognitionResult& result,
      OnSpeechRecognitionRecognitionEventCallback reply) override {}
  void OnSpeechRecognitionStopped() override {}
  void OnSpeechRecognitionError() override {}
  void OnLanguageIdentificationEvent(
      media::mojom::LanguageIdentificationEventPtr event) override {}
};

class FakeMachineLearningServiceConnection
    : public chromeos::machine_learning::FakeServiceConnectionImpl {
 public:
  explicit FakeMachineLearningServiceConnection(
      base::OnceCallback<void(chromeos::machine_learning::mojom::SodaConfigPtr
                                  soda_config)> soda_config_cb)
      : soda_config_cb_(std::move(soda_config_cb)) {}

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

  ~FakeMachineLearningServiceConnection() override = default;

  void LoadSpeechRecognizer(
      chromeos::machine_learning::mojom::SodaConfigPtr soda_config,
      mojo::PendingRemote<chromeos::machine_learning::mojom::SodaClient>
          soda_client,
      mojo::PendingReceiver<chromeos::machine_learning::mojom::SodaRecognizer>
          soda_recognizer,
      chromeos::machine_learning::mojom::MachineLearningService::
          LoadSpeechRecognizerCallback callback) override {
    std::move(soda_config_cb_).Run(std::move(soda_config));
    std::move(callback).Run(
        chromeos::machine_learning::mojom::LoadModelResult::OK);
  }

 private:
  base::OnceCallback<void(
      chromeos::machine_learning::mojom::SodaConfigPtr soda_config)>
      soda_config_cb_;
};

using CrosSpeechRecognitionRecognizerImplMaskOffensiveWordsTest =
    testing::TestWithParam<bool>;

TEST_P(CrosSpeechRecognitionRecognizerImplMaskOffensiveWordsTest,
       SetMaskOffensiveWords) {
  base::test::TaskEnvironment task_environment;
  base::test::ScopedFeatureList features{
      ash::features::kOnDeviceSpeechRecognition};
  base::test::TestFuture<chromeos::machine_learning::mojom::SodaConfigPtr>
      test_future;
  FakeMachineLearningServiceConnection fake_service_connection(
      test_future.GetCallback());
  fake_service_connection.Initialize();
  chromeos::machine_learning::ServiceConnection::
      UseFakeServiceConnectionForTesting(&fake_service_connection);
  speech::MockSodaInstaller soda_installer;
  FakeSpeechRecognitionRecognizerClient recognizer_client;
  mojo::Receiver<media::mojom::SpeechRecognitionRecognizerClient> receiver_{
      &recognizer_client};
  CrosSpeechRecognitionRecognizerImpl recognizer(
      receiver_.BindNewPipeAndPassRemote(),
      media::mojom::SpeechRecognitionOptions::New(
          /*recognition_mode*/ media::mojom::SpeechRecognitionMode::kCaption,
          /*enable_formatting=*/false, /*language=*/"en-US",
          /*is_server_based=*/false,
          media::mojom::RecognizerClientType::kLiveCaption),
      base::FilePath(), base::flat_map<std::string, base::FilePath>(), "en-US",
      /*mask_offensive_words=*/GetParam());

  recognizer.AddAudio(
      media::mojom::AudioDataS16::New(/*channel_count=*/1,
                                      /*sample_rate=*/1,
                                      /*frame_count=*/1,
                                      /*data=*/std::vector<int16_t>{1}));
  chromeos::machine_learning::mojom::SodaConfigPtr config = test_future.Take();

  EXPECT_EQ(config->mask_offensive_words, GetParam());
}

INSTANTIATE_TEST_SUITE_P(
    CrosSpeechRecognitionRecognizerImplMaskOffensiveWordsTestSuite,
    CrosSpeechRecognitionRecognizerImplMaskOffensiveWordsTest,
    testing::Bool());

}  // namespace
}  // namespace speech