diff --git a/browser/brave_ads/BUILD.gn b/browser/brave_ads/BUILD.gn index 33852e692e30..88c2ebee2dae 100644 --- a/browser/brave_ads/BUILD.gn +++ b/browser/brave_ads/BUILD.gn @@ -61,7 +61,9 @@ source_set("impl") { "//brave/browser/ui/brave_ads", "//brave/components/brave_adaptive_captcha", "//brave/components/brave_ads/browser/application_state", + "//brave/components/l10n/common", "//brave/components/p3a_utils", + "//brave/components/skus/browser", "//chrome/browser:browser_process", "//chrome/browser:browser_public_dependencies", "//chrome/browser:primitives", diff --git a/browser/brave_ads/ads_service_delegate.cc b/browser/brave_ads/ads_service_delegate.cc index 9090b4d17905..38a9c0b21064 100644 --- a/browser/brave_ads/ads_service_delegate.cc +++ b/browser/brave_ads/ads_service_delegate.cc @@ -5,14 +5,19 @@ #include "brave/browser/brave_ads/ads_service_delegate.h" +#include #include +#include "base/json/json_reader.h" #include "base/strings/utf_string_conversions.h" #include "base/version_info/channel.h" +#include "base/version_info/version_info.h" #include "brave/browser/brave_ads/ad_units/notification_ad/notification_ad_platform_bridge.h" #include "brave/browser/brave_ads/application_state/notification_helper/notification_helper.h" #include "brave/browser/ui/brave_ads/notification_ad.h" #include "brave/components/brave_adaptive_captcha/brave_adaptive_captcha_service.h" +#include "brave/components/l10n/common/locale_util.h" +#include "brave/components/skus/browser/pref_names.h" #include "build/build_config.h" #include "chrome/browser/notifications/notification_display_service.h" #include "chrome/browser/profiles/profile.h" @@ -32,6 +37,71 @@ namespace brave_ads { +namespace { + +constexpr char kSkuEnvironmentPrefix[] = "skus:"; +constexpr char kSkuOrdersKey[] = "orders"; +constexpr char kSkuOrderLocationKey[] = "location"; +constexpr char kSkuOrderCreatedAtKey[] = "created_at"; +constexpr char kSkuOrderExpiresAtKey[] = "expires_at"; +constexpr char kSkuOrderLastPaidAtKey[] = "last_paid_at"; +constexpr char kSkuOrderStatusKey[] = "status"; + +std::string StripSkuEnvironmentPrefix(const std::string& environment) { + const size_t pos = environment.find(':'); + return environment.substr(pos + 1); +} + +std::string NormalizeSkuStatus(const std::string& status) { + return status == "cancelled" ? "canceled" : status; +} + +base::Value::Dict ParseSkuOrder(const base::Value::Dict& dict) { + base::Value::Dict order; + + if (const auto* const created_at = dict.FindString(kSkuOrderCreatedAtKey)) { + order.Set(kSkuOrderCreatedAtKey, *created_at); + } + + if (const auto* const expires_at = dict.FindString(kSkuOrderExpiresAtKey)) { + order.Set(kSkuOrderExpiresAtKey, *expires_at); + } + + if (const auto* const last_paid_at = + dict.FindString(kSkuOrderLastPaidAtKey)) { + order.Set(kSkuOrderLastPaidAtKey, *last_paid_at); + } + + if (const auto* const status = dict.FindString(kSkuOrderStatusKey)) { + const std::string normalized_status = NormalizeSkuStatus(*status); + order.Set(kSkuOrderStatusKey, normalized_status); + } + + return order; +} + +base::Value::Dict ParseSkuOrders(const base::Value::Dict& dict) { + base::Value::Dict orders; + + for (const auto [/*id*/ _, value] : dict) { + const base::Value::Dict* const order = value.GetIfDict(); + if (!order) { + continue; + } + + const std::string* const location = order->FindString(kSkuOrderLocationKey); + if (!location) { + continue; + } + + orders.Set(*location, ParseSkuOrder(*order)); + } + + return orders; +} + +} // namespace + AdsServiceDelegate::AdsServiceDelegate( Profile* profile, PrefService* local_state, @@ -53,6 +123,49 @@ AdsServiceDelegate::AdsServiceDelegate( AdsServiceDelegate::~AdsServiceDelegate() {} +std::string AdsServiceDelegate::GetDefaultSearchEngineName() { + const auto template_url_data = + TemplateURLPrepopulateData::GetPrepopulatedFallbackSearch( + profile_->GetPrefs(), &search_engine_choice_service_); + + const std::u16string& default_search_engine_name = + template_url_data ? template_url_data->short_name() : u""; + return base::UTF16ToUTF8(default_search_engine_name); +} + +base::Value::Dict AdsServiceDelegate::GetSkus() const { + base::Value::Dict skus; + + if (!local_state_->FindPreference(skus::prefs::kSkusState)) { + // No SKUs in local state. + return skus; + } + + const base::Value::Dict& skus_state = + local_state_->GetDict(skus::prefs::kSkusState); + for (const auto [environment, value] : skus_state) { + if (!environment.starts_with(kSkuEnvironmentPrefix)) { + continue; + } + + // Parse the SKUs JSON because it is stored as a string in local state. + const std::optional sku_state = + base::JSONReader::ReadDict(value.GetString()); + if (!sku_state) { + continue; + } + + const base::Value::Dict* const orders = sku_state->FindDict(kSkuOrdersKey); + if (!orders) { + continue; + } + + skus.Set(StripSkuEnvironmentPrefix(environment), ParseSkuOrders(*orders)); + } + + return skus; +} + void AdsServiceDelegate::OpenNewTabWithUrl(const GURL& url) { #if BUILDFLAG(IS_ANDROID) // ServiceTabLauncher can currently only launch new tabs @@ -145,18 +258,25 @@ bool AdsServiceDelegate::IsFullScreenMode() { #endif base::Value::Dict AdsServiceDelegate::GetVirtualPrefs() { - const auto template_url_data = - TemplateURLPrepopulateData::GetPrepopulatedFallbackSearch( - profile_->GetPrefs(), &search_engine_choice_service_); - if (!template_url_data) { - return {}; - } - return base::Value::Dict() - .Set("[virtual]:default_search_engine.name", - base::UTF16ToUTF8(template_url_data->short_name())) - .Set("[virtual]:build_channel.name", - version_info::GetChannelString(chrome::GetChannel())); + .Set("[virtual]:browser", + base::Value::Dict() + .Set("build_channel", + version_info::GetChannelString(chrome::GetChannel())) + .Set("version", version_info::GetVersionNumber())) + .Set("[virtual]:operating_system", + base::Value::Dict() + .Set("locale", + base::Value::Dict() + .Set("language", + brave_l10n::GetDefaultISOLanguageCodeString()) + .Set("region", + brave_l10n::GetDefaultISOCountryCodeString())) + .Set("name", version_info::GetOSType())) + .Set( + "[virtual]:search_engine", + base::Value::Dict().Set("default_name", GetDefaultSearchEngineName())) + .Set("[virtual]:skus", GetSkus()); } } // namespace brave_ads diff --git a/browser/brave_ads/ads_service_delegate.h b/browser/brave_ads/ads_service_delegate.h index 959820c3060c..c2240d2e5ae0 100644 --- a/browser/brave_ads/ads_service_delegate.h +++ b/browser/brave_ads/ads_service_delegate.h @@ -47,6 +47,10 @@ class AdsServiceDelegate : public AdsService::Delegate { ~AdsServiceDelegate() override; + std::string GetDefaultSearchEngineName(); + + base::Value::Dict GetSkus() const; + // AdsService::Delegate implementation void InitNotificationHelper() override; bool CanShowSystemNotificationsWhileBrowserIsBackgrounded() override; diff --git a/components/brave_ads/core/internal/BUILD.gn b/components/brave_ads/core/internal/BUILD.gn index 29fd853de9a4..834b766d4e15 100644 --- a/components/brave_ads/core/internal/BUILD.gn +++ b/components/brave_ads/core/internal/BUILD.gn @@ -617,8 +617,8 @@ static_library("internal") { "diagnostics/entries/diagnostic_entry_interface.h", "diagnostics/entries/last_unidle_time_diagnostic_entry.cc", "diagnostics/entries/last_unidle_time_diagnostic_entry.h", - "diagnostics/entries/last_unidle_time_diagnostic_util.cc", - "diagnostics/entries/last_unidle_time_diagnostic_util.h", + "diagnostics/entries/last_unidle_time_diagnostic_entry_util.cc", + "diagnostics/entries/last_unidle_time_diagnostic_entry_util.h", "diagnostics/entries/locale_diagnostic_entry.cc", "diagnostics/entries/locale_diagnostic_entry.h", "diagnostics/entries/opted_into_brave_news_ads_diagnostic_entry.cc", @@ -834,9 +834,6 @@ static_library("internal") { "serving/inline_content_ad_serving_feature.h", "serving/new_tab_page_ad_serving.cc", "serving/new_tab_page_ad_serving.h", - "serving/new_tab_page_ad_serving_condition_matcher_util.cc", - "serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc", - "serving/new_tab_page_ad_serving_condition_matcher_util_internal.h", "serving/new_tab_page_ad_serving_delegate.h", "serving/new_tab_page_ad_serving_feature.cc", "serving/new_tab_page_ad_serving_feature.h", @@ -941,6 +938,23 @@ static_library("internal") { "serving/prediction/model_based/weight/creative_notification_ad_model_based_predictor_weights_builder.cc", "serving/prediction/model_based/weight/creative_notification_ad_model_based_predictor_weights_builder.h", "serving/prediction/model_based/weight/segment/creative_ad_model_based_predictor_segment_weight_info.h", + "serving/targeting/condition_matcher/condition_matcher_util.cc", + "serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.cc", + "serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h", + "serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc", + "serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h", + "serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.cc", + "serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h", + "serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.cc", + "serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h", + "serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc", + "serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h", + "serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc", + "serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h", + "serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc", + "serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h", + "serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc", + "serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h", "serving/targeting/segments/top_segments.cc", "serving/targeting/segments/top_segments.h", "serving/targeting/segments/top_user_model_segments.cc", diff --git a/components/brave_ads/core/internal/account/account_unittest.cc b/components/brave_ads/core/internal/account/account_unittest.cc index 7fcd8a11b124..949d31764d11 100644 --- a/components/brave_ads/core/internal/account/account_unittest.cc +++ b/components/brave_ads/core/internal/account/account_unittest.cc @@ -217,7 +217,7 @@ TEST_F(BraveAdsAccountTest, DepositForCash) { EXPECT_CALL(account_observer_mock_, OnDidProcessDeposit(/*transaction=*/::testing::FieldsAre( /*id*/ ::testing::_, /*created_at*/ test::Now(), - test::kCreativeInstanceId, test::kSegment, /*value*/ 1.0, + test::kCreativeInstanceId, test::kSegment, test::kValue, mojom::AdType::kNotificationAd, mojom::ConfirmationType::kViewedImpression, /*reconciled_at*/ std::nullopt))); @@ -251,7 +251,7 @@ TEST_F(BraveAdsAccountTest, DepositForCashWithUserData) { EXPECT_CALL(account_observer_mock_, OnDidProcessDeposit(/*transaction=*/::testing::FieldsAre( /*id*/ ::testing::_, /*created_at*/ test::Now(), - test::kCreativeInstanceId, test::kSegment, /*value*/ 1.0, + test::kCreativeInstanceId, test::kSegment, test::kValue, mojom::AdType::kNotificationAd, mojom::ConfirmationType::kViewedImpression, /*reconciled_at*/ std::nullopt))); @@ -330,7 +330,7 @@ TEST_F(BraveAdsAccountTest, AddTransactionWhenDepositingCashForRewardsUser) { EXPECT_CALL(account_observer_mock_, OnDidProcessDeposit(/*transaction=*/::testing::FieldsAre( /*id*/ ::testing::_, /*created_at*/ test::Now(), - test::kCreativeInstanceId, test::kSegment, /*value*/ 1.0, + test::kCreativeInstanceId, test::kSegment, test::kValue, mojom::AdType::kNotificationAd, mojom::ConfirmationType::kViewedImpression, /*reconciled_at*/ std::nullopt))); diff --git a/components/brave_ads/core/internal/account/confirmations/reward/reward_credential_json_writer.cc b/components/brave_ads/core/internal/account/confirmations/reward/reward_credential_json_writer.cc index 600d880eedea..16c4783b3af8 100644 --- a/components/brave_ads/core/internal/account/confirmations/reward/reward_credential_json_writer.cc +++ b/components/brave_ads/core/internal/account/confirmations/reward/reward_credential_json_writer.cc @@ -23,7 +23,7 @@ std::optional WriteRewardCredential( } const std::optional credential = - cbr::BuildCredential(reward->unblinded_token, payload); + cbr::MaybeBuildCredential(reward->unblinded_token, payload); if (!credential) { return std::nullopt; } diff --git a/components/brave_ads/core/internal/account/deposits/cash_deposit_test.cc b/components/brave_ads/core/internal/account/deposits/cash_deposit_test.cc index 0e63e051ae16..2331922b8214 100644 --- a/components/brave_ads/core/internal/account/deposits/cash_deposit_test.cc +++ b/components/brave_ads/core/internal/account/deposits/cash_deposit_test.cc @@ -35,7 +35,7 @@ TEST_F(BraveAdsCashDepositIntegrationTest, GetValue) { // Act & Assert base::MockCallback callback; - EXPECT_CALL(callback, Run(/*success=*/true, /*value=*/1.0)); + EXPECT_CALL(callback, Run(/*success=*/true, test::kValue)); deposit.GetValue(test::kCreativeInstanceId, callback.Get()); } diff --git a/components/brave_ads/core/internal/account/utility/redeem_payment_tokens/url_request_builders/redeem_payment_tokens_url_request_builder.cc b/components/brave_ads/core/internal/account/utility/redeem_payment_tokens/url_request_builders/redeem_payment_tokens_url_request_builder.cc index dbdcc4df2b50..72cc5d4a17bc 100644 --- a/components/brave_ads/core/internal/account/utility/redeem_payment_tokens/url_request_builders/redeem_payment_tokens_url_request_builder.cc +++ b/components/brave_ads/core/internal/account/utility/redeem_payment_tokens/url_request_builders/redeem_payment_tokens_url_request_builder.cc @@ -94,7 +94,7 @@ base::Value::List RedeemPaymentTokensUrlRequestBuilder::BuildPaymentRequestDTO( for (const auto& payment_token : payment_tokens_) { std::optional credential = - cbr::BuildCredential(payment_token.unblinded_token, payload); + cbr::MaybeBuildCredential(payment_token.unblinded_token, payload); if (!credential) { continue; } diff --git a/components/brave_ads/core/internal/ad_units/ad_test_constants.h b/components/brave_ads/core/internal/ad_units/ad_test_constants.h index 6bd542e98041..6003a9323eb1 100644 --- a/components/brave_ads/core/internal/ad_units/ad_test_constants.h +++ b/components/brave_ads/core/internal/ad_units/ad_test_constants.h @@ -51,7 +51,7 @@ inline constexpr char kTargetUrl[] = "https://brave.com"; inline constexpr char kTitle[] = "Test Ad Title"; inline constexpr char kDescription[] = "Test Ad Description"; -inline constexpr int kValue = 1.0; +inline constexpr double kValue = 1.0; } // namespace brave_ads::test diff --git a/components/brave_ads/core/internal/catalog/campaign/creative_set/creative/new_tab_page_ad/catalog_new_tab_page_ad_wallpaper_info.h b/components/brave_ads/core/internal/catalog/campaign/creative_set/creative/new_tab_page_ad/catalog_new_tab_page_ad_wallpaper_info.h index 731efb54ecdf..8d499359a69e 100644 --- a/components/brave_ads/core/internal/catalog/campaign/creative_set/creative/new_tab_page_ad/catalog_new_tab_page_ad_wallpaper_info.h +++ b/components/brave_ads/core/internal/catalog/campaign/creative_set/creative/new_tab_page_ad/catalog_new_tab_page_ad_wallpaper_info.h @@ -9,7 +9,7 @@ #include #include "brave/components/brave_ads/core/internal/catalog/campaign/creative_set/creative/new_tab_page_ad/catalog_new_tab_page_ad_wallpaper_focal_point_info.h" -#include "brave/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "url/gurl.h" namespace brave_ads { @@ -31,7 +31,7 @@ struct CatalogNewTabPageAdWallpaperInfo final { GURL image_url; CatalogNewTabPageAdWallpaperFocalPointInfo focal_point; - NewTabPageAdConditionMatchers condition_matchers; + ConditionMatcherMap condition_matchers; }; using CatalogNewTabPageAdWallpaperList = diff --git a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.cc b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.cc index ea41ea160e37..af78fac33616 100644 --- a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.cc +++ b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.cc @@ -20,7 +20,7 @@ constexpr char kTokenPreimageKey[] = "t"; } // namespace -std::optional BuildCredential( +std::optional MaybeBuildCredential( const UnblindedToken& unblinded_token, const std::string& payload) { CHECK(unblinded_token.has_value()); diff --git a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.h b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.h index d79fe0f1628f..57bf21c426d8 100644 --- a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.h +++ b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder.h @@ -15,7 +15,7 @@ namespace brave_ads::cbr { class UnblindedToken; -std::optional BuildCredential( +std::optional MaybeBuildCredential( const UnblindedToken& unblinded_token, const std::string& payload); diff --git a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder_unittest.cc b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder_unittest.cc index c2432361311b..8d8540b00926 100644 --- a/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder_unittest.cc +++ b/components/brave_ads/core/internal/common/challenge_bypass_ristretto/credential_builder_unittest.cc @@ -16,7 +16,7 @@ namespace brave_ads { TEST(BraveAdsChallengeBypassRistrettoTest, BuildCredential) { // Act - const std::optional credential = cbr::BuildCredential( + const std::optional credential = cbr::MaybeBuildCredential( cbr::UnblindedToken(cbr::test::kUnblindedTokenBase64), /*payload=*/"definition: the weight of a payload"); ASSERT_TRUE(credential); diff --git a/components/brave_ads/core/internal/common/test/README.md b/components/brave_ads/core/internal/common/test/README.md index c54b25940396..3d27773dafd9 100644 --- a/components/brave_ads/core/internal/common/test/README.md +++ b/components/brave_ads/core/internal/common/test/README.md @@ -39,7 +39,7 @@ See [command_line_switch_test_util.h](command_line_switch_test_util.h). ## Mocking Prefs -Preferences **MUST** be registered in [pref_registry_test_util.cc](./pref_registry_test_util.cc). To set a preference and notify listeners, use `SetProfile*Pref` or `SetLocalState*Pref`. To set a preference without notifying listeners, use `SetProfile*PrefValue` or `SetLocalState*PrefValue`. See [profile_pref_value_test_util.h](./profile_pref_value_test_util.h) and [local_state_pref_value_test_util.h](./local_state_pref_value_test_util.h). +You can register preferences in [pref_registry_test_util.cc](./pref_registry_test_util.cc) or by using `RegisterProfile*Pref` or `RegisterLocalState*Pref`. To set a preference and notify listeners, use `SetProfile*Pref` or `SetLocalState*Pref`. To set a preference without notifying listeners, use `SetProfile*PrefValue` or `SetLocalState*PrefValue`. See [profile_pref_value_test_util.h](./profile_pref_value_test_util.h) and [local_state_pref_value_test_util.h](./local_state_pref_value_test_util.h). ## Mocking Files diff --git a/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.cc b/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.cc similarity index 96% rename from components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.cc rename to components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.cc index 935718b0523b..2f24f5c77954 100644 --- a/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.cc +++ b/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.cc @@ -3,7 +3,7 @@ * 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/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h" +#include "brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h" #include diff --git a/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h b/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h similarity index 85% rename from components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h rename to components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h index 92b207c3d01f..4e179b887642 100644 --- a/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h +++ b/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h @@ -3,8 +3,8 @@ * 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_CORE_INTERNAL_COMMON_TEST_INTERNAL_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ -#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_INTERNAL_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_H_ #include #include @@ -40,4 +40,4 @@ void RegisterLocalStateTimeDeltaPref(const std::string& path, } // namespace brave_ads::test -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_INTERNAL_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_LOCAL_STATE_PREF_REGISTRY_TEST_UTIL_H_ diff --git a/components/brave_ads/core/internal/common/test/pref_registry_test_util.cc b/components/brave_ads/core/internal/common/test/pref_registry_test_util.cc index 658deec1624a..233a861c6e1e 100644 --- a/components/brave_ads/core/internal/common/test/pref_registry_test_util.cc +++ b/components/brave_ads/core/internal/common/test/pref_registry_test_util.cc @@ -6,8 +6,8 @@ #include "brave/components/brave_ads/core/internal/common/test/pref_registry_test_util.h" #include "base/time/time.h" -#include "brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h" -#include "brave/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h" +#include "brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h" +#include "brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h" #include "brave/components/brave_ads/core/internal/common/test/time_test_util.h" #include "brave/components/brave_ads/core/public/prefs/pref_names.h" #include "brave/components/brave_news/common/pref_names.h" diff --git a/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.cc b/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.cc similarity index 96% rename from components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.cc rename to components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.cc index 65d93bdca45b..fcc7bc22bff8 100644 --- a/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.cc +++ b/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.cc @@ -3,7 +3,7 @@ * 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/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h" +#include "brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h" #include diff --git a/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h b/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h similarity index 84% rename from components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h rename to components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h index 818d7b84ae3d..dff71b6ca195 100644 --- a/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h +++ b/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h @@ -3,8 +3,8 @@ * 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_CORE_INTERNAL_COMMON_TEST_INTERNAL_PROFILE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ -#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_INTERNAL_PROFILE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_PROFILE_PREF_REGISTRY_TEST_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_PROFILE_PREF_REGISTRY_TEST_UTIL_H_ #include #include @@ -36,4 +36,4 @@ void RegisterProfileTimeDeltaPref(const std::string& path, } // namespace brave_ads::test -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_INTERNAL_PROFILE_PREF_REGISTRY_TEST_UTIL_INTERNAL_H_ +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_COMMON_TEST_PROFILE_PREF_REGISTRY_TEST_UTIL_H_ diff --git a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpaper_info.h b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpaper_info.h index 5629b645bb16..e95be43dbe1b 100644 --- a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpaper_info.h +++ b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpaper_info.h @@ -9,7 +9,7 @@ #include #include "brave/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpaper_focal_point_info.h" -#include "brave/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "url/gurl.h" namespace brave_ads { @@ -32,7 +32,7 @@ struct CreativeNewTabPageAdWallpaperInfo final { GURL image_url; CreativeNewTabPageAdWallpaperFocalPointInfo focal_point; - NewTabPageAdConditionMatchers condition_matchers; + ConditionMatcherMap condition_matchers; }; using CreativeNewTabPageAdWallpaperList = diff --git a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpapers_database_table.cc b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpapers_database_table.cc index 410f75000a7e..ad2435dfd6aa 100644 --- a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpapers_database_table.cc +++ b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ad_wallpapers_database_table.cc @@ -26,7 +26,7 @@ namespace { constexpr char kTableName[] = "creative_new_tab_page_ad_wallpapers"; std::string ConditionMatchersToString( - const NewTabPageAdConditionMatchers& condition_matchers) { + const ConditionMatcherMap& condition_matchers) { std::vector condition_matchers_as_string; condition_matchers_as_string.reserve(condition_matchers.size()); diff --git a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ads_database_table.cc b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ads_database_table.cc index b8427def1aef..46b5e33900c2 100644 --- a/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ads_database_table.cc +++ b/components/brave_ads/core/internal/creatives/new_tab_page_ads/creative_new_tab_page_ads_database_table.cc @@ -27,7 +27,7 @@ #include "brave/components/brave_ads/core/internal/segments/segment_util.h" #include "brave/components/brave_ads/core/mojom/brave_ads.mojom.h" #include "brave/components/brave_ads/core/public/ads_client/ads_client.h" -#include "brave/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "url/gurl.h" namespace brave_ads::database::table { @@ -41,13 +41,12 @@ constexpr char kTableName[] = "creative_new_tab_page_ads"; constexpr int kDefaultBatchSize = 50; -NewTabPageAdConditionMatchers StringToConditionMatchers( - const std::string& value) { +ConditionMatcherMap StringToConditionMatchers(const std::string& value) { const std::vector condition_matchers_as_string = base::SplitString(value, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - NewTabPageAdConditionMatchers condition_matchers; + ConditionMatcherMap condition_matchers; for (const auto& condition_matcher_as_string : condition_matchers_as_string) { const std::vector condition_matcher = base::SplitString(condition_matcher_as_string, "|", diff --git a/components/brave_ads/core/internal/diagnostics/diagnostic_manager_unittest.cc b/components/brave_ads/core/internal/diagnostics/diagnostic_manager_unittest.cc index a8bb90f7cc2f..9a687490a7d4 100644 --- a/components/brave_ads/core/internal/diagnostics/diagnostic_manager_unittest.cc +++ b/components/brave_ads/core/internal/diagnostics/diagnostic_manager_unittest.cc @@ -13,7 +13,7 @@ #include "brave/components/brave_ads/core/internal/common/test/mock_test_util.h" #include "brave/components/brave_ads/core/internal/common/test/test_base.h" #include "brave/components/brave_ads/core/internal/common/test/time_test_util.h" -#include "brave/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h" +#include "brave/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h" #include "brave/components/brave_ads/core/public/ads_callback.h" #include "brave/components/l10n/common/test/scoped_default_locale.h" diff --git a/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.cc b/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.cc similarity index 95% rename from components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.cc rename to components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.cc index 6b538c91ad9a..47a097e0ebf8 100644 --- a/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.cc +++ b/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.cc @@ -3,7 +3,7 @@ * 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/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h" +#include "brave/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h" #include #include diff --git a/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h b/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h similarity index 81% rename from components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h rename to components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h index fa30044f4e26..85c3f8dbff18 100644 --- a/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h +++ b/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h @@ -3,8 +3,8 @@ * 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_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_UTIL_H_ -#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_UTIL_H_ +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_ENTRY_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_ENTRY_UTIL_H_ namespace base { class Time; @@ -16,4 +16,4 @@ void SetLastUnIdleTimeDiagnosticEntry(base::Time last_unidle_at); } // namespace brave_ads -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_UTIL_H_ +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_DIAGNOSTICS_ENTRIES_LAST_UNIDLE_TIME_DIAGNOSTIC_ENTRY_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/README.md b/components/brave_ads/core/internal/serving/README.md index 29d96f545095..4b5c7046320c 100644 --- a/components/brave_ads/core/internal/serving/README.md +++ b/components/brave_ads/core/internal/serving/README.md @@ -1,6 +1,9 @@ # Serving -When an ad is served, it is delivered to a specific audience that has been targeted based on demographics, interests, or behaviors. This is done using local machine learning with the browser profile to only place ads in optimal conditions. Ads are anonymously matched to opportunities, and users become partners instead of targets. +When an ad is served, it is delivered to a specific audience that has been +targeted based on demographics, interests, or behaviors. This is done using +local on device matching with the browser profile to only place ads in optimal +conditions. Ads are anonymously matched to opportunities. | format | description | |---|---| diff --git a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util.cc b/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util.cc deleted file mode 100644 index 7e34fa4e11f4..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util.cc +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 2024 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/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" - -#include - -#include "base/ranges/algorithm.h" -#include "brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h" -#include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" - -namespace brave_ads { - -bool MatchConditions(const PrefProviderInterface* const pref_provider, - const NewTabPageAdConditionMatchers& condition_matchers) { - CHECK(pref_provider); - - return base::ranges::all_of( - condition_matchers, [pref_provider](const auto& condition_matcher) { - const auto& [pref_path, condition] = condition_matcher; - - const std::optional value = - MaybeGetPrefValueAsString(pref_provider, pref_path); - if (!value) { - // Do not serve the ad due to an unknown preference path or - // unsupported value type. - return false; - } - - return MatchOperator(*value, condition) || - MatchPattern(*value, condition) || MatchRegex(*value, condition); - }); -} - -} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h b/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h deleted file mode 100644 index 3f240628c83a..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_INTERNAL_H_ -#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_INTERNAL_H_ - -#include -#include -#include - -#include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" - -namespace base { -class TimeDelta; -} // namespace base - -namespace brave_ads { - -// Converts a base::Value to a string representation if possible. Returns -// `std::nullopt` for unsupported types. -std::optional ToString(const base::Value& value); - -// Parses the number of days from a condition string. Returns `std::nullopt` if -// the condition is malformed. -std::optional ParseDays(std::string_view condition); - -// Checks if a timestamp is a Unix epoch timestamp. -bool IsUnixEpochTimestamp(int64_t timestamp); - -// Converts a Windows epoch timestamp to a Unix epoch timestamp. -int64_t WindowsToUnixEpoch(int64_t timestamp); - -// Calculates the time delta since the Unix or Windows epoch for a given -// timestamp. -base::TimeDelta TimeDeltaSinceEpoch(int64_t timestamp); - -// Matches a value against a condition using operators. Supports equality, -// greater than, and greater than or equal operators. -bool MatchOperator(std::string_view value, std::string_view condition); - -// Matches a value against a regular expression condition. -bool MatchRegex(std::string_view value, std::string_view condition); - -// Matches a value against a pattern condition. -bool MatchPattern(std::string_view value, std::string_view condition); - -// Get the pref value from the provider for the given path. Handles nested -// dictionaries, lists, and dot-separated keys. `base::Value::Find*ByDottedPath` -// is not used because path keys can contain dots. Returns `std::nullopt` if the -// path is malformed or unknown. Path keys should be separated by `|`. Example -// `list|1` would return the second element of a list. -std::optional MaybeGetPrefValue( - const PrefProviderInterface* pref_provider, - const std::string& pref_path); - -// Get the pref value as a string from the provider for the given path. Handles -// nested dictionaries, lists, and dot-separated keys. -// `base::Value::Find*ByDottedPath` is not used because path keys can contain -// dots. Returns `std::nullopt` if the path is malformed or unknown. Path keys -// should be separated by `|`. Example `list|1` would return the second element -// of a list. -std::optional MaybeGetPrefValueAsString( - const PrefProviderInterface* pref_provider, - const std::string& pref_path); - -} // namespace brave_ads - -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_INTERNAL_H_ diff --git a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc b/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc deleted file mode 100644 index 2baa5bed966f..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc +++ /dev/null @@ -1,723 +0,0 @@ -/* Copyright (c) 2024 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/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h" - -#include "base/containers/span.h" -#include "base/values.h" -#include "brave/components/brave_ads/core/internal/ads_client/ads_client_pref_provider.h" -#include "brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h" -#include "brave/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h" -#include "brave/components/brave_ads/core/internal/common/test/test_base.h" -#include "brave/components/brave_ads/core/internal/common/test/time_test_util.h" - -// npm run test -- brave_unit_tests --filter=BraveAds* - -namespace brave_ads { - -class BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest - : public test::TestBase { - protected: - const AdsClientPrefProvider pref_provider_; -}; - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotConvertNoneValueTypeToString) { - // Act & Assert - EXPECT_FALSE(ToString(base::Value())); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - BooleanValueTypeToString) { - // Act & Assert - EXPECT_EQ("0", ToString(base::Value(false))); - EXPECT_EQ("1", ToString(base::Value(true))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - IntegerValueTypeToString) { - // Act & Assert - EXPECT_EQ("123", ToString(base::Value(123))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoubleValueTypeToString) { - // Act & Assert - EXPECT_EQ("1.23", ToString(base::Value(1.23))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - StringValueTypeToString) { - // Act & Assert - EXPECT_EQ("123", ToString(base::Value("123"))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotConvertDictValueTypeToString) { - // Act & Assert - EXPECT_FALSE(ToString(base::Value(base::Value::Dict().Set("foo", "bar")))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotConvertListValueTypeToString) { - // Act & Assert - EXPECT_FALSE(ToString(base::Value(base::Value::List().Append("foo")))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotConvertBinaryValueTypeToString) { - // Arrange - const base::span binary({0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, - 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, - 0x21}); - - // Act & Assert - EXPECT_FALSE(ToString(base::Value(binary))); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotParseNegativeDays) { - // Act & Assert - EXPECT_FALSE(ParseDays("[=]:-1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - ParseDayZero) { - // Act & Assert - EXPECT_EQ(0, ParseDays("[=]:0")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, ParseDays) { - // Act & Assert - EXPECT_EQ(7, ParseDays("[=]:7")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotParseNonIntegerDays) { - // Act & Assert - EXPECT_FALSE(ParseDays("[=]:1.5")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotParseMalformedDays) { - // Act & Assert - EXPECT_FALSE(ParseDays("[=]: 7 ")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotParseInvalidDays) { - // Act & Assert - EXPECT_FALSE(ParseDays("[=]:seven")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - IsUnixEpochTimestamp) { - // Act & Assert - EXPECT_TRUE(IsUnixEpochTimestamp(0)); - EXPECT_TRUE(IsUnixEpochTimestamp(2147483647)); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - IsNotUnixEpochTimestamp) { - // Act & Assert - EXPECT_FALSE(IsUnixEpochTimestamp(-1)); - EXPECT_FALSE(IsUnixEpochTimestamp(2147483648)); - EXPECT_FALSE(IsUnixEpochTimestamp( - 13372214400000000 /* 1st October 2024 00:00:00 UTC */)); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - TimeDeltaSinceUnixEpoch) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_EQ(base::Days(2), - TimeDeltaSinceEpoch(1727740800 /*1st October 2024 00:00:00 UTC*/)); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - TimeDeltaSinceWindowsEpoch) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_EQ(base::Days(2), - TimeDeltaSinceEpoch( - 13372214400000000 /*1st October 2024 00:00:00.000 UTC*/)); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchIfNotAnOperator) { - // Act & Assert - EXPECT_FALSE(MatchOperator( - "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "baz")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchMalformedOperator) { - // Act & Assert - EXPECT_FALSE(MatchOperator( - "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "[=]: 7 ")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[=]:2")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[=]:3")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchGreaterThanOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[>]:1")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchGreaterThanOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[>]:2")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchGreaterThanOrEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≥]:1")); // Event occurred 2 days ago. - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≥]:2")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchGreaterThanOrEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≥]:3")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchLessThanOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[<]:3")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchLessThanOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[<]:2")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchLessThanOrEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≤]:3")); // Event occurred 2 days ago. - EXPECT_TRUE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≤]:2")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchLessThanOrEqualOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE( - MatchOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, - "[≤]:1")); // Event occurred 2 days ago. -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchUnknownOperator) { - // Arrange - AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); - - // Act & Assert - EXPECT_FALSE(MatchOperator( - "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "[_]:2")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchRegex) { - // Act & Assert - EXPECT_TRUE(MatchRegex("foo.baz.bar", "(foo|bar)")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchEmptyRegex) { - // Act & Assert - EXPECT_TRUE(MatchRegex("", "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchRegex) { - // Act & Assert - EXPECT_FALSE(MatchRegex("foo.baz.bar", "(waldo|fred)")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchMalformedRegex) { - // Act & Assert - EXPECT_FALSE(MatchRegex("foo.baz.bar", "* ?")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchPattern) { - // Act & Assert - EXPECT_TRUE(MatchPattern("foo.baz.bar", "foo?baz.*")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchEmptyPattern) { - // Act & Assert - EXPECT_TRUE(MatchPattern("", "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - MatchEscapedPattern) { - // Act & Assert - EXPECT_TRUE(MatchRegex(R"(*.bar.?)", R"(\*.bar.\?)")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotMatchPattern) { - // Act & Assert - EXPECT_FALSE(MatchRegex("foo.baz.bar", "bar.*.foo")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetVirtualPrefValue) { - // Arrange - ON_CALL(ads_client_mock_, GetVirtualPrefs).WillByDefault([]() { - return base::Value::Dict().Set("[virtual]:matrix", /*room*/ 303); - }); - - // Act & Assert - EXPECT_EQ(base::Value(/*room*/ 303), - MaybeGetPrefValue(&pref_provider_, "[virtual]:matrix")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetUnknownVirtualPrefValue) { - // Arrange - ON_CALL(ads_client_mock_, GetVirtualPrefs).WillByDefault([]() { - return base::Value::Dict().Set("[virtual]:inverse.matrices", /*room*/ 101); - }); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "[virtual]:matrix")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetBooleanProfilePrefValue) { - // Arrange - test::RegisterProfileBooleanPref("boolean", true); - - // Act & Assert - EXPECT_EQ(base::Value(true), MaybeGetPrefValue(&pref_provider_, "boolean")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetIntegerProfilePrefValue) { - // Arrange - test::RegisterProfileIntegerPref("integer", 123); - - // Act & Assert - EXPECT_EQ(base::Value(123), MaybeGetPrefValue(&pref_provider_, "integer")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDoubleProfilePrefValue) { - // Arrange - test::RegisterProfileDoublePref("double", 1.23); - - // Act & Assert - EXPECT_EQ(base::Value(1.23), MaybeGetPrefValue(&pref_provider_, "double")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetStringProfilePrefValue) { - // Arrange - test::RegisterProfileStringPref("string", "foo"); - - // Act & Assert - EXPECT_EQ(base::Value("foo"), MaybeGetPrefValue(&pref_provider_, "string")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDictProfilePrefValue) { - // Arrange - test::RegisterProfileDictPref("dict", base::Value::Dict().Set("foo", "bar")); - - // Act & Assert - EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "dict|foo")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedDictProfilePrefValue) { - // Arrange - test::RegisterProfileDictPref( - "dict", - base::Value::Dict().Set("foo", base::Value::Dict().Set("bar", "qux"))); - - // Act & Assert - EXPECT_EQ(base::Value("qux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo|bar")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDeeplyNestedDictProfilePrefValue) { - // Arrange - test::RegisterProfileDictPref( - "dict", base::Value::Dict().Set( - "foo", base::Value::List().Append("bar").Append( - base::Value::Dict().Set("baz", "qux")))); - - // Act & Assert - EXPECT_EQ(base::Value("qux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo|1|baz")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedDictProfilePrefValueWithDotSeparatedPathComponents) { - // Arrange - test::RegisterProfileDictPref( - "dict", base::Value::Dict().Set( - "foo.bar", base::Value::Dict().Set("baz.qux", "quux"))); - - // Act & Assert - EXPECT_EQ(base::Value("quux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz.qux")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetMalformedDictProfilePrefValue) { - test::RegisterProfileDictPref("dict", - base::Value::Dict().Set("foo.bar", "baz")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetListProfilePrefValue) { - // Arrange - test::RegisterProfileListPref( - "list", base::Value::List().Append("foo").Append("bar")); - - // Act & Assert - EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedListProfilePrefValue) { - // Arrange - test::RegisterProfileListPref( - "list", base::Value::List().Append( - base::Value::List().Append("foo").Append("bar"))); - - // Act & Assert - EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|0|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDeeplyNestedListProfilePrefValue) { - // Arrange - test::RegisterProfileListPref( - "list", base::Value::List().Append(base::Value::Dict().Set( - "foo", base::Value::List().Append("bar").Append("baz")))); - - // Act & Assert - EXPECT_EQ(base::Value("baz"), - MaybeGetPrefValue(&pref_provider_, "list|0|foo|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedListProfilePrefValueWithDotSeparatedPathComponents) { - // Arrange - test::RegisterProfileListPref( - "list", base::Value::List().Append(base::Value::Dict().Set( - "foo.bar", - base::Value::List().Append("baz.qux").Append("quux.corge")))); - - // Act & Assert - EXPECT_EQ(base::Value("quux.corge"), - MaybeGetPrefValue(&pref_provider_, "list|0|foo.bar|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetListProfilePrefValueWithOutOfBoundsListIndicies) { - // Arrange - test::RegisterProfileListPref( - "list", base::Value::List().Append("foo").Append("bar")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|-1")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|2")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetMalformedListProfilePrefValue) { - test::RegisterProfileListPref("list", base::Value::List().Append("foo")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|0|foo")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|bar")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetBooleanLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateBooleanPref("boolean", true); - - // Act & Assert - EXPECT_EQ(base::Value(true), MaybeGetPrefValue(&pref_provider_, "boolean")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetIntegerLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateIntegerPref("integer", 123); - - // Act & Assert - EXPECT_EQ(base::Value(123), MaybeGetPrefValue(&pref_provider_, "integer")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDoubleLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateDoublePref("double", 1.23); - - // Act & Assert - EXPECT_EQ(base::Value(1.23), MaybeGetPrefValue(&pref_provider_, "double")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetStringLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateStringPref("string", "foo"); - - // Act & Assert - EXPECT_EQ(base::Value("foo"), MaybeGetPrefValue(&pref_provider_, "string")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDictLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateDictPref("dict", - base::Value::Dict().Set("foo.bar", "baz")); - - // Act & Assert - EXPECT_EQ(base::Value("baz"), - MaybeGetPrefValue(&pref_provider_, "dict|foo.bar")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedDictLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateDictPref( - "dict", - base::Value::Dict().Set("foo", base::Value::Dict().Set("bar", "qux"))); - - // Act & Assert - EXPECT_EQ(base::Value("qux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo|bar")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDeeplyNestedDictLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateDictPref( - "dict", base::Value::Dict().Set( - "foo", base::Value::List().Append("bar").Append( - base::Value::Dict().Set("baz", "qux")))); - - // Act & Assert - EXPECT_EQ(base::Value("qux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo|1|baz")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedDictLocalStatePrefValueWithDotSeparatedPathComponents) { - // Arrange - test::RegisterLocalStateDictPref( - "dict", base::Value::Dict().Set( - "foo.bar", base::Value::Dict().Set("baz.qux", "quux"))); - - // Act & Assert - EXPECT_EQ(base::Value("quux"), - MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz.qux")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetMalformedDictLocalStatePrefValue) { - test::RegisterLocalStateDictPref("dict", - base::Value::Dict().Set("foo.bar", "baz")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetListLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateListPref( - "list", base::Value::List().Append("foo").Append("bar")); - - // Act & Assert - EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedListLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateListPref( - "list", base::Value::List().Append( - base::Value::List().Append("foo").Append("bar"))); - - // Act & Assert - EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|0|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetDeeplyNestedListLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateListPref( - "list", base::Value::List().Append(base::Value::Dict().Set( - "foo", base::Value::List().Append("bar").Append("baz")))); - - // Act & Assert - EXPECT_EQ(base::Value("baz"), - MaybeGetPrefValue(&pref_provider_, "list|0|foo|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetNestedListLocalStatePrefValueWithDotSeparatedPathComponents) { - // Arrange - test::RegisterLocalStateListPref( - "list", base::Value::List().Append(base::Value::Dict().Set( - "foo.bar", - base::Value::List().Append("baz.qux").Append("quux.corge")))); - - // Act & Assert - EXPECT_EQ(base::Value("quux.corge"), - MaybeGetPrefValue(&pref_provider_, "list|0|foo.bar|1")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetMalformedListLocalStatePrefValue) { - // Arrange - test::RegisterLocalStateListPref("list", base::Value::List().Append("foo")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|0|foo")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|bar")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetListLocalStatePrefValueWithOutOfBoundsListIndicies) { - // Arrange - test::RegisterLocalStateListPref( - "list", base::Value::List().Append("foo").Append("bar")); - - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|-1")); - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|2")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetUnknownPrefValue) { - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "foo.bar")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetProfilePrefValueAsString) { - // Arrange - test::RegisterProfileBooleanPref("boolean", true); - - // Act & Assert - EXPECT_EQ("1", MaybeGetPrefValueAsString(&pref_provider_, "boolean")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - GetLocalStatePrefValueAsString) { - // Arrange - test::RegisterLocalStateBooleanPref("boolean", true); - - // Act & Assert - EXPECT_EQ("1", MaybeGetPrefValueAsString(&pref_provider_, "boolean")); -} - -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotGetUnknownPrefValueAsString) { - // Act & Assert - EXPECT_FALSE(MaybeGetPrefValueAsString(&pref_provider_, "foo.bar")); - EXPECT_FALSE(MaybeGetPrefValueAsString(&pref_provider_, "")); -} - -} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/README.md b/components/brave_ads/core/internal/serving/targeting/README.md index d75d60dbd3dd..b5f93de07828 100644 --- a/components/brave_ads/core/internal/serving/targeting/README.md +++ b/components/brave_ads/core/internal/serving/targeting/README.md @@ -1,5 +1,5 @@ # Targeting -Targeting helps us place ads in front of users most likely to purchase. +Targeting helps us place ads in front of users most likely to engage. Please add to it! diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/README.md b/components/brave_ads/core/internal/serving/targeting/condition_matcher/README.md new file mode 100644 index 000000000000..9d83782223ca --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/README.md @@ -0,0 +1,6 @@ +# Condition Matcher + +Condition matchers assess if specific conditions or criteria are fulfilled for +ad targeting. + +Please add to it! diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util.cc new file mode 100644 index 000000000000..57d7316b9ad2 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util.cc @@ -0,0 +1,74 @@ +/* Copyright (c) 2024 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/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" + +#include +#include + +#include "base/ranges/algorithm.h" +#include "base/strings/pattern.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h" +#include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" + +namespace brave_ads { + +namespace { + +constexpr char kOperatorPrefixPattern[] = "[?]:*"; +constexpr char kNotOperatorPrefix[] = "[!]:"; + +std::string MaybeStripOperatorPrefix(const std::string& pref_path) { + if (!base::MatchPattern(pref_path, kOperatorPrefixPattern)) { + // Not an operator. + return pref_path; + } + + const size_t pos = pref_path.find(':'); + return pref_path.substr(pos + 1); +} + +bool HasNotOperator(const std::string_view pref_path) { + return pref_path.starts_with(kNotOperatorPrefix); +} + +bool MatchCondition(const std::string_view value, + const std::string_view condition) { + return MatchEpochOperator(value, condition) || + MatchNumericalOperator(value, condition) || + MatchPattern(value, condition) || MatchRegex(value, condition); +} + +} // namespace + +bool MatchConditions(const PrefProviderInterface* const pref_provider, + const ConditionMatcherMap& condition_matchers) { + CHECK(pref_provider); + + return base::ranges::all_of( + condition_matchers, [pref_provider](const auto& condition_matcher) { + const auto& [pref_path, condition] = condition_matcher; + + // If `has_not_operator` is `true`, the condition will match only if the + // pref path does not exist. + const bool has_not_operator = HasNotOperator(pref_path); + + const std::string stripped_pref_path = + MaybeStripOperatorPrefix(pref_path); + if (const std::optional value = + MaybeGetPrefValueAsString(pref_provider, stripped_pref_path)) { + return !has_not_operator && MatchCondition(*value, condition); + } + + // Unknown pref path. + return has_not_operator; + }); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util_unittest.cc similarity index 55% rename from components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_unittest.cc rename to components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util_unittest.cc index bbcc29d98d18..febe0880016d 100644 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_unittest.cc +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util_unittest.cc @@ -3,7 +3,7 @@ * 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/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "base/time/time.h" #include "brave/components/brave_ads/core/internal/ads_client/ads_client_pref_provider.h" @@ -15,10 +15,9 @@ namespace brave_ads { -class BraveAdsNewTabPageAdServingConditionMatcherUtilTest - : public test::TestBase { +class BraveAdsConditionMatcherUtilTest : public test::TestBase { public: - BraveAdsNewTabPageAdServingConditionMatcherUtilTest() : pref_provider_() { + BraveAdsConditionMatcherUtilTest() : pref_provider_() { // We need to set the clock to 00:00:00 UTC here to ensure the pref registry // in `common/test/pref_registry_test_util.cc` is initialized correctly with // deterministic default values. @@ -29,16 +28,15 @@ class BraveAdsNewTabPageAdServingConditionMatcherUtilTest const AdsClientPrefProvider pref_provider_; }; -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchEmptyConditions) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchEmptyConditions) { // Act & Assert EXPECT_TRUE(MatchConditions(&pref_provider_, /*condition_matchers=*/{})); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, +TEST_F(BraveAdsConditionMatcherUtilTest, MatchConditionsIfAllConditionsAreTrue) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "AUTO"}, {prefs::kOptedInToNotificationAds, "1"}}; @@ -46,11 +44,10 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchEqualOperatorCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchEpochEqualOperatorCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { - {prefs::kServeAdAt, "[=]:7"}}; + const ConditionMatcherMap condition_matchers = { + {prefs::kServeAdAt, "[T=]:7"}}; AdvanceClockBy(base::Days(7)); @@ -58,11 +55,11 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchEqualOperatorCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, + DoNotMatchEpochEqualOperatorCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { - {prefs::kServeAdAt, "[=]:7"}}; + const ConditionMatcherMap condition_matchers = { + {prefs::kServeAdAt, "[T=]:7"}}; AdvanceClockBy(base::Days(7) - base::Milliseconds(1)); @@ -70,10 +67,30 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchPatternCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchNumericalEqualOperatorCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { + {prefs::kMaximumNotificationAdsPerHour, + "[R=]:-1"}}; // Value is "-1" in the pref. + + // Act & Assert + EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); +} + +TEST_F(BraveAdsConditionMatcherUtilTest, + DoNotMatchNumericalEqualOperatorCondition) { + // Arrange + const ConditionMatcherMap condition_matchers = { + {prefs::kMaximumNotificationAdsPerHour, + "[R=]:1"}}; // Value is "-1" in the pref. + + // Act & Assert + EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); +} + +TEST_F(BraveAdsConditionMatcherUtilTest, MatchPatternCondition) { + // Arrange + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "?UT*"}}; // Value is "AUTO" in the pref. @@ -81,10 +98,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchPatternCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchPatternCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "?FOO*"}}; // Value is "AUTO" in the pref. @@ -92,10 +108,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchRegexCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchRegexCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "^AU"}}; // Value is "AUTO" in the pref. @@ -103,10 +118,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchRegexCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchRegexCondition) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "^FOO"}}; // Value is "AUTO" in the pref. @@ -114,21 +128,30 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchConditionsIfPrefPathWasNotFound) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = {{"foo.bar", "baz"}}; + const ConditionMatcherMap condition_matchers = {{"foo.bar", "baz"}}; // Act & Assert EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, +TEST_F(BraveAdsConditionMatcherUtilTest, + MatchConditionsWithNotOperatorWhenPrefPathNotFound) { + // Arrange + const ConditionMatcherMap condition_matchers = {{"[!]:foo.bar", "baz"}}; + + // Act & Assert + EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); +} + +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchConditionsIfAllConditionsAreFalse) { // Arrange - const NewTabPageAdConditionMatchers condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kOptedInToNotificationAds, "0"}, // Value is "1" in the pref. - {prefs::kServeAdAt, "[>]:7"}}; // 5 days ago in the pref. + {prefs::kServeAdAt, "[T>]:7"}}; // 5 days ago in the pref. AdvanceClockBy(base::Days(5)); diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/README.md b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/README.md new file mode 100644 index 000000000000..bef0b236189d --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/README.md @@ -0,0 +1,5 @@ +# Condition Matchers + +Condition matchers evaluate whether specific criteria for ad targeting are met by comparing them against operators and patterns. + +Please add to it! diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.cc new file mode 100644 index 000000000000..a2c00bb25a32 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.cc @@ -0,0 +1,75 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h" + +#include + +#include "base/logging.h" +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h" + +namespace brave_ads { + +namespace { + +constexpr char kEqualOperatorConditionMatcherPrefix[] = "[T=]:"; +constexpr char kGreaterThanOperatorConditionMatcherPrefix[] = "[T>]:"; +constexpr char kGreaterThanOrEqualOperatorConditionMatcherPrefix[] = "[T≥]:"; +constexpr char kLessThanOperatorConditionMatcherPrefix[] = "[T<]:"; +constexpr char kLessThanOrEqualOperatorConditionMatcherPrefix[] = "[T≤]:"; + +} // namespace + +bool MatchEpochOperator(const std::string_view value, + const std::string_view condition) { + if (!base::MatchPattern(condition, + kEpochOperatorConditionMatcherPrefixPattern)) { + // Not an operator. + return false; + } + + const std::optional days = ParseDays(condition); + if (!days) { + // Invalid days. + return false; + } + + const std::optional time_delta = ParseTimeDelta(value); + if (!time_delta) { + // Invalid time delta. + VLOG(1) << "Invalid epoch operator condition matcher for " << condition; + return false; + } + + if (condition.starts_with(kEqualOperatorConditionMatcherPrefix)) { + return time_delta->InDays() == days; + } + + if (condition.starts_with(kGreaterThanOperatorConditionMatcherPrefix)) { + return time_delta->InDays() > days; + } + + if (condition.starts_with( + kGreaterThanOrEqualOperatorConditionMatcherPrefix)) { + return time_delta->InDays() >= days; + } + + if (condition.starts_with(kLessThanOperatorConditionMatcherPrefix)) { + return time_delta->InDays() < days; + } + + if (condition.starts_with(kLessThanOrEqualOperatorConditionMatcherPrefix)) { + return time_delta->InDays() <= days; + } + + // Unknown operator. + VLOG(1) << "Unknown epoch operator condition matcher for " << condition; + return false; +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h new file mode 100644 index 000000000000..4e35e42f3572 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_H_ + +#include + +namespace brave_ads { + +// Matches a value against a condition using epoch operators. Supports equality, +// greater than, greater than or equal, less than. and less than or equal +// operators. +bool MatchEpochOperator(std::string_view value, std::string_view condition); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc new file mode 100644 index 000000000000..f786cc1afc76 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc @@ -0,0 +1,153 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" +#include "brave/components/brave_ads/core/internal/common/test/time_test_util.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsEpochOperatorConditionMatcherUtilTest : public test::TestBase {}; + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, DoNotMatchNonOperator) { + // Act & Assert + EXPECT_FALSE(MatchEpochOperator( + "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "baz")); +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchMalformedOperator) { + // Act & Assert + EXPECT_FALSE(MatchEpochOperator( + "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "[T=]: 7 ")); +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, MatchEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T=]:2")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, DoNotMatchEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T=]:3")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + MatchGreaterThanOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T>]:1")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchGreaterThanOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T>]:2")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + MatchGreaterThanOrEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≥]:1")); // Event occurred 2 days ago. + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≥]:2")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchGreaterThanOrEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≥]:3")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, MatchLessThanOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T<]:3")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchLessThanOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T<]:2")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + MatchLessThanOrEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≤]:3")); // Event occurred 2 days ago. + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≤]:2")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchLessThanOrEqualOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[T≤]:1")); // Event occurred 2 days ago. +} + +TEST_F(BraveAdsEpochOperatorConditionMatcherUtilTest, + DoNotMatchUnknownOperator) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_FALSE(MatchEpochOperator( + "13372214400000000" /*1st October 2024 00:00:00 UTC*/, "[T_]:2")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc new file mode 100644 index 000000000000..65f4f978029b --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc @@ -0,0 +1,89 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h" + +#include +#include +#include + +#include "base/check.h" +#include "base/logging.h" +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" + +namespace brave_ads { + +namespace { +constexpr int32_t kMaxUnixEpochTimestamp = std::numeric_limits::max(); +} // namespace + +std::optional ParseDays(const std::string_view condition) { + CHECK(base::MatchPattern(condition, + kEpochOperatorConditionMatcherPrefixPattern)); + + const size_t pos = condition.find(':'); + if (pos == std::string::npos || pos + 1 >= condition.size()) { + // Malformed operator. + VLOG(1) << "Malformed epoch operator condition matcher for " << condition; + return std::nullopt; + } + + int days; + if (!base::StringToInt(condition.substr(pos + 1), &days)) { + // Malformed days. + VLOG(1) << "Malformed epoch operator condition matcher for " << condition; + return std::nullopt; + } + + if (days < 0) { + VLOG(1) << "Invalid epoch operator condition matcher for " << condition; + return std::nullopt; + } + + return days; +} + +bool IsUnixEpochTimestamp(const int64_t timestamp) { + // 32-bit Unix epoch timestamps will fail in the Year 2038 (Y2038K), whereas + // Windows epoch timestamps are 64-bit and will not fail within a foreseeable + // timeframe. We should support Unix epoch timestamps that were not serialized + // using `base::Time::ToDeltaSinceWindowsEpoch`. + return timestamp >= 0 && timestamp <= kMaxUnixEpochTimestamp; +} + +int64_t WindowsToUnixEpoch(const int64_t timestamp) { + return (timestamp - base::Time::kTimeTToMicrosecondsOffset) / + base::Time::kMicrosecondsPerSecond; +} + +base::TimeDelta TimeDeltaSinceEpoch(const int64_t timestamp) { + base::Time now = base::Time::Now(); + + if (!IsUnixEpochTimestamp(timestamp)) { + return now - base::Time::FromDeltaSinceWindowsEpoch( + base::Microseconds(timestamp)); + } + + return now - + base::Time::FromSecondsSinceUnixEpoch(static_cast(timestamp)); +} + +std::optional ParseTimeDelta(const std::string_view value) { + double timestamp; + if (base::StringToDouble(value, ×tamp)) { + return TimeDeltaSinceEpoch(static_cast(timestamp)); + } + + base::Time time; + if (base::Time::FromUTCString(value.data(), &time)) { + return TimeDeltaSinceEpoch(time.ToTimeT()); + } + + return std::nullopt; +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h new file mode 100644 index 000000000000..e00ba69d37c0 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ + +#include +#include +#include + +namespace base { +class TimeDelta; +} // namespace base + +namespace brave_ads { + +inline constexpr char kEpochOperatorConditionMatcherPrefixPattern[] = "[T?]:*"; + +// Parses a number of days from a condition. +std::optional ParseDays(std::string_view condition); + +// Returns `true` if a Unix epoch timestamp. +bool IsUnixEpochTimestamp(int64_t timestamp); + +// Converts a Windows timestamp to a Unix timestamp. +int64_t WindowsToUnixEpoch(int64_t timestamp); + +// Returns the time delta since a Unix or Windows timestamp or an ISO 8601 +// formatted date and time. +base::TimeDelta TimeDeltaSinceEpoch(int64_t timestamp); + +// Parses a time delta from a string. +std::optional ParseTimeDelta(std::string_view value); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_EPOCH_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc new file mode 100644 index 000000000000..02ddb946ef81 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc @@ -0,0 +1,170 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" +#include "brave/components/brave_ads/core/internal/common/test/time_test_util.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsOperatorConditionMatcherUtilInternalTest : public test::TestBase { +}; + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseNegativeDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[T=]:-1")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, ParseDayZero) { + // Act & Assert + EXPECT_EQ(0, ParseDays("[T=]:0")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, ParseDays) { + // Act & Assert + EXPECT_EQ(7, ParseDays("[T=]:7")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseNonIntegerDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[T=]:1.5")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseMalformedDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[T=]: 7 ")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseInvalidDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[T=]:seven")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, IsUnixEpochTimestamp) { + // Act & Assert + EXPECT_TRUE(IsUnixEpochTimestamp(0)); + EXPECT_TRUE(IsUnixEpochTimestamp(2147483647)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + IsNotUnixEpochTimestamp) { + // Act & Assert + EXPECT_FALSE(IsUnixEpochTimestamp(-1)); + EXPECT_FALSE(IsUnixEpochTimestamp(2147483648)); + EXPECT_FALSE(IsUnixEpochTimestamp( + 13372214400000000 /* 1st October 2024 00:00:00 UTC */)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, WindowsToUnixEpoch) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ(1727740800, + WindowsToUnixEpoch( + 13372214400000000 /* 1st October 2024 00:00:00 UTC */)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + TimeDeltaSinceUnixEpoch) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ(base::Days(2), + TimeDeltaSinceEpoch(1727740800 /*1st October 2024 00:00:00 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + TimeDeltaSinceWindowsEpoch) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ(base::Days(2), + TimeDeltaSinceEpoch( + 13372214400000000 /*1st October 2024 00:00:00.000 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + ParseWindowsEpochTimeDelta) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ( + base::Days(2), + ParseTimeDelta("13372214400000000" /*1st October 2024 00:00:00 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + ParseUnixEpochWithFractionalSecondsTimeDelta) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ( + base::Days(2), + ParseTimeDelta("1727740800.3237710" /*1st October 2024 00:00:00 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + ParseUnixEpochTimeDelta) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ(base::Days(2), + ParseTimeDelta("1727740800" /*1st October 2024 00:00:00 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + ParseTimeStringTimeDelta) { + // Arrange + AdvanceClockTo(test::TimeFromUTCString("3 October 2024")); + + // Act & Assert + EXPECT_EQ( + base::Days(2), + ParseTimeDelta("2024-10-01T00:00:00Z" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ( + base::Days(2), + ParseTimeDelta( + "2024-10-01T00:00:00.000000Z" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ( + base::Days(2), + ParseTimeDelta("2024-10-01T00:00:00" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ( + base::Days(2), + ParseTimeDelta( + "2024-10-01T00:00:00.000000" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ(base::Days(2), + ParseTimeDelta( + "2024-09-30T19:00:00-05:00" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ( + base::Days(2), + ParseTimeDelta("1 Oct 2024 00:00:00" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ( + base::Days(2), + ParseTimeDelta( + "1 Oct 2024 00:00:00.000000" /*1st October 2024 00:00:00 UTC*/)); + EXPECT_EQ(base::Days(2), + ParseTimeDelta( + "30 Sept 2024 19:00:00 EST" /*1st October 2024 00:00:00 UTC*/)); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, DoNotParseTimeDelta) { + // Act & Assert + EXPECT_FALSE(ParseTimeDelta("broken time")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.cc new file mode 100644 index 000000000000..df265563d6c4 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.cc @@ -0,0 +1,41 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h" + +#include +#include + +#include "base/check.h" +#include "base/logging.h" +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" + +namespace brave_ads { + +std::optional ParseNumber(const std::string_view condition) { + CHECK(base::MatchPattern(condition, + kNumericalOperatorConditionMatcherPrefixPattern)); + + const size_t pos = condition.find(':'); + if (pos == std::string::npos || pos + 1 >= condition.size()) { + // Malformed operator. + VLOG(1) << "Malformed numerical operator condition matcher for " + << condition; + return std::nullopt; + } + + double number; + if (!base::StringToDouble(condition.substr(pos + 1), &number)) { + // Malformed number. + VLOG(1) << "Malformed numerical operator condition matcher for " + << condition; + return std::nullopt; + } + + return number; +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h new file mode 100644 index 000000000000..b9f0b1cce463 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ + +#include +#include + +namespace brave_ads { + +inline constexpr char kNumericalOperatorConditionMatcherPrefixPattern[] = + "[R?]:*"; + +// Parses number from condition. +std::optional ParseNumber(std::string_view condition); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal_unittest.cc new file mode 100644 index 000000000000..0fc088b9b6c3 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal_unittest.cc @@ -0,0 +1,47 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsNumericalOperatorConditionMatcherUtilInternalTest + : public test::TestBase {}; + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest, + ParseIntegerNumber) { + // Act & Assert + const std::optional number = ParseNumber("[R=]:1"); + ASSERT_TRUE(number); + + EXPECT_DOUBLE_EQ(1.0, *number); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest, + ParseDoubleNumber) { + // Act & Assert + const std::optional number = ParseNumber("[R=]:1.0"); + ASSERT_TRUE(number); + + EXPECT_DOUBLE_EQ(1.0, *number); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest, + DoNotParseMalformedNumber) { + // Act & Assert + EXPECT_FALSE(ParseNumber("[R=]: 1 ")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest, + DoNotParseInvalidNumber) { + // Act & Assert + EXPECT_FALSE(ParseNumber("[R=]:one")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.cc new file mode 100644 index 000000000000..dc50885449c3 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.cc @@ -0,0 +1,84 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h" + +#include +#include + +#include "base/logging.h" +#include "base/numerics/ranges.h" +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h" + +namespace brave_ads { + +namespace { + +constexpr char kEqualOperatorConditionMatcherPrefix[] = "[R=]:"; +constexpr char kNotEqualOperatorConditionMatcherPrefix[] = "[R≠]:"; +constexpr char kGreaterThanOperatorConditionMatcherPrefix[] = "[R>]:"; +constexpr char kGreaterThanOrEqualOperatorConditionMatcherPrefix[] = "[R≥]:"; +constexpr char kLessThanOperatorConditionMatcherPrefix[] = "[R<]:"; +constexpr char kLessThanOrEqualOperatorConditionMatcherPrefix[] = "[R≤]:"; + +} // namespace + +bool MatchNumericalOperator(const std::string_view value, + const std::string_view condition) { + if (!base::MatchPattern(condition, + kNumericalOperatorConditionMatcherPrefixPattern)) { + // Not an operator. + return false; + } + + const std::optional number = ParseNumber(condition); + if (!number) { + // Invalid number. + return false; + } + + double value_as_double; + if (!base::StringToDouble(value, &value_as_double)) { + // Malformed value. + VLOG(1) << "Malformed numerical operator condition matcher for " + << condition; + return false; + } + + if (condition.starts_with(kEqualOperatorConditionMatcherPrefix)) { + return base::IsApproximatelyEqual(value_as_double, *number, + std::numeric_limits::epsilon()); + } + + if (condition.starts_with(kNotEqualOperatorConditionMatcherPrefix)) { + return !base::IsApproximatelyEqual(value_as_double, *number, + std::numeric_limits::epsilon()); + } + + if (condition.starts_with(kGreaterThanOperatorConditionMatcherPrefix)) { + return value_as_double > number; + } + + if (condition.starts_with( + kGreaterThanOrEqualOperatorConditionMatcherPrefix)) { + return value_as_double >= number; + } + + if (condition.starts_with(kLessThanOperatorConditionMatcherPrefix)) { + return value_as_double < number; + } + + if (condition.starts_with(kLessThanOrEqualOperatorConditionMatcherPrefix)) { + return value_as_double <= number; + } + + // Unknown operator. + VLOG(1) << "Unknown numerical operator condition matcher for " << condition; + return false; +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h new file mode 100644 index 000000000000..eb5f2511499c --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_ + +#include + +namespace brave_ads { + +// Matches a value against a condition using numerical operators. Supports +// equality, greater than, greater than or equal, less than, less than or equal +// and not equal operators. +bool MatchNumericalOperator(std::string_view value, std::string_view condition); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util_unittest.cc new file mode 100644 index 000000000000..4e39b9456748 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util_unittest.cc @@ -0,0 +1,152 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsNumericalOperatorConditionMatcherUtilTest + : public test::TestBase {}; + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchNonOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1", "baz")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchMalformedOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1", "[R=]: 1 ")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, MatchEqualOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R=]:1")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R=]:1")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R=]:1.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R=]:1.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchEqualOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R=]:2")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R=]:2")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R=]:2.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R=]:2.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + MatchNotEqualOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≠]:2")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≠]:2")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≠]:2.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≠]:2.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchNotEqualOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≠]:1")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≠]:1")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≠]:1.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≠]:1.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + MatchGreaterThanOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R>]:0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R>]:0")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R>]:0.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R>]:0.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchGreaterThanOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R>]:1")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R>]:1")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R>]:1.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R>]:1.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + MatchGreaterThanOrEqualOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:0")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:0.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:0.0")); + + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:1")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:1")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:1.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:1.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchGreaterThanOrEqualOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≥]:2")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≥]:2")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≥]:2.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≥]:2.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + MatchLessThanOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R<]:2")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R<]:2")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R<]:2.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R<]:2.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchLessThanOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R<]:1")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R<]:1")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R<]:1.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R<]:1.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + MatchLessThanOrEqualOperator) { + // Act & Assert + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:1")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:1")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:1.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:1.0")); + + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:2")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:2")); + EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:2.0")); + EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:2.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchLessThanOrEqualOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≤]:0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≤]:0")); + EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≤]:0.0")); + EXPECT_FALSE(MatchNumericalOperator("1", "[R≤]:0.0")); +} + +TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, + DoNotMatchUnknownOperator) { + // Act & Assert + EXPECT_FALSE(MatchNumericalOperator("1", "[_]:2")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc new file mode 100644 index 000000000000..31d3a6b2d6cb --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc @@ -0,0 +1,17 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h" + +#include "base/strings/pattern.h" + +namespace brave_ads { + +bool MatchPattern(const std::string_view value, + const std::string_view condition) { + return base::MatchPattern(value, condition); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h new file mode 100644 index 000000000000..cd644bd362f4 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_PATTERN_CONDITION_MATCHER_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_PATTERN_CONDITION_MATCHER_UTIL_H_ + +#include + +namespace brave_ads { + +// Matches a value against a pattern condition. +bool MatchPattern(std::string_view value, std::string_view condition); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_PATTERN_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc new file mode 100644 index 000000000000..9d344fa87e52 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc @@ -0,0 +1,36 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsPatternConditionMatcherUtilTest : public test::TestBase {}; + +TEST_F(BraveAdsPatternConditionMatcherUtilTest, MatchPattern) { + // Act & Assert + EXPECT_TRUE(MatchPattern("foo.baz.bar", "foo?baz.*")); +} + +TEST_F(BraveAdsPatternConditionMatcherUtilTest, MatchEmptyPattern) { + // Act & Assert + EXPECT_TRUE(MatchPattern("", "")); +} + +TEST_F(BraveAdsPatternConditionMatcherUtilTest, MatchEscapedPattern) { + // Act & Assert + EXPECT_TRUE(MatchPattern(R"(*.bar.?)", R"(\*.bar.\?)")); +} + +TEST_F(BraveAdsPatternConditionMatcherUtilTest, DoNotMatchPattern) { + // Act & Assert + EXPECT_FALSE(MatchPattern("foo.baz.bar", "bar.*.foo")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc new file mode 100644 index 000000000000..3fe5d1c61cbe --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h" + +#include "third_party/re2/src/re2/re2.h" + +namespace brave_ads { + +bool MatchRegex(const std::string_view value, + const std::string_view condition) { + const re2::RE2 re(condition, re2::RE2::Quiet); + if (!re.ok()) { + return false; + } + + return re2::RE2::PartialMatch(value, re); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h new file mode 100644 index 000000000000..ffa9283bb86d --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_REGEX_CONDITION_MATCHER_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_REGEX_CONDITION_MATCHER_UTIL_H_ + +#include + +namespace brave_ads { + +// Matches a value against a regular expression condition. +bool MatchRegex(std::string_view value, std::string_view condition); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_REGEX_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc new file mode 100644 index 000000000000..a56d7426b805 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc @@ -0,0 +1,36 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h" + +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsRegexConditionMatcherUtilTest : public test::TestBase {}; + +TEST_F(BraveAdsRegexConditionMatcherUtilTest, MatchRegex) { + // Act & Assert + EXPECT_TRUE(MatchRegex("foo.baz.bar", "(foo|bar)")); +} + +TEST_F(BraveAdsRegexConditionMatcherUtilTest, MatchEmptyRegex) { + // Act & Assert + EXPECT_TRUE(MatchRegex("", "")); +} + +TEST_F(BraveAdsRegexConditionMatcherUtilTest, DoNotMatchRegex) { + // Act & Assert + EXPECT_FALSE(MatchRegex("foo.baz.bar", "(waldo|fred)")); +} + +TEST_F(BraveAdsRegexConditionMatcherUtilTest, DoNotMatchMalformedRegex) { + // Act & Assert + EXPECT_FALSE(MatchRegex("foo.baz.bar", "* ?")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/README.md b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/README.md new file mode 100644 index 000000000000..5c8b3078ccb8 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/README.md @@ -0,0 +1,5 @@ +# Condition Matcher Prefs + +Condition matchers are utilized to assess if specific conditions or criteria are fulfilled for ad targeting by matching against profile, local state, and virtual preferences. + +Please add to it! diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc new file mode 100644 index 000000000000..abacdad257b5 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc @@ -0,0 +1,28 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h" + +#include "base/check.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h" +#include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" + +namespace brave_ads { + +std::optional MaybeGetPrefValueAsString( + const PrefProviderInterface* const pref_provider, + const std::string& pref_path) { + CHECK(pref_provider); + + if (const std::optional value = + MaybeGetPrefValue(pref_provider, pref_path)) { + return ToString(*value); + } + + // Unknown pref path. + return std::nullopt; +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h new file mode 100644 index 000000000000..b921d7aa8b78 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_CONDITION_MATCHER_PREF_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_CONDITION_MATCHER_PREF_UTIL_H_ + +#include +#include + +namespace brave_ads { + +class PrefProviderInterface; + +// Get the pref value as a string from the provider for the given path. Handles +// nested dictionaries, lists, and dot-separated keys. +// `base::Value::Find*ByDottedPath` is not used because path keys can contain +// dots. Returns `std::nullopt` if the path is malformed or unknown. Path keys +// should be separated by `|`. Example `list|1` would return the second element +// of a list. +std::optional MaybeGetPrefValueAsString( + const PrefProviderInterface* pref_provider, + const std::string& pref_path); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_CONDITION_MATCHER_PREF_UTIL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc new file mode 100644 index 000000000000..45b93135efdb --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc @@ -0,0 +1,44 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h" + +#include "brave/components/brave_ads/core/internal/ads_client/ads_client_pref_provider.h" +#include "brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h" +#include "brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h" +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsConditionMatcherPrefUtilTest : public test::TestBase { + protected: + const AdsClientPrefProvider pref_provider_; +}; + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetProfilePrefValueAsString) { + // Arrange + test::RegisterProfileBooleanPref("boolean", true); + + // Act & Assert + EXPECT_EQ("1", MaybeGetPrefValueAsString(&pref_provider_, "boolean")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetLocalStatePrefValueAsString) { + // Arrange + test::RegisterLocalStateBooleanPref("boolean", true); + + // Act & Assert + EXPECT_EQ("1", MaybeGetPrefValueAsString(&pref_provider_, "boolean")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, DoNotGetUnknownPrefValueAsString) { + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValueAsString(&pref_provider_, "foo.bar")); + EXPECT_FALSE(MaybeGetPrefValueAsString(&pref_provider_, "")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc similarity index 50% rename from components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc rename to components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc index 613882c92879..88da962f65b6 100644 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc @@ -3,37 +3,51 @@ * 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/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h" +#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h" -#include -#include #include #include #include "base/check.h" #include "base/logging.h" #include "base/notreached.h" -#include "base/strings/pattern.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" -#include "base/time/time.h" #include "base/types/cxx23_to_underlying.h" -#include "base/values.h" #include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" -#include "third_party/re2/src/re2/re2.h" namespace brave_ads { -namespace { +std::optional ToString(const base::Value& value) { + switch (value.type()) { + case base::Value::Type::BOOLEAN: { + return base::NumberToString(static_cast(value.GetBool())); + } -constexpr char kOperatorMatcherPatternPrefix[] = "[?]:*"; -constexpr char kEqualOperatorMatcherPrefix[] = "[=]:"; -constexpr char kGreaterThanOperatorMatcherPrefix[] = "[>]:"; -constexpr char kGreaterThanOrEqualOperatorMatcherPrefix[] = "[≥]:"; -constexpr char kLessThanOperatorMatcherPrefix[] = "[<]:"; -constexpr char kLessThanOrEqualOperatorMatcherPrefix[] = "[≤]:"; + case base::Value::Type::INTEGER: { + return base::NumberToString(value.GetInt()); + } -constexpr int32_t kMaxUnixEpochTimestamp = std::numeric_limits::max(); + case base::Value::Type::DOUBLE: { + return base::NumberToString(value.GetDouble()); + } + + case base::Value::Type::STRING: { + return value.GetString(); + } + + case base::Value::Type::NONE: + case base::Value::Type::BINARY: + case base::Value::Type::DICT: + case base::Value::Type::LIST: { + // Unsupported value type. + return std::nullopt; + } + } + + NOTREACHED_NORETURN() << "Unexpected value for base::Value::Type: " + << base::to_underlying(value.type()); +} std::optional MaybeGetRootPrefValue( const PrefProviderInterface* const pref_provider, @@ -100,153 +114,6 @@ std::optional MaybeGetNextPrefValue(const base::Value& pref_value, return std::nullopt; } -} // namespace - -std::optional ToString(const base::Value& value) { - switch (value.type()) { - case base::Value::Type::BOOLEAN: { - return base::NumberToString(static_cast(value.GetBool())); - } - - case base::Value::Type::INTEGER: { - return base::NumberToString(value.GetInt()); - } - - case base::Value::Type::DOUBLE: { - return base::NumberToString(value.GetDouble()); - } - - case base::Value::Type::STRING: { - return value.GetString(); - } - - case base::Value::Type::NONE: - case base::Value::Type::BINARY: - case base::Value::Type::DICT: - case base::Value::Type::LIST: { - // Unsupported value type. - return std::nullopt; - } - } - - NOTREACHED_NORETURN() << "Unexpected value for base::Value::Type: " - << base::to_underlying(value.type()); -} - -std::optional ParseDays(const std::string_view condition) { - CHECK(base::MatchPattern(condition, kOperatorMatcherPatternPrefix)); - - const size_t pos = condition.find(':'); - if (pos == std::string::npos || pos + 1 >= condition.size()) { - // Malformed operator. - VLOG(1) << "Malformed SmartNTT days operator for " << condition - << " condition"; - return std::nullopt; - } - - int days; - if (!base::StringToInt(condition.substr(pos + 1), &days)) { - // Malformed days. - VLOG(1) << "Malformed SmartNTT days operator for " << condition - << " condition"; - return std::nullopt; - } - - if (days < 0) { - VLOG(1) << "Invalid SmartNTT " << days << " days operator for " << condition - << " condition"; - return std::nullopt; - } - - return days; -} - -bool IsUnixEpochTimestamp(const int64_t timestamp) { - // 32-bit Unix epoch timestamps will fail in the Year 2038 (Y2038K), whereas - // Windows epoch timestamps are 64-bit and will not fail within a foreseeable - // timeframe. We should support Unix epoch timestamps that were not serialized - // using `base::Time::ToDeltaSinceWindowsEpoch`. - return timestamp >= 0 && timestamp <= kMaxUnixEpochTimestamp; -} - -int64_t WindowsToUnixEpoch(const int64_t timestamp) { - return (timestamp - base::Time::kTimeTToMicrosecondsOffset) / - base::Time::kMicrosecondsPerSecond; -} - -base::TimeDelta TimeDeltaSinceEpoch(const int64_t timestamp) { - base::Time now = base::Time::Now(); - - if (!IsUnixEpochTimestamp(timestamp)) { - return now - base::Time::FromDeltaSinceWindowsEpoch( - base::Microseconds(timestamp)); - } - - return now - - base::Time::FromSecondsSinceUnixEpoch(static_cast(timestamp)); -} - -bool MatchOperator(const std::string_view value, - const std::string_view condition) { - if (!base::MatchPattern(condition, kOperatorMatcherPatternPrefix)) { - // Not an operator. - return false; - } - - const std::optional days = ParseDays(condition); - if (!days) { - // Invalid days. - return false; - } - - int64_t timestamp; - if (!base::StringToInt64(value, ×tamp)) { - // Invalid timestamp. - VLOG(1) << "Invalid SmartNTT " << value << " timestamp operator for " - << condition << " condition"; - return false; - } - const base::TimeDelta time_delta = TimeDeltaSinceEpoch(timestamp); - - if (condition.starts_with(kEqualOperatorMatcherPrefix)) { - return time_delta.InDays() == days; - } - - if (condition.starts_with(kGreaterThanOperatorMatcherPrefix)) { - return time_delta.InDays() > days; - } - - if (condition.starts_with(kGreaterThanOrEqualOperatorMatcherPrefix)) { - return time_delta.InDays() >= days; - } - - if (condition.starts_with(kLessThanOperatorMatcherPrefix)) { - return time_delta.InDays() < days; - } - - if (condition.starts_with(kLessThanOrEqualOperatorMatcherPrefix)) { - return time_delta.InDays() <= days; - } - - // Unknown operator. - VLOG(1) << "Unknown SmartNTT operator for " << condition << " condition"; - return false; -} - -bool MatchRegex(const std::string_view value, - const std::string_view condition) { - const re2::RE2 re(condition, re2::RE2::Quiet); - if (!re.ok()) { - return false; - } - - return re2::RE2::PartialMatch(value, re); -} - -bool MatchPattern(std::string_view value, std::string_view condition) { - return base::MatchPattern(value, condition); -} - std::optional MaybeGetPrefValue( const PrefProviderInterface* const pref_provider, const std::string& pref_path) { @@ -257,7 +124,7 @@ std::optional MaybeGetPrefValue( pref_path, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (keys.empty()) { // Invalid pref path. - VLOG(1) << "Invalid SmartNTT pref path: " << pref_path; + VLOG(1) << "Invalid condition matcher pref path: " << pref_path; return std::nullopt; } @@ -275,7 +142,7 @@ std::optional MaybeGetPrefValue( } // Unknown pref path key. - VLOG(1) << "Unknown SmartNTT " << key << " key for " << pref_path + VLOG(1) << "Unknown condition matcher " << key << " key for " << pref_path << " pref path"; return std::nullopt; } @@ -284,7 +151,7 @@ std::optional MaybeGetPrefValue( pref_value = MaybeGetNextPrefValue(*pref_value, key); if (!pref_value) { // Unknown pref path key. - VLOG(1) << "Unknown SmartNTT " << key << " key for " << pref_path + VLOG(1) << "Unknown condition matcher " << key << " key for " << pref_path << " pref path"; return std::nullopt; } @@ -296,7 +163,7 @@ std::optional MaybeGetPrefValue( if (iter != keys.cend() - 1) { // Invalid pref path, because this should be the last pref path key. - VLOG(1) << "Invalid SmartNTT " << key << " key for " << pref_path + VLOG(1) << "Invalid condition matcher " << key << " key for " << pref_path << " pref path"; return std::nullopt; } @@ -308,18 +175,4 @@ std::optional MaybeGetPrefValue( return pref_value; } -std::optional MaybeGetPrefValueAsString( - const PrefProviderInterface* const pref_provider, - const std::string& pref_path) { - CHECK(pref_provider); - - if (const std::optional value = - MaybeGetPrefValue(pref_provider, pref_path)) { - return ToString(*value); - } - - // Unknown pref path. - return std::nullopt; -} - } // namespace brave_ads diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h new file mode 100644 index 000000000000..ce2ff868336b --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2024 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_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_PREF_UTIL_INTERNAL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_PREF_UTIL_INTERNAL_H_ + +#include +#include + +#include "base/values.h" + +namespace brave_ads { + +class PrefProviderInterface; + +std::optional ToString(const base::Value& value); + +std::optional MaybeGetRootPrefValue( + const PrefProviderInterface* pref_provider, + const std::string& pref_path); + +std::optional MaybeGetDictPrefValue(const base::Value& pref_value, + const std::string& key); + +std::optional MaybeGetListPrefValue(const base::Value& pref_value, + const std::string& key); + +std::optional MaybeGetNextPrefValue(const base::Value& pref_value, + const std::string& key); + +// Get the pref value from the provider for the given path. Handles nested +// dictionaries, lists, and dot-separated keys. +// `base::Value::Find*ByDottedPath` is not used because path keys can contain +// dots. Returns `std::nullopt` if the path is malformed or unknown. Path keys +// should be separated by `|`. Example `list|1` would return the second +// element of a list. +std::optional MaybeGetPrefValue( + const PrefProviderInterface* pref_provider, + const std::string& pref_path); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_PREF_UTIL_INTERNAL_H_ diff --git a/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc new file mode 100644 index 000000000000..4ffa682bd260 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc @@ -0,0 +1,412 @@ +/* Copyright (c) 2024 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/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h" + +#include "base/containers/span.h" +#include "brave/components/brave_ads/core/internal/ads_client/ads_client_pref_provider.h" +#include "brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h" +#include "brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h" +#include "brave/components/brave_ads/core/internal/common/test/test_base.h" + +// npm run test -- brave_unit_tests --filter=BraveAds* + +namespace brave_ads { + +class BraveAdsConditionMatcherPrefUtilTest : public test::TestBase { + protected: + const AdsClientPrefProvider pref_provider_; +}; + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotConvertNoneValueTypeToString) { + // Act & Assert + EXPECT_FALSE(ToString(base::Value())); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, BooleanValueTypeToString) { + // Act & Assert + EXPECT_EQ("0", ToString(base::Value(false))); + EXPECT_EQ("1", ToString(base::Value(true))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, IntegerValueTypeToString) { + // Act & Assert + EXPECT_EQ("123", ToString(base::Value(123))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, DoubleValueTypeToString) { + // Act & Assert + EXPECT_EQ("1.23", ToString(base::Value(1.23))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, StringValueTypeToString) { + // Act & Assert + EXPECT_EQ("123", ToString(base::Value("123"))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotConvertDictValueTypeToString) { + // Act & Assert + EXPECT_FALSE(ToString(base::Value(base::Value::Dict().Set("foo", "bar")))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotConvertListValueTypeToString) { + // Act & Assert + EXPECT_FALSE(ToString(base::Value(base::Value::List().Append("foo")))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotConvertBinaryValueTypeToString) { + // Arrange + const base::span binary({0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, + 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, + 0x21}); + + // Act & Assert + EXPECT_FALSE(ToString(base::Value(binary))); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetVirtualPrefValue) { + // Arrange + ON_CALL(ads_client_mock_, GetVirtualPrefs).WillByDefault([]() { + return base::Value::Dict().Set("[virtual]:matrix", /*room*/ 303); + }); + + // Act & Assert + EXPECT_EQ(base::Value(/*room*/ 303), + MaybeGetPrefValue(&pref_provider_, "[virtual]:matrix")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, DoNotGetUnknownVirtualPrefValue) { + // Arrange + ON_CALL(ads_client_mock_, GetVirtualPrefs).WillByDefault([]() { + return base::Value::Dict().Set("[virtual]:inverse.matrices", /*room*/ 101); + }); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "[virtual]:matrix")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetBooleanProfilePrefValue) { + // Arrange + test::RegisterProfileBooleanPref("boolean", true); + + // Act & Assert + EXPECT_EQ(base::Value(true), MaybeGetPrefValue(&pref_provider_, "boolean")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetIntegerProfilePrefValue) { + // Arrange + test::RegisterProfileIntegerPref("integer", 123); + + // Act & Assert + EXPECT_EQ(base::Value(123), MaybeGetPrefValue(&pref_provider_, "integer")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetDoubleProfilePrefValue) { + // Arrange + test::RegisterProfileDoublePref("double", 1.23); + + // Act & Assert + EXPECT_EQ(base::Value(1.23), MaybeGetPrefValue(&pref_provider_, "double")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetStringProfilePrefValue) { + // Arrange + test::RegisterProfileStringPref("string", "foo"); + + // Act & Assert + EXPECT_EQ(base::Value("foo"), MaybeGetPrefValue(&pref_provider_, "string")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetDictProfilePrefValue) { + // Arrange + test::RegisterProfileDictPref("dict", base::Value::Dict().Set("foo", "bar")); + + // Act & Assert + EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "dict|foo")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetNestedDictProfilePrefValue) { + // Arrange + test::RegisterProfileDictPref( + "dict", + base::Value::Dict().Set("foo", base::Value::Dict().Set("bar", "qux"))); + + // Act & Assert + EXPECT_EQ(base::Value("qux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo|bar")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetDeeplyNestedDictProfilePrefValue) { + // Arrange + test::RegisterProfileDictPref( + "dict", base::Value::Dict().Set( + "foo", base::Value::List().Append("bar").Append( + base::Value::Dict().Set("baz", "qux")))); + + // Act & Assert + EXPECT_EQ(base::Value("qux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo|1|baz")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetNestedDictProfilePrefValueWithDotSeparatedPathComponents) { + // Arrange + test::RegisterProfileDictPref( + "dict", base::Value::Dict().Set( + "foo.bar", base::Value::Dict().Set("baz.qux", "quux"))); + + // Act & Assert + EXPECT_EQ(base::Value("quux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz.qux")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetMalformedDictProfilePrefValue) { + test::RegisterProfileDictPref("dict", + base::Value::Dict().Set("foo.bar", "baz")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetListProfilePrefValue) { + // Arrange + test::RegisterProfileListPref( + "list", base::Value::List().Append("foo").Append("bar")); + + // Act & Assert + EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetNestedListProfilePrefValue) { + // Arrange + test::RegisterProfileListPref( + "list", base::Value::List().Append( + base::Value::List().Append("foo").Append("bar"))); + + // Act & Assert + EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|0|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetDeeplyNestedListProfilePrefValue) { + // Arrange + test::RegisterProfileListPref( + "list", base::Value::List().Append(base::Value::Dict().Set( + "foo", base::Value::List().Append("bar").Append("baz")))); + + // Act & Assert + EXPECT_EQ(base::Value("baz"), + MaybeGetPrefValue(&pref_provider_, "list|0|foo|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetNestedListProfilePrefValueWithDotSeparatedPathComponents) { + // Arrange + test::RegisterProfileListPref( + "list", base::Value::List().Append(base::Value::Dict().Set( + "foo.bar", + base::Value::List().Append("baz.qux").Append("quux.corge")))); + + // Act & Assert + EXPECT_EQ(base::Value("quux.corge"), + MaybeGetPrefValue(&pref_provider_, "list|0|foo.bar|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetListProfilePrefValueWithOutOfBoundsListIndicies) { + // Arrange + test::RegisterProfileListPref( + "list", base::Value::List().Append("foo").Append("bar")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|-1")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|2")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetMalformedListProfilePrefValue) { + test::RegisterProfileListPref("list", base::Value::List().Append("foo")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|0|foo")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|bar")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetBooleanLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateBooleanPref("boolean", true); + + // Act & Assert + EXPECT_EQ(base::Value(true), MaybeGetPrefValue(&pref_provider_, "boolean")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetIntegerLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateIntegerPref("integer", 123); + + // Act & Assert + EXPECT_EQ(base::Value(123), MaybeGetPrefValue(&pref_provider_, "integer")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetDoubleLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateDoublePref("double", 1.23); + + // Act & Assert + EXPECT_EQ(base::Value(1.23), MaybeGetPrefValue(&pref_provider_, "double")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetStringLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateStringPref("string", "foo"); + + // Act & Assert + EXPECT_EQ(base::Value("foo"), MaybeGetPrefValue(&pref_provider_, "string")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetDictLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateDictPref("dict", + base::Value::Dict().Set("foo.bar", "baz")); + + // Act & Assert + EXPECT_EQ(base::Value("baz"), + MaybeGetPrefValue(&pref_provider_, "dict|foo.bar")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetNestedDictLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateDictPref( + "dict", + base::Value::Dict().Set("foo", base::Value::Dict().Set("bar", "qux"))); + + // Act & Assert + EXPECT_EQ(base::Value("qux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo|bar")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetDeeplyNestedDictLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateDictPref( + "dict", base::Value::Dict().Set( + "foo", base::Value::List().Append("bar").Append( + base::Value::Dict().Set("baz", "qux")))); + + // Act & Assert + EXPECT_EQ(base::Value("qux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo|1|baz")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetNestedDictLocalStatePrefValueWithDotSeparatedPathComponents) { + // Arrange + test::RegisterLocalStateDictPref( + "dict", base::Value::Dict().Set( + "foo.bar", base::Value::Dict().Set("baz.qux", "quux"))); + + // Act & Assert + EXPECT_EQ(base::Value("quux"), + MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz.qux")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetMalformedDictLocalStatePrefValue) { + test::RegisterLocalStateDictPref("dict", + base::Value::Dict().Set("foo.bar", "baz")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|foo.bar|baz")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "dict|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetListLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateListPref( + "list", base::Value::List().Append("foo").Append("bar")); + + // Act & Assert + EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, GetNestedListLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateListPref( + "list", base::Value::List().Append( + base::Value::List().Append("foo").Append("bar"))); + + // Act & Assert + EXPECT_EQ(base::Value("bar"), MaybeGetPrefValue(&pref_provider_, "list|0|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetDeeplyNestedListLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateListPref( + "list", base::Value::List().Append(base::Value::Dict().Set( + "foo", base::Value::List().Append("bar").Append("baz")))); + + // Act & Assert + EXPECT_EQ(base::Value("baz"), + MaybeGetPrefValue(&pref_provider_, "list|0|foo|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + GetNestedListLocalStatePrefValueWithDotSeparatedPathComponents) { + // Arrange + test::RegisterLocalStateListPref( + "list", base::Value::List().Append(base::Value::Dict().Set( + "foo.bar", + base::Value::List().Append("baz.qux").Append("quux.corge")))); + + // Act & Assert + EXPECT_EQ(base::Value("quux.corge"), + MaybeGetPrefValue(&pref_provider_, "list|0|foo.bar|1")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetMalformedListLocalStatePrefValue) { + // Arrange + test::RegisterLocalStateListPref("list", base::Value::List().Append("foo")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|0|foo")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|bar")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "|")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, + DoNotGetListLocalStatePrefValueWithOutOfBoundsListIndicies) { + // Arrange + test::RegisterLocalStateListPref( + "list", base::Value::List().Append("foo").Append("bar")); + + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|-1")); + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "list|2")); +} + +TEST_F(BraveAdsConditionMatcherPrefUtilTest, DoNotGetUnknownPrefValue) { + // Act & Assert + EXPECT_FALSE(MaybeGetPrefValue(&pref_provider_, "foo.bar")); +} + +} // namespace brave_ads diff --git a/components/brave_ads/core/internal/targeting/geographical/subdivision/supported_subdivisions.cc b/components/brave_ads/core/internal/targeting/geographical/subdivision/supported_subdivisions.cc index 8dc991300550..bffb2832a8fe 100644 --- a/components/brave_ads/core/internal/targeting/geographical/subdivision/supported_subdivisions.cc +++ b/components/brave_ads/core/internal/targeting/geographical/subdivision/supported_subdivisions.cc @@ -11,7 +11,7 @@ namespace brave_ads { namespace { -base::Value::List ToValue(const SupportedSubdivisions& subdivisions) { +base::Value::List ToValue(const SubdivisionMap& subdivisions) { base::Value::List list; for (const auto& [subdivision, name] : subdivisions) { diff --git a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_event_types.h b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_event_types.h index ac79e440cfdb..9f6344d2037e 100644 --- a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_event_types.h +++ b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_event_types.h @@ -31,7 +31,7 @@ enum class UserActivityEventType : int8_t { /*0E*/ kTabStartedPlayingMedia, /*0F*/ kTabStoppedPlayingMedia, /*10*/ kSubmittedForm, - /*11*/ kTabUpdated, + /*11*/ kTabDidChange, /*12*/ kTypedAndSelectedNonUrl, /*13*/ kTypedKeywordOtherThanDefaultSearchProvider, /*14*/ kTypedUrl, diff --git a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager.cc b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager.cc index f1813707966c..7eb5fffbda91 100644 --- a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager.cc +++ b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager.cc @@ -160,7 +160,7 @@ void UserActivityManager::OnTabDidChangeFocus(const int32_t /*tab_id*/) { } void UserActivityManager::OnTabDidChange(const TabInfo& /*tab*/) { - RecordEvent(UserActivityEventType::kTabUpdated); + RecordEvent(UserActivityEventType::kTabDidChange); } void UserActivityManager::OnDidOpenNewTab(const TabInfo& /*tab*/) { diff --git a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager_unittest.cc b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager_unittest.cc index 741d0edce886..56e7fe580517 100644 --- a/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager_unittest.cc +++ b/components/brave_ads/core/internal/user_attention/user_activity/user_activity_manager_unittest.cc @@ -242,7 +242,7 @@ TEST_F(BraveAdsUserActivityManagerTest, RecordSubmittedFormEvent) { TEST_F(BraveAdsUserActivityManagerTest, RecordTabUpdatedEvent) { // Arrange const UserActivityEventType user_activity_event_type = - UserActivityEventType::kTabUpdated; + UserActivityEventType::kTabDidChange; // Act UserActivityManager::GetInstance().RecordEvent(user_activity_event_type); @@ -382,7 +382,7 @@ TEST_F(BraveAdsUserActivityManagerTest, GetHistoryForTimeWindow) { const base::Time now = test::Now(); UserActivityManager::GetInstance().RecordEvent( - UserActivityEventType::kTabUpdated); + UserActivityEventType::kTabDidChange); UserActivityManager::GetInstance().RecordEvent( UserActivityEventType::kTypedAndSelectedNonUrl); UserActivityManager::GetInstance().RecordEvent( @@ -399,7 +399,7 @@ TEST_F(BraveAdsUserActivityManagerTest, GetHistoryForTimeWindow) { UserActivityEventInfo expected_user_activity_event; - expected_user_activity_event.type = UserActivityEventType::kTabUpdated; + expected_user_activity_event.type = UserActivityEventType::kTabDidChange; expected_user_activity_event.created_at = now; expected_user_activity_events.push_back(expected_user_activity_event); diff --git a/components/brave_ads/core/internal/user_attention/user_idle_detection/user_idle_detection.cc b/components/brave_ads/core/internal/user_attention/user_idle_detection/user_idle_detection.cc index 71e51debff03..c842763c3495 100644 --- a/components/brave_ads/core/internal/user_attention/user_idle_detection/user_idle_detection.cc +++ b/components/brave_ads/core/internal/user_attention/user_idle_detection/user_idle_detection.cc @@ -8,7 +8,7 @@ #include "base/time/time.h" #include "brave/components/brave_ads/core/internal/ads_client/ads_client_util.h" #include "brave/components/brave_ads/core/internal/common/logging_util.h" -#include "brave/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_util.h" +#include "brave/components/brave_ads/core/internal/diagnostics/entries/last_unidle_time_diagnostic_entry_util.h" #include "brave/components/brave_ads/core/internal/settings/settings.h" #include "brave/components/brave_ads/core/public/ads_client/ads_client.h" diff --git a/components/brave_ads/core/public/BUILD.gn b/components/brave_ads/core/public/BUILD.gn index fbf4539671df..c7abb1d946d5 100644 --- a/components/brave_ads/core/public/BUILD.gn +++ b/components/brave_ads/core/public/BUILD.gn @@ -47,7 +47,7 @@ source_set("headers") { "prefs/pref_provider.h", "prefs/pref_provider_interface.h", "prefs/pref_registry.h", - "serving/new_tab_page_ad_serving_condition_matcher_util.h", + "serving/targeting/condition_matcher/condition_matcher_util.h", "targeting/geographical/subdivision/supported_subdivisions.h", "user_attention/user_idle_detection/user_idle_detection_feature.h", "user_engagement/ad_events/ad_event_cache.h", diff --git a/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h b/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h deleted file mode 100644 index a4d1478c21fa..000000000000 --- a/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h +++ /dev/null @@ -1,135 +0,0 @@ -/* Copyright (c) 2024 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_CORE_PUBLIC_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_H_ -#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_H_ - -#include -#include - -namespace brave_ads { - -using NewTabPageAdConditionMatchers = - std::multimap; - -class PrefProviderInterface; - -// Matchers are one or more preference paths and conditions, using AND logic, -// that must all be met for a new tab takeover ad to be served. Preference path -// keys should be separated by |, where paths may include list indices (e.g., -// "list|1") or dictionary keys (e.g., "dict|key"). Paths can also be nested. -// Both Brave local state and profile preferences are supported. See -// https://github.com/brave/brave-browser/wiki/P3A for more information on P3A -// pref path usage. -// -// For non-Rewards users, condition matchers should be included in the -// "photo.json" file under the NTP (New Tab Page) sponsored images component, -// within "campaigns2", falling back to "campaigns", or falling back to the root -// "campaign" for backwards compatibility. -// -// For Rewards users, these matchers should be placed in the catalog under -// "wallpapers" with the "imageUrl" prefixed with "[SmartNTT]" for backwards -// compatibility, where legacy browsers will discard these wallpapers due to an -// invalid URL. -// -// If no condition matchers are present ads will always be shown, unless -// frequency capped for Rewards users. If condition matchers are malformed or -// have unknown pref paths, the ad will not be shown. -// -// Supported Condition Matchers: -// -// 1. [operator]:days Matcher: -// - Support operators: -// - '=': Exact -// - '>': Greater than -// - '≥': Greater than or equal to -// - '<': Less than -// - '≤': Less than or equal to -// - This matcher triggers an ad based on when an event occurred or will -// occur, using a timestamp (Unix or Windows epoch) stored at "prefPath". -// For instance, the example below will serve an ad only if the timestamp -// for "foo|bar" occurred more than 3 days ago: -// -// "conditionMatchers": [ -// { -// "condition": "[>]:3", -// "prefPath": "foo|bar" -// } -// ] -// -// 2. Regex Matcher: -// - Uses an RE2 regular expression to partially match values at "prefPath", -// see https://github.com/google/re2/wiki/syntax. For example, the -// following will serve an ad if the value at "foo|bar" starts with "abc": -// -// "conditionMatchers": [ -// { -// "condition": "^abc", -// "prefPath": "foo|bar" -// } -// ] -// -// 3. Pattern Matcher: -// - Supports wildcards "*" and "?". "*" matches zero or more characters, -// while "?" matches zero or one character. To use these literally, escape -// them with "\". In the example below, an ad will be served only if the -// value at "foo|bar" matches the pattern "*baz?qux*": -// -// "conditionMatchers": [ -// { -// "condition": "*baz?qux*", -// "prefPath": "foo|bar" -// } -// ] -// -// For example, the following condition matchers would only serve a new tab -// takeover ad if the default search provider is set to "Startpage", the user -// has exactly one bookmark, and the browser was installed between three and -// seven days ago: -// -// "conditionMatchers": [ -// { -// "condition": "*-538868000510", -// "prefPath": "default_search_provider|guid" -// }, -// { -// "condition": "1", -// "prefPath": "p3a|logs_constellation_prep|Brave.Core.BookmarkCount|value" -// }, -// { -// "condition": "[≥]:3", -// "prefPath": "uninstall_metrics|installation_date2" -// }, -// { -// "condition": "[≤]:7", -// "prefPath": "uninstall_metrics|installation_date2" -// } -// ] -// -// We support virtual preferences for values that are not stored in the profile -// or local state preferences. Virtual preference path keys should be prefixed -// with "[virtual]:". -// -// "[virtual]:build_channel.name" retrieves the build channel of the browser, -// returning one of the following values: "stable", "beta", "dev", "nightly", or -// "unknown". -// -// "[virtual]:default_search_engine.name" retrieves the default search engine -// chosen during browser installation, returning one of the following values: -// "Brave", "Google", "Yandex", "Bing", "Daum", "네이버", "DuckDuckGo", "Qwant", -// "Startpage", or "Ecosia". -// -// NOTE: To identify condition matchers, first create a copy of your preference -// files. Next, change a brave://setting or enable a feature, quit the browser -// and then compare the original and modified versions to determine which -// key/value pairs are required. Invalid or malformed condition matchers will be -// logged to the console, they are not logged to the Rewards log. - -bool MatchConditions(const PrefProviderInterface* pref_provider, - const NewTabPageAdConditionMatchers& condition_matchers); - -} // namespace brave_ads - -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h b/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h new file mode 100644 index 000000000000..e62f5d2cece7 --- /dev/null +++ b/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2024 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_CORE_PUBLIC_SERVING_TARGETING_CONDITION_MATCHER_CONDITION_MATCHER_UTIL_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_TARGETING_CONDITION_MATCHER_CONDITION_MATCHER_UTIL_H_ + +#include +#include + +namespace brave_ads { + +using ConditionMatcherMap = + std::multimap; + +class PrefProviderInterface; + +// Matchers are one or more pref paths and conditions, using AND logic, that +// must all be met for an ad to be served. Pref path keys should be separated by +// "|", where paths may include list indices (e.g., "list|1") or dictionary keys +// (e.g., "dict|key"). Paths can also be nested. Both Brave local state and +// profile prefs are supported. +// +// For non-Rewards users, condition matchers should be included in the +// "photo.json" file under the NTP (New Tab Page) sponsored images component, +// within "campaigns2", falling back to "campaigns", or the root "campaign" for +// backwards compatibility. +// +// For Rewards users, these matchers should be placed in the catalog under +// "wallpapers" with the "imageUrl" prefixed with "[SmartNTT]" for backwards +// compatibility, where legacy browsers will discard these wallpapers due to an +// invalid URL. +// +// If no condition matchers are present ads will always be shown, unless +// frequency capped for Rewards users. If condition matchers are malformed or +// have unknown pref paths, the ad will not be shown. +// +// Supported Condition Matchers: +// +// 1. [epoch operator]:days Matcher: +// - Support operators: +// - 'T=': Equal +// - 'T>': Greater than +// - 'T≥': Greater than or equal to +// - 'T<': Less than +// - 'T≤': Less than or equal to +// - This matcher triggers an ad based on when an event occurred or will +// occur, using a timestamp (Unix or Windows epoch) stored at "prefPath". +// For instance, the example below will serve an ad only if the timestamp +// for "foo.bar" occurred more than 3 days ago: +// +// "conditionMatchers": [ +// { +// "condition": "[T>]:3", +// "prefPath": "foo.bar" +// } +// ] +// +// 2. [numerical operator]:number Matcher: +// - Support operators: +// - 'R=': Equal +// - 'R≠': Not equal +// - 'R>': Greater than +// - 'R≥': Greater than or equal to +// - 'R<': Less than +// - 'R≤': Less than or equal to +// - This matcher triggers an ad based on when a real number (integers or +// fractional) stored at "prefPath". For instance, the example below will +// serve an ad only if the value stored at "foo.bar" is not equal to 3: +// +// "conditionMatchers": [ +// { +// "condition": "[R≠]:3", +// "prefPath": "foo.bar" +// } +// ] +// +// 3. Regex Matcher: +// - Uses an RE2 regular expression to partially match values at "prefPath", +// see https://github.com/google/re2/wiki/syntax. For example, the +// following will serve an ad if the value at "foo.bar" starts with "abc": +// +// "conditionMatchers": [ +// { +// "condition": "^abc", +// "prefPath": "foo.bar" +// } +// ] +// +// 4. Pattern Matcher: +// - Supports wildcards "*" and "?". "*" matches zero or more characters, +// while "?" matches zero or one character. To use these literally, escape +// them with "\". In the example below, an ad will be served only if the +// value at "foo.bar" matches the pattern "*baz?qux*": +// +// "conditionMatchers": [ +// { +// "condition": "*baz?qux*", +// "prefPath": "foo.bar" +// } +// ] +// +// For example, the following condition matchers would only serve a new tab +// takeover ad if the default search provider is set to "Startpage", the user +// has less than 10 bookmarks, and the browser was installed between three and +// seven days ago: +// +// "conditionMatchers": [ +// { +// "condition": "*-538868000510", +// "prefPath": "default_search_provider.guid" +// }, +// { +// "condition": "[R<]:10", +// "prefPath": "p3a.logs_constellation_prep|Brave.Core.BookmarkCount|value" +// }, +// { +// "condition": "[T≥]:3", +// "prefPath": "uninstall_metrics.installation_date2" +// }, +// { +// "condition": "[T≤]:7", +// "prefPath": "uninstall_metrics.installation_date2" +// } +// ] +// +// We support virtual prefs for values that are not stored in the profile or +// local state prefs. Virtual pref path keys should be prefixed with +// "[virtual]:". +// +// "[virtual]:browser|version" retrieves the browser version, e.g. "72.0.59.3". +// +// "[virtual]:browser|build_channel" retrieves the build channel of the browser, +// returning one of the following values: "stable", "beta", "dev", "nightly", or +// "unknown". +// +// "[virtual]:operating_system|locale|language" retrieves the operating system's +// language, e.g., "en", and "[virtual]:operating_system|locale|region" +// retrieves the operating system's region, e.g., "US". +// +// "[virtual]:operating_system|name" retrieves the operating system, returning +// one of the following values: "Windows", "Mac OS X", "Linux", "Android", +// "iOS", or "Unknown". +// +// "[virtual]:search_engine|default_name" retrieves the default search engine +// chosen during browser installation, returning one of the following values: +// "Brave", "Google", "Yandex", "Bing", "Daum", "네이버", "DuckDuckGo", "Qwant", +// "Startpage", or "Ecosia". For the most up-to-date list of possible default +// search engines, see `TemplateURLPrepopulateData::GetDefaultSearchEngine`. +// +// "[virtual]:skus|environment|location|key" retrieves the value from either the +// production or staging environment, the "talk.brave.com", "vpn.brave.com", or +// "leo.brave.com" location, and the "created_at", "expires_at", "last_paid_at", +// or "status" key. Status returns one of the following values: `trial`, `beta`, +// `paid`, or `canceled`. For example, the following will serve an ad if the +// user has canceled their Brave VPN subscription: +// +// "conditionMatchers": [ +// { +// "condition": "canceled", +// "prefPath": "[virtual]:skus|production|vpn.brave.com|status" +// } +// ] +// +// NOTE: To identify condition matchers, first create a copy of your pref files. +// Next, change a brave://setting or enable a feature, quit the browser and then +// compare the original and modified versions to determine which key/value pairs +// are required. Invalid or malformed condition matchers will be logged to the +// console, they are not logged to the Rewards log. + +bool MatchConditions(const PrefProviderInterface* pref_provider, + const ConditionMatcherMap& condition_matchers); + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_TARGETING_CONDITION_MATCHER_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/public/targeting/geographical/subdivision/supported_subdivisions.h b/components/brave_ads/core/public/targeting/geographical/subdivision/supported_subdivisions.h index 6bc3560a9b73..78edf58df07b 100644 --- a/components/brave_ads/core/public/targeting/geographical/subdivision/supported_subdivisions.h +++ b/components/brave_ads/core/public/targeting/geographical/subdivision/supported_subdivisions.h @@ -16,11 +16,11 @@ namespace brave_ads { -using SupportedSubdivisions = base::flat_map; +using SubdivisionMap = base::flat_map; using SupportedSubdivisionMap = - base::flat_map; + base::flat_map; ADS_EXPORT const SupportedSubdivisionMap& GetSupportedSubdivisions(); diff --git a/components/brave_ads/core/test/BUILD.gn b/components/brave_ads/core/test/BUILD.gn index 76ba2bbe7bda..271ec942ca1c 100644 --- a/components/brave_ads/core/test/BUILD.gn +++ b/components/brave_ads/core/test/BUILD.gn @@ -289,8 +289,6 @@ source_set("brave_ads_unit_tests") { "//brave/components/brave_ads/core/internal/common/test/internal/command_line_switch_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/current_test_util_internal.cc", "//brave/components/brave_ads/core/internal/common/test/internal/current_test_util_internal.h", - "//brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.cc", - "//brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_registry_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_storage_test_util_internal.cc", "//brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_storage_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/local_state_pref_value_test_util_internal.cc", @@ -299,8 +297,6 @@ source_set("brave_ads_unit_tests") { "//brave/components/brave_ads/core/internal/common/test/internal/mock_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/pref_value_test_info.cc", "//brave/components/brave_ads/core/internal/common/test/internal/pref_value_test_info.h", - "//brave/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.cc", - "//brave/components/brave_ads/core/internal/common/test/internal/profile_pref_registry_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/profile_pref_storage_test_util_internal.cc", "//brave/components/brave_ads/core/internal/common/test/internal/profile_pref_storage_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/profile_pref_value_test_util_internal.cc", @@ -311,12 +307,16 @@ source_set("brave_ads_unit_tests") { "//brave/components/brave_ads/core/internal/common/test/internal/tag_parser_test_util_internal.h", "//brave/components/brave_ads/core/internal/common/test/internal/url_response_test_util_internal.cc", "//brave/components/brave_ads/core/internal/common/test/internal/url_response_test_util_internal.h", + "//brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.cc", + "//brave/components/brave_ads/core/internal/common/test/local_state_pref_registry_test_util.h", "//brave/components/brave_ads/core/internal/common/test/local_state_pref_value_test_util.cc", "//brave/components/brave_ads/core/internal/common/test/local_state_pref_value_test_util.h", "//brave/components/brave_ads/core/internal/common/test/mock_test_util.cc", "//brave/components/brave_ads/core/internal/common/test/mock_test_util.h", "//brave/components/brave_ads/core/internal/common/test/pref_registry_test_util.cc", "//brave/components/brave_ads/core/internal/common/test/pref_registry_test_util.h", + "//brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.cc", + "//brave/components/brave_ads/core/internal/common/test/profile_pref_registry_test_util.h", "//brave/components/brave_ads/core/internal/common/test/profile_pref_value_test_util.cc", "//brave/components/brave_ads/core/internal/common/test/profile_pref_value_test_util.h", "//brave/components/brave_ads/core/internal/common/test/test_base.cc", @@ -463,8 +463,6 @@ source_set("brave_ads_unit_tests") { "//brave/components/brave_ads/core/internal/serving/inline_content_ad_serving_delegate_mock.h", "//brave/components/brave_ads/core/internal/serving/inline_content_ad_serving_feature_unittest.cc", "//brave/components/brave_ads/core/internal/serving/inline_content_ad_serving_unittest.cc", - "//brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc", - "//brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_unittest.cc", "//brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_delegate_mock.cc", "//brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_delegate_mock.h", "//brave/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_feature_unittest.cc", @@ -526,6 +524,15 @@ source_set("brave_ads_unit_tests") { "//brave/components/brave_ads/core/internal/serving/prediction/model_based/weight/creative_notification_ad_model_based_predictor_weights_builder_unittest.cc", "//brave/components/brave_ads/core/internal/serving/prediction/model_based/weight/segment/creative_ad_model_based_predictor_segment_weight_test_util.cc", "//brave/components/brave_ads/core/internal/serving/prediction/model_based/weight/segment/creative_ad_model_based_predictor_segment_weight_test_util.h", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc", + "//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc", "//brave/components/brave_ads/core/internal/serving/targeting/segments/top_segments_unittest.cc", "//brave/components/brave_ads/core/internal/serving/targeting/segments/top_user_model_segments_unittest.cc", "//brave/components/brave_ads/core/internal/serving/targeting/segments/top_user_model_segments_util_unittest.cc", diff --git a/components/ntp_background_images/browser/ntp_sponsored_images_data.h b/components/ntp_background_images/browser/ntp_sponsored_images_data.h index ba0d091f3410..22b659e8f815 100644 --- a/components/ntp_background_images/browser/ntp_sponsored_images_data.h +++ b/components/ntp_background_images/browser/ntp_sponsored_images_data.h @@ -13,7 +13,7 @@ #include "base/files/file_path.h" #include "base/values.h" -#include "brave/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" @@ -58,7 +58,7 @@ struct Logo { struct SponsoredBackground { base::FilePath image_file; gfx::Point focal_point; - brave_ads::NewTabPageAdConditionMatchers condition_matchers; + brave_ads::ConditionMatcherMap condition_matchers; std::string background_color; std::string creative_instance_id; diff --git a/components/ntp_background_images/browser/view_counter_service.cc b/components/ntp_background_images/browser/view_counter_service.cc index e03e61f9c933..bffbb5b7930e 100644 --- a/components/ntp_background_images/browser/view_counter_service.cc +++ b/components/ntp_background_images/browser/view_counter_service.cc @@ -239,14 +239,14 @@ ViewCounterService::GetCurrentBrandedWallpaper() { return GetNextBrandedWallpaperWhichMatchesConditions(); } -std::optional +std::optional ViewCounterService::GetConditionMatchers(const base::Value::Dict& dict) { const auto* const list = dict.FindList(kWallpaperConditionMatchersKey); if (!list || list->empty()) { return std::nullopt; } - brave_ads::NewTabPageAdConditionMatchers condition_matchers; + brave_ads::ConditionMatcherMap condition_matchers; for (const auto& value : *list) { const auto& condition_matcher = value.GetDict(); @@ -292,8 +292,8 @@ ViewCounterService::GetNextBrandedWallpaperWhichMatchesConditions() { return std::nullopt; } - const std::optional - condition_matchers = GetConditionMatchers(*branded_wallpaper); + const std::optional condition_matchers = + GetConditionMatchers(*branded_wallpaper); if (!condition_matchers) { // No condition matchers, so we can return the branded wallpaper. return branded_wallpaper; diff --git a/components/ntp_background_images/browser/view_counter_service.h b/components/ntp_background_images/browser/view_counter_service.h index 089a72bbe195..76dce975a544 100644 --- a/components/ntp_background_images/browser/view_counter_service.h +++ b/components/ntp_background_images/browser/view_counter_service.h @@ -16,7 +16,7 @@ #include "base/scoped_observation.h" #include "base/timer/wall_clock_timer.h" #include "base/values.h" -#include "brave/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h" +#include "brave/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h" #include "brave/components/ntp_background_images/browser/ntp_background_images_service.h" #include "brave/components/ntp_background_images/browser/view_counter_model.h" #include "brave/components/ntp_background_images/buildflags/buildflags.h" @@ -84,7 +84,7 @@ class ViewCounterService : public KeyedService, std::optional GetCurrentWallpaperForDisplay(); std::optional GetCurrentWallpaper() const; std::optional GetCurrentBrandedWallpaper(); - std::optional GetConditionMatchers( + std::optional GetConditionMatchers( const base::Value::Dict& dict); std::optional GetNextBrandedWallpaperWhichMatchesConditions();