From 898059b5a28cff5d67f5742404311a95fdda02f4 Mon Sep 17 00:00:00 2001 From: Terry Mancey Date: Wed, 27 May 2026 15:45:55 -0500 Subject: [PATCH] [ads] Unregister resource components on shutdown and opt-out (#36769) Resource components were registered with the component updater but never explicitly unregistered, causing it to keep downloading updates for country and language models even after the ads service shut down or the user opted out of notification ads. Components are now unregistered in OnPrefChanged when the service becomes ineligible, and the language component is additionally unregistered immediately when the user opts out of notification ads while the service is still running. --- components/brave_ads/browser/BUILD.gn | 5 ++ .../brave_ads/browser/ads_service_impl.cc | 59 ++++++++++++------ .../brave_ads/browser/ads_service_impl.h | 16 ++--- .../browser/ads_service_impl_unittest.cc | 60 ++++++++++++++++++- .../component_updater/resource_component.cc | 12 ++-- .../component_updater/resource_component.h | 8 ++- .../resource_component_registrar.cc | 11 ++++ .../resource_component_registrar.h | 1 + .../test/fake_component_updater_delegate.cc | 44 ++++++++++++++ .../test/fake_component_updater_delegate.h | 59 ++++++++++++++++++ .../browser/test/fake_rewards_service.cc | 5 +- .../browser/test/mock_resource_component.cc | 15 +++++ .../browser/test/mock_resource_component.h | 43 +++++++++++++ 13 files changed, 305 insertions(+), 33 deletions(-) create mode 100644 components/brave_ads/browser/test/fake_component_updater_delegate.cc create mode 100644 components/brave_ads/browser/test/fake_component_updater_delegate.h create mode 100644 components/brave_ads/browser/test/mock_resource_component.cc create mode 100644 components/brave_ads/browser/test/mock_resource_component.h diff --git a/components/brave_ads/browser/BUILD.gn b/components/brave_ads/browser/BUILD.gn index 228837fd098..8e47f6b34e8 100644 --- a/components/brave_ads/browser/BUILD.gn +++ b/components/brave_ads/browser/BUILD.gn @@ -113,6 +113,7 @@ source_set("test_support") { "//brave/components/services/bat_ads/public/interfaces", "//components/prefs", "//mojo/public/cpp/bindings", + "//testing/gmock", "//ui/base/idle", "//url", ] @@ -137,6 +138,10 @@ source_set("unit_tests") { "ads_service_impl_unittest.cc", "application_state/application_state_monitor_unittest.cc", "component_updater/resource_component_registrar_unittest.cc", + "test/fake_component_updater_delegate.cc", + "test/fake_component_updater_delegate.h", + "test/mock_resource_component.cc", + "test/mock_resource_component.h", ] deps = [ diff --git a/components/brave_ads/browser/ads_service_impl.cc b/components/brave_ads/browser/ads_service_impl.cc index d1538cfca62..7c328ad924c 100644 --- a/components/brave_ads/browser/ads_service_impl.cc +++ b/components/brave_ads/browser/ads_service_impl.cc @@ -192,8 +192,8 @@ AdsServiceImpl::AdsServiceImpl( // upgrades regardless of whether the service is eligible to start. Migrate(); - // Must be active regardless of whether the service starts so that - // pref-driven eligibility changes are always observed. + // Always registered to observe eligibility pref changes even when the + // service is not running. InitializeLocalStatePrefChangeRegistrar(); InitializePrefChangeRegistrar(); @@ -209,15 +209,7 @@ AdsServiceImpl::~AdsServiceImpl() = default; /////////////////////////////////////////////////////////////////////////////// -void AdsServiceImpl::RegisterResourceComponents() { - RegisterCountryResourceComponent(); - if (UserHasOptedInToNotificationAds()) { - // Only utilized for text classification, which requires the user to have - // joined Brave Rewards and opted into notification ads. - RegisterLanguageResourceComponent(); - } -} void AdsServiceImpl::Migrate() { // Added 10/2025. @@ -232,6 +224,16 @@ void AdsServiceImpl::Migrate() { } } +void AdsServiceImpl::RegisterResourceComponents() { + RegisterCountryResourceComponent(); + + if (UserHasOptedInToNotificationAds()) { + // Only utilized for text classification, which requires the user to have + // joined Brave Rewards and opted into notification ads. + RegisterLanguageResourceComponent(); + } +} + void AdsServiceImpl::RegisterCountryResourceComponent() { if (resource_component_) { resource_component_->RegisterCountryComponent( @@ -239,13 +241,25 @@ void AdsServiceImpl::RegisterCountryResourceComponent() { } } +void AdsServiceImpl::UnregisterCountryResourceComponent() { + resource_component_->UnregisterCountryComponent(); +} + void AdsServiceImpl::RegisterLanguageResourceComponent() { if (resource_component_) { resource_component_->RegisterLanguageComponent(CurrentLanguageCode()); } } +void AdsServiceImpl::UnregisterLanguageResourceComponent() { + resource_component_->UnregisterLanguageComponent(); +} + bool AdsServiceImpl::UserHasJoinedBraveRewards() const { + if (prefs_->IsManagedPreference(brave_rewards::prefs::kDisabledByPolicy) && + prefs_->GetBoolean(brave_rewards::prefs::kDisabledByPolicy)) { + return false; + } return prefs_->GetBoolean(brave_rewards::prefs::kEnabled); } @@ -690,6 +704,11 @@ void AdsServiceImpl::InitializePrefChangeRegistrar() { } void AdsServiceImpl::InitializeBraveRewardsPrefChangeRegistrar() { + pref_change_registrar_.Add( + brave_rewards::prefs::kDisabledByPolicy, + base::BindRepeating(&AdsServiceImpl::OnAdsPrefChanged, + base::Unretained(this))); + pref_change_registrar_.Add( brave_rewards::prefs::kEnabled, base::BindRepeating(&AdsServiceImpl::NotifyPrefChanged, @@ -749,18 +768,24 @@ void AdsServiceImpl::InitializeSearchResultAdsPrefChangeRegistrar() { void AdsServiceImpl::OnAdsPrefChanged(const std::string& path) { if (!CanStartBatAdsService()) { - // The pref change made the service ineligible to run, so tear it down. + // The pref change made the service ineligible to run, so tear it down and + // release resource components that are no longer needed. + UnregisterCountryResourceComponent(); + UnregisterLanguageResourceComponent(); return ShutdownAdsService(); } - if (bat_ads_service_remote_.is_bound() && UserHasOptedInToNotificationAds() && + if (bat_ads_service_remote_.is_bound() && path == prefs::kOptedInToNotificationAds) { - // Register language resource components if the user has joined Brave - // Rewards, opted into notification ads, and the Bat Ads Service has - // already started. - RegisterLanguageResourceComponent(); + if (UserHasOptedInToNotificationAds()) { + // Register now that the user has opted in. + RegisterLanguageResourceComponent(); - delegate_->MaybeInitNotificationHelper(); + delegate_->MaybeInitNotificationHelper(); + } else { + // Unregister now that the user has opted out. + UnregisterLanguageResourceComponent(); + } } MaybeStartBatAdsService(); diff --git a/components/brave_ads/browser/ads_service_impl.h b/components/brave_ads/browser/ads_service_impl.h index 5cbc1d416cf..6b1fa8756cc 100644 --- a/components/brave_ads/browser/ads_service_impl.h +++ b/components/brave_ads/browser/ads_service_impl.h @@ -97,11 +97,11 @@ class AdsServiceImpl : public AdsService, #endif public content_settings::Observer { public: - // `http_client`, `resource_component`, `history_service`, and - // `host_content_settings` can be `nullptr` in tests. `rewards_service` - // can be `nullptr` when Rewards is unsupported or disabled by policy. - // `policy_initialization_waiter` defers the initial ads-eligibility gate - // until the policy bundle has been merged into the managed pref store. + // `http_client`, `history_service`, and `host_content_settings` can be + // `nullptr` in tests. `rewards_service` can be `nullptr` when Rewards is + // unsupported or disabled by policy. `policy_initialization_waiter` defers + // the initial ads-eligibility gate until the policy bundle has been merged + // into the managed pref store. explicit AdsServiceImpl( std::unique_ptr delegate, PrefService& prefs, @@ -138,11 +138,13 @@ class AdsServiceImpl : public AdsService, private: friend class BraveAdsAdsServiceImplTest; + void Migrate(); + void RegisterResourceComponents(); void RegisterCountryResourceComponent(); + void UnregisterCountryResourceComponent(); void RegisterLanguageResourceComponent(); - - void Migrate(); + void UnregisterLanguageResourceComponent(); bool UserHasJoinedBraveRewards() const; bool UserHasOptedInToNewTabPageAds() const; diff --git a/components/brave_ads/browser/ads_service_impl_unittest.cc b/components/brave_ads/browser/ads_service_impl_unittest.cc index d707938ad23..1335a691747 100644 --- a/components/brave_ads/browser/ads_service_impl_unittest.cc +++ b/components/brave_ads/browser/ads_service_impl_unittest.cc @@ -22,6 +22,7 @@ #include "brave/components/brave_ads/browser/test/fake_bat_ads_service_factory.h" #include "brave/components/brave_ads/browser/test/fake_device_id.h" #include "brave/components/brave_ads/browser/test/fake_virtual_pref_provider_delegate.h" +#include "brave/components/brave_ads/browser/test/mock_resource_component.h" #include "brave/components/brave_ads/core/public/prefs/pref_names.h" #include "brave/components/brave_ads/core/public/prefs/pref_registry.h" #include "brave/components/brave_policy/policy_initialization_waiter.h" @@ -92,7 +93,7 @@ class BraveAdsAdsServiceImplTest : public testing::Test { std::make_unique(), /*channel_name=*/"foo", profile_dir_.GetPath(), std::make_unique(), std::move(device_id), - std::move(bat_ads_service_factory), /*resource_component=*/nullptr, + std::move(bat_ads_service_factory), &mock_resource_component_, /*history_service=*/nullptr, #if BUILDFLAG(ENABLE_BRAVE_REWARDS) &rewards_service_, @@ -139,6 +140,8 @@ class BraveAdsAdsServiceImplTest : public testing::Test { test::FakeRewardsService rewards_service_; #endif // BUILDFLAG(ENABLE_BRAVE_REWARDS) + testing::NiceMock mock_resource_component_; + std::unique_ptr ads_service_; }; @@ -636,4 +639,59 @@ TEST_F(BraveAdsAdsServiceImplTest, EXPECT_THAT(prefs_.GetList(prefs::kNotificationAds), testing::IsEmpty()); } +#if BUILDFLAG(ENABLE_BRAVE_REWARDS) +TEST_F(BraveAdsAdsServiceImplTest, + RegistersLanguageResourceComponentWhenUserOptsInToNotificationAds) { + // Arrange: start the service via search result ads and wait for + // initialization so `bat_ads_service_remote_` is bound. + prefs_.SetBoolean(prefs::kOptedInToSearchResultAds, true); + prefs_.SetBoolean(brave_rewards::prefs::kEnabled, true); + prefs_.SetBoolean(prefs::kOptedInToNotificationAds, false); + Startup(); + ASSERT_TRUE(base::test::RunUntil( + [&] { return bat_ads_service_factory_->initialize_count() == 1U; })); + + EXPECT_CALL(mock_resource_component_, RegisterLanguageComponent); + + // Act + prefs_.SetBoolean(prefs::kOptedInToNotificationAds, true); +} + +TEST_F(BraveAdsAdsServiceImplTest, + UnregistersLanguageResourceComponentWhenUserOptsOutOfNotificationAds) { + // Arrange: start with notification ads opted in so the language component + // is already registered; service must be running before opting out. + prefs_.SetBoolean(prefs::kOptedInToSearchResultAds, true); + prefs_.SetBoolean(brave_rewards::prefs::kEnabled, true); + prefs_.SetBoolean(prefs::kOptedInToNotificationAds, true); + Startup(); + ASSERT_TRUE(base::test::RunUntil( + [&] { return bat_ads_service_factory_->initialize_count() == 1U; })); + + EXPECT_CALL(mock_resource_component_, UnregisterLanguageComponent()); + + // Act + prefs_.SetBoolean(prefs::kOptedInToNotificationAds, false); +} +#endif // BUILDFLAG(ENABLE_BRAVE_REWARDS) + +#if !BUILDFLAG(IS_ANDROID) +TEST_F(BraveAdsAdsServiceImplTest, + UnregistersResourceComponentsWhenServiceBecomesIneligible) { + // Arrange: start the service so resource components are registered; then + // disable ads via policy to trigger unregistration. + prefs_.SetBoolean(prefs::kOptedInToSearchResultAds, true); + Startup(); + ASSERT_TRUE(base::test::RunUntil( + [&] { return bat_ads_service_factory_->initialize_count() == 1U; })); + + EXPECT_CALL(mock_resource_component_, UnregisterCountryComponent()); + EXPECT_CALL(mock_resource_component_, UnregisterLanguageComponent()); + + // Act + prefs_.SetManagedPref(brave_rewards::prefs::kDisabledByPolicy, + base::Value(true)); +} +#endif // !BUILDFLAG(IS_ANDROID) + } // namespace brave_ads diff --git a/components/brave_ads/browser/component_updater/resource_component.cc b/components/brave_ads/browser/component_updater/resource_component.cc index 4f1b5153ea6..68c47289d25 100644 --- a/components/brave_ads/browser/component_updater/resource_component.cc +++ b/components/brave_ads/browser/component_updater/resource_component.cc @@ -76,12 +76,20 @@ void ResourceComponent::RegisterCountryComponent( country_resource_component_registrar_.RegisterResourceComponent(country_code); } +void ResourceComponent::UnregisterCountryComponent() { + country_resource_component_registrar_.UnregisterResourceComponent(); +} + void ResourceComponent::RegisterLanguageComponent( const std::string& language_code) { language_resource_component_registrar_.RegisterResourceComponent( language_code); } +void ResourceComponent::UnregisterLanguageComponent() { + language_resource_component_registrar_.UnregisterResourceComponent(); +} + std::optional ResourceComponent::MaybeGetPath( const std::string& id, int version) { @@ -127,8 +135,6 @@ void ResourceComponent::OnResourceComponentUnregistered( void ResourceComponent::LoadManifestCallback(const std::string& component_id, const base::FilePath& install_dir, const std::string& json) { - VLOG(8) << "Manifest JSON: " << json; - std::optional dict = base::JSONReader::ReadDict(json, base::JSON_PARSE_RFC); if (!dict) { @@ -155,8 +161,6 @@ void ResourceComponent::LoadResourceCallback( const std::string& component_id, const base::FilePath& install_dir, const std::string& json) { - VLOG(8) << "Resource JSON: " << json; - std::optional root = base::JSONReader::ReadDict(json, base::JSON_PARSE_RFC); if (!root) { diff --git a/components/brave_ads/browser/component_updater/resource_component.h b/components/brave_ads/browser/component_updater/resource_component.h index 11a7f2c70a8..7fde2e818b7 100644 --- a/components/brave_ads/browser/component_updater/resource_component.h +++ b/components/brave_ads/browser/component_updater/resource_component.h @@ -28,7 +28,7 @@ static_assert(BUILDFLAG(ENABLE_BRAVE_ADS)); namespace brave_ads { -class ResourceComponent final : public ResourceComponentRegistrarDelegate { +class ResourceComponent : public ResourceComponentRegistrarDelegate { public: explicit ResourceComponent( brave_component_updater::BraveComponent::Delegate* delegate); @@ -41,8 +41,10 @@ class ResourceComponent final : public ResourceComponentRegistrarDelegate { void AddObserver(ResourceComponentObserver* observer); void RemoveObserver(ResourceComponentObserver* observer); - void RegisterCountryComponent(const std::string& country_code); - void RegisterLanguageComponent(const std::string& language_code); + virtual void RegisterCountryComponent(const std::string& country_code); + virtual void UnregisterCountryComponent(); + virtual void RegisterLanguageComponent(const std::string& language_code); + virtual void UnregisterLanguageComponent(); std::optional MaybeGetPath(const std::string& id, int version); diff --git a/components/brave_ads/browser/component_updater/resource_component_registrar.cc b/components/brave_ads/browser/component_updater/resource_component_registrar.cc index 85d60a5f419..23c6624329f 100644 --- a/components/brave_ads/browser/component_updater/resource_component_registrar.cc +++ b/components/brave_ads/browser/component_updater/resource_component_registrar.cc @@ -68,6 +68,17 @@ void ResourceComponentRegistrar::RegisterResourceComponent( } } +void ResourceComponentRegistrar::UnregisterResourceComponent() { + if (!resource_component_id_) { + return; + } + + Unregister(); + OnComponentUnregistered(*resource_component_id_); + last_install_dir_.reset(); + resource_component_id_.reset(); +} + /////////////////////////////////////////////////////////////////////////////// void ResourceComponentRegistrar::OnComponentReady( diff --git a/components/brave_ads/browser/component_updater/resource_component_registrar.h b/components/brave_ads/browser/component_updater/resource_component_registrar.h index 5589054f5d2..1efb6623870 100644 --- a/components/brave_ads/browser/component_updater/resource_component_registrar.h +++ b/components/brave_ads/browser/component_updater/resource_component_registrar.h @@ -32,6 +32,7 @@ class ResourceComponentRegistrar final ~ResourceComponentRegistrar() override; void RegisterResourceComponent(const std::string& resource_id); + void UnregisterResourceComponent(); private: // brave_component_updater::BraveComponent: diff --git a/components/brave_ads/browser/test/fake_component_updater_delegate.cc b/components/brave_ads/browser/test/fake_component_updater_delegate.cc new file mode 100644 index 00000000000..738b306a25a --- /dev/null +++ b/components/brave_ads/browser/test/fake_component_updater_delegate.cc @@ -0,0 +1,44 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_ads/browser/test/fake_component_updater_delegate.h" + +#include "base/strings/string_util.h" + +namespace brave_ads::test { + +FakeComponentUpdaterDelegate::FakeComponentUpdaterDelegate() = default; + +FakeComponentUpdaterDelegate::~FakeComponentUpdaterDelegate() = default; + +void FakeComponentUpdaterDelegate::Register( + const std::string& /*component_name*/, + const std::string& /*component_base64_public_key*/, + base::OnceClosure registered_callback, + brave_component_updater::BraveComponent::ReadyCallback /*ready_callback*/) { + if (registered_callback) { + std::move(registered_callback).Run(); + } +} + +bool FakeComponentUpdaterDelegate::Unregister( + const std::string& /*component_id*/) { + return true; +} + +scoped_refptr +FakeComponentUpdaterDelegate::GetTaskRunner() { + return nullptr; +} + +const std::string& FakeComponentUpdaterDelegate::locale() const { + return base::EmptyString(); +} + +PrefService* FakeComponentUpdaterDelegate::local_state() { + return nullptr; +} + +} // namespace brave_ads::test diff --git a/components/brave_ads/browser/test/fake_component_updater_delegate.h b/components/brave_ads/browser/test/fake_component_updater_delegate.h new file mode 100644 index 00000000000..902962c2883 --- /dev/null +++ b/components/brave_ads/browser/test/fake_component_updater_delegate.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_FAKE_COMPONENT_UPDATER_DELEGATE_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_FAKE_COMPONENT_UPDATER_DELEGATE_H_ + +#include + +#include "base/functional/callback.h" +#include "base/memory/scoped_refptr.h" +#include "base/task/sequenced_task_runner.h" +#include "brave/components/brave_component_updater/browser/brave_component.h" + +class PrefService; + +namespace brave_ads::test { + +// Minimal no-op `BraveComponent::Delegate` for use in unit tests that need a +// `ResourceComponent` without a real component updater. +class FakeComponentUpdaterDelegate final + : public brave_component_updater::BraveComponent::Delegate { + public: + FakeComponentUpdaterDelegate(); + + FakeComponentUpdaterDelegate(const FakeComponentUpdaterDelegate&) = delete; + FakeComponentUpdaterDelegate& operator=(const FakeComponentUpdaterDelegate&) = + delete; + + ~FakeComponentUpdaterDelegate() override; + + void Register(const std::string& /*component_name*/, + const std::string& /*component_base64_public_key*/, + base::OnceClosure registered_callback, + brave_component_updater::BraveComponent::ReadyCallback + /*ready_callback*/) override; + + bool Unregister(const std::string& /*component_id*/) override; + + void EnsureInstalled(const std::string& /*component_id*/) override {} + + void AddObserver(brave_component_updater::BraveComponent::ComponentObserver* + /*observer*/) override {} + + void RemoveObserver( + brave_component_updater::BraveComponent::ComponentObserver* + /*observer*/) override {} + + scoped_refptr GetTaskRunner() override; + + const std::string& locale() const override; + + PrefService* local_state() override; +}; + +} // namespace brave_ads::test + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_FAKE_COMPONENT_UPDATER_DELEGATE_H_ diff --git a/components/brave_ads/browser/test/fake_rewards_service.cc b/components/brave_ads/browser/test/fake_rewards_service.cc index 0ede1682597..64515dba4de 100644 --- a/components/brave_ads/browser/test/fake_rewards_service.cc +++ b/components/brave_ads/browser/test/fake_rewards_service.cc @@ -288,7 +288,10 @@ void FakeRewardsService::GetEventLogs( void FakeRewardsService::GetRewardsWallet( brave_rewards::GetRewardsWalletCallback callback) { - std::move(callback).Run(nullptr); + auto wallet = brave_rewards::mojom::RewardsWallet::New(); + wallet->payment_id = "foo"; + wallet->recovery_seed = std::vector(32, 0); + std::move(callback).Run(std::move(wallet)); } void FakeRewardsService::GetEnvironment( diff --git a/components/brave_ads/browser/test/mock_resource_component.cc b/components/brave_ads/browser/test/mock_resource_component.cc new file mode 100644 index 00000000000..b31b603102d --- /dev/null +++ b/components/brave_ads/browser/test/mock_resource_component.cc @@ -0,0 +1,15 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_ads/browser/test/mock_resource_component.h" + +namespace brave_ads::test { + +MockResourceComponent::MockResourceComponent() + : ResourceComponent(&fake_component_updater_delegate_) {} + +MockResourceComponent::~MockResourceComponent() = default; + +} // namespace brave_ads::test diff --git a/components/brave_ads/browser/test/mock_resource_component.h b/components/brave_ads/browser/test/mock_resource_component.h new file mode 100644 index 00000000000..060219b88fd --- /dev/null +++ b/components/brave_ads/browser/test/mock_resource_component.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_MOCK_RESOURCE_COMPONENT_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_MOCK_RESOURCE_COMPONENT_H_ + +#include + +#include "brave/components/brave_ads/browser/component_updater/resource_component.h" +#include "brave/components/brave_ads/browser/test/fake_component_updater_delegate.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace brave_ads::test { + +class MockResourceComponent : public ResourceComponent { + public: + MockResourceComponent(); + + MockResourceComponent(const MockResourceComponent&) = delete; + MockResourceComponent& operator=(const MockResourceComponent&) = delete; + + ~MockResourceComponent() override; + + MOCK_METHOD(void, + RegisterCountryComponent, + (const std::string& country_code), + (override)); + MOCK_METHOD(void, UnregisterCountryComponent, (), (override)); + MOCK_METHOD(void, + RegisterLanguageComponent, + (const std::string& language_code), + (override)); + MOCK_METHOD(void, UnregisterLanguageComponent, (), (override)); + + private: + FakeComponentUpdaterDelegate fake_component_updater_delegate_; +}; + +} // namespace brave_ads::test + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_TEST_MOCK_RESOURCE_COMPONENT_H_