blob: beba8232018eea2e8f2a58340f348668adc4018a [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/live_caption/live_caption_controller.h"
#include <memory>
#include <string>
#include "ash/constants/ash_features.h"
#include "base/functional/callback_forward.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/live_caption/caption_bubble_context.h"
#include "components/live_caption/caption_bubble_controller.h"
#include "components/live_caption/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/soda/constants.h"
#include "components/soda/soda_installer.h"
#include "media/mojo/mojom/speech_recognition_result.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/native_theme/caption_style.h"
#include "ui/views/layout/layout_provider.h"
namespace captions {
namespace {
class MockSodaInstaller : public speech::SodaInstaller {
public:
MockSodaInstaller() = default;
MockSodaInstaller(const MockSodaInstaller&) = delete;
MockSodaInstaller& operator=(const MockSodaInstaller&) = delete;
~MockSodaInstaller() override = default;
MOCK_METHOD(base::FilePath, GetSodaBinaryPath, (), (const, override));
MOCK_METHOD(base::FilePath,
GetLanguagePath,
(const std::string&),
(const, override));
MOCK_METHOD(void,
InstallLanguage,
(const std::string&, PrefService*),
(override));
MOCK_METHOD(void,
UninstallLanguage,
(const std::string&, PrefService*),
(override));
MOCK_METHOD(std::vector<std::string>,
GetAvailableLanguages,
(),
(const, override));
MOCK_METHOD(void, InstallSoda, (PrefService*), (override));
MOCK_METHOD(void, UninstallSoda, (PrefService*), (override));
MOCK_METHOD(void, Init, (PrefService*, PrefService*), (override));
};
class MockCaptionControllerDelgate : public CaptionControllerBase::Delegate {
public:
MockCaptionControllerDelgate() = default;
~MockCaptionControllerDelgate() override = default;
MOCK_METHOD(std::unique_ptr<CaptionBubbleController>,
CreateCaptionBubbleController,
(CaptionBubbleSettings*, const std::string&),
(override));
void AddCaptionStyleObserver(ui::NativeThemeObserver*) override {}
void RemoveCaptionStyleObserver(ui::NativeThemeObserver*) override {}
};
void RegisterStylePrefs(TestingPrefServiceSimple* pref_service) {
const std::string kCaptionsTextSize = "20%";
const std::string kCaptionsTextFont = "aerial";
const std::string kCaptionsTextColor = "255,99,71";
const std::string kCaptionsBackgroundColor = "90,255,50";
const std::string kCaptionsTextShadow = "10px";
constexpr int kCaptionsTextOpacity = 50;
constexpr int kCaptionsBackgroundOpacity = 30;
pref_service->registry()->RegisterStringPref(
prefs::kAccessibilityCaptionsTextSize, kCaptionsTextSize);
pref_service->registry()->RegisterStringPref(
prefs::kAccessibilityCaptionsTextFont, kCaptionsTextFont);
pref_service->registry()->RegisterStringPref(
prefs::kAccessibilityCaptionsTextColor, kCaptionsTextColor);
pref_service->registry()->RegisterIntegerPref(
prefs::kAccessibilityCaptionsTextOpacity, kCaptionsTextOpacity);
pref_service->registry()->RegisterStringPref(
prefs::kAccessibilityCaptionsBackgroundColor, kCaptionsBackgroundColor);
pref_service->registry()->RegisterStringPref(
prefs::kAccessibilityCaptionsTextShadow, kCaptionsTextShadow);
pref_service->registry()->RegisterIntegerPref(
prefs::kAccessibilityCaptionsBackgroundOpacity,
kCaptionsBackgroundOpacity);
}
class LiveCaptionControllerTest : public testing::Test {
public:
LiveCaptionControllerTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{ash::features::kOnDeviceSpeechRecognition},
/*disabled_features=*/{});
}
~LiveCaptionControllerTest() override = default;
void SetUp() override {
testing::Test::SetUp();
LiveCaptionController::RegisterProfilePrefs(
static_cast<user_prefs::PrefRegistrySyncable*>(
testing_pref_service_.registry()));
RegisterStylePrefs(&testing_pref_service_);
// Set up soda Installer
soda_installer_.NeverDownloadSodaForTesting();
ON_CALL(soda_installer_, Init).WillByDefault(testing::Return());
}
void SetNonEmptyFilePathForSoda() {
auto non_empty_filepath = base::FilePath("any/path/thats/not/empty");
ASSERT_FALSE(non_empty_filepath.empty());
ON_CALL(soda_installer_, GetLanguagePath)
.WillByDefault(testing::Return(non_empty_filepath));
}
void NotifySodaBinaryInstalled() {
// Calling this method without parameters represents an installation
// of the SODA binary rather than any given language pack. Listeners
// will not be notified of language pack installations until this
// method is invoked.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
}
bool HasBubbleController(LiveCaptionController& controller) {
return controller.caption_bubble_controller_for_testing() != nullptr;
}
TestingPrefServiceSimple testing_pref_service_;
MockSodaInstaller soda_installer_;
views::LayoutProvider layout_provider_;
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that profile prefs are registered correctly in
// this unit test.
TEST_F(LiveCaptionControllerTest, RegisterProfilePrefsCorrect) {
EXPECT_FALSE(
testing_pref_service_.GetBoolean(prefs::kLiveCaptionBubbleExpanded));
EXPECT_FALSE(testing_pref_service_.GetBoolean(prefs::kLiveCaptionEnabled));
EXPECT_FALSE(
testing_pref_service_.GetBoolean(prefs::kLiveCaptionMaskOffensiveWords));
EXPECT_EQ(testing_pref_service_.GetString(prefs::kLiveCaptionLanguageCode),
speech::kUsEnglishLocale);
}
// Tests that the LiveCaptionController starts live caption
// on construction given the correct preconditions are met.
TEST_F(LiveCaptionControllerTest, StartsLiveCaptionOnCtorIfEnabled) {
base::test::SingleThreadTaskEnvironment task_environment;
testing_pref_service_.SetBoolean(prefs::kLiveCaptionEnabled, true);
// This actually doesn't notify anyone as there are no listeners at this
// point, however it will update the state of the SODA Installer such that the
// default language will appear installed to the controller.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
speech::GetLanguageCode(speech::kUsEnglishLocale));
NotifySodaBinaryInstalled();
SetNonEmptyFilePathForSoda();
EXPECT_CALL(soda_installer_, GetLanguagePath);
auto mock_delegate = std::make_unique<MockCaptionControllerDelgate>();
auto* mock_delegate_ptr = mock_delegate.get();
EXPECT_CALL(*mock_delegate_ptr, CreateCaptionBubbleController).Times(1);
LiveCaptionController controller_under_test = LiveCaptionController(
&testing_pref_service_, &testing_pref_service_, speech::kUsEnglishLocale,
/*browser_context=*/nullptr, std::move(mock_delegate));
}
// Tests that the LiveCaptionController starts live caption
// after soda installation if preference was set to true.
TEST_F(LiveCaptionControllerTest,
StartsIfEnabledOnCtorAndSodaNeedsInstallation) {
base::test::SingleThreadTaskEnvironment task_environment;
testing_pref_service_.SetBoolean(prefs::kLiveCaptionEnabled, true);
SetNonEmptyFilePathForSoda();
EXPECT_CALL(soda_installer_, GetLanguagePath);
EXPECT_CALL(soda_installer_, Init);
auto mock_delegate = std::make_unique<MockCaptionControllerDelgate>();
auto* mock_delegate_ptr = mock_delegate.get();
LiveCaptionController controller_under_test = LiveCaptionController(
&testing_pref_service_, &testing_pref_service_, speech::kUsEnglishLocale,
/*browser_context=*/nullptr, std::move(mock_delegate));
EXPECT_CALL(*mock_delegate_ptr, CreateCaptionBubbleController).Times(1);
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
speech::GetLanguageCode(speech::kUsEnglishLocale));
NotifySodaBinaryInstalled();
}
} // namespace
} // namespace captions